OLD | NEW |
| (Empty) |
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 "ui/base/test/ui_controls.h" | |
6 | |
7 #include <gdk/gdkkeysyms.h> | |
8 #include <gtk/gtk.h> | |
9 | |
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" | |
16 | |
17 namespace { | |
18 bool g_ui_controls_enabled = false; | |
19 | |
20 // static | |
21 guint32 XTimeNow() { | |
22 struct timespec ts; | |
23 clock_gettime(CLOCK_MONOTONIC, &ts); | |
24 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; | |
25 } | |
26 | |
27 class EventWaiter : public base::MessageLoopForUI::Observer { | |
28 public: | |
29 EventWaiter(const base::Closure& task, GdkEventType type, int count) | |
30 : task_(task), | |
31 type_(type), | |
32 count_(count) { | |
33 base::MessageLoopForUI::current()->AddObserver(this); | |
34 } | |
35 | |
36 virtual ~EventWaiter() { | |
37 base::MessageLoopForUI::current()->RemoveObserver(this); | |
38 } | |
39 | |
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 | |
45 // notifying. | |
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_); | |
50 delete this; | |
51 } | |
52 } | |
53 | |
54 virtual void DidProcessEvent(GdkEvent* event) OVERRIDE { | |
55 // No-op. | |
56 } | |
57 | |
58 private: | |
59 base::Closure task_; | |
60 GdkEventType type_; | |
61 // The number of events of this type to wait for. | |
62 int count_; | |
63 }; | |
64 | |
65 void FakeAMouseMotionEvent(gint x, gint y) { | |
66 GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY); | |
67 | |
68 event->motion.send_event = false; | |
69 event->motion.time = XTimeNow(); | |
70 | |
71 GtkWidget* grab_widget = gtk_grab_get_current(); | |
72 if (grab_widget) { | |
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); | |
76 } else { | |
77 event->motion.window = gdk_window_at_pointer(&x, &y); | |
78 } | |
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; | |
86 | |
87 event->motion.device = gdk_device_get_core_pointer(); | |
88 event->type = GDK_MOTION_NOTIFY; | |
89 | |
90 gdk_event_put(event); | |
91 gdk_event_free(event); | |
92 } | |
93 | |
94 } // namespace | |
95 | |
96 namespace ui_controls { | |
97 | |
98 void EnableUIControls() { | |
99 g_ui_controls_enabled = true; | |
100 } | |
101 | |
102 bool SendKeyPress(gfx::NativeWindow window, | |
103 ui::KeyboardCode key, | |
104 bool control, | |
105 bool shift, | |
106 bool alt, | |
107 bool command) { | |
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(); | |
112 if (grab_widget) { | |
113 // If there is a grab, send all events to the grabbed widget. | |
114 event_window = gtk_widget_get_window(grab_widget); | |
115 } else if (window) { | |
116 event_window = gtk_widget_get_window(GTK_WIDGET(window)); | |
117 } else { | |
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)); | |
124 break; | |
125 } | |
126 } | |
127 g_list_free(windows); | |
128 } | |
129 if (!event_window) { | |
130 NOTREACHED() << "Window not specified and none is active"; | |
131 return false; | |
132 } | |
133 | |
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); | |
141 } | |
142 | |
143 return true; | |
144 } | |
145 | |
146 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, | |
147 ui::KeyboardCode key, | |
148 bool control, | |
149 bool shift, | |
150 bool alt, | |
151 bool command, | |
152 const base::Closure& task) { | |
153 CHECK(g_ui_controls_enabled); | |
154 DCHECK(!command); // No command key on Linux | |
155 int release_count = 1; | |
156 if (control) | |
157 release_count++; | |
158 if (shift) | |
159 release_count++; | |
160 if (alt) | |
161 release_count++; | |
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); | |
165 } | |
166 | |
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(), | |
170 x, y); | |
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); | |
175 return true; | |
176 } | |
177 | |
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); | |
182 return rv; | |
183 } | |
184 | |
185 bool SendMouseEvents(MouseButton type, int state) { | |
186 CHECK(g_ui_controls_enabled); | |
187 GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS); | |
188 | |
189 event->button.send_event = false; | |
190 event->button.time = XTimeNow(); | |
191 | |
192 gint x, y; | |
193 GtkWidget* grab_widget = gtk_grab_get_current(); | |
194 if (grab_widget) { | |
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); | |
199 } else { | |
200 event->button.window = gdk_window_at_pointer(&x, &y); | |
201 CHECK(event->button.window); | |
202 } | |
203 | |
204 g_object_ref(event->button.window); | |
205 event->button.x = x; | |
206 event->button.y = y; | |
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; | |
211 | |
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(); | |
218 | |
219 event->button.type = GDK_BUTTON_PRESS; | |
220 if (state & DOWN) | |
221 gdk_event_put(event); | |
222 | |
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++; | |
227 if (state & UP) | |
228 gdk_event_put(release_event); | |
229 | |
230 gdk_event_free(event); | |
231 gdk_event_free(release_event); | |
232 | |
233 return false; | |
234 } | |
235 | |
236 bool SendMouseEventsNotifyWhenDone(MouseButton type, | |
237 int state, | |
238 const base::Closure& task) { | |
239 CHECK(g_ui_controls_enabled); | |
240 bool rv = SendMouseEvents(type, state); | |
241 GdkEventType wait_type; | |
242 if (state & UP) { | |
243 wait_type = GDK_BUTTON_RELEASE; | |
244 } else { | |
245 if (type == LEFT) | |
246 wait_type = GDK_BUTTON_PRESS; | |
247 else if (type == MIDDLE) | |
248 wait_type = GDK_2BUTTON_PRESS; | |
249 else | |
250 wait_type = GDK_3BUTTON_PRESS; | |
251 } | |
252 new EventWaiter(task, wait_type, 1); | |
253 return rv; | |
254 } | |
255 | |
256 bool SendMouseClick(MouseButton type) { | |
257 CHECK(g_ui_controls_enabled); | |
258 return SendMouseEvents(type, UP | DOWN); | |
259 } | |
260 | |
261 } // namespace ui_controls | |
OLD | NEW |