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 "ui/base/test/ui_controls.h"
7 #include <gdk/gdkkeysyms.h>
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "ui/base/gtk/event_synthesis_gtk.h"
14 #include "ui/base/gtk/gtk_screen_util.h"
15 #include "ui/gfx/rect.h"
18 bool g_ui_controls_enabled
= false;
23 clock_gettime(CLOCK_MONOTONIC
, &ts
);
24 return ts
.tv_sec
* 1000 + ts
.tv_nsec
/ 1000000;
27 class EventWaiter
: public base::MessageLoopForUI::Observer
{
29 EventWaiter(const base::Closure
& task
, GdkEventType type
, int count
)
33 base::MessageLoopForUI::current()->AddObserver(this);
36 virtual ~EventWaiter() {
37 base::MessageLoopForUI::current()->RemoveObserver(this);
40 // MessageLoop::Observer implementation:
41 virtual void WillProcessEvent(GdkEvent
* event
) OVERRIDE
{
42 if ((event
->type
== type_
) && (--count_
== 0)) {
43 // At the time we're invoked the event has not actually been processed.
44 // Use PostTask to make sure the event has been processed before
46 // NOTE: if processing a message results in running a nested message
47 // loop, then DidProcessEvent isn't immediately sent. As such, we do
48 // the processing in WillProcessEvent rather than DidProcessEvent.
49 base::MessageLoop::current()->PostTask(FROM_HERE
, task_
);
54 virtual void DidProcessEvent(GdkEvent
* event
) OVERRIDE
{
61 // The number of events of this type to wait for.
65 void FakeAMouseMotionEvent(gint x
, gint y
) {
66 GdkEvent
* event
= gdk_event_new(GDK_MOTION_NOTIFY
);
68 event
->motion
.send_event
= false;
69 event
->motion
.time
= XTimeNow();
71 GtkWidget
* grab_widget
= gtk_grab_get_current();
73 // If there is a grab, we need to target all events at it regardless of
74 // what widget the mouse is over.
75 event
->motion
.window
= gtk_widget_get_window(grab_widget
);
77 event
->motion
.window
= gdk_window_at_pointer(&x
, &y
);
79 g_object_ref(event
->motion
.window
);
80 gint window_x
, window_y
;
81 gdk_window_get_origin(event
->motion
.window
, &window_x
, &window_y
);
82 event
->motion
.x
= x
- window_x
;
83 event
->motion
.y
= y
- window_y
;
84 event
->motion
.x_root
= x
;
85 event
->motion
.y_root
= y
;
87 event
->motion
.device
= gdk_device_get_core_pointer();
88 event
->type
= GDK_MOTION_NOTIFY
;
91 gdk_event_free(event
);
96 namespace ui_controls
{
98 void EnableUIControls() {
99 g_ui_controls_enabled
= true;
102 bool SendKeyPress(gfx::NativeWindow window
,
103 ui::KeyboardCode key
,
108 CHECK(g_ui_controls_enabled
);
109 DCHECK(!command
); // No command key on Linux
110 GdkWindow
* event_window
= NULL
;
111 GtkWidget
* grab_widget
= gtk_grab_get_current();
113 // If there is a grab, send all events to the grabbed widget.
114 event_window
= gtk_widget_get_window(grab_widget
);
116 event_window
= gtk_widget_get_window(GTK_WIDGET(window
));
118 // No target was specified. Send the events to the active toplevel.
119 GList
* windows
= gtk_window_list_toplevels();
120 for (GList
* element
= windows
; element
; element
= g_list_next(element
)) {
121 GtkWindow
* this_window
= GTK_WINDOW(element
->data
);
122 if (gtk_window_is_active(this_window
)) {
123 event_window
= gtk_widget_get_window(GTK_WIDGET(this_window
));
127 g_list_free(windows
);
130 NOTREACHED() << "Window not specified and none is active";
134 std::vector
<GdkEvent
*> events
;
135 ui::SynthesizeKeyPressEvents(event_window
, key
, control
, shift
, alt
, &events
);
136 for (std::vector
<GdkEvent
*>::iterator iter
= events
.begin();
137 iter
!= events
.end(); ++iter
) {
138 gdk_event_put(*iter
);
139 // gdk_event_put appends a copy of the event.
140 gdk_event_free(*iter
);
146 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window
,
147 ui::KeyboardCode key
,
152 const base::Closure
& task
) {
153 CHECK(g_ui_controls_enabled
);
154 DCHECK(!command
); // No command key on Linux
155 int release_count
= 1;
162 // This object will delete itself after running |task|.
163 new EventWaiter(task
, GDK_KEY_RELEASE
, release_count
);
164 return SendKeyPress(window
, key
, control
, shift
, alt
, command
);
167 bool SendMouseMove(long x
, long y
) {
168 CHECK(g_ui_controls_enabled
);
169 gdk_display_warp_pointer(gdk_display_get_default(), gdk_screen_get_default(),
171 // Sometimes gdk_display_warp_pointer fails to send back any indication of
172 // the move, even though it succesfully moves the server cursor. We fake it in
173 // order to get drags to work.
174 FakeAMouseMotionEvent(x
, y
);
178 bool SendMouseMoveNotifyWhenDone(long x
, long y
, const base::Closure
& task
) {
179 CHECK(g_ui_controls_enabled
);
180 bool rv
= SendMouseMove(x
, y
);
181 new EventWaiter(task
, GDK_MOTION_NOTIFY
, 1);
185 bool SendMouseEvents(MouseButton type
, int state
) {
186 CHECK(g_ui_controls_enabled
);
187 GdkEvent
* event
= gdk_event_new(GDK_BUTTON_PRESS
);
189 event
->button
.send_event
= false;
190 event
->button
.time
= XTimeNow();
193 GtkWidget
* grab_widget
= gtk_grab_get_current();
195 // If there is a grab, we need to target all events at it regardless of
196 // what widget the mouse is over.
197 event
->button
.window
= gtk_widget_get_window(grab_widget
);
198 gdk_window_get_pointer(event
->button
.window
, &x
, &y
, NULL
);
200 event
->button
.window
= gdk_window_at_pointer(&x
, &y
);
201 CHECK(event
->button
.window
);
204 g_object_ref(event
->button
.window
);
207 gint origin_x
, origin_y
;
208 gdk_window_get_origin(event
->button
.window
, &origin_x
, &origin_y
);
209 event
->button
.x_root
= x
+ origin_x
;
210 event
->button
.y_root
= y
+ origin_y
;
212 event
->button
.axes
= NULL
;
213 GdkModifierType modifier
;
214 gdk_window_get_pointer(event
->button
.window
, NULL
, NULL
, &modifier
);
215 event
->button
.state
= modifier
;
216 event
->button
.button
= type
== LEFT
? 1 : (type
== MIDDLE
? 2 : 3);
217 event
->button
.device
= gdk_device_get_core_pointer();
219 event
->button
.type
= GDK_BUTTON_PRESS
;
221 gdk_event_put(event
);
223 // Also send a release event.
224 GdkEvent
* release_event
= gdk_event_copy(event
);
225 release_event
->button
.type
= GDK_BUTTON_RELEASE
;
226 release_event
->button
.time
++;
228 gdk_event_put(release_event
);
230 gdk_event_free(event
);
231 gdk_event_free(release_event
);
236 bool SendMouseEventsNotifyWhenDone(MouseButton type
,
238 const base::Closure
& task
) {
239 CHECK(g_ui_controls_enabled
);
240 bool rv
= SendMouseEvents(type
, state
);
241 GdkEventType wait_type
;
243 wait_type
= GDK_BUTTON_RELEASE
;
246 wait_type
= GDK_BUTTON_PRESS
;
247 else if (type
== MIDDLE
)
248 wait_type
= GDK_2BUTTON_PRESS
;
250 wait_type
= GDK_3BUTTON_PRESS
;
252 new EventWaiter(task
, wait_type
, 1);
256 bool SendMouseClick(MouseButton type
) {
257 CHECK(g_ui_controls_enabled
);
258 return SendMouseEvents(type
, UP
| DOWN
);
261 } // namespace ui_controls