Home | History | Annotate | Download | only in test
      1 // Copyright 2013 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 <X11/keysym.h>
      6 #include <X11/Xlib.h>
      7 
      8 // X macro fail.
      9 #if defined(RootWindow)
     10 #undef RootWindow
     11 #endif
     12 
     13 #include "base/bind.h"
     14 #include "base/logging.h"
     15 #include "ui/aura/client/screen_position_client.h"
     16 #include "ui/aura/env.h"
     17 #include "ui/aura/test/aura_test_utils.h"
     18 #include "ui/aura/test/ui_controls_factory_aura.h"
     19 #include "ui/aura/window_event_dispatcher.h"
     20 #include "ui/base/test/ui_controls_aura.h"
     21 #include "ui/base/x/x11_util.h"
     22 #include "ui/compositor/dip_util.h"
     23 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
     24 #include "ui/events/test/platform_event_waiter.h"
     25 #include "ui/gfx/x/x11_connection.h"
     26 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
     27 
     28 namespace views {
     29 namespace test {
     30 namespace {
     31 
     32 using ui_controls::DOWN;
     33 using ui_controls::LEFT;
     34 using ui_controls::MIDDLE;
     35 using ui_controls::MouseButton;
     36 using ui_controls::RIGHT;
     37 using ui_controls::UIControlsAura;
     38 using ui_controls::UP;
     39 
     40 // Mask of the buttons currently down.
     41 unsigned button_down_mask = 0;
     42 
     43 // Returns atom that indidates that the XEvent is marker event.
     44 Atom MarkerEventAtom() {
     45   return XInternAtom(gfx::GetXDisplay(), "marker_event", False);
     46 }
     47 
     48 // Returns true when the event is a marker event.
     49 bool Matcher(const base::NativeEvent& event) {
     50   return event->xany.type == ClientMessage &&
     51       event->xclient.message_type == MarkerEventAtom();
     52 }
     53 
     54 class UIControlsDesktopX11 : public UIControlsAura {
     55  public:
     56   UIControlsDesktopX11()
     57       : x_display_(gfx::GetXDisplay()),
     58         x_root_window_(DefaultRootWindow(x_display_)),
     59         x_window_(XCreateWindow(
     60             x_display_, x_root_window_,
     61             -100, -100, 10, 10,  // x, y, width, height
     62             0,                   // border width
     63             CopyFromParent,      // depth
     64             InputOnly,
     65             CopyFromParent,      // visual
     66             0,
     67             NULL)) {
     68     XStoreName(x_display_, x_window_, "Chromium UIControlsDesktopX11 Window");
     69   }
     70 
     71   virtual ~UIControlsDesktopX11() {
     72     XDestroyWindow(x_display_, x_window_);
     73   }
     74 
     75   virtual bool SendKeyPress(gfx::NativeWindow window,
     76                             ui::KeyboardCode key,
     77                             bool control,
     78                             bool shift,
     79                             bool alt,
     80                             bool command) OVERRIDE {
     81     DCHECK(!command);  // No command key on Aura
     82     return SendKeyPressNotifyWhenDone(
     83         window, key, control, shift, alt, command, base::Closure());
     84   }
     85 
     86   virtual bool SendKeyPressNotifyWhenDone(
     87       gfx::NativeWindow window,
     88       ui::KeyboardCode key,
     89       bool control,
     90       bool shift,
     91       bool alt,
     92       bool command,
     93       const base::Closure& closure) OVERRIDE {
     94     DCHECK(!command);  // No command key on Aura
     95 
     96     aura::WindowTreeHost* host = window->GetHost();
     97 
     98     XEvent xevent = {0};
     99     xevent.xkey.type = KeyPress;
    100     if (control) {
    101       SetKeycodeAndSendThenMask(host, &xevent, XK_Control_L, ControlMask);
    102     }
    103     if (shift)
    104       SetKeycodeAndSendThenMask(host, &xevent, XK_Shift_L, ShiftMask);
    105     if (alt)
    106       SetKeycodeAndSendThenMask(host, &xevent, XK_Alt_L, Mod1Mask);
    107     xevent.xkey.keycode =
    108         XKeysymToKeycode(x_display_,
    109                          ui::XKeysymForWindowsKeyCode(key, shift));
    110     host->PostNativeEvent(&xevent);
    111 
    112     // Send key release events.
    113     xevent.xkey.type = KeyRelease;
    114     host->PostNativeEvent(&xevent);
    115     if (alt)
    116       UnmaskAndSetKeycodeThenSend(host, &xevent, Mod1Mask, XK_Alt_L);
    117     if (shift)
    118       UnmaskAndSetKeycodeThenSend(host, &xevent, ShiftMask, XK_Shift_L);
    119     if (control) {
    120       UnmaskAndSetKeycodeThenSend(host, &xevent, ControlMask, XK_Control_L);
    121     }
    122     DCHECK(!xevent.xkey.state);
    123     RunClosureAfterAllPendingUIEvents(closure);
    124     return true;
    125   }
    126 
    127   virtual bool SendMouseMove(long screen_x, long screen_y) OVERRIDE {
    128     return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::Closure());
    129   }
    130   virtual bool SendMouseMoveNotifyWhenDone(
    131       long screen_x,
    132       long screen_y,
    133       const base::Closure& closure) OVERRIDE {
    134     gfx::Point screen_location(screen_x, screen_y);
    135     gfx::Point root_location = screen_location;
    136     aura::Window* root_window = RootWindowForPoint(screen_location);
    137 
    138     aura::client::ScreenPositionClient* screen_position_client =
    139           aura::client::GetScreenPositionClient(root_window);
    140     if (screen_position_client) {
    141       screen_position_client->ConvertPointFromScreen(root_window,
    142                                                      &root_location);
    143     }
    144 
    145     aura::WindowTreeHost* host = root_window->GetHost();
    146     gfx::Point root_current_location =
    147         aura::test::QueryLatestMousePositionRequestInHost(host);
    148     host->ConvertPointFromHost(&root_current_location);
    149 
    150     if (root_location != root_current_location && button_down_mask == 0) {
    151       // Move the cursor because EnterNotify/LeaveNotify are generated with the
    152       // current mouse position as a result of XGrabPointer()
    153       root_window->MoveCursorTo(root_location);
    154     } else {
    155       XEvent xevent = {0};
    156       XMotionEvent* xmotion = &xevent.xmotion;
    157       xmotion->type = MotionNotify;
    158       xmotion->x = root_location.x();
    159       xmotion->y = root_location.y();
    160       xmotion->state = button_down_mask;
    161       xmotion->same_screen = True;
    162       // RootWindow will take care of other necessary fields.
    163       host->PostNativeEvent(&xevent);
    164     }
    165     RunClosureAfterAllPendingUIEvents(closure);
    166     return true;
    167   }
    168   virtual bool SendMouseEvents(MouseButton type, int state) OVERRIDE {
    169     return SendMouseEventsNotifyWhenDone(type, state, base::Closure());
    170   }
    171   virtual bool SendMouseEventsNotifyWhenDone(
    172       MouseButton type,
    173       int state,
    174       const base::Closure& closure) OVERRIDE {
    175     XEvent xevent = {0};
    176     XButtonEvent* xbutton = &xevent.xbutton;
    177     gfx::Point mouse_loc = aura::Env::GetInstance()->last_mouse_location();
    178     aura::Window* root_window = RootWindowForPoint(mouse_loc);
    179     aura::client::ScreenPositionClient* screen_position_client =
    180           aura::client::GetScreenPositionClient(root_window);
    181     if (screen_position_client)
    182       screen_position_client->ConvertPointFromScreen(root_window, &mouse_loc);
    183     xbutton->x = mouse_loc.x();
    184     xbutton->y = mouse_loc.y();
    185     xbutton->same_screen = True;
    186     switch (type) {
    187       case LEFT:
    188         xbutton->button = Button1;
    189         xbutton->state = Button1Mask;
    190         break;
    191       case MIDDLE:
    192         xbutton->button = Button2;
    193         xbutton->state = Button2Mask;
    194         break;
    195       case RIGHT:
    196         xbutton->button = Button3;
    197         xbutton->state = Button3Mask;
    198         break;
    199     }
    200     // RootWindow will take care of other necessary fields.
    201     if (state & DOWN) {
    202       xevent.xbutton.type = ButtonPress;
    203       root_window->GetHost()->PostNativeEvent(&xevent);
    204       button_down_mask |= xbutton->state;
    205     }
    206     if (state & UP) {
    207       xevent.xbutton.type = ButtonRelease;
    208       root_window->GetHost()->PostNativeEvent(&xevent);
    209       button_down_mask = (button_down_mask | xbutton->state) ^ xbutton->state;
    210     }
    211     RunClosureAfterAllPendingUIEvents(closure);
    212     return true;
    213   }
    214   virtual bool SendMouseClick(MouseButton type) OVERRIDE {
    215     return SendMouseEvents(type, UP | DOWN);
    216   }
    217   virtual void RunClosureAfterAllPendingUIEvents(
    218       const base::Closure& closure) OVERRIDE {
    219     if (closure.is_null())
    220       return;
    221     static XEvent* marker_event = NULL;
    222     if (!marker_event) {
    223       marker_event = new XEvent();
    224       marker_event->xclient.type = ClientMessage;
    225       marker_event->xclient.display = x_display_;
    226       marker_event->xclient.window = x_window_;
    227       marker_event->xclient.format = 8;
    228     }
    229     marker_event->xclient.message_type = MarkerEventAtom();
    230     XSendEvent(x_display_, x_window_, False, 0, marker_event);
    231     ui::PlatformEventWaiter::Create(closure, base::Bind(&Matcher));
    232   }
    233  private:
    234   aura::Window* RootWindowForPoint(const gfx::Point& point) {
    235     // Most interactive_ui_tests run inside of the aura_test_helper
    236     // environment. This means that we can't rely on gfx::Screen and several
    237     // other things to work properly. Therefore we hack around this by
    238     // iterating across the windows owned DesktopWindowTreeHostX11 since this
    239     // doesn't rely on having a DesktopScreenX11.
    240     std::vector<aura::Window*> windows =
    241         DesktopWindowTreeHostX11::GetAllOpenWindows();
    242     for (std::vector<aura::Window*>::const_iterator it = windows.begin();
    243          it != windows.end(); ++it) {
    244       if ((*it)->GetBoundsInScreen().Contains(point)) {
    245         return (*it)->GetRootWindow();
    246       }
    247     }
    248 
    249     NOTREACHED() << "Coulding find RW for " << point.ToString() << " among "
    250                  << windows.size() << " RWs.";
    251     return NULL;
    252   }
    253 
    254   void SetKeycodeAndSendThenMask(aura::WindowTreeHost* host,
    255                                  XEvent* xevent,
    256                                  KeySym keysym,
    257                                  unsigned int mask) {
    258     xevent->xkey.keycode = XKeysymToKeycode(x_display_, keysym);
    259     host->PostNativeEvent(xevent);
    260     xevent->xkey.state |= mask;
    261   }
    262 
    263   void UnmaskAndSetKeycodeThenSend(aura::WindowTreeHost* host,
    264                                    XEvent* xevent,
    265                                    unsigned int mask,
    266                                    KeySym keysym) {
    267     xevent->xkey.state ^= mask;
    268     xevent->xkey.keycode = XKeysymToKeycode(x_display_, keysym);
    269     host->PostNativeEvent(xevent);
    270   }
    271 
    272   // Our X11 state.
    273   Display* x_display_;
    274   ::Window x_root_window_;
    275 
    276   // Input-only window used for events.
    277   ::Window x_window_;
    278 
    279   DISALLOW_COPY_AND_ASSIGN(UIControlsDesktopX11);
    280 };
    281 
    282 }  // namespace
    283 
    284 UIControlsAura* CreateUIControlsDesktopAura() {
    285   // The constructor of UIControlsDesktopX11 needs X11 connection to be
    286   // initialized.
    287   gfx::InitializeThreadedX11();
    288   return new UIControlsDesktopX11();
    289 }
    290 
    291 }  // namespace test
    292 }  // namespace views
    293