diff options
author | erg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-01 11:47:48 +0000 |
---|---|---|
committer | erg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-01 11:47:48 +0000 |
commit | 1260757ead730630481a8790748da152bbc14537 (patch) | |
tree | 4ee23af04c32ee486061420a336202ff36a1a14e /ui | |
parent | c40785ecdbc4891fe3d42096238cbb1ed37d3ff8 (diff) | |
download | chromium_src-1260757ead730630481a8790748da152bbc14537.zip chromium_src-1260757ead730630481a8790748da152bbc14537.tar.gz chromium_src-1260757ead730630481a8790748da152bbc14537.tar.bz2 |
Desktop aura: Fix tab dragging
- Tabs are now draggable into a window.
- You can no longer drag with the right mouse button.
- We now create an invisible window over the screen while dragging a
tab. Previously, if you moved your mouse cursor quickly, you could
end up with your cursor outside the window you were dragging and
this broke tab dragging.
BUG=125106
Review URL: https://chromiumcodereview.appspot.com/11364013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@165335 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/base/x/x11_util.cc | 6 | ||||
-rw-r--r-- | ui/base/x/x11_util.h | 3 | ||||
-rw-r--r-- | ui/views/widget/desktop_root_window_host_linux.cc | 36 | ||||
-rw-r--r-- | ui/views/widget/x11_desktop_window_move_client.cc | 123 | ||||
-rw-r--r-- | ui/views/widget/x11_desktop_window_move_client.h | 33 | ||||
-rw-r--r-- | ui/views/widget/x11_window_event_filter.cc | 3 |
6 files changed, 132 insertions, 72 deletions
diff --git a/ui/base/x/x11_util.cc b/ui/base/x/x11_util.cc index b187f45..05b6f56 100644 --- a/ui/base/x/x11_util.cc +++ b/ui/base/x/x11_util.cc @@ -927,6 +927,12 @@ bool GetWindowDesktop(XID window, int* desktop) { return GetIntProperty(window, "_NET_WM_DESKTOP", desktop); } +std::string GetX11ErrorString(Display* display, int err) { + char buffer[256]; + XGetErrorText(display, err, buffer, arraysize(buffer)); + return buffer; +} + // Returns true if |window| is a named window. bool IsWindowNamed(XID window) { XTextProperty prop; diff --git a/ui/base/x/x11_util.h b/ui/base/x/x11_util.h index b3f2545..45286a0 100644 --- a/ui/base/x/x11_util.h +++ b/ui/base/x/x11_util.h @@ -192,6 +192,9 @@ static const int kAllDesktops = -1; // property not found. bool GetWindowDesktop(XID window, int* desktop); +// Translates an X11 error code into a printable string. +UI_EXPORT std::string GetX11ErrorString(Display* display, int err); + // Implementers of this interface receive a notification for every X window of // the main display. class EnumerateWindowsDelegate { diff --git a/ui/views/widget/desktop_root_window_host_linux.cc b/ui/views/widget/desktop_root_window_host_linux.cc index b05fcf3..2304baf 100644 --- a/ui/views/widget/desktop_root_window_host_linux.cc +++ b/ui/views/widget/desktop_root_window_host_linux.cc @@ -227,7 +227,6 @@ aura::RootWindow* DesktopRootWindowHostLinux::InitRootWindow( root_window_event_filter_->AddFilter(x11_window_event_filter_.get()); x11_window_move_client_.reset(new X11DesktopWindowMoveClient); - root_window_event_filter_->AddFilter(x11_window_move_client_.get()); aura::client::SetWindowMoveClient(root_window_, x11_window_move_client_.get()); @@ -308,7 +307,6 @@ void DesktopRootWindowHostLinux::Close() { void DesktopRootWindowHostLinux::CloseNow() { // Remove the event listeners we've installed. We need to remove these // because otherwise we get assert during ~RootWindow(). - root_window_event_filter_->RemoveFilter(x11_window_move_client_.get()); root_window_event_filter_->RemoveFilter(x11_window_event_filter_.get()); root_window_event_filter_->RemoveFilter(input_method_filter_.get()); @@ -665,16 +663,36 @@ gfx::Rect DesktopRootWindowHostLinux::GetBounds() const { } void DesktopRootWindowHostLinux::SetBounds(const gfx::Rect& bounds) { - bool size_changed = bounds.size() != bounds_.size(); - - if (bounds != bounds_) { - XMoveResizeWindow(xdisplay_, xwindow_, bounds.x(), bounds.y(), - bounds.width(), bounds.height()); - bounds_ = bounds; + bool origin_changed = bounds_.origin() != bounds.origin(); + bool size_changed = bounds_.size() != bounds.size(); + XWindowChanges changes = {0}; + unsigned value_mask = 0; + + if (size_changed) { + changes.width = bounds.width(); + changes.height = bounds.height(); + value_mask |= CWHeight | CWWidth; } + if (origin_changed) { + changes.x = bounds.x(); + changes.y = bounds.y(); + value_mask |= CWX | CWY; + } + if (value_mask) + XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); + + // Assume that the resize will go through as requested, which should be the + // case if we're running without a window manager. If there's a window + // manager, it can modify or ignore the request, but (per ICCCM) we'll get a + // (possibly synthetic) ConfigureNotify about the actual size and correct + // |bounds_| later. + bounds_ = bounds; + + if (origin_changed) + native_widget_delegate_->AsWidget()->OnNativeWidgetMove(); if (size_changed) - root_window_host_delegate_->OnHostResized(bounds_.size()); + root_window_host_delegate_->OnHostResized(bounds.size()); else root_window_host_delegate_->OnHostPaint(); } diff --git a/ui/views/widget/x11_desktop_window_move_client.cc b/ui/views/widget/x11_desktop_window_move_client.cc index e17db87..d587775 100644 --- a/ui/views/widget/x11_desktop_window_move_client.cc +++ b/ui/views/widget/x11_desktop_window_move_client.cc @@ -8,6 +8,7 @@ // Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. #undef RootWindow +#include "base/debug/stack_trace.h" #include "base/message_loop.h" #include "base/message_pump_aurax11.h" #include "base/run_loop.h" @@ -20,54 +21,41 @@ namespace views { X11DesktopWindowMoveClient::X11DesktopWindowMoveClient() - : in_move_loop_(false) { + : in_move_loop_(false), + grab_input_window_(None), + root_window_(NULL) { } X11DesktopWindowMoveClient::~X11DesktopWindowMoveClient() {} -bool X11DesktopWindowMoveClient::PreHandleKeyEvent(aura::Window* target, - ui::KeyEvent* event) { - return false; -} - -bool X11DesktopWindowMoveClient::PreHandleMouseEvent(aura::Window* target, - ui::MouseEvent* event) { - if (in_move_loop_) { - switch (event->type()) { - case ui::ET_MOUSE_DRAGGED: - case ui::ET_MOUSE_MOVED: { - DCHECK(event->valid_system_location()); - gfx::Point system_loc = - event->system_location().Subtract(window_offset_); - aura::RootWindow* root_window = target->GetRootWindow(); - root_window->SetHostBounds(gfx::Rect( - system_loc, root_window->GetHostSize())); - return true; - } - case ui::ET_MOUSE_CAPTURE_CHANGED: - case ui::ET_MOUSE_RELEASED: { - EndMoveLoop(); - return true; - } - default: - break; +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostLinux, MessageLoop::Dispatcher implementation: + +bool X11DesktopWindowMoveClient::Dispatch(const base::NativeEvent& event) { + XEvent* xev = event; + + // Note: the escape key is handled in the tab drag controller, which has + // keyboard focus even though we took pointer grab. + switch (xev->type) { + case MotionNotify: { + gfx::Point cursor_point(xev->xmotion.x_root, xev->xmotion.y_root); + gfx::Point system_loc = cursor_point.Subtract(window_offset_); + root_window_->SetHostBounds(gfx::Rect( + system_loc, root_window_->GetHostSize())); + break; + } + case ButtonPress: + case ButtonRelease: { + EndMoveLoop(); + break; } } - return false; -} - -ui::TouchStatus X11DesktopWindowMoveClient::PreHandleTouchEvent( - aura::Window* target, - ui::TouchEvent* event) { - return ui::TOUCH_STATUS_UNKNOWN; + return true; } -ui::EventResult X11DesktopWindowMoveClient::PreHandleGestureEvent( - aura::Window* target, - ui::GestureEvent* event) { - return ui::ER_UNHANDLED; -} +//////////////////////////////////////////////////////////////////////////////// +// DesktopRootWindowHostLinux, aura::client::WindowMoveClient implementation: aura::client::WindowMoveResult X11DesktopWindowMoveClient::RunMoveLoop( aura::Window* source, @@ -76,25 +64,51 @@ aura::client::WindowMoveResult X11DesktopWindowMoveClient::RunMoveLoop( in_move_loop_ = true; window_offset_ = drag_offset; - source->GetRootWindow()->ShowRootWindow(); + root_window_ = source->GetRootWindow(); Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); + + // 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)); + swa.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | + StructureNotifyMask; + swa.override_redirect = True; + grab_input_window_ = XCreateWindow( + display, + DefaultRootWindow(display), + -100, -100, 10, 10, + 0, 0, InputOnly, CopyFromParent, + attribute_mask, &swa); + base::MessagePumpAuraX11::Current()->AddDispatcherForWindow( + this, grab_input_window_); + + // Wait for the window to be mapped. If we don't, XGrabPointer fails. + XMapRaised(display, grab_input_window_); + base::MessagePumpAuraX11::Current()->BlockUntilWindowMapped( + grab_input_window_); + XGrabServer(display); XUngrabPointer(display, CurrentTime); - - aura::RootWindow* root_window = source->GetRootWindow(); - int ret = XGrabPointer(display, - root_window->GetAcceleratedWidget(), - False, - ButtonReleaseMask | PointerMotionMask, - GrabModeAsync, - GrabModeAsync, - None, - None, - CurrentTime); + int ret = XGrabPointer( + display, + grab_input_window_, + False, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, + GrabModeAsync, + None, + None, + CurrentTime); XUngrabServer(display); if (ret != GrabSuccess) { - DLOG(ERROR) << "Grabbing new tab for dragging failed: " << ret; + DLOG(ERROR) << "Grabbing new tab for dragging failed: " + << ui::GetX11ErrorString(display, ret); return aura::client::MOVE_CANCELED; } @@ -118,6 +132,11 @@ void X11DesktopWindowMoveClient::EndMoveLoop() { Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); XUngrabPointer(display, CurrentTime); + base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow( + grab_input_window_); + root_window_ = NULL; + XDestroyWindow(display, grab_input_window_); + in_move_loop_ = false; quit_closure_.Run(); } diff --git a/ui/views/widget/x11_desktop_window_move_client.h b/ui/views/widget/x11_desktop_window_move_client.h index 9cb3a7c..0e3dae5 100644 --- a/ui/views/widget/x11_desktop_window_move_client.h +++ b/ui/views/widget/x11_desktop_window_move_client.h @@ -5,33 +5,35 @@ #ifndef UI_VIEWS_WIDGET_X11_DESKTOP_WINDOW_MOVE_CLIENT_H_ #define UI_VIEWS_WIDGET_X11_DESKTOP_WINDOW_MOVE_CLIENT_H_ +#include <X11/Xlib.h> + +// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. +#undef RootWindow + #include "base/callback.h" #include "base/compiler_specific.h" +#include "base/message_loop.h" #include "ui/aura/client/window_move_client.h" #include "ui/aura/event_filter.h" #include "ui/views/views_export.h" #include "ui/gfx/point.h" +namespace aura { +class RootWindow; +} + namespace views { // When we're dragging tabs, we need to manually position our window. class VIEWS_EXPORT X11DesktopWindowMoveClient - : public aura::EventFilter, + : public MessageLoop::Dispatcher, public aura::client::WindowMoveClient { public: X11DesktopWindowMoveClient(); virtual ~X11DesktopWindowMoveClient(); - // Overridden from aura::EventFilter: - virtual bool PreHandleKeyEvent(aura::Window* target, - ui::KeyEvent* event) OVERRIDE; - virtual bool PreHandleMouseEvent(aura::Window* target, - ui::MouseEvent* event) OVERRIDE; - virtual ui::TouchStatus PreHandleTouchEvent(aura::Window* target, - ui::TouchEvent* event) OVERRIDE; - virtual ui::EventResult PreHandleGestureEvent( - aura::Window* target, - ui::GestureEvent* event) OVERRIDE; + // Overridden from MessageLoop::Dispatcher: + virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; // Overridden from aura::client::WindowMoveClient: virtual aura::client::WindowMoveResult RunMoveLoop( @@ -43,6 +45,15 @@ class VIEWS_EXPORT X11DesktopWindowMoveClient // Are we running a nested message loop from RunMoveLoop()? bool in_move_loop_; + // An invisible InputOnly window . We create this window so we can track the + // cursor wherever it goes on screen during a drag, since normal windows + // don't receive pointer motion events outside of their bounds. + ::Window grab_input_window_; + + // We need to keep track of this so we can actually move it when reacting to + // events from |grab_input_window_| during Dispatch(). + aura::RootWindow* root_window_; + // Our cursor offset from the top left window origin when the drag // started. Used to calculate the window's new bounds relative to the current // location of the cursor. diff --git a/ui/views/widget/x11_window_event_filter.cc b/ui/views/widget/x11_window_event_filter.cc index 9d8d70b..83b2fc6 100644 --- a/ui/views/widget/x11_window_event_filter.cc +++ b/ui/views/widget/x11_window_event_filter.cc @@ -98,6 +98,9 @@ bool X11WindowEventFilter::PreHandleMouseEvent(aura::Window* target, if (event->type() != ui::ET_MOUSE_PRESSED) return false; + if (!event->IsLeftMouseButton()) + return false; + int component = target->delegate()->GetNonClientComponent(event->location()); if (component == HTCLIENT) |