Index: ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc |
diff --git a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc |
index 76a56186095856d66cabb5ac87091bf5848419e3..afef9ab86eeda3e8e1814ca5fbdcee0bf52f65e7 100644 |
--- a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc |
+++ b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc |
@@ -9,6 +9,7 @@ |
#include "base/bind.h" |
#include "base/message_loop/message_loop.h" |
#include "base/run_loop.h" |
+#include "ui/aura/client/capture_client.h" |
#include "ui/aura/env.h" |
#include "ui/aura/window.h" |
#include "ui/aura/window_event_dispatcher.h" |
@@ -19,38 +20,17 @@ |
#include "ui/events/keycodes/keyboard_code_conversion_x.h" |
#include "ui/events/platform/scoped_event_dispatcher.h" |
#include "ui/events/platform/x11/x11_event_source.h" |
-#include "ui/gfx/point_conversions.h" |
namespace views { |
-namespace { |
- |
-class ScopedCapturer { |
- public: |
- explicit ScopedCapturer(aura::WindowTreeHost* host) |
- : host_(host) { |
- host_->SetCapture(); |
- } |
- |
- ~ScopedCapturer() { |
- host_->ReleaseCapture(); |
- } |
- |
- private: |
- aura::WindowTreeHost* host_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ScopedCapturer); |
-}; |
- |
-} // namespace |
- |
X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(X11MoveLoopDelegate* delegate) |
: delegate_(delegate), |
in_move_loop_(false), |
+ initial_cursor_(ui::kCursorNull), |
should_reset_mouse_flags_(false), |
grab_input_window_(None), |
+ grabbed_pointer_(false), |
canceled_(false), |
- has_grab_(false), |
weak_factory_(this) { |
last_xmotion_.type = LASTEvent; |
} |
@@ -74,19 +54,11 @@ bool X11WholeScreenMoveLoop::CanDispatchEvent(const ui::PlatformEvent& event) { |
} |
uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { |
- // This method processes all events for the grab_input_window_ as well as |
- // mouse events for all windows while the move loop is active - even before |
- // the grab is granted by X. This allows mouse notification events that were |
- // sent after the capture was requested but before the capture was granted |
- // to be dispatched. It is especially important to process the mouse release |
- // event that should have stopped the drag even if that mouse release happened |
- // before the grab was granted. |
+ // This method processes all events while the move loop is active. |
if (!in_move_loop_) |
return ui::POST_DISPATCH_PERFORM_DEFAULT; |
- XEvent* xev = event; |
- // Note: the escape key is handled in the tab drag controller, which has |
- // keyboard focus even though we took pointer grab. |
+ XEvent* xev = event; |
switch (xev->type) { |
case MotionNotify: { |
last_xmotion_ = xev->xmotion; |
@@ -107,6 +79,13 @@ uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { |
// break the drag if the left mouse button was released. |
DispatchMouseMovement(); |
delegate_->OnMouseReleased(); |
+ |
+ if (!grabbed_pointer_) { |
+ // If the source widget had capture prior to the move loop starting, |
+ // it may be relying on views::Widget getting the mouse release and |
+ // releasing capture in Widget::OnMouseEvent(). |
+ return ui::POST_DISPATCH_PERFORM_DEFAULT; |
+ } |
} |
return ui::POST_DISPATCH_NONE; |
} |
@@ -118,11 +97,6 @@ uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { |
} |
break; |
} |
- case FocusOut: { |
- if (xev->xfocus.mode != NotifyGrab) |
- has_grab_ = false; |
- break; |
- } |
case GenericEvent: { |
ui::EventType type = ui::EventTypeFromNative(xev); |
switch (type) { |
@@ -146,8 +120,7 @@ uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { |
gfx::Point point(ui::EventSystemLocationFromNative(xev)); |
xevent.xmotion.x_root = point.x(); |
xevent.xmotion.y_root = point.y(); |
- DispatchEvent(&xevent); |
- return ui::POST_DISPATCH_NONE; |
+ return DispatchEvent(&xevent); |
} |
default: |
break; |
@@ -155,31 +128,39 @@ uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { |
} |
} |
- return (event->xany.window == grab_input_window_) ? |
- ui::POST_DISPATCH_NONE : ui::POST_DISPATCH_PERFORM_DEFAULT; |
+ return ui::POST_DISPATCH_PERFORM_DEFAULT; |
} |
bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source, |
gfx::NativeCursor cursor) { |
DCHECK(!in_move_loop_); // Can only handle one nested loop at a time. |
- // Start a capture on the host, so that it continues to receive events during |
- // the drag. This may be second time we are capturing the mouse events - the |
- // first being when a mouse is first pressed. That first capture needs to be |
- // released before the call to GrabPointerAndKeyboard below, otherwise it may |
- // get released while we still need the pointer grab, which is why we restrict |
- // the scope here. |
- { |
- ScopedCapturer capturer(source->GetHost()); |
- |
- grab_input_window_ = CreateDragInputWindow(gfx::GetXDisplay()); |
- // Releasing ScopedCapturer ensures that any other instance of |
- // X11ScopedCapture will not prematurely release grab that will be acquired |
- // below. |
+ // Query the mouse cursor prior to the move loop starting so that it can be |
+ // restored when the move loop finishes. |
+ initial_cursor_ = source->GetHost()->last_cursor(); |
+ |
+ grab_input_window_ = CreateDragInputWindow(gfx::GetXDisplay()); |
+ |
+ // Only grab mouse capture of |grab_input_window_| if |source| does not have |
+ // capture. |
+ // - The caller may intend to transfer capture to a different aura::Window |
+ // when the move loop ends and not release capture. |
+ // - Releasing capture and X window destruction are both asynchronous. We drop |
+ // events targeted at |grab_input_window_| in the time between the move |
+ // loop ends and |grab_input_window_| loses capture. |
+ grabbed_pointer_ = false; |
+ if (!source->HasCapture()) { |
+ aura::client::CaptureClient* capture_client = |
+ aura::client::GetCaptureClient(source->GetRootWindow()); |
+ CHECK(capture_client->GetGlobalCaptureWindow() == NULL); |
+ grabbed_pointer_ = GrabPointer(cursor); |
+ if (!grabbed_pointer_) { |
+ XDestroyWindow(gfx::GetXDisplay(), grab_input_window_); |
+ return false; |
+ } |
} |
- // TODO(varkha): Consider integrating GrabPointerAndKeyboard with |
- // ScopedCapturer to avoid possibility of logically keeping multiple grabs. |
- if (!GrabPointerAndKeyboard(cursor)) { |
+ |
+ if (!GrabKeyboard()) { |
XDestroyWindow(gfx::GetXDisplay(), grab_input_window_); |
return false; |
} |
@@ -189,9 +170,9 @@ bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source, |
nested_dispatcher_ = |
ui::PlatformEventSource::GetInstance()->OverrideDispatcher(this); |
- // We are handling a mouse drag outside of the aura::RootWindow system. We |
- // must manually make aura think that the mouse button is pressed so that we |
- // don't draw extraneous tooltips. |
+ // We are handling a mouse drag outside of the aura::Window system. We must |
+ // manually make aura think that the mouse button is pressed so that we don't |
+ // draw extraneous tooltips. |
aura::Env* env = aura::Env::GetInstance(); |
if (!env->IsMouseButtonDown()) { |
env->set_mouse_button_flags(ui::EF_LEFT_MOUSE_BUTTON); |
@@ -211,10 +192,13 @@ bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source, |
void X11WholeScreenMoveLoop::UpdateCursor(gfx::NativeCursor cursor) { |
if (in_move_loop_) { |
- // If we're still in the move loop, regrab the pointer with the updated |
- // cursor. Note: we can be called from handling an XdndStatus message after |
- // EndMoveLoop() was called, but before we return from the nested RunLoop. |
- GrabPointerAndKeyboard(cursor); |
+ // We cannot call GrabPointer() because we do not want to change the |
+ // "owner_events" property of the active pointer grab. |
+ XChangeActivePointerGrab( |
+ gfx::GetXDisplay(), |
+ ButtonPressMask | ButtonReleaseMask | PointerMotionMask, |
+ cursor.platform(), |
+ CurrentTime); |
} |
} |
@@ -238,11 +222,12 @@ void X11WholeScreenMoveLoop::EndMoveLoop() { |
// Ungrab before we let go of the window. |
XDisplay* display = gfx::GetXDisplay(); |
- // Only ungrab pointer if capture was not switched to another window. |
- if (has_grab_) { |
+ if (grabbed_pointer_) |
XUngrabPointer(display, CurrentTime); |
- XUngrabKeyboard(display, CurrentTime); |
- } |
+ else |
+ UpdateCursor(initial_cursor_); |
+ |
+ XUngrabKeyboard(display, CurrentTime); |
// Restore the previous dispatcher. |
nested_dispatcher_.reset(); |
@@ -254,15 +239,16 @@ void X11WholeScreenMoveLoop::EndMoveLoop() { |
quit_closure_.Run(); |
} |
-bool X11WholeScreenMoveLoop::GrabPointerAndKeyboard(gfx::NativeCursor cursor) { |
+bool X11WholeScreenMoveLoop::GrabPointer(gfx::NativeCursor cursor) { |
XDisplay* display = gfx::GetXDisplay(); |
XGrabServer(display); |
- XUngrabPointer(display, CurrentTime); |
+ // Pass "owner_events" as false so that X sends all mouse events to |
+ // |grab_input_window_|. |
int ret = XGrabPointer( |
display, |
grab_input_window_, |
- False, |
+ False, // owner_events |
ButtonPressMask | ButtonReleaseMask | PointerMotionMask, |
GrabModeAsync, |
GrabModeAsync, |
@@ -272,33 +258,29 @@ bool X11WholeScreenMoveLoop::GrabPointerAndKeyboard(gfx::NativeCursor cursor) { |
if (ret != GrabSuccess) { |
DLOG(ERROR) << "Grabbing pointer for dragging failed: " |
<< ui::GetX11ErrorString(display, ret); |
- } else { |
- has_grab_ = true; |
- XUngrabKeyboard(display, CurrentTime); |
- ret = XGrabKeyboard( |
- display, |
- grab_input_window_, |
- False, |
- GrabModeAsync, |
- GrabModeAsync, |
- CurrentTime); |
- if (ret != GrabSuccess) { |
- DLOG(ERROR) << "Grabbing keyboard for dragging failed: " |
- << ui::GetX11ErrorString(display, ret); |
- } |
} |
- |
XUngrabServer(display); |
XFlush(display); |
return ret == GrabSuccess; |
} |
+bool X11WholeScreenMoveLoop::GrabKeyboard() { |
+ XDisplay* display = gfx::GetXDisplay(); |
+ int ret = XGrabKeyboard(display, |
+ grab_input_window_, |
+ False, |
+ GrabModeAsync, |
+ GrabModeAsync, |
+ CurrentTime); |
+ if (ret != GrabSuccess) { |
+ DLOG(ERROR) << "Grabbing keyboard for dragging failed: " |
+ << ui::GetX11ErrorString(display, ret); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
Window X11WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) { |
- // Creates an invisible, InputOnly toplevel window. This window will receive |
- // all mouse movement for drags. It turns out that normal windows doing a |
- // grab doesn't redirect pointer motion events if the pointer isn't over the |
- // grabbing window. But InputOnly windows are able to grab everything. This |
- // is what GTK+ does, and I found a patch to KDE that did something similar. |
unsigned long attribute_mask = CWEventMask | CWOverrideRedirect; |
XSetWindowAttributes swa; |
memset(&swa, 0, sizeof(swa)); |