// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include // X macro fail. #if defined(RootWindow) #undef RootWindow #endif #include "base/logging.h" #include "base/message_pump_x.h" #include "ui/aura/root_window.h" #include "ui/aura/ui_controls_aura.h" #include "ui/base/keycodes/keyboard_code_conversion_x.h" #include "ui/ui_controls/ui_controls_aura.h" namespace aura { namespace { // Mask of the buttons currently down. unsigned button_down_mask = 0; // Event waiter executes the specified closure|when a matching event // is found. // TODO(oshima): Move this to base. class EventWaiter : public MessageLoopForUI::Observer { public: typedef bool (*EventWaiterMatcher)(const base::NativeEvent& event); EventWaiter(const base::Closure& closure, EventWaiterMatcher matcher) : closure_(closure), matcher_(matcher) { MessageLoopForUI::current()->AddObserver(this); } virtual ~EventWaiter() { MessageLoopForUI::current()->RemoveObserver(this); } // MessageLoop::Observer implementation: virtual base::EventStatus WillProcessEvent( const base::NativeEvent& event) OVERRIDE { if ((*matcher_)(event)) { MessageLoop::current()->PostTask(FROM_HERE, closure_); delete this; } return base::EVENT_CONTINUE; } virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { } private: base::Closure closure_; EventWaiterMatcher matcher_; DISALLOW_COPY_AND_ASSIGN(EventWaiter); }; // Latest mouse pointer location set by SendMouseMoveNotifyWhenDone. int g_current_x = -1000; int g_current_y = -1000; // Returns atom that indidates that the XEvent is marker event. Atom MarkerEventAtom() { return XInternAtom(base::MessagePumpX::GetDefaultXDisplay(), "marker_event", False); } // Returns true when the event is a marker event. bool Matcher(const base::NativeEvent& event) { return event->xany.type == ClientMessage && event->xclient.message_type == MarkerEventAtom(); } class UIControlsX11 : public ui_controls::UIControlsAura { public: UIControlsX11(RootWindow* root_window) : root_window_(root_window) { } virtual bool SendKeyPress(gfx::NativeWindow window, ui::KeyboardCode key, bool control, bool shift, bool alt, bool command) { DCHECK(!command); // No command key on Aura return SendKeyPressNotifyWhenDone( window, key, control, shift, alt, command, base::Closure()); } virtual bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, ui::KeyboardCode key, bool control, bool shift, bool alt, bool command, const base::Closure& closure) { DCHECK(!command); // No command key on Aura XEvent xevent = {0}; xevent.xkey.type = KeyPress; if (control) SetKeycodeAndSendThenMask(&xevent, XK_Control_L, ControlMask); if (shift) SetKeycodeAndSendThenMask(&xevent, XK_Shift_L, ShiftMask); if (alt) SetKeycodeAndSendThenMask(&xevent, XK_Alt_L, Mod1Mask); xevent.xkey.keycode = XKeysymToKeycode(base::MessagePumpX::GetDefaultXDisplay(), ui::XKeysymForWindowsKeyCode(key, shift)); root_window_->PostNativeEvent(&xevent); // Send key release events. xevent.xkey.type = KeyRelease; root_window_->PostNativeEvent(&xevent); if (alt) UnmaskAndSetKeycodeThenSend(&xevent, Mod1Mask, XK_Alt_L); if (shift) UnmaskAndSetKeycodeThenSend(&xevent, ShiftMask, XK_Shift_L); if (control) UnmaskAndSetKeycodeThenSend(&xevent, ControlMask, XK_Control_L); DCHECK(!xevent.xkey.state); RunClosureAfterAllPendingUIEvents(closure); return true; } // Simulate a mouse move. (x,y) are absolute screen coordinates. virtual bool SendMouseMove(long x, long y) { return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); } virtual bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& closure) { XEvent xevent = {0}; XMotionEvent* xmotion = &xevent.xmotion; xmotion->type = MotionNotify; g_current_x = xmotion->x = x; g_current_y = xmotion->y = y; xmotion->state = button_down_mask; xmotion->same_screen = True; // RootWindow will take care of other necessary fields. root_window_->PostNativeEvent(&xevent); RunClosureAfterAllPendingUIEvents(closure); return true; } virtual bool SendMouseEvents(ui_controls::MouseButton type, int state) { return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); } virtual bool SendMouseEventsNotifyWhenDone(ui_controls::MouseButton type, int state, const base::Closure& closure) { XEvent xevent = {0}; XButtonEvent* xbutton = &xevent.xbutton; DCHECK_NE(g_current_x, -1000); DCHECK_NE(g_current_y, -1000); xbutton->x = g_current_x; xbutton->y = g_current_y; xbutton->same_screen = True; switch (type) { case ui_controls::LEFT: xbutton->button = Button1; xbutton->state = Button1Mask; break; case ui_controls::MIDDLE: xbutton->button = Button2; xbutton->state = Button2Mask; break; case ui_controls::RIGHT: xbutton->button = Button3; xbutton->state = Button3Mask; break; } // RootWindow will take care of other necessary fields. if (state & ui_controls::DOWN) { xevent.xbutton.type = ButtonPress; root_window_->PostNativeEvent(&xevent); button_down_mask |= xbutton->state; } if (state & ui_controls::UP) { xevent.xbutton.type = ButtonRelease; root_window_->PostNativeEvent(&xevent); button_down_mask = (button_down_mask | xbutton->state) ^ xbutton->state; } RunClosureAfterAllPendingUIEvents(closure); return true; } virtual bool SendMouseClick(ui_controls::MouseButton type) { return SendMouseEvents(type, ui_controls::UP | ui_controls::DOWN); } virtual void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { if (closure.is_null()) return; static XEvent* marker_event = NULL; if (!marker_event) { marker_event = new XEvent(); marker_event->xclient.type = ClientMessage; marker_event->xclient.display = NULL; marker_event->xclient.window = None; marker_event->xclient.format = 8; } marker_event->xclient.message_type = MarkerEventAtom(); root_window_->PostNativeEvent(marker_event); new EventWaiter(closure, &Matcher); } private: void SetKeycodeAndSendThenMask(XEvent* xevent, KeySym keysym, unsigned int mask) { xevent->xkey.keycode = XKeysymToKeycode(base::MessagePumpX::GetDefaultXDisplay(), keysym); root_window_->PostNativeEvent(xevent); xevent->xkey.state |= mask; } void UnmaskAndSetKeycodeThenSend(XEvent* xevent, unsigned int mask, KeySym keysym) { xevent->xkey.state ^= mask; xevent->xkey.keycode = XKeysymToKeycode(base::MessagePumpX::GetDefaultXDisplay(), keysym); root_window_->PostNativeEvent(xevent); } RootWindow* root_window_; DISALLOW_COPY_AND_ASSIGN(UIControlsX11); }; } // namespace ui_controls::UIControlsAura* CreateUIControlsAura(RootWindow* root_window) { return new UIControlsX11(root_window); } } // namespace aura