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