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#include "base/bind.h"
9#include "base/logging.h"
10#include "ui/aura/client/screen_position_client.h"
11#include "ui/aura/env.h"
12#include "ui/aura/test/aura_test_utils.h"
13#include "ui/aura/test/ui_controls_factory_aura.h"
14#include "ui/aura/window.h"
15#include "ui/aura/window_tree_host.h"
16#include "ui/base/test/ui_controls_aura.h"
17#include "ui/base/x/x11_util.h"
18#include "ui/compositor/dip_util.h"
19#include "ui/events/keycodes/keyboard_code_conversion_x.h"
20#include "ui/events/test/platform_event_waiter.h"
21
22namespace aura {
23namespace test {
24namespace {
25
26using ui_controls::DOWN;
27using ui_controls::LEFT;
28using ui_controls::MIDDLE;
29using ui_controls::MouseButton;
30using ui_controls::RIGHT;
31using ui_controls::UIControlsAura;
32using ui_controls::UP;
33
34// Mask of the buttons currently down.
35unsigned button_down_mask = 0;
36
37// Returns atom that indidates that the XEvent is marker event.
38Atom MarkerEventAtom() {
39  return XInternAtom(gfx::GetXDisplay(), "marker_event", False);
40}
41
42// Returns true when the event is a marker event.
43bool Matcher(const base::NativeEvent& event) {
44  return event->xany.type == ClientMessage &&
45      event->xclient.message_type == MarkerEventAtom();
46}
47
48class UIControlsX11 : public UIControlsAura {
49 public:
50  UIControlsX11(WindowTreeHost* host) : host_(host) {
51  }
52
53  virtual bool SendKeyPress(gfx::NativeWindow window,
54                            ui::KeyboardCode key,
55                            bool control,
56                            bool shift,
57                            bool alt,
58                            bool command) OVERRIDE {
59    return SendKeyPressNotifyWhenDone(
60        window, key, control, shift, alt, command, base::Closure());
61  }
62  virtual bool SendKeyPressNotifyWhenDone(
63      gfx::NativeWindow window,
64      ui::KeyboardCode key,
65      bool control,
66      bool shift,
67      bool alt,
68      bool command,
69      const base::Closure& closure) OVERRIDE {
70    XEvent xevent = {0};
71    xevent.xkey.type = KeyPress;
72    if (control)
73      SetKeycodeAndSendThenMask(&xevent, XK_Control_L, ControlMask);
74    if (shift)
75      SetKeycodeAndSendThenMask(&xevent, XK_Shift_L, ShiftMask);
76    if (alt)
77      SetKeycodeAndSendThenMask(&xevent, XK_Alt_L, Mod1Mask);
78    if (command)
79      SetKeycodeAndSendThenMask(&xevent, XK_Meta_L, Mod4Mask);
80    xevent.xkey.keycode =
81        XKeysymToKeycode(gfx::GetXDisplay(),
82                         ui::XKeysymForWindowsKeyCode(key, shift));
83    host_->PostNativeEvent(&xevent);
84
85    // Send key release events.
86    xevent.xkey.type = KeyRelease;
87    host_->PostNativeEvent(&xevent);
88    if (alt)
89      UnmaskAndSetKeycodeThenSend(&xevent, Mod1Mask, XK_Alt_L);
90    if (shift)
91      UnmaskAndSetKeycodeThenSend(&xevent, ShiftMask, XK_Shift_L);
92    if (control)
93      UnmaskAndSetKeycodeThenSend(&xevent, ControlMask, XK_Control_L);
94    if (command)
95      UnmaskAndSetKeycodeThenSend(&xevent, Mod4Mask, XK_Meta_L);
96    DCHECK(!xevent.xkey.state);
97    RunClosureAfterAllPendingUIEvents(closure);
98    return true;
99  }
100
101  virtual bool SendMouseMove(long screen_x, long screen_y) OVERRIDE {
102    return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::Closure());
103  }
104  virtual bool SendMouseMoveNotifyWhenDone(
105      long screen_x,
106      long screen_y,
107      const base::Closure& closure) OVERRIDE {
108    gfx::Point root_location(screen_x, screen_y);
109    aura::client::ScreenPositionClient* screen_position_client =
110        aura::client::GetScreenPositionClient(host_->window());
111    if (screen_position_client) {
112      screen_position_client->ConvertPointFromScreen(host_->window(),
113                                                     &root_location);
114    }
115    gfx::Point root_current_location =
116        QueryLatestMousePositionRequestInHost(host_);
117    host_->ConvertPointFromHost(&root_current_location);
118
119    if (root_location != root_current_location && button_down_mask == 0) {
120      // Move the cursor because EnterNotify/LeaveNotify are generated with the
121      // current mouse position as a result of XGrabPointer()
122      host_->window()->MoveCursorTo(root_location);
123    } else {
124      XEvent xevent = {0};
125      XMotionEvent* xmotion = &xevent.xmotion;
126      xmotion->type = MotionNotify;
127      xmotion->x = root_location.x();
128      xmotion->y = root_location.y();
129      xmotion->state = button_down_mask;
130      xmotion->same_screen = True;
131      // WindowTreeHost will take care of other necessary fields.
132      host_->PostNativeEvent(&xevent);
133    }
134    RunClosureAfterAllPendingUIEvents(closure);
135    return true;
136  }
137  virtual bool SendMouseEvents(MouseButton type, int state) OVERRIDE {
138    return SendMouseEventsNotifyWhenDone(type, state, base::Closure());
139  }
140  virtual bool SendMouseEventsNotifyWhenDone(
141      MouseButton type,
142      int state,
143      const base::Closure& closure) OVERRIDE {
144    XEvent xevent = {0};
145    XButtonEvent* xbutton = &xevent.xbutton;
146    gfx::Point mouse_loc = aura::Env::GetInstance()->last_mouse_location();
147    aura::client::ScreenPositionClient* screen_position_client =
148          aura::client::GetScreenPositionClient(host_->window());
149    if (screen_position_client) {
150      screen_position_client->ConvertPointFromScreen(host_->window(),
151                                                     &mouse_loc);
152    }
153    xbutton->x = mouse_loc.x();
154    xbutton->y = mouse_loc.y();
155    xbutton->same_screen = True;
156    switch (type) {
157      case LEFT:
158        xbutton->button = Button1;
159        xbutton->state = Button1Mask;
160        break;
161      case MIDDLE:
162        xbutton->button = Button2;
163        xbutton->state = Button2Mask;
164        break;
165      case RIGHT:
166        xbutton->button = Button3;
167        xbutton->state = Button3Mask;
168        break;
169    }
170    // WindowEventDispatcher will take care of other necessary fields.
171    if (state & DOWN) {
172      xevent.xbutton.type = ButtonPress;
173      host_->PostNativeEvent(&xevent);
174      button_down_mask |= xbutton->state;
175    }
176    if (state & UP) {
177      xevent.xbutton.type = ButtonRelease;
178      host_->PostNativeEvent(&xevent);
179      button_down_mask = (button_down_mask | xbutton->state) ^ xbutton->state;
180    }
181    RunClosureAfterAllPendingUIEvents(closure);
182    return true;
183  }
184  virtual bool SendMouseClick(MouseButton type) OVERRIDE {
185    return SendMouseEvents(type, UP | DOWN);
186  }
187  virtual void RunClosureAfterAllPendingUIEvents(
188      const base::Closure& closure) OVERRIDE {
189    if (closure.is_null())
190      return;
191    static XEvent* marker_event = NULL;
192    if (!marker_event) {
193      marker_event = new XEvent();
194      marker_event->xclient.type = ClientMessage;
195      marker_event->xclient.display = NULL;
196      marker_event->xclient.window = None;
197      marker_event->xclient.format = 8;
198    }
199    marker_event->xclient.message_type = MarkerEventAtom();
200    host_->PostNativeEvent(marker_event);
201    ui::PlatformEventWaiter::Create(closure, base::Bind(&Matcher));
202  }
203 private:
204  void SetKeycodeAndSendThenMask(XEvent* xevent,
205                                 KeySym keysym,
206                                 unsigned int mask) {
207    xevent->xkey.keycode =
208        XKeysymToKeycode(gfx::GetXDisplay(), keysym);
209    host_->PostNativeEvent(xevent);
210    xevent->xkey.state |= mask;
211  }
212
213  void UnmaskAndSetKeycodeThenSend(XEvent* xevent,
214                                   unsigned int mask,
215                                   KeySym keysym) {
216    xevent->xkey.state ^= mask;
217    xevent->xkey.keycode =
218        XKeysymToKeycode(gfx::GetXDisplay(), keysym);
219    host_->PostNativeEvent(xevent);
220  }
221
222  WindowTreeHost* host_;
223
224  DISALLOW_COPY_AND_ASSIGN(UIControlsX11);
225};
226
227}  // namespace
228
229UIControlsAura* CreateUIControlsAura(WindowTreeHost* host) {
230  return new UIControlsX11(host);
231}
232
233}  // namespace test
234}  // namespace aura
235