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 ~UIControlsDesktopX11() override
{ XDestroyWindow(x_display_
, x_window_
); }
73 bool SendKeyPress(gfx::NativeWindow window
,
78 bool command
) override
{
79 DCHECK(!command
); // No command key on Aura
80 return SendKeyPressNotifyWhenDone(
81 window
, key
, control
, shift
, alt
, command
, base::Closure());
84 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window
,
90 const base::Closure
& closure
) override
{
91 DCHECK(!command
); // No command key on Aura
93 aura::WindowTreeHost
* host
= window
->GetHost();
96 xevent
.xkey
.type
= KeyPress
;
98 SetKeycodeAndSendThenMask(host
, &xevent
, XK_Control_L
, ControlMask
);
101 SetKeycodeAndSendThenMask(host
, &xevent
, XK_Shift_L
, ShiftMask
);
103 SetKeycodeAndSendThenMask(host
, &xevent
, XK_Alt_L
, Mod1Mask
);
104 xevent
.xkey
.keycode
=
105 XKeysymToKeycode(x_display_
,
106 ui::XKeysymForWindowsKeyCode(key
, shift
));
107 host
->PostNativeEvent(&xevent
);
109 // Send key release events.
110 xevent
.xkey
.type
= KeyRelease
;
111 host
->PostNativeEvent(&xevent
);
113 UnmaskAndSetKeycodeThenSend(host
, &xevent
, Mod1Mask
, XK_Alt_L
);
115 UnmaskAndSetKeycodeThenSend(host
, &xevent
, ShiftMask
, XK_Shift_L
);
117 UnmaskAndSetKeycodeThenSend(host
, &xevent
, ControlMask
, XK_Control_L
);
119 DCHECK(!xevent
.xkey
.state
);
120 RunClosureAfterAllPendingUIEvents(closure
);
124 bool SendMouseMove(long screen_x
, long screen_y
) override
{
125 return SendMouseMoveNotifyWhenDone(screen_x
, screen_y
, base::Closure());
127 bool SendMouseMoveNotifyWhenDone(long screen_x
,
129 const base::Closure
& closure
) override
{
130 gfx::Point
screen_location(screen_x
, screen_y
);
131 gfx::Point root_location
= screen_location
;
132 aura::Window
* root_window
= RootWindowForPoint(screen_location
);
134 aura::client::ScreenPositionClient
* screen_position_client
=
135 aura::client::GetScreenPositionClient(root_window
);
136 if (screen_position_client
) {
137 screen_position_client
->ConvertPointFromScreen(root_window
,
141 aura::WindowTreeHost
* host
= root_window
->GetHost();
142 gfx::Point root_current_location
=
143 aura::test::QueryLatestMousePositionRequestInHost(host
);
144 host
->ConvertPointFromHost(&root_current_location
);
146 if (root_location
!= root_current_location
&& button_down_mask
== 0) {
147 // Move the cursor because EnterNotify/LeaveNotify are generated with the
148 // current mouse position as a result of XGrabPointer()
149 root_window
->MoveCursorTo(root_location
);
152 XMotionEvent
* xmotion
= &xevent
.xmotion
;
153 xmotion
->type
= MotionNotify
;
154 xmotion
->x
= root_location
.x();
155 xmotion
->y
= root_location
.y();
156 xmotion
->state
= button_down_mask
;
157 xmotion
->same_screen
= True
;
158 // RootWindow will take care of other necessary fields.
159 host
->PostNativeEvent(&xevent
);
161 RunClosureAfterAllPendingUIEvents(closure
);
164 bool SendMouseEvents(MouseButton type
, int state
) override
{
165 return SendMouseEventsNotifyWhenDone(type
, state
, base::Closure());
167 bool SendMouseEventsNotifyWhenDone(MouseButton type
,
169 const base::Closure
& closure
) override
{
171 XButtonEvent
* xbutton
= &xevent
.xbutton
;
172 gfx::Point mouse_loc
= aura::Env::GetInstance()->last_mouse_location();
173 aura::Window
* root_window
= RootWindowForPoint(mouse_loc
);
174 aura::client::ScreenPositionClient
* screen_position_client
=
175 aura::client::GetScreenPositionClient(root_window
);
176 if (screen_position_client
)
177 screen_position_client
->ConvertPointFromScreen(root_window
, &mouse_loc
);
178 xbutton
->x
= mouse_loc
.x();
179 xbutton
->y
= mouse_loc
.y();
180 xbutton
->same_screen
= True
;
183 xbutton
->button
= Button1
;
184 xbutton
->state
= Button1Mask
;
187 xbutton
->button
= Button2
;
188 xbutton
->state
= Button2Mask
;
191 xbutton
->button
= Button3
;
192 xbutton
->state
= Button3Mask
;
195 // RootWindow will take care of other necessary fields.
197 xevent
.xbutton
.type
= ButtonPress
;
198 root_window
->GetHost()->PostNativeEvent(&xevent
);
199 button_down_mask
|= xbutton
->state
;
202 xevent
.xbutton
.type
= ButtonRelease
;
203 root_window
->GetHost()->PostNativeEvent(&xevent
);
204 button_down_mask
= (button_down_mask
| xbutton
->state
) ^ xbutton
->state
;
206 RunClosureAfterAllPendingUIEvents(closure
);
209 bool SendMouseClick(MouseButton type
) override
{
210 return SendMouseEvents(type
, UP
| DOWN
);
212 void RunClosureAfterAllPendingUIEvents(
213 const base::Closure
& closure
) override
{
214 if (closure
.is_null())
216 static XEvent
* marker_event
= NULL
;
218 marker_event
= new XEvent();
219 marker_event
->xclient
.type
= ClientMessage
;
220 marker_event
->xclient
.display
= x_display_
;
221 marker_event
->xclient
.window
= x_window_
;
222 marker_event
->xclient
.format
= 8;
224 marker_event
->xclient
.message_type
= MarkerEventAtom();
225 XSendEvent(x_display_
, x_window_
, False
, 0, marker_event
);
226 ui::PlatformEventWaiter::Create(closure
, base::Bind(&Matcher
));
229 aura::Window
* RootWindowForPoint(const gfx::Point
& point
) {
230 // Most interactive_ui_tests run inside of the aura_test_helper
231 // environment. This means that we can't rely on gfx::Screen and several
232 // other things to work properly. Therefore we hack around this by
233 // iterating across the windows owned DesktopWindowTreeHostX11 since this
234 // doesn't rely on having a DesktopScreenX11.
235 std::vector
<aura::Window
*> windows
=
236 DesktopWindowTreeHostX11::GetAllOpenWindows();
237 for (std::vector
<aura::Window
*>::const_iterator it
= windows
.begin();
238 it
!= windows
.end(); ++it
) {
239 if ((*it
)->GetBoundsInScreen().Contains(point
)) {
240 return (*it
)->GetRootWindow();
244 NOTREACHED() << "Coulding find RW for " << point
.ToString() << " among "
245 << windows
.size() << " RWs.";
249 void SetKeycodeAndSendThenMask(aura::WindowTreeHost
* host
,
253 xevent
->xkey
.keycode
= XKeysymToKeycode(x_display_
, keysym
);
254 host
->PostNativeEvent(xevent
);
255 xevent
->xkey
.state
|= mask
;
258 void UnmaskAndSetKeycodeThenSend(aura::WindowTreeHost
* host
,
262 xevent
->xkey
.state
^= mask
;
263 xevent
->xkey
.keycode
= XKeysymToKeycode(x_display_
, keysym
);
264 host
->PostNativeEvent(xevent
);
269 ::Window x_root_window_
;
271 // Input-only window used for events.
274 DISALLOW_COPY_AND_ASSIGN(UIControlsDesktopX11
);
279 UIControlsAura
* CreateUIControlsDesktopAura() {
280 // The constructor of UIControlsDesktopX11 needs X11 connection to be
282 gfx::InitializeThreadedX11();
283 return new UIControlsDesktopX11();