Commit e5f88338 authored by pkotwicz@chromium.org's avatar pkotwicz@chromium.org

Do not release capture when transferring capture between Chrome windows on

Linux.

This is part #1 for fixing the remaining cases where tab dragging leaves the
user in a weird state if the mouse is released at the "right" time.

BUG=392480
TEST=DesktopWindowTreeHostX11Test.CaptureEventForwarding
TBR=erg (For ui/views/views.gyp)

Review URL: https://codereview.chromium.org/380943003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287381 0039d316-1c4b-4281-b951-d872f2087c98
parent eed2a99b
...@@ -417,8 +417,6 @@ ...@@ -417,8 +417,6 @@
'widget/desktop_aura/x11_desktop_handler.h', 'widget/desktop_aura/x11_desktop_handler.h',
'widget/desktop_aura/x11_desktop_window_move_client.cc', 'widget/desktop_aura/x11_desktop_window_move_client.cc',
'widget/desktop_aura/x11_desktop_window_move_client.h', 'widget/desktop_aura/x11_desktop_window_move_client.h',
'widget/desktop_aura/x11_scoped_capture.cc',
'widget/desktop_aura/x11_scoped_capture.h',
'widget/desktop_aura/x11_topmost_window_finder.cc', 'widget/desktop_aura/x11_topmost_window_finder.cc',
'widget/desktop_aura/x11_topmost_window_finder.h', 'widget/desktop_aura/x11_topmost_window_finder.h',
'widget/desktop_aura/x11_whole_screen_move_loop.cc', 'widget/desktop_aura/x11_whole_screen_move_loop.cc',
......
...@@ -49,7 +49,6 @@ ...@@ -49,7 +49,6 @@
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_observer_x11.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host_observer_x11.h"
#include "ui/views/widget/desktop_aura/x11_desktop_handler.h" #include "ui/views/widget/desktop_aura/x11_desktop_handler.h"
#include "ui/views/widget/desktop_aura/x11_desktop_window_move_client.h" #include "ui/views/widget/desktop_aura/x11_desktop_window_move_client.h"
#include "ui/views/widget/desktop_aura/x11_scoped_capture.h"
#include "ui/views/widget/desktop_aura/x11_window_event_filter.h" #include "ui/views/widget/desktop_aura/x11_window_event_filter.h"
#include "ui/wm/core/compound_event_filter.h" #include "ui/wm/core/compound_event_filter.h"
#include "ui/wm/core/window_util.h" #include "ui/wm/core/window_util.h"
...@@ -921,24 +920,33 @@ gfx::Point DesktopWindowTreeHostX11::GetLocationOnNativeScreen() const { ...@@ -921,24 +920,33 @@ gfx::Point DesktopWindowTreeHostX11::GetLocationOnNativeScreen() const {
} }
void DesktopWindowTreeHostX11::SetCapture() { void DesktopWindowTreeHostX11::SetCapture() {
// This is vaguely based on the old NativeWidgetGtk implementation. // Grabbing the mouse is asynchronous. However, we synchronously start
// // forwarding all mouse events received by Chrome to the
// X11's XPointerGrab() shouldn't be used for everything; it doesn't map // aura::WindowEventDispatcher which has capture. This makes capture
// cleanly to Windows' SetCapture(). GTK only provides a separate concept of // synchronous for all intents and purposes if either:
// a grab that wasn't the X11 pointer grab, but was instead a manual // - |g_current_capture|'s X window has capture.
// redirection of the event. (You need to drop into GDK if you want to // OR
// perform a raw X11 grab). // - The topmost window underneath the mouse is managed by Chrome.
if (g_current_capture) if (g_current_capture)
g_current_capture->OnCaptureReleased(); g_current_capture->OnHostLostWindowCapture();
g_current_capture = this; g_current_capture = this;
x11_capture_.reset(new X11ScopedCapture(xwindow_));
unsigned int event_mask = PointerMotionMask | ButtonReleaseMask |
ButtonPressMask;
XGrabPointer(xdisplay_, xwindow_, True, event_mask, GrabModeAsync,
GrabModeAsync, None, None, CurrentTime);
} }
void DesktopWindowTreeHostX11::ReleaseCapture() { void DesktopWindowTreeHostX11::ReleaseCapture() {
if (g_current_capture == this) if (g_current_capture == this) {
g_current_capture->OnCaptureReleased(); // Release mouse grab asynchronously. A window managed by Chrome is likely
// the topmost window underneath the mouse so the capture release being
// asynchronous is likely inconsequential.
g_current_capture = NULL;
XUngrabPointer(xdisplay_, CurrentTime);
OnHostLostWindowCapture();
}
} }
void DesktopWindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) { void DesktopWindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) {
...@@ -1367,12 +1375,6 @@ void DesktopWindowTreeHostX11::SetUseNativeFrame(bool use_native_frame) { ...@@ -1367,12 +1375,6 @@ void DesktopWindowTreeHostX11::SetUseNativeFrame(bool use_native_frame) {
ResetWindowRegion(); ResetWindowRegion();
} }
void DesktopWindowTreeHostX11::OnCaptureReleased() {
x11_capture_.reset();
g_current_capture = NULL;
OnHostLostWindowCapture();
}
void DesktopWindowTreeHostX11::DispatchMouseEvent(ui::MouseEvent* event) { void DesktopWindowTreeHostX11::DispatchMouseEvent(ui::MouseEvent* event) {
// In Windows, the native events sent to chrome are separated into client // In Windows, the native events sent to chrome are separated into client
// and non-client versions of events, which we record on our LocatedEvent // and non-client versions of events, which we record on our LocatedEvent
......
...@@ -37,7 +37,6 @@ class DesktopDragDropClientAuraX11; ...@@ -37,7 +37,6 @@ class DesktopDragDropClientAuraX11;
class DesktopDispatcherClient; class DesktopDispatcherClient;
class DesktopWindowTreeHostObserverX11; class DesktopWindowTreeHostObserverX11;
class X11DesktopWindowMoveClient; class X11DesktopWindowMoveClient;
class X11ScopedCapture;
class X11WindowEventFilter; class X11WindowEventFilter;
class VIEWS_EXPORT DesktopWindowTreeHostX11 class VIEWS_EXPORT DesktopWindowTreeHostX11
...@@ -202,10 +201,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 ...@@ -202,10 +201,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
// Sets whether the window's borders are provided by the window manager. // Sets whether the window's borders are provided by the window manager.
void SetUseNativeFrame(bool use_native_frame); void SetUseNativeFrame(bool use_native_frame);
// Called when another DRWHL takes capture, or when capture is released
// entirely.
void OnCaptureReleased();
// Dispatches a mouse event, taking mouse capture into account. If a // Dispatches a mouse event, taking mouse capture into account. If a
// different host has capture, we translate the event to its coordinate space // different host has capture, we translate the event to its coordinate space
// and dispatch it to that host instead. // and dispatch it to that host instead.
...@@ -323,19 +318,14 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 ...@@ -323,19 +318,14 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
// The size of the window manager provided borders (if any). // The size of the window manager provided borders (if any).
gfx::Insets native_window_frame_borders_; gfx::Insets native_window_frame_borders_;
// The current root window host that has capture. While X11 has something // The current DesktopWindowTreeHostX11 which has capture. Set synchronously
// like Windows SetCapture()/ReleaseCapture(), it is entirely implicit and // when capture is requested via SetCapture().
// there are no notifications when this changes. We need to track this so we
// can notify widgets when they have lost capture, which controls a bunch of
// things in views like hiding menus.
static DesktopWindowTreeHostX11* g_current_capture; static DesktopWindowTreeHostX11* g_current_capture;
// A list of all (top-level) windows that have been created but not yet // A list of all (top-level) windows that have been created but not yet
// destroyed. // destroyed.
static std::list<XID>* open_windows_; static std::list<XID>* open_windows_;
scoped_ptr<X11ScopedCapture> x11_capture_;
base::string16 window_title_; base::string16 window_title_;
// Whether we currently are flashing our frame. This feature is implemented // Whether we currently are flashing our frame. This feature is implemented
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <vector> #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
#include <X11/Xlib.h> #include <X11/Xlib.h>
...@@ -12,11 +12,13 @@ ...@@ -12,11 +12,13 @@
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h" #include "ui/aura/window_tree_host.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_paths.h"
#include "ui/base/x/x11_util.h" #include "ui/base/x/x11_util.h"
#include "ui/events/event_handler.h"
#include "ui/events/platform/x11/x11_event_source.h" #include "ui/events/platform/x11/x11_event_source.h"
#include "ui/gfx/rect.h" #include "ui/gfx/rect.h"
#include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_atom_cache.h"
...@@ -53,18 +55,68 @@ class ActivationWaiter : public X11PropertyChangeWaiter { ...@@ -53,18 +55,68 @@ class ActivationWaiter : public X11PropertyChangeWaiter {
DISALLOW_COPY_AND_ASSIGN(ActivationWaiter); DISALLOW_COPY_AND_ASSIGN(ActivationWaiter);
}; };
// Creates a widget of size 100x100. // An event handler which counts the number of mouse moves it has seen.
scoped_ptr<Widget> CreateWidget() { class MouseMoveCounterHandler : public ui::EventHandler {
public:
MouseMoveCounterHandler() : count_(0) {
}
virtual ~MouseMoveCounterHandler() {
}
// ui::EventHandler:
virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
if (event->type() == ui::ET_MOUSE_MOVED)
++count_;
}
int num_mouse_moves() const {
return count_;
}
private:
int count_;
DISALLOW_COPY_AND_ASSIGN(MouseMoveCounterHandler);
};
// Creates a widget with the given bounds.
scoped_ptr<Widget> CreateWidget(const gfx::Rect& bounds) {
scoped_ptr<Widget> widget(new Widget); scoped_ptr<Widget> widget(new Widget);
Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.remove_standard_frame = true; params.remove_standard_frame = true;
params.native_widget = new DesktopNativeWidgetAura(widget.get()); params.native_widget = new DesktopNativeWidgetAura(widget.get());
params.bounds = gfx::Rect(100, 100, 100, 100); params.bounds = bounds;
widget->Init(params); widget->Init(params);
return widget.Pass(); return widget.Pass();
} }
// Dispatches an XMotionEvent targeted at |host|'s X window with location
// |point_in_screen|.
void DispatchMouseMotionEvent(DesktopWindowTreeHostX11* desktop_host,
const gfx::Point& point_in_screen) {
aura::WindowTreeHost* host = static_cast<aura::WindowTreeHost*>(desktop_host);
gfx::Rect bounds_in_screen = desktop_host->window()->GetBoundsInScreen();
Display* display = gfx::GetXDisplay();
XEvent xev;
xev.xmotion.type = MotionNotify;
xev.xmotion.display = display;
xev.xmotion.window = host->GetAcceleratedWidget();
xev.xmotion.root = DefaultRootWindow(display);
xev.xmotion.subwindow = 0;
xev.xmotion.time = CurrentTime;
xev.xmotion.x = point_in_screen.x() - bounds_in_screen.x();
xev.xmotion.y = point_in_screen.y() - bounds_in_screen.y();
xev.xmotion.x_root = point_in_screen.x();
xev.xmotion.y_root = point_in_screen.y();
xev.xmotion.state = 0;
xev.xmotion.is_hint = NotifyNormal;
xev.xmotion.same_screen = True;
static_cast<ui::PlatformEventDispatcher*>(desktop_host)->DispatchEvent(&xev);
}
} // namespace } // namespace
class DesktopWindowTreeHostX11Test : public ViewsTestBase { class DesktopWindowTreeHostX11Test : public ViewsTestBase {
...@@ -82,6 +134,7 @@ class DesktopWindowTreeHostX11Test : public ViewsTestBase { ...@@ -82,6 +134,7 @@ class DesktopWindowTreeHostX11Test : public ViewsTestBase {
ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path); ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
} }
// testing::Test
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
ViewsTestBase::SetUp(); ViewsTestBase::SetUp();
...@@ -103,7 +156,7 @@ class DesktopWindowTreeHostX11Test : public ViewsTestBase { ...@@ -103,7 +156,7 @@ class DesktopWindowTreeHostX11Test : public ViewsTestBase {
// Chrome even if it not possible to deactivate the window wrt to the x server. // Chrome even if it not possible to deactivate the window wrt to the x server.
// This behavior is required by several interactive_ui_tests. // This behavior is required by several interactive_ui_tests.
TEST_F(DesktopWindowTreeHostX11Test, Deactivate) { TEST_F(DesktopWindowTreeHostX11Test, Deactivate) {
scoped_ptr<Widget> widget(CreateWidget()); scoped_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100)));
ActivationWaiter waiter( ActivationWaiter waiter(
widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget()); widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
...@@ -124,4 +177,81 @@ TEST_F(DesktopWindowTreeHostX11Test, Deactivate) { ...@@ -124,4 +177,81 @@ TEST_F(DesktopWindowTreeHostX11Test, Deactivate) {
EXPECT_TRUE(widget->IsActive()); EXPECT_TRUE(widget->IsActive());
} }
// Chrome attempts to make mouse capture look synchronous on Linux. Test that
// Chrome synchronously switches the window that mouse events are forwarded to
// when capture is changed.
TEST_F(DesktopWindowTreeHostX11Test, CaptureEventForwarding) {
scoped_ptr<Widget> widget1(CreateWidget(gfx::Rect(100, 100, 100, 100)));
aura::Window* window1 = widget1->GetNativeWindow();
DesktopWindowTreeHostX11* host1 =
static_cast<DesktopWindowTreeHostX11*>(window1->GetHost());
widget1->Show();
scoped_ptr<Widget> widget2(CreateWidget(gfx::Rect(200, 100, 100, 100)));
aura::Window* window2 = widget2->GetNativeWindow();
DesktopWindowTreeHostX11* host2 =
static_cast<DesktopWindowTreeHostX11*>(window2->GetHost());
widget2->Show();
MouseMoveCounterHandler recorder1;
window1->AddPreTargetHandler(&recorder1);
MouseMoveCounterHandler recorder2;
window2->AddPreTargetHandler(&recorder2);
// Move the mouse to the center of |widget2|.
gfx::Point point_in_screen = widget2->GetWindowBoundsInScreen().CenterPoint();
DispatchMouseMotionEvent(host2, point_in_screen);
EXPECT_EQ(0, recorder1.num_mouse_moves());
EXPECT_EQ(1, recorder2.num_mouse_moves());
EXPECT_EQ(point_in_screen.ToString(),
aura::Env::GetInstance()->last_mouse_location().ToString());
// Set capture to |widget1|. Because DesktopWindowTreeHostX11 calls
// XGrabPointer() with owner == False, the X server sends events to |widget2|
// as long as the mouse is hovered over |widget2|. Verify that Chrome
// redirects mouse events to |widget1|.
widget1->SetCapture(NULL);
point_in_screen += gfx::Vector2d(1, 0);
DispatchMouseMotionEvent(host2, point_in_screen);
EXPECT_EQ(1, recorder1.num_mouse_moves());
EXPECT_EQ(1, recorder2.num_mouse_moves());
// If the event's location was correctly changed to be relative to |widget1|,
// Env's last mouse position will be correct.
EXPECT_EQ(point_in_screen.ToString(),
aura::Env::GetInstance()->last_mouse_location().ToString());
// Set capture to |widget2|. Subsequent events sent to |widget2| should not be
// forwarded.
widget2->SetCapture(NULL);
point_in_screen += gfx::Vector2d(1, 0);
DispatchMouseMotionEvent(host2, point_in_screen);
EXPECT_EQ(1, recorder1.num_mouse_moves());
EXPECT_EQ(2, recorder2.num_mouse_moves());
EXPECT_EQ(point_in_screen.ToString(),
aura::Env::GetInstance()->last_mouse_location().ToString());
// If the mouse is not hovered over |widget1| or |widget2|, the X server will
// send events to the window which has capture. Test the mouse events sent to
// |widget2| are not forwarded.
DispatchMouseMotionEvent(host2, point_in_screen);
EXPECT_EQ(1, recorder1.num_mouse_moves());
EXPECT_EQ(3, recorder2.num_mouse_moves());
EXPECT_EQ(point_in_screen.ToString(),
aura::Env::GetInstance()->last_mouse_location().ToString());
// Release capture. Test that when capture is released, mouse events are no
// longer forwarded to other widgets.
widget2->ReleaseCapture();
point_in_screen = widget1->GetWindowBoundsInScreen().CenterPoint();
DispatchMouseMotionEvent(host1, point_in_screen);
EXPECT_EQ(2, recorder1.num_mouse_moves());
EXPECT_EQ(3, recorder2.num_mouse_moves());
EXPECT_EQ(point_in_screen.ToString(),
aura::Env::GetInstance()->last_mouse_location().ToString());
// Cleanup
window1->RemovePreTargetHandler(&recorder1);
window2->RemovePreTargetHandler(&recorder2);
}
} // namespace views } // namespace views
// Copyright 2013 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 "ui/views/widget/desktop_aura/x11_scoped_capture.h"
#include <X11/X.h>
#include <X11/Xlib.h>
#include "ui/gfx/x/x11_types.h"
namespace views {
X11ScopedCapture::X11ScopedCapture(XID window)
: captured_(false) {
// TODO(sad): Use XI2 API instead.
unsigned int event_mask = PointerMotionMask | ButtonReleaseMask |
ButtonPressMask;
int status = XGrabPointer(gfx::GetXDisplay(), window, True, event_mask,
GrabModeAsync, GrabModeAsync, None, None,
CurrentTime);
captured_ = status == GrabSuccess;
}
X11ScopedCapture::~X11ScopedCapture() {
if (captured_) {
XUngrabPointer(gfx::GetXDisplay(), CurrentTime);
}
}
} // namespace views
// Copyright 2013 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.
#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_X11_SCOPED_CAPTURE_H_
#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_SCOPED_CAPTURE_H_
#include "base/basictypes.h"
typedef unsigned long XID;
namespace views {
// Grabs X11 input on a window., i.e. all subsequent mouse events are dispatched
// to the window, while the capture is active.
class X11ScopedCapture {
public:
explicit X11ScopedCapture(XID window);
virtual ~X11ScopedCapture();
private:
bool captured_;
DISALLOW_COPY_AND_ASSIGN(X11ScopedCapture);
};
} // namespace views
#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_SCOPED_CAPTURE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment