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/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"
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
{
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
64 CopyFromParent
, // depth
66 CopyFromParent
, // visual
69 XStoreName(x_display_
, x_window_
, "Chromium UIControlsDesktopX11 Window");
72 ~UIControlsDesktopX11() override
{ XDestroyWindow(x_display_
, x_window_
); }
74 bool SendKeyPress(gfx::NativeWindow window
,
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
,
91 const base::Closure
& closure
) override
{
92 DCHECK(!command
); // No command key on Aura
94 aura::WindowTreeHost
* host
= window
->GetHost();
97 xevent
.xkey
.type
= KeyPress
;
99 SetKeycodeAndSendThenMask(host
, &xevent
, XK_Control_L
, ControlMask
);
102 SetKeycodeAndSendThenMask(host
, &xevent
, XK_Shift_L
, ShiftMask
);
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
);
114 UnmaskAndSetKeycodeThenSend(host
, &xevent
, Mod1Mask
, XK_Alt_L
);
116 UnmaskAndSetKeycodeThenSend(host
, &xevent
, ShiftMask
, XK_Shift_L
);
118 UnmaskAndSetKeycodeThenSend(host
, &xevent
, ControlMask
, XK_Control_L
);
120 DCHECK(!xevent
.xkey
.state
);
121 RunClosureAfterAllPendingUIEvents(closure
);
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
,
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
,
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
);
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
);
165 bool SendMouseEvents(MouseButton type
, int state
) override
{
166 return SendMouseEventsNotifyWhenDone(type
, state
, base::Closure());
168 bool SendMouseEventsNotifyWhenDone(MouseButton type
,
170 const base::Closure
& closure
) override
{
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
;
184 xbutton
->button
= Button1
;
185 xbutton
->state
= Button1Mask
;
188 xbutton
->button
= Button2
;
189 xbutton
->state
= Button2Mask
;
192 xbutton
->button
= Button3
;
193 xbutton
->state
= Button3Mask
;
196 // RootWindow will take care of other necessary fields.
198 xevent
.xbutton
.type
= ButtonPress
;
199 aura::test::PostEventToWindowTreeHost(xevent
, root_window
->GetHost());
200 button_down_mask
|= xbutton
->state
;
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
);
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())
217 static XEvent
* marker_event
= NULL
;
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
));
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.";
250 void SetKeycodeAndSendThenMask(aura::WindowTreeHost
* host
,
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
,
263 xevent
->xkey
.state
^= mask
;
264 xevent
->xkey
.keycode
= XKeysymToKeycode(x_display_
, keysym
);
265 aura::test::PostEventToWindowTreeHost(*xevent
, host
);
270 ::Window x_root_window_
;
272 // Input-only window used for events.
275 DISALLOW_COPY_AND_ASSIGN(UIControlsDesktopX11
);
280 UIControlsAura
* CreateUIControlsDesktopAura() {
281 // The constructor of UIControlsDesktopX11 needs X11 connection to be
283 gfx::InitializeThreadedX11();
284 return new UIControlsDesktopX11();