| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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 "chrome/browser/automation/ui_controls.h" | |
| 6 | |
| 7 #include <X11/keysym.h> | |
| 8 #include <X11/Xlib.h> | |
| 9 | |
| 10 // X macro fail. | |
| 11 #if defined(RootWindow) | |
| 12 #undef RootWindow | |
| 13 #endif | |
| 14 | |
| 15 #include "ash/shell.h" | |
| 16 #include "base/callback.h" | |
| 17 #include "base/logging.h" | |
| 18 #include "base/message_pump_x.h" | |
| 19 #include "chrome/browser/automation/ui_controls_internal.h" | |
| 20 #include "ui/aura/root_window.h" | |
| 21 #include "ui/base/keycodes/keyboard_code_conversion_x.h" | |
| 22 #include "ui/views/view.h" | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 // Mask of the buttons currently down. | |
| 27 unsigned button_down_mask = 0; | |
| 28 | |
| 29 // Event waiter executes the specified closure|when a matching event | |
| 30 // is found. | |
| 31 // TODO(oshima): Move this to base. | |
| 32 class EventWaiter : public MessageLoopForUI::Observer { | |
| 33 public: | |
| 34 typedef bool (*EventWaiterMatcher)(const base::NativeEvent& event); | |
| 35 | |
| 36 EventWaiter(const base::Closure& closure, EventWaiterMatcher matcher) | |
| 37 : closure_(closure), | |
| 38 matcher_(matcher) { | |
| 39 MessageLoopForUI::current()->AddObserver(this); | |
| 40 } | |
| 41 | |
| 42 virtual ~EventWaiter() { | |
| 43 MessageLoopForUI::current()->RemoveObserver(this); | |
| 44 } | |
| 45 | |
| 46 // MessageLoop::Observer implementation: | |
| 47 virtual base::EventStatus WillProcessEvent( | |
| 48 const base::NativeEvent& event) OVERRIDE { | |
| 49 if ((*matcher_)(event)) { | |
| 50 MessageLoop::current()->PostTask(FROM_HERE, closure_); | |
| 51 delete this; | |
| 52 } | |
| 53 return base::EVENT_CONTINUE; | |
| 54 } | |
| 55 | |
| 56 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { | |
| 57 } | |
| 58 | |
| 59 private: | |
| 60 base::Closure closure_; | |
| 61 EventWaiterMatcher matcher_; | |
| 62 DISALLOW_COPY_AND_ASSIGN(EventWaiter); | |
| 63 }; | |
| 64 | |
| 65 // Latest mouse pointer location set by SendMouseMoveNotifyWhenDone. | |
| 66 int g_current_x = -1000; | |
| 67 int g_current_y = -1000; | |
| 68 | |
| 69 // Returns atom that indidates that the XEvent is marker event. | |
| 70 Atom MarkerEventAtom() { | |
| 71 return XInternAtom(base::MessagePumpX::GetDefaultXDisplay(), | |
| 72 "marker_event", | |
| 73 False); | |
| 74 } | |
| 75 | |
| 76 // Returns true when the event is a marker event. | |
| 77 bool Matcher(const base::NativeEvent& event) { | |
| 78 return event->xany.type == ClientMessage && | |
| 79 event->xclient.message_type == MarkerEventAtom(); | |
| 80 } | |
| 81 | |
| 82 } // namespace | |
| 83 | |
| 84 namespace ui_controls { | |
| 85 | |
| 86 bool SendKeyPress(gfx::NativeWindow window, | |
| 87 ui::KeyboardCode key, | |
| 88 bool control, | |
| 89 bool shift, | |
| 90 bool alt, | |
| 91 bool command) { | |
| 92 DCHECK(!command); // No command key on Aura | |
| 93 return SendKeyPressNotifyWhenDone( | |
| 94 window, key, control, shift, alt, command, base::Closure()); | |
| 95 } | |
| 96 | |
| 97 void SetKeycodeAndSendThenMask(XEvent* xevent, | |
| 98 KeySym keysym, | |
| 99 unsigned int mask) { | |
| 100 xevent->xkey.keycode = | |
| 101 XKeysymToKeycode(base::MessagePumpX::GetDefaultXDisplay(), | |
| 102 keysym); | |
| 103 ash::Shell::GetRootWindow()->PostNativeEvent(xevent); | |
| 104 xevent->xkey.state |= mask; | |
| 105 } | |
| 106 | |
| 107 void UnmaskAndSetKeycodeThenSend(XEvent* xevent, | |
| 108 unsigned int mask, | |
| 109 KeySym keysym) { | |
| 110 xevent->xkey.state ^= mask; | |
| 111 xevent->xkey.keycode = | |
| 112 XKeysymToKeycode(base::MessagePumpX::GetDefaultXDisplay(), | |
| 113 keysym); | |
| 114 ash::Shell::GetRootWindow()->PostNativeEvent(xevent); | |
| 115 } | |
| 116 | |
| 117 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, | |
| 118 ui::KeyboardCode key, | |
| 119 bool control, | |
| 120 bool shift, | |
| 121 bool alt, | |
| 122 bool command, | |
| 123 const base::Closure& closure) { | |
| 124 DCHECK(!command); // No command key on Aura | |
| 125 XEvent xevent = {0}; | |
| 126 xevent.xkey.type = KeyPress; | |
| 127 if (control) | |
| 128 SetKeycodeAndSendThenMask(&xevent, XK_Control_L, ControlMask); | |
| 129 if (shift) | |
| 130 SetKeycodeAndSendThenMask(&xevent, XK_Shift_L, ShiftMask); | |
| 131 if (alt) | |
| 132 SetKeycodeAndSendThenMask(&xevent, XK_Alt_L, Mod1Mask); | |
| 133 xevent.xkey.keycode = | |
| 134 XKeysymToKeycode(base::MessagePumpX::GetDefaultXDisplay(), | |
| 135 ui::XKeysymForWindowsKeyCode(key, shift)); | |
| 136 ash::Shell::GetRootWindow()->PostNativeEvent(&xevent); | |
| 137 | |
| 138 // Send key release events. | |
| 139 xevent.xkey.type = KeyRelease; | |
| 140 ash::Shell::GetRootWindow()->PostNativeEvent(&xevent); | |
| 141 if (alt) | |
| 142 UnmaskAndSetKeycodeThenSend(&xevent, Mod1Mask, XK_Alt_L); | |
| 143 if (shift) | |
| 144 UnmaskAndSetKeycodeThenSend(&xevent, ShiftMask, XK_Shift_L); | |
| 145 if (control) | |
| 146 UnmaskAndSetKeycodeThenSend(&xevent, ControlMask, XK_Control_L); | |
| 147 DCHECK(!xevent.xkey.state); | |
| 148 RunClosureAfterAllPendingUIEvents(closure); | |
| 149 return true; | |
| 150 } | |
| 151 | |
| 152 bool SendMouseMove(long x, long y) { | |
| 153 return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); | |
| 154 } | |
| 155 | |
| 156 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& closure) { | |
| 157 XEvent xevent = {0}; | |
| 158 XMotionEvent* xmotion = &xevent.xmotion; | |
| 159 xmotion->type = MotionNotify; | |
| 160 g_current_x = xmotion->x = x; | |
| 161 g_current_y = xmotion->y = y; | |
| 162 xmotion->state = button_down_mask; | |
| 163 xmotion->same_screen = True; | |
| 164 // RootWindow will take care of other necessary fields. | |
| 165 ash::Shell::GetRootWindow()->PostNativeEvent(&xevent); | |
| 166 RunClosureAfterAllPendingUIEvents(closure); | |
| 167 return true; | |
| 168 } | |
| 169 | |
| 170 bool SendMouseEvents(MouseButton type, int state) { | |
| 171 return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); | |
| 172 } | |
| 173 | |
| 174 bool SendMouseEventsNotifyWhenDone(MouseButton type, | |
| 175 int state, | |
| 176 const base::Closure& closure) { | |
| 177 XEvent xevent = {0}; | |
| 178 XButtonEvent* xbutton = &xevent.xbutton; | |
| 179 DCHECK_NE(g_current_x, -1000); | |
| 180 DCHECK_NE(g_current_y, -1000); | |
| 181 xbutton->x = g_current_x; | |
| 182 xbutton->y = g_current_y; | |
| 183 xbutton->same_screen = True; | |
| 184 switch (type) { | |
| 185 case LEFT: | |
| 186 xbutton->button = Button1; | |
| 187 xbutton->state = Button1Mask; | |
| 188 break; | |
| 189 case MIDDLE: | |
| 190 xbutton->button = Button2; | |
| 191 xbutton->state = Button2Mask; | |
| 192 break; | |
| 193 case RIGHT: | |
| 194 xbutton->button = Button3; | |
| 195 xbutton->state = Button3Mask; | |
| 196 break; | |
| 197 } | |
| 198 // RootWindow will take care of other necessary fields. | |
| 199 | |
| 200 aura::RootWindow* root_window = ash::Shell::GetRootWindow(); | |
| 201 if (state & DOWN) { | |
| 202 xevent.xbutton.type = ButtonPress; | |
| 203 root_window->PostNativeEvent(&xevent); | |
| 204 button_down_mask |= xbutton->state; | |
| 205 } | |
| 206 if (state & UP) { | |
| 207 xevent.xbutton.type = ButtonRelease; | |
| 208 root_window->PostNativeEvent(&xevent); | |
| 209 button_down_mask = (button_down_mask | xbutton->state) ^ xbutton->state; | |
| 210 } | |
| 211 RunClosureAfterAllPendingUIEvents(closure); | |
| 212 return true; | |
| 213 } | |
| 214 | |
| 215 bool SendMouseClick(MouseButton type) { | |
| 216 return SendMouseEvents(type, UP | DOWN); | |
| 217 } | |
| 218 | |
| 219 void MoveMouseToCenterAndPress(views::View* view, MouseButton button, | |
| 220 int state, const base::Closure& closure) { | |
| 221 DCHECK(view); | |
| 222 DCHECK(view->GetWidget()); | |
| 223 gfx::Point view_center(view->width() / 2, view->height() / 2); | |
| 224 views::View::ConvertPointToScreen(view, &view_center); | |
| 225 SendMouseMove(view_center.x(), view_center.y()); | |
| 226 SendMouseEventsNotifyWhenDone(button, state, closure); | |
| 227 } | |
| 228 | |
| 229 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { | |
| 230 if (closure.is_null()) | |
| 231 return; | |
| 232 static XEvent* marker_event = NULL; | |
| 233 if (!marker_event) { | |
| 234 marker_event = new XEvent(); | |
| 235 marker_event->xclient.type = ClientMessage; | |
| 236 marker_event->xclient.display = NULL; | |
| 237 marker_event->xclient.window = None; | |
| 238 marker_event->xclient.format = 8; | |
| 239 } | |
| 240 marker_event->xclient.message_type = MarkerEventAtom(); | |
| 241 ash::Shell::GetRootWindow()->PostNativeEvent(marker_event); | |
| 242 new EventWaiter(closure, &Matcher); | |
| 243 } | |
| 244 | |
| 245 } // namespace ui_controls | |
| OLD | NEW |