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 #include "base/callback.h" |
| 11 #include "base/logging.h" |
| 12 #include "base/message_pump_x.h" |
| 13 #include "chrome/browser/automation/ui_controls_internal.h" |
| 14 #include "ui/aura/desktop.h" |
| 15 #include "ui/base/keycodes/keyboard_code_conversion_x.h" |
| 16 #include "views/view.h" |
| 17 |
| 18 namespace { |
| 19 |
| 20 // Event waiter executes the specified closure|when a matching event |
| 21 // is found. |
| 22 // TODO(oshima): Move this to base. |
| 23 class EventWaiter : public MessageLoopForUI::Observer { |
| 24 public: |
| 25 typedef bool (*EventWaiterMatcher)(const base::NativeEvent& event); |
| 26 |
| 27 EventWaiter(const base::Closure& closure, EventWaiterMatcher matcher) |
| 28 : closure_(closure), |
| 29 matcher_(matcher) { |
| 30 MessageLoopForUI::current()->AddObserver(this); |
| 31 } |
| 32 |
| 33 virtual ~EventWaiter() { |
| 34 MessageLoopForUI::current()->RemoveObserver(this); |
| 35 } |
| 36 |
| 37 // MessageLoop::Observer implementation: |
| 38 virtual base::EventStatus WillProcessEvent( |
| 39 const base::NativeEvent& event) OVERRIDE { |
| 40 if ((*matcher_)(event)) { |
| 41 MessageLoop::current()->PostTask(FROM_HERE, closure_); |
| 42 delete this; |
| 43 } |
| 44 return base::EVENT_CONTINUE; |
| 45 } |
| 46 |
| 47 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { |
| 48 } |
| 49 |
| 50 private: |
| 51 base::Closure closure_; |
| 52 EventWaiterMatcher matcher_; |
| 53 DISALLOW_COPY_AND_ASSIGN(EventWaiter); |
| 54 }; |
| 55 |
| 56 // Latest mouse pointer location set by SendMouseMoveNotifyWhenDone. |
| 57 int g_current_x = -1000; |
| 58 int g_current_y = -1000; |
| 59 |
| 60 // Returns atom that indidates that the XEvent is marker event. |
| 61 Atom MarkerEventAtom() { |
| 62 return XInternAtom(base::MessagePumpX::GetDefaultXDisplay(), |
| 63 "marker_event", |
| 64 False); |
| 65 } |
| 66 |
| 67 // Returns true when the event is a marker event. |
| 68 bool Matcher(const base::NativeEvent& event) { |
| 69 return event->xany.type == ClientMessage && |
| 70 event->xclient.message_type == MarkerEventAtom(); |
| 71 } |
| 72 |
| 73 void RunClosureAfterEvents(const base::Closure closure) { |
| 74 if (closure.is_null()) |
| 75 return; |
| 76 static XEvent* marker_event = NULL; |
| 77 if (!marker_event) { |
| 78 marker_event = new XEvent(); |
| 79 marker_event->xclient.type = ClientMessage; |
| 80 marker_event->xclient.display = NULL; |
| 81 marker_event->xclient.window = None; |
| 82 marker_event->xclient.format = 8; |
| 83 } |
| 84 marker_event->xclient.message_type = MarkerEventAtom(); |
| 85 aura::Desktop::GetInstance()->PostNativeEvent(marker_event); |
| 86 new EventWaiter(closure, &Matcher); |
| 87 } |
| 88 |
| 89 } // namespace |
| 90 |
| 91 namespace ui_controls { |
| 92 |
| 93 bool SendKeyPress(gfx::NativeWindow window, |
| 94 ui::KeyboardCode key, |
| 95 bool control, |
| 96 bool shift, |
| 97 bool alt, |
| 98 bool command) { |
| 99 DCHECK(!command); // No command key on Aura |
| 100 return SendKeyPressNotifyWhenDone( |
| 101 window, key, control, shift, alt, command, base::Closure()); |
| 102 } |
| 103 |
| 104 void SetMaskAndKeycodeThenSend(XEvent* xevent, |
| 105 unsigned int mask, |
| 106 unsigned int keycode) { |
| 107 xevent->xkey.state |= mask; |
| 108 xevent->xkey.keycode = keycode; |
| 109 aura::Desktop::GetInstance()->PostNativeEvent(xevent); |
| 110 } |
| 111 |
| 112 void SetKeycodeAndSendThenUnmask(XEvent* xevent, |
| 113 unsigned int mask, |
| 114 unsigned int keycode) { |
| 115 xevent->xkey.keycode = keycode; |
| 116 aura::Desktop::GetInstance()->PostNativeEvent(xevent); |
| 117 xevent->xkey.state ^= mask; |
| 118 } |
| 119 |
| 120 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, |
| 121 ui::KeyboardCode key, |
| 122 bool control, |
| 123 bool shift, |
| 124 bool alt, |
| 125 bool command, |
| 126 const base::Closure& closure) { |
| 127 DCHECK(!command); // No command key on Aura |
| 128 XEvent xevent = {0}; |
| 129 xevent.xkey.type = KeyPress; |
| 130 if (control) |
| 131 SetMaskAndKeycodeThenSend(&xevent, ControlMask, XK_Control_L); |
| 132 if (shift) |
| 133 SetMaskAndKeycodeThenSend(&xevent, ShiftMask, XK_Shift_L); |
| 134 if (alt) |
| 135 SetMaskAndKeycodeThenSend(&xevent, Mod1Mask, XK_Alt_L); |
| 136 xevent.xkey.keycode = ui::XKeysymForWindowsKeyCode(key, shift); |
| 137 aura::Desktop::GetInstance()->PostNativeEvent(&xevent); |
| 138 |
| 139 // Send key release events. |
| 140 xevent.xkey.type = KeyRelease; |
| 141 aura::Desktop::GetInstance()->PostNativeEvent(&xevent); |
| 142 if (alt) |
| 143 SetKeycodeAndSendThenUnmask(&xevent, Mod1Mask, XK_Alt_L); |
| 144 if (shift) |
| 145 SetKeycodeAndSendThenUnmask(&xevent, ShiftMask, XK_Shift_L); |
| 146 if (control) |
| 147 SetKeycodeAndSendThenUnmask(&xevent, ControlMask, XK_Control_L); |
| 148 DCHECK(!xevent.xkey.state); |
| 149 RunClosureAfterEvents(closure); |
| 150 return true; |
| 151 } |
| 152 |
| 153 bool SendMouseMove(long x, long y) { |
| 154 return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); |
| 155 } |
| 156 |
| 157 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& closure) { |
| 158 XEvent xevent = {0}; |
| 159 XMotionEvent* xmotion = &xevent.xmotion; |
| 160 xmotion->type = MotionNotify; |
| 161 g_current_x = xmotion->x = x; |
| 162 g_current_y = xmotion->y = y; |
| 163 xmotion->same_screen = True; |
| 164 // Desktop will take care of other necessary fields. |
| 165 aura::Desktop::GetInstance()->PostNativeEvent(&xevent); |
| 166 RunClosureAfterEvents(closure); |
| 167 return false; |
| 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 // Desktop will take care of other necessary fields. |
| 199 |
| 200 aura::Desktop* desktop = aura::Desktop::GetInstance(); |
| 201 if (state & DOWN) { |
| 202 xevent.xbutton.type = ButtonPress; |
| 203 desktop->PostNativeEvent(&xevent); |
| 204 } |
| 205 if (state & UP) { |
| 206 xevent.xbutton.type = ButtonRelease; |
| 207 desktop->PostNativeEvent(&xevent); |
| 208 } |
| 209 RunClosureAfterEvents(closure); |
| 210 return false; |
| 211 } |
| 212 |
| 213 bool SendMouseClick(MouseButton type) { |
| 214 return SendMouseEvents(type, UP | DOWN); |
| 215 } |
| 216 |
| 217 void MoveMouseToCenterAndPress(views::View* view, MouseButton button, |
| 218 int state, const base::Closure& closure) { |
| 219 DCHECK(view); |
| 220 DCHECK(view->GetWidget()); |
| 221 gfx::Point view_center(view->width() / 2, view->height() / 2); |
| 222 views::View::ConvertPointToScreen(view, &view_center); |
| 223 SendMouseMove(view_center.x(), view_center.y()); |
| 224 SendMouseEventsNotifyWhenDone(button, state, closure); |
| 225 } |
| 226 |
| 227 } // namespace ui_controls |
OLD | NEW |