Home | History | Annotate | Download | only in desktop_aura
      1 // Copyright (c) 2012 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 "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h"
      6 
      7 #include <X11/Xlib.h>
      8 // Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class.
      9 #undef RootWindow
     10 
     11 #include "base/debug/stack_trace.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/message_loop/message_pump_aurax11.h"
     14 #include "base/run_loop.h"
     15 #include "ui/aura/env.h"
     16 #include "ui/aura/root_window.h"
     17 #include "ui/base/events/event.h"
     18 #include "ui/base/x/x11_util.h"
     19 #include "ui/gfx/screen.h"
     20 
     21 namespace views {
     22 
     23 X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(
     24     X11WholeScreenMoveLoopDelegate* delegate)
     25     : delegate_(delegate),
     26       in_move_loop_(false),
     27       grab_input_window_(None) /*,
     28                                  root_window_(NULL)*/ {
     29 }
     30 
     31 X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() {}
     32 
     33 ////////////////////////////////////////////////////////////////////////////////
     34 // DesktopRootWindowHostLinux, MessageLoop::Dispatcher implementation:
     35 
     36 bool X11WholeScreenMoveLoop::Dispatch(const base::NativeEvent& event) {
     37   XEvent* xev = event;
     38 
     39   // Note: the escape key is handled in the tab drag controller, which has
     40   // keyboard focus even though we took pointer grab.
     41   switch (xev->type) {
     42     case MotionNotify: {
     43       delegate_->OnMouseMovement(&xev->xmotion);
     44       break;
     45     }
     46     case ButtonRelease: {
     47       if (xev->xbutton.button == Button1) {
     48         // Assume that drags are being done with the left mouse button. Only
     49         // break the drag if the left mouse button was released.
     50         delegate_->OnMouseReleased();
     51       }
     52       break;
     53     }
     54   }
     55 
     56   return true;
     57 }
     58 
     59 ////////////////////////////////////////////////////////////////////////////////
     60 // DesktopRootWindowHostLinux, aura::client::WindowMoveClient implementation:
     61 
     62 bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source,
     63                                          gfx::NativeCursor cursor) {
     64   DCHECK(!in_move_loop_);  // Can only handle one nested loop at a time.
     65   in_move_loop_ = true;
     66 
     67   Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
     68 
     69   // Creates an invisible, InputOnly toplevel window. This window will receive
     70   // all mouse movement for drags. It turns out that normal windows doing a
     71   // grab doesn't redirect pointer motion events if the pointer isn't over the
     72   // grabbing window. But InputOnly windows are able to grab everything. This
     73   // is what GTK+ does, and I found a patch to KDE that did something similar.
     74   unsigned long attribute_mask = CWEventMask | CWOverrideRedirect;
     75   XSetWindowAttributes swa;
     76   memset(&swa, 0, sizeof(swa));
     77   swa.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
     78                    StructureNotifyMask;
     79   swa.override_redirect = True;
     80   grab_input_window_ = XCreateWindow(
     81       display,
     82       DefaultRootWindow(display),
     83       -100, -100, 10, 10,
     84       0, 0, InputOnly, CopyFromParent,
     85       attribute_mask, &swa);
     86   base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(
     87       this, grab_input_window_);
     88 
     89   // Wait for the window to be mapped. If we don't, XGrabPointer fails.
     90   XMapRaised(display, grab_input_window_);
     91   base::MessagePumpAuraX11::Current()->BlockUntilWindowMapped(
     92       grab_input_window_);
     93 
     94   if (!GrabPointerWithCursor(cursor))
     95     return false;
     96 
     97   base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
     98   base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
     99   base::RunLoop run_loop(aura::Env::GetInstance()->GetDispatcher());
    100   quit_closure_ = run_loop.QuitClosure();
    101   run_loop.Run();
    102   return true;
    103 }
    104 
    105 void X11WholeScreenMoveLoop::UpdateCursor(gfx::NativeCursor cursor) {
    106   DCHECK(in_move_loop_);
    107   GrabPointerWithCursor(cursor);
    108 }
    109 
    110 void X11WholeScreenMoveLoop::EndMoveLoop() {
    111   if (!in_move_loop_)
    112     return;
    113 
    114   // TODO(erg): Is this ungrab the cause of having to click to give input focus
    115   // on drawn out windows? Not ungrabbing here screws the X server until I kill
    116   // the chrome process.
    117 
    118   // Ungrab before we let go of the window.
    119   Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
    120   XUngrabPointer(display, CurrentTime);
    121 
    122   base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(
    123       grab_input_window_);
    124   delegate_->OnMoveLoopEnded();
    125   XDestroyWindow(display, grab_input_window_);
    126 
    127   in_move_loop_ = false;
    128   quit_closure_.Run();
    129 }
    130 
    131 bool X11WholeScreenMoveLoop::GrabPointerWithCursor(gfx::NativeCursor cursor) {
    132   Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
    133   XGrabServer(display);
    134   XUngrabPointer(display, CurrentTime);
    135   int ret = XGrabPointer(
    136       display,
    137       grab_input_window_,
    138       False,
    139       ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
    140       GrabModeAsync,
    141       GrabModeAsync,
    142       None,
    143       cursor.platform(),
    144       CurrentTime);
    145   XUngrabServer(display);
    146   if (ret != GrabSuccess) {
    147     DLOG(ERROR) << "Grabbing new tab for dragging failed: "
    148                 << ui::GetX11ErrorString(display, ret);
    149     return false;
    150   }
    151 
    152   return true;
    153 }
    154 
    155 }  // namespace views
    156