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