summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorerg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-01 11:47:48 +0000
committererg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-01 11:47:48 +0000
commit1260757ead730630481a8790748da152bbc14537 (patch)
tree4ee23af04c32ee486061420a336202ff36a1a14e /ui
parentc40785ecdbc4891fe3d42096238cbb1ed37d3ff8 (diff)
downloadchromium_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.cc6
-rw-r--r--ui/base/x/x11_util.h3
-rw-r--r--ui/views/widget/desktop_root_window_host_linux.cc36
-rw-r--r--ui/views/widget/x11_desktop_window_move_client.cc123
-rw-r--r--ui/views/widget/x11_desktop_window_move_client.h33
-rw-r--r--ui/views/widget/x11_window_event_filter.cc3
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)