OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/automation/ui_controls.h" | 5 #include "chrome/browser/automation/ui_controls.h" |
6 | 6 |
7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
8 #include <gdk/gdkkeysyms.h> | 8 #include <gdk/gdkkeysyms.h> |
9 | 9 |
10 #include "base/gfx/rect.h" | 10 #include "base/gfx/rect.h" |
(...skipping 10 matching lines...) Expand all Loading... |
21 namespace { | 21 namespace { |
22 | 22 |
23 guint32 EventTimeNow() { | 23 guint32 EventTimeNow() { |
24 struct timespec ts; | 24 struct timespec ts; |
25 clock_gettime(CLOCK_MONOTONIC, &ts); | 25 clock_gettime(CLOCK_MONOTONIC, &ts); |
26 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; | 26 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; |
27 } | 27 } |
28 | 28 |
29 class EventWaiter : public MessageLoopForUI::Observer { | 29 class EventWaiter : public MessageLoopForUI::Observer { |
30 public: | 30 public: |
31 EventWaiter(Task* task, GdkEventType type) : task_(task), type_(type) { | 31 EventWaiter(Task* task, GdkEventType type, int count) |
| 32 : task_(task), |
| 33 type_(type), |
| 34 count_(count) { |
32 MessageLoopForUI::current()->AddObserver(this); | 35 MessageLoopForUI::current()->AddObserver(this); |
33 } | 36 } |
34 | 37 |
35 virtual ~EventWaiter() { | 38 virtual ~EventWaiter() { |
36 MessageLoopForUI::current()->RemoveObserver(this); | 39 MessageLoopForUI::current()->RemoveObserver(this); |
37 } | 40 } |
38 | 41 |
39 // MessageLoop::Observer implementation: | 42 // MessageLoop::Observer implementation: |
40 virtual void WillProcessEvent(GdkEvent* event) { | 43 virtual void WillProcessEvent(GdkEvent* event) { |
41 if (event->type == type_) { | 44 if ((event->type == type_) && (--count_ == 0)) { |
42 // At the time we're invoked the event has not actually been processed. | 45 // At the time we're invoked the event has not actually been processed. |
43 // Use PostTask to make sure the event has been processed before | 46 // Use PostTask to make sure the event has been processed before |
44 // notifying. | 47 // notifying. |
45 // NOTE: if processing a message results in running a nested message | 48 // NOTE: if processing a message results in running a nested message |
46 // loop, then DidProcessEvent isn't immediately sent. As such, we do | 49 // loop, then DidProcessEvent isn't immediately sent. As such, we do |
47 // the processing in WillProcessEvent rather than DidProcessEvent. | 50 // the processing in WillProcessEvent rather than DidProcessEvent. |
48 MessageLoop::current()->PostTask(FROM_HERE, task_); | 51 MessageLoop::current()->PostTask(FROM_HERE, task_); |
49 delete this; | 52 delete this; |
50 } | 53 } |
51 } | 54 } |
52 | 55 |
53 virtual void DidProcessEvent(GdkEvent* event) { | 56 virtual void DidProcessEvent(GdkEvent* event) { |
54 // No-op. | 57 // No-op. |
55 } | 58 } |
56 | 59 |
57 private: | 60 private: |
58 // We pass ownership of task_ to MessageLoop when the corrent event is | 61 // We pass ownership of task_ to MessageLoop when the corrent event is |
59 // received. | 62 // received. |
60 Task *task_; | 63 Task *task_; |
61 GdkEventType type_; | 64 GdkEventType type_; |
| 65 // The number of events of this type to wait for. |
| 66 int count_; |
62 }; | 67 }; |
63 | 68 |
64 class ClickTask : public Task { | 69 class ClickTask : public Task { |
65 public: | 70 public: |
66 ClickTask(ui_controls::MouseButton button, int state, Task* followup) | 71 ClickTask(ui_controls::MouseButton button, int state, Task* followup) |
67 : button_(button), state_(state), followup_(followup) { | 72 : button_(button), state_(state), followup_(followup) { |
68 } | 73 } |
69 | 74 |
70 virtual ~ClickTask() {} | 75 virtual ~ClickTask() {} |
71 | 76 |
72 virtual void Run() { | 77 virtual void Run() { |
73 if (followup_) | 78 if (followup_) |
74 ui_controls::SendMouseEventsNotifyWhenDone(button_, state_, followup_); | 79 ui_controls::SendMouseEventsNotifyWhenDone(button_, state_, followup_); |
75 else | 80 else |
76 ui_controls::SendMouseEvents(button_, state_); | 81 ui_controls::SendMouseEvents(button_, state_); |
77 } | 82 } |
78 | 83 |
79 private: | 84 private: |
80 ui_controls::MouseButton button_; | 85 ui_controls::MouseButton button_; |
81 int state_; | 86 int state_; |
82 Task* followup_; | 87 Task* followup_; |
83 }; | 88 }; |
84 | 89 |
85 } // namespace | |
86 | 90 |
87 namespace ui_controls { | 91 bool SendKeyEvent(GdkWindow* window, bool press, guint key, guint state) { |
| 92 GdkEvent* event = gdk_event_new(press ? GDK_KEY_PRESS : GDK_KEY_RELEASE); |
88 | 93 |
89 bool SendKeyPress(gfx::NativeWindow window, | 94 event->key.type = press ? GDK_KEY_PRESS : GDK_KEY_RELEASE; |
90 wchar_t key, bool control, bool shift, bool alt) { | 95 event->key.window = window; |
91 // TODO(estade): send a release as well? | |
92 GdkEvent* event = gdk_event_new(GDK_KEY_PRESS); | |
93 | |
94 event->key.type = GDK_KEY_PRESS; | |
95 GtkWidget* grab_widget = gtk_grab_get_current(); | |
96 if (grab_widget) { | |
97 // If there is a grab, send all events to the grabbed widget. | |
98 event->key.window = grab_widget->window; | |
99 } else if (window) { | |
100 event->key.window = GTK_WIDGET(window)->window; | |
101 } else { | |
102 // No target was specified. Send the events to the focused window. | |
103 GList* windows = gtk_window_list_toplevels(); | |
104 for (GList* element = windows; element; element = g_list_next(element)) { | |
105 GtkWindow* window = GTK_WINDOW(element->data); | |
106 if (gtk_window_is_active(window)) { | |
107 event->key.window = GTK_WIDGET(window)->window; | |
108 break; | |
109 } | |
110 } | |
111 g_list_free(windows); | |
112 } | |
113 DCHECK(event->key.window); | |
114 g_object_ref(event->key.window); | 96 g_object_ref(event->key.window); |
115 event->key.send_event = false; | 97 event->key.send_event = false; |
116 event->key.time = EventTimeNow(); | 98 event->key.time = EventTimeNow(); |
117 | 99 |
118 // TODO(estade): handle other state flags besides control, shift, alt? | 100 event->key.state = state; |
119 // For example caps lock. | |
120 event->key.state = (control ? GDK_CONTROL_MASK : 0) | | |
121 (shift ? GDK_SHIFT_MASK : 0) | | |
122 (alt ? GDK_MOD1_MASK : 0); | |
123 event->key.keyval = key; | 101 event->key.keyval = key; |
124 // TODO(estade): fill in the string? | |
125 | 102 |
126 GdkKeymapKey* keys; | 103 GdkKeymapKey* keys; |
127 gint n_keys; | 104 gint n_keys; |
128 if (!gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), | 105 if (!gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), |
129 event->key.keyval, &keys, &n_keys)) { | 106 event->key.keyval, &keys, &n_keys)) { |
130 return false; | 107 return false; |
131 } | 108 } |
132 event->key.hardware_keycode = keys[0].keycode; | 109 event->key.hardware_keycode = keys[0].keycode; |
133 event->key.group = keys[0].group; | 110 event->key.group = keys[0].group; |
134 g_free(keys); | 111 g_free(keys); |
135 | 112 |
136 gdk_event_put(event); | 113 gdk_event_put(event); |
137 // gdk_event_put appends a copy of the event. | 114 // gdk_event_put appends a copy of the event. |
138 gdk_event_free(event); | 115 gdk_event_free(event); |
139 return true; | 116 return true; |
140 } | 117 } |
141 | 118 |
| 119 } // namespace |
| 120 |
| 121 namespace ui_controls { |
| 122 |
| 123 bool SendKeyPress(gfx::NativeWindow window, |
| 124 wchar_t key, bool control, bool shift, bool alt) { |
| 125 GdkWindow* event_window = NULL; |
| 126 GtkWidget* grab_widget = gtk_grab_get_current(); |
| 127 if (grab_widget) { |
| 128 // If there is a grab, send all events to the grabbed widget. |
| 129 event_window = grab_widget->window; |
| 130 } else if (window) { |
| 131 event_window = GTK_WIDGET(window)->window; |
| 132 } else { |
| 133 // No target was specified. Send the events to the active toplevel. |
| 134 GList* windows = gtk_window_list_toplevels(); |
| 135 for (GList* element = windows; element; element = g_list_next(element)) { |
| 136 GtkWindow* this_window = GTK_WINDOW(element->data); |
| 137 if (gtk_window_is_active(this_window)) { |
| 138 event_window = GTK_WIDGET(this_window)->window; |
| 139 break; |
| 140 } |
| 141 } |
| 142 g_list_free(windows); |
| 143 } |
| 144 if (!event_window) { |
| 145 NOTREACHED() << "Window not specified and none is active"; |
| 146 return false; |
| 147 } |
| 148 |
| 149 bool rv = true; |
| 150 |
| 151 if (control) |
| 152 rv = rv && SendKeyEvent(event_window, true, GDK_Control_L, 0); |
| 153 |
| 154 if (shift) { |
| 155 rv = rv && SendKeyEvent(event_window, true, GDK_Shift_L, |
| 156 control ? GDK_CONTROL_MASK : 0); |
| 157 } |
| 158 |
| 159 if (alt) { |
| 160 guint state = (control ? GDK_CONTROL_MASK : 0) | |
| 161 (shift ? GDK_SHIFT_MASK : 0); |
| 162 rv = rv && SendKeyEvent(event_window, true, GDK_Alt_L, state); |
| 163 } |
| 164 |
| 165 // TODO(estade): handle other state flags besides control, shift, alt? |
| 166 // For example caps lock. |
| 167 guint state = (control ? GDK_CONTROL_MASK : 0) | |
| 168 (shift ? GDK_SHIFT_MASK : 0) | |
| 169 (alt ? GDK_MOD1_MASK : 0); |
| 170 rv = rv && SendKeyEvent(event_window, true, key, state); |
| 171 rv = rv && SendKeyEvent(event_window, false, key, state); |
| 172 |
| 173 if (alt) { |
| 174 guint state = (control ? GDK_CONTROL_MASK : 0) | |
| 175 (shift ? GDK_SHIFT_MASK : 0); |
| 176 rv = rv && SendKeyEvent(event_window, false, GDK_Alt_L, state); |
| 177 } |
| 178 |
| 179 if (shift) { |
| 180 rv = rv && SendKeyEvent(event_window, false, GDK_Shift_L, |
| 181 control ? GDK_CONTROL_MASK : 0); |
| 182 } |
| 183 |
| 184 if (control) |
| 185 rv = rv && SendKeyEvent(event_window, false, GDK_Control_L, 0); |
| 186 |
| 187 return rv; |
| 188 } |
| 189 |
142 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, wchar_t key, | 190 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, wchar_t key, |
143 bool control, bool shift, | 191 bool control, bool shift, |
144 bool alt, Task* task) { | 192 bool alt, Task* task) { |
| 193 int release_count = 1; |
| 194 if (control) |
| 195 release_count++; |
| 196 if (shift) |
| 197 release_count++; |
| 198 if (alt) |
| 199 release_count++; |
145 // This object will delete itself after running |task|. | 200 // This object will delete itself after running |task|. |
146 new EventWaiter(task, GDK_KEY_PRESS); | 201 new EventWaiter(task, GDK_KEY_RELEASE, release_count); |
147 return SendKeyPress(window, key, control, shift, alt); | 202 return SendKeyPress(window, key, control, shift, alt); |
148 } | 203 } |
149 | 204 |
150 bool SendMouseMove(long x, long y) { | 205 bool SendMouseMove(long x, long y) { |
151 gdk_display_warp_pointer(gdk_display_get_default(), gdk_screen_get_default(), | 206 gdk_display_warp_pointer(gdk_display_get_default(), gdk_screen_get_default(), |
152 x, y); | 207 x, y); |
153 return true; | 208 return true; |
154 } | 209 } |
155 | 210 |
156 bool SendMouseMoveNotifyWhenDone(long x, long y, Task* task) { | 211 bool SendMouseMoveNotifyWhenDone(long x, long y, Task* task) { |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
216 if (state & UP) { | 271 if (state & UP) { |
217 wait_type = GDK_BUTTON_RELEASE; | 272 wait_type = GDK_BUTTON_RELEASE; |
218 } else { | 273 } else { |
219 if (type == LEFT) | 274 if (type == LEFT) |
220 wait_type = GDK_BUTTON_PRESS; | 275 wait_type = GDK_BUTTON_PRESS; |
221 else if (type == MIDDLE) | 276 else if (type == MIDDLE) |
222 wait_type = GDK_2BUTTON_PRESS; | 277 wait_type = GDK_2BUTTON_PRESS; |
223 else | 278 else |
224 wait_type = GDK_3BUTTON_PRESS; | 279 wait_type = GDK_3BUTTON_PRESS; |
225 } | 280 } |
226 new EventWaiter(task, wait_type); | 281 new EventWaiter(task, wait_type, 1); |
227 return rv; | 282 return rv; |
228 } | 283 } |
229 | 284 |
230 bool SendMouseClick(MouseButton type) { | 285 bool SendMouseClick(MouseButton type) { |
231 return SendMouseEvents(type, UP | DOWN); | 286 return SendMouseEvents(type, UP | DOWN); |
232 } | 287 } |
233 | 288 |
234 #if defined(TOOLKIT_VIEWS) | 289 #if defined(TOOLKIT_VIEWS) |
235 void MoveMouseToCenterAndPress(views::View* view, MouseButton button, | 290 void MoveMouseToCenterAndPress(views::View* view, MouseButton button, |
236 int state, Task* task) { | 291 int state, Task* task) { |
237 gfx::Point view_center(view->width() / 2, view->height() / 2); | 292 gfx::Point view_center(view->width() / 2, view->height() / 2); |
238 views::View::ConvertPointToScreen(view, &view_center); | 293 views::View::ConvertPointToScreen(view, &view_center); |
239 SendMouseMoveNotifyWhenDone(view_center.x(), view_center.y(), | 294 SendMouseMoveNotifyWhenDone(view_center.x(), view_center.y(), |
240 new ClickTask(button, state, task)); | 295 new ClickTask(button, state, task)); |
241 } | 296 } |
242 #else | 297 #else |
243 void MoveMouseToCenterAndPress(GtkWidget* widget, | 298 void MoveMouseToCenterAndPress(GtkWidget* widget, |
244 MouseButton button, | 299 MouseButton button, |
245 int state, | 300 int state, |
246 Task* task) { | 301 Task* task) { |
247 gfx::Rect bounds = gtk_util::GetWidgetScreenBounds(widget); | 302 gfx::Rect bounds = gtk_util::GetWidgetScreenBounds(widget); |
248 SendMouseMoveNotifyWhenDone(bounds.x() + bounds.width() / 2, | 303 SendMouseMoveNotifyWhenDone(bounds.x() + bounds.width() / 2, |
249 bounds.y() + bounds.height() / 2, | 304 bounds.y() + bounds.height() / 2, |
250 new ClickTask(button, state, task)); | 305 new ClickTask(button, state, task)); |
251 } | 306 } |
252 #endif | 307 #endif |
253 | 308 |
254 } // namespace ui_controls | 309 } // namespace ui_controls |
OLD | NEW |