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.
5 #include <X11/keysym.h>
9 #if defined(RootWindow)
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"
32 using ui_controls::DOWN;
33 using ui_controls::LEFT;
34 using ui_controls::MIDDLE;
35 using ui_controls::MouseButton;
36 using ui_controls::RIGHT;
37 using ui_controls::UIControlsAura;
38 using ui_controls::UP;
40 // Mask of the buttons currently down.
41 unsigned button_down_mask = 0;
43 // Returns atom that indidates that the XEvent is marker event.
44 Atom MarkerEventAtom() {
45 return XInternAtom(gfx::GetXDisplay(), "marker_event", False);
48 // Returns true when the event is a marker event.
49 bool Matcher(const base::NativeEvent& event) {
50 return event->xany.type == ClientMessage &&
51 event->xclient.message_type == MarkerEventAtom();
54 class UIControlsDesktopX11 : public UIControlsAura {
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
63 CopyFromParent, // depth
65 CopyFromParent, // visual
68 XStoreName(x_display_, x_window_, "Chromium UIControlsDesktopX11 Window");
71 virtual ~UIControlsDesktopX11() {
72 XDestroyWindow(x_display_, x_window_);
75 virtual bool SendKeyPress(gfx::NativeWindow window,
80 bool command) OVERRIDE {
81 DCHECK(!command); // No command key on Aura
82 return SendKeyPressNotifyWhenDone(
83 window, key, control, shift, alt, command, base::Closure());
86 virtual bool SendKeyPressNotifyWhenDone(
87 gfx::NativeWindow window,
93 const base::Closure& closure) OVERRIDE {
94 DCHECK(!command); // No command key on Aura
96 aura::WindowTreeHost* host = window->GetHost();
99 xevent.xkey.type = KeyPress;
101 SetKeycodeAndSendThenMask(host, &xevent, XK_Control_L, ControlMask);
104 SetKeycodeAndSendThenMask(host, &xevent, XK_Shift_L, ShiftMask);
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);
112 // Send key release events.
113 xevent.xkey.type = KeyRelease;
114 host->PostNativeEvent(&xevent);
116 UnmaskAndSetKeycodeThenSend(host, &xevent, Mod1Mask, XK_Alt_L);
118 UnmaskAndSetKeycodeThenSend(host, &xevent, ShiftMask, XK_Shift_L);
120 UnmaskAndSetKeycodeThenSend(host, &xevent, ControlMask, XK_Control_L);
122 DCHECK(!xevent.xkey.state);
123 RunClosureAfterAllPendingUIEvents(closure);
127 virtual bool SendMouseMove(long screen_x, long screen_y) OVERRIDE {
128 return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::Closure());
130 virtual bool SendMouseMoveNotifyWhenDone(
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);
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,
145 aura::WindowTreeHost* host = root_window->GetHost();
146 gfx::Point root_current_location =
147 aura::test::QueryLatestMousePositionRequestInHost(host);
148 host->ConvertPointFromHost(&root_current_location);
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);
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);
165 RunClosureAfterAllPendingUIEvents(closure);
168 virtual bool SendMouseEvents(MouseButton type, int state) OVERRIDE {
169 return SendMouseEventsNotifyWhenDone(type, state, base::Closure());
171 virtual bool SendMouseEventsNotifyWhenDone(
174 const base::Closure& closure) OVERRIDE {
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;
188 xbutton->button = Button1;
189 xbutton->state = Button1Mask;
192 xbutton->button = Button2;
193 xbutton->state = Button2Mask;
196 xbutton->button = Button3;
197 xbutton->state = Button3Mask;
200 // RootWindow will take care of other necessary fields.
202 xevent.xbutton.type = ButtonPress;
203 root_window->GetHost()->PostNativeEvent(&xevent);
204 button_down_mask |= xbutton->state;
207 xevent.xbutton.type = ButtonRelease;
208 root_window->GetHost()->PostNativeEvent(&xevent);
209 button_down_mask = (button_down_mask | xbutton->state) ^ xbutton->state;
211 RunClosureAfterAllPendingUIEvents(closure);
214 virtual bool SendMouseClick(MouseButton type) OVERRIDE {
215 return SendMouseEvents(type, UP | DOWN);
217 virtual void RunClosureAfterAllPendingUIEvents(
218 const base::Closure& closure) OVERRIDE {
219 if (closure.is_null())
221 static XEvent* marker_event = NULL;
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;
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));
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();
249 NOTREACHED() << "Coulding find RW for " << point.ToString() << " among "
250 << windows.size() << " RWs.";
254 void SetKeycodeAndSendThenMask(aura::WindowTreeHost* host,
258 xevent->xkey.keycode = XKeysymToKeycode(x_display_, keysym);
259 host->PostNativeEvent(xevent);
260 xevent->xkey.state |= mask;
263 void UnmaskAndSetKeycodeThenSend(aura::WindowTreeHost* host,
267 xevent->xkey.state ^= mask;
268 xevent->xkey.keycode = XKeysymToKeycode(x_display_, keysym);
269 host->PostNativeEvent(xevent);
274 ::Window x_root_window_;
276 // Input-only window used for events.
279 DISALLOW_COPY_AND_ASSIGN(UIControlsDesktopX11);
284 UIControlsAura* CreateUIControlsDesktopAura() {
285 // The constructor of UIControlsDesktopX11 needs X11 connection to be
287 gfx::InitializeThreadedX11();
288 return new UIControlsDesktopX11();