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