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" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
13 #include "chrome/common/gtk_util.h" | 13 #include "chrome/common/gtk_util.h" |
14 #include "chrome/test/automation/automation_constants.h" | 14 #include "chrome/test/automation/automation_constants.h" |
15 | 15 |
| 16 #if defined(TOOLKIT_VIEWS) |
| 17 #include "views/view.h" |
| 18 #include "views/widget/widget.h" |
| 19 #endif |
| 20 |
16 namespace { | 21 namespace { |
17 | 22 |
18 guint32 EventTimeNow() { | 23 guint32 EventTimeNow() { |
19 struct timespec ts; | 24 struct timespec ts; |
20 clock_gettime(CLOCK_MONOTONIC, &ts); | 25 clock_gettime(CLOCK_MONOTONIC, &ts); |
21 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; | 26 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; |
22 } | 27 } |
23 | 28 |
24 class EventWaiter : public MessageLoopForUI::Observer { | 29 class EventWaiter : public MessageLoopForUI::Observer { |
25 public: | 30 public: |
26 EventWaiter(Task* task, GdkEventType type) : task_(task), type_(type) { | 31 EventWaiter(Task* task, GdkEventType type) : task_(task), type_(type) { |
27 MessageLoopForUI::current()->AddObserver(this); | 32 MessageLoopForUI::current()->AddObserver(this); |
28 } | 33 } |
29 | 34 |
30 virtual ~EventWaiter() { | 35 virtual ~EventWaiter() { |
31 MessageLoopForUI::current()->RemoveObserver(this); | 36 MessageLoopForUI::current()->RemoveObserver(this); |
32 } | 37 } |
33 | 38 |
34 // MessageLoop::Observer implementation: | 39 // MessageLoop::Observer implementation: |
35 virtual void WillProcessEvent(GdkEvent* event) { | 40 virtual void WillProcessEvent(GdkEvent* event) { |
36 // No-op. | 41 if (event->type == type_) { |
37 } | 42 // At the time we're invoked the event has not actually been processed. |
38 | 43 // Use PostTask to make sure the event has been processed before |
39 virtual void DidProcessEvent(GdkEvent* event) { | 44 // notifying. |
40 if (event->any.type == type_) { | 45 // NOTE: if processing a message results in running a nested message |
41 task_->Run(); | 46 // loop, then DidProcessEvent isn't immediately sent. As such, we do |
| 47 // the processing in WillProcessEvent rather than DidProcessEvent. |
| 48 MessageLoop::current()->PostTask(FROM_HERE, task_); |
42 delete this; | 49 delete this; |
43 } | 50 } |
44 } | 51 } |
45 | 52 |
| 53 virtual void DidProcessEvent(GdkEvent* event) { |
| 54 // No-op. |
| 55 } |
| 56 |
46 private: | 57 private: |
47 scoped_ptr<Task> task_; | 58 // We pass ownership of task_ to MessageLoop when the corrent event is |
| 59 // received. |
| 60 Task *task_; |
48 GdkEventType type_; | 61 GdkEventType type_; |
49 }; | 62 }; |
50 | 63 |
51 class ClickTask : public Task { | 64 class ClickTask : public Task { |
52 public: | 65 public: |
53 ClickTask(ui_controls::MouseButton button, int state, Task* followup) | 66 ClickTask(ui_controls::MouseButton button, int state, Task* followup) |
54 : button_(button), state_(state), followup_(followup) { | 67 : button_(button), state_(state), followup_(followup) { |
55 } | 68 } |
56 | 69 |
57 virtual ~ClickTask() {} | 70 virtual ~ClickTask() {} |
58 | 71 |
59 virtual void Run() { | 72 virtual void Run() { |
60 ui_controls::SendMouseEventsNotifyWhenDone(button_, state_, followup_); | 73 if (followup_) |
| 74 ui_controls::SendMouseEventsNotifyWhenDone(button_, state_, followup_); |
| 75 else |
| 76 ui_controls::SendMouseEvents(button_, state_); |
61 } | 77 } |
62 | 78 |
63 private: | 79 private: |
64 ui_controls::MouseButton button_; | 80 ui_controls::MouseButton button_; |
65 int state_; | 81 int state_; |
66 Task* followup_; | 82 Task* followup_; |
67 }; | 83 }; |
68 | 84 |
69 } // namespace | 85 } // namespace |
70 | 86 |
71 namespace ui_controls { | 87 namespace ui_controls { |
72 | 88 |
73 bool SendKeyPress(gfx::NativeWindow window, | 89 bool SendKeyPress(gfx::NativeWindow window, |
74 wchar_t key, bool control, bool shift, bool alt) { | 90 wchar_t key, bool control, bool shift, bool alt) { |
75 // TODO(estade): send a release as well? | 91 // TODO(estade): send a release as well? |
76 GdkEvent* event = gdk_event_new(GDK_KEY_PRESS); | 92 GdkEvent* event = gdk_event_new(GDK_KEY_PRESS); |
77 | 93 |
78 event->key.type = GDK_KEY_PRESS; | 94 event->key.type = GDK_KEY_PRESS; |
79 event->key.window = GTK_WIDGET(window)->window; | 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); |
80 g_object_ref(event->key.window); | 114 g_object_ref(event->key.window); |
81 event->key.send_event = false; | 115 event->key.send_event = false; |
82 event->key.time = EventTimeNow(); | 116 event->key.time = EventTimeNow(); |
83 | 117 |
84 // TODO(estade): handle other state flags besides control, shift, alt? | 118 // TODO(estade): handle other state flags besides control, shift, alt? |
85 // For example caps lock. | 119 // For example caps lock. |
86 event->key.state = (control ? GDK_CONTROL_MASK : 0) | | 120 event->key.state = (control ? GDK_CONTROL_MASK : 0) | |
87 (shift ? GDK_SHIFT_MASK : 0) | | 121 (shift ? GDK_SHIFT_MASK : 0) | |
88 (alt ? GDK_MOD1_MASK : 0); | 122 (alt ? GDK_MOD1_MASK : 0); |
89 event->key.keyval = key; | 123 event->key.keyval = key; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
129 return rv; | 163 return rv; |
130 } | 164 } |
131 | 165 |
132 bool SendMouseEvents(MouseButton type, int state) { | 166 bool SendMouseEvents(MouseButton type, int state) { |
133 GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS); | 167 GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS); |
134 | 168 |
135 event->button.send_event = false; | 169 event->button.send_event = false; |
136 event->button.time = EventTimeNow(); | 170 event->button.time = EventTimeNow(); |
137 | 171 |
138 gint x, y; | 172 gint x, y; |
139 event->button.window = gdk_window_at_pointer(&x, &y); | 173 GtkWidget* grab_widget = gtk_grab_get_current(); |
| 174 if (grab_widget) { |
| 175 // If there is a grab, we need to target all events at it regardless of |
| 176 // what widget the mouse is over. |
| 177 event->button.window = grab_widget->window; |
| 178 gdk_window_get_pointer(event->button.window, &x, &y, NULL); |
| 179 } else { |
| 180 event->button.window = gdk_window_at_pointer(&x, &y); |
| 181 } |
140 g_object_ref(event->button.window); | 182 g_object_ref(event->button.window); |
141 event->motion.x = x; | 183 event->motion.x = x; |
142 event->motion.y = y; | 184 event->motion.y = y; |
143 gint origin_x, origin_y; | 185 gint origin_x, origin_y; |
144 gdk_window_get_origin(event->button.window, &origin_x, &origin_y); | 186 gdk_window_get_origin(event->button.window, &origin_x, &origin_y); |
145 event->button.x_root = x + origin_x; | 187 event->button.x_root = x + origin_x; |
146 event->button.y_root = y + origin_y; | 188 event->button.y_root = y + origin_y; |
147 | 189 |
148 event->button.axes = NULL; | 190 event->button.axes = NULL; |
149 // TODO(estade): as above, we may want to pack this with the actual state. | 191 // TODO(estade): as above, we may want to pack this with the actual state. |
(...skipping 13 matching lines...) Expand all Loading... |
163 gdk_event_put(release_event); | 205 gdk_event_put(release_event); |
164 | 206 |
165 gdk_event_free(event); | 207 gdk_event_free(event); |
166 gdk_event_free(release_event); | 208 gdk_event_free(release_event); |
167 | 209 |
168 return false; | 210 return false; |
169 } | 211 } |
170 | 212 |
171 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task) { | 213 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task) { |
172 bool rv = SendMouseEvents(type, state); | 214 bool rv = SendMouseEvents(type, state); |
173 MessageLoop::current()->PostTask(FROM_HERE, task); | 215 GdkEventType wait_type; |
| 216 if (state & UP) { |
| 217 wait_type = GDK_BUTTON_RELEASE; |
| 218 } else { |
| 219 if (type == LEFT) |
| 220 wait_type = GDK_BUTTON_PRESS; |
| 221 else if (type == MIDDLE) |
| 222 wait_type = GDK_2BUTTON_PRESS; |
| 223 else |
| 224 wait_type = GDK_3BUTTON_PRESS; |
| 225 } |
| 226 new EventWaiter(task, wait_type); |
174 return rv; | 227 return rv; |
175 } | 228 } |
176 | 229 |
177 bool SendMouseClick(MouseButton type) { | 230 bool SendMouseClick(MouseButton type) { |
178 return SendMouseEvents(type, UP | DOWN); | 231 return SendMouseEvents(type, UP | DOWN); |
179 } | 232 } |
180 | 233 |
| 234 #if defined(TOOLKIT_VIEWS) |
| 235 void MoveMouseToCenterAndPress(views::View* view, MouseButton button, |
| 236 int state, Task* task) { |
| 237 gfx::Point view_center(view->width() / 2, view->height() / 2); |
| 238 views::View::ConvertPointToScreen(view, &view_center); |
| 239 SendMouseMoveNotifyWhenDone(view_center.x(), view_center.y(), |
| 240 new ClickTask(button, state, task)); |
| 241 } |
| 242 #else |
181 void MoveMouseToCenterAndPress(GtkWidget* widget, | 243 void MoveMouseToCenterAndPress(GtkWidget* widget, |
182 MouseButton button, | 244 MouseButton button, |
183 int state, | 245 int state, |
184 Task* task) { | 246 Task* task) { |
185 gfx::Rect bounds = gtk_util::GetWidgetScreenBounds(widget); | 247 gfx::Rect bounds = gtk_util::GetWidgetScreenBounds(widget); |
186 SendMouseMoveNotifyWhenDone(bounds.x() + bounds.width() / 2, | 248 SendMouseMoveNotifyWhenDone(bounds.x() + bounds.width() / 2, |
187 bounds.y() + bounds.height() / 2, | 249 bounds.y() + bounds.height() / 2, |
188 new ClickTask(button, state, task)); | 250 new ClickTask(button, state, task)); |
189 } | 251 } |
| 252 #endif |
190 | 253 |
191 } // namespace ui_controls | 254 } // namespace ui_controls |
OLD | NEW |