1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <X11/keysym.h> 6#include <X11/Xlib.h> 7 8// X macro fail. 9#if defined(RootWindow) 10#undef RootWindow 11#endif 12 13#include "base/bind.h" 14#include "base/logging.h" 15#include "ui/aura/client/screen_position_client.h" 16#include "ui/aura/env.h" 17#include "ui/aura/test/aura_test_utils.h" 18#include "ui/aura/test/ui_controls_factory_aura.h" 19#include "ui/aura/window_event_dispatcher.h" 20#include "ui/base/test/ui_controls_aura.h" 21#include "ui/base/x/x11_util.h" 22#include "ui/compositor/dip_util.h" 23#include "ui/events/keycodes/keyboard_code_conversion_x.h" 24#include "ui/events/test/platform_event_waiter.h" 25#include "ui/gfx/x/x11_connection.h" 26#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" 27 28namespace views { 29namespace test { 30namespace { 31 32using ui_controls::DOWN; 33using ui_controls::LEFT; 34using ui_controls::MIDDLE; 35using ui_controls::MouseButton; 36using ui_controls::RIGHT; 37using ui_controls::UIControlsAura; 38using ui_controls::UP; 39 40// Mask of the buttons currently down. 41unsigned button_down_mask = 0; 42 43// Returns atom that indidates that the XEvent is marker event. 44Atom MarkerEventAtom() { 45 return XInternAtom(gfx::GetXDisplay(), "marker_event", False); 46} 47 48// Returns true when the event is a marker event. 49bool Matcher(const base::NativeEvent& event) { 50 return event->xany.type == ClientMessage && 51 event->xclient.message_type == MarkerEventAtom(); 52} 53 54class UIControlsDesktopX11 : public UIControlsAura { 55 public: 56 UIControlsDesktopX11() 57 : x_display_(gfx::GetXDisplay()), 58 x_root_window_(DefaultRootWindow(x_display_)), 59 x_window_(XCreateWindow( 60 x_display_, x_root_window_, 61 -100, -100, 10, 10, // x, y, width, height 62 0, // border width 63 CopyFromParent, // depth 64 InputOnly, 65 CopyFromParent, // visual 66 0, 67 NULL)) { 68 XStoreName(x_display_, x_window_, "Chromium UIControlsDesktopX11 Window"); 69 } 70 71 virtual ~UIControlsDesktopX11() { 72 XDestroyWindow(x_display_, x_window_); 73 } 74 75 virtual bool SendKeyPress(gfx::NativeWindow window, 76 ui::KeyboardCode key, 77 bool control, 78 bool shift, 79 bool alt, 80 bool command) OVERRIDE { 81 DCHECK(!command); // No command key on Aura 82 return SendKeyPressNotifyWhenDone( 83 window, key, control, shift, alt, command, base::Closure()); 84 } 85 86 virtual bool SendKeyPressNotifyWhenDone( 87 gfx::NativeWindow window, 88 ui::KeyboardCode key, 89 bool control, 90 bool shift, 91 bool alt, 92 bool command, 93 const base::Closure& closure) OVERRIDE { 94 DCHECK(!command); // No command key on Aura 95 96 aura::WindowTreeHost* host = window->GetHost(); 97 98 XEvent xevent = {0}; 99 xevent.xkey.type = KeyPress; 100 if (control) { 101 SetKeycodeAndSendThenMask(host, &xevent, XK_Control_L, ControlMask); 102 } 103 if (shift) 104 SetKeycodeAndSendThenMask(host, &xevent, XK_Shift_L, ShiftMask); 105 if (alt) 106 SetKeycodeAndSendThenMask(host, &xevent, XK_Alt_L, Mod1Mask); 107 xevent.xkey.keycode = 108 XKeysymToKeycode(x_display_, 109 ui::XKeysymForWindowsKeyCode(key, shift)); 110 host->PostNativeEvent(&xevent); 111 112 // Send key release events. 113 xevent.xkey.type = KeyRelease; 114 host->PostNativeEvent(&xevent); 115 if (alt) 116 UnmaskAndSetKeycodeThenSend(host, &xevent, Mod1Mask, XK_Alt_L); 117 if (shift) 118 UnmaskAndSetKeycodeThenSend(host, &xevent, ShiftMask, XK_Shift_L); 119 if (control) { 120 UnmaskAndSetKeycodeThenSend(host, &xevent, ControlMask, XK_Control_L); 121 } 122 DCHECK(!xevent.xkey.state); 123 RunClosureAfterAllPendingUIEvents(closure); 124 return true; 125 } 126 127 virtual bool SendMouseMove(long screen_x, long screen_y) OVERRIDE { 128 return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::Closure()); 129 } 130 virtual bool SendMouseMoveNotifyWhenDone( 131 long screen_x, 132 long screen_y, 133 const base::Closure& closure) OVERRIDE { 134 gfx::Point screen_location(screen_x, screen_y); 135 gfx::Point root_location = screen_location; 136 aura::Window* root_window = RootWindowForPoint(screen_location); 137 138 aura::client::ScreenPositionClient* screen_position_client = 139 aura::client::GetScreenPositionClient(root_window); 140 if (screen_position_client) { 141 screen_position_client->ConvertPointFromScreen(root_window, 142 &root_location); 143 } 144 145 aura::WindowTreeHost* host = root_window->GetHost(); 146 gfx::Point root_current_location = 147 aura::test::QueryLatestMousePositionRequestInHost(host); 148 host->ConvertPointFromHost(&root_current_location); 149 150 if (root_location != root_current_location && button_down_mask == 0) { 151 // Move the cursor because EnterNotify/LeaveNotify are generated with the 152 // current mouse position as a result of XGrabPointer() 153 root_window->MoveCursorTo(root_location); 154 } else { 155 XEvent xevent = {0}; 156 XMotionEvent* xmotion = &xevent.xmotion; 157 xmotion->type = MotionNotify; 158 xmotion->x = root_location.x(); 159 xmotion->y = root_location.y(); 160 xmotion->state = button_down_mask; 161 xmotion->same_screen = True; 162 // RootWindow will take care of other necessary fields. 163 host->PostNativeEvent(&xevent); 164 } 165 RunClosureAfterAllPendingUIEvents(closure); 166 return true; 167 } 168 virtual bool SendMouseEvents(MouseButton type, int state) OVERRIDE { 169 return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); 170 } 171 virtual bool SendMouseEventsNotifyWhenDone( 172 MouseButton type, 173 int state, 174 const base::Closure& closure) OVERRIDE { 175 XEvent xevent = {0}; 176 XButtonEvent* xbutton = &xevent.xbutton; 177 gfx::Point mouse_loc = aura::Env::GetInstance()->last_mouse_location(); 178 aura::Window* root_window = RootWindowForPoint(mouse_loc); 179 aura::client::ScreenPositionClient* screen_position_client = 180 aura::client::GetScreenPositionClient(root_window); 181 if (screen_position_client) 182 screen_position_client->ConvertPointFromScreen(root_window, &mouse_loc); 183 xbutton->x = mouse_loc.x(); 184 xbutton->y = mouse_loc.y(); 185 xbutton->same_screen = True; 186 switch (type) { 187 case LEFT: 188 xbutton->button = Button1; 189 xbutton->state = Button1Mask; 190 break; 191 case MIDDLE: 192 xbutton->button = Button2; 193 xbutton->state = Button2Mask; 194 break; 195 case RIGHT: 196 xbutton->button = Button3; 197 xbutton->state = Button3Mask; 198 break; 199 } 200 // RootWindow will take care of other necessary fields. 201 if (state & DOWN) { 202 xevent.xbutton.type = ButtonPress; 203 root_window->GetHost()->PostNativeEvent(&xevent); 204 button_down_mask |= xbutton->state; 205 } 206 if (state & UP) { 207 xevent.xbutton.type = ButtonRelease; 208 root_window->GetHost()->PostNativeEvent(&xevent); 209 button_down_mask = (button_down_mask | xbutton->state) ^ xbutton->state; 210 } 211 RunClosureAfterAllPendingUIEvents(closure); 212 return true; 213 } 214 virtual bool SendMouseClick(MouseButton type) OVERRIDE { 215 return SendMouseEvents(type, UP | DOWN); 216 } 217 virtual void RunClosureAfterAllPendingUIEvents( 218 const base::Closure& closure) OVERRIDE { 219 if (closure.is_null()) 220 return; 221 static XEvent* marker_event = NULL; 222 if (!marker_event) { 223 marker_event = new XEvent(); 224 marker_event->xclient.type = ClientMessage; 225 marker_event->xclient.display = x_display_; 226 marker_event->xclient.window = x_window_; 227 marker_event->xclient.format = 8; 228 } 229 marker_event->xclient.message_type = MarkerEventAtom(); 230 XSendEvent(x_display_, x_window_, False, 0, marker_event); 231 ui::PlatformEventWaiter::Create(closure, base::Bind(&Matcher)); 232 } 233 private: 234 aura::Window* RootWindowForPoint(const gfx::Point& point) { 235 // Most interactive_ui_tests run inside of the aura_test_helper 236 // environment. This means that we can't rely on gfx::Screen and several 237 // other things to work properly. Therefore we hack around this by 238 // iterating across the windows owned DesktopWindowTreeHostX11 since this 239 // doesn't rely on having a DesktopScreenX11. 240 std::vector<aura::Window*> windows = 241 DesktopWindowTreeHostX11::GetAllOpenWindows(); 242 for (std::vector<aura::Window*>::const_iterator it = windows.begin(); 243 it != windows.end(); ++it) { 244 if ((*it)->GetBoundsInScreen().Contains(point)) { 245 return (*it)->GetRootWindow(); 246 } 247 } 248 249 NOTREACHED() << "Coulding find RW for " << point.ToString() << " among " 250 << windows.size() << " RWs."; 251 return NULL; 252 } 253 254 void SetKeycodeAndSendThenMask(aura::WindowTreeHost* host, 255 XEvent* xevent, 256 KeySym keysym, 257 unsigned int mask) { 258 xevent->xkey.keycode = XKeysymToKeycode(x_display_, keysym); 259 host->PostNativeEvent(xevent); 260 xevent->xkey.state |= mask; 261 } 262 263 void UnmaskAndSetKeycodeThenSend(aura::WindowTreeHost* host, 264 XEvent* xevent, 265 unsigned int mask, 266 KeySym keysym) { 267 xevent->xkey.state ^= mask; 268 xevent->xkey.keycode = XKeysymToKeycode(x_display_, keysym); 269 host->PostNativeEvent(xevent); 270 } 271 272 // Our X11 state. 273 Display* x_display_; 274 ::Window x_root_window_; 275 276 // Input-only window used for events. 277 ::Window x_window_; 278 279 DISALLOW_COPY_AND_ASSIGN(UIControlsDesktopX11); 280}; 281 282} // namespace 283 284UIControlsAura* CreateUIControlsDesktopAura() { 285 // The constructor of UIControlsDesktopX11 needs X11 connection to be 286 // initialized. 287 gfx::InitializeThreadedX11(); 288 return new UIControlsDesktopX11(); 289} 290 291} // namespace test 292} // namespace views 293