#include "base/bind.h"
#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_pump_x11.h"
#include "base/run_loop.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/aura/env.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/event.h"
+#include "ui/events/event_utils.h"
#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"
#include "ui/gfx/screen.h"
#include "ui/views/controls/image_view.h"
in_move_loop_(false),
should_reset_mouse_flags_(false),
grab_input_window_(None),
+ canceled_(false),
+ has_grab_(false),
weak_factory_(this) {
last_xmotion_.type = LASTEvent;
}
X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() {}
-////////////////////////////////////////////////////////////////////////////////
-// DesktopWindowTreeHostLinux, MessagePumpDispatcher implementation:
-
void X11WholeScreenMoveLoop::DispatchMouseMovement() {
if (!weak_factory_.HasWeakPtrs())
return;
last_xmotion_.type = LASTEvent;
}
-uint32_t X11WholeScreenMoveLoop::Dispatch(const base::NativeEvent& event) {
+////////////////////////////////////////////////////////////////////////////////
+// DesktopWindowTreeHostLinux, ui::PlatformEventDispatcher implementation:
+
+bool X11WholeScreenMoveLoop::CanDispatchEvent(const ui::PlatformEvent& event) {
+ return in_move_loop_;
+}
+
+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.
+ 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
base::Bind(&X11WholeScreenMoveLoop::DispatchMouseMovement,
weak_factory_.GetWeakPtr()));
}
- break;
+ return ui::POST_DISPATCH_NONE;
}
case ButtonRelease: {
if (xev->xbutton.button == Button1) {
DispatchMouseMovement();
delegate_->OnMouseReleased();
}
- break;
+ return ui::POST_DISPATCH_NONE;
}
case KeyPress: {
- if (ui::KeyboardCodeFromXKeyEvent(xev) == ui::VKEY_ESCAPE)
+ if (ui::KeyboardCodeFromXKeyEvent(xev) == ui::VKEY_ESCAPE) {
+ canceled_ = true;
EndMoveLoop();
+ return ui::POST_DISPATCH_NONE;
+ }
break;
}
+ case FocusOut: {
+ if (xev->xfocus.mode != NotifyGrab)
+ has_grab_ = false;
+ break;
+ }
+ case GenericEvent: {
+ ui::EventType type = ui::EventTypeFromNative(xev);
+ switch (type) {
+ case ui::ET_MOUSE_MOVED:
+ case ui::ET_MOUSE_DRAGGED:
+ case ui::ET_MOUSE_RELEASED: {
+ XEvent xevent = {0};
+ if (type == ui::ET_MOUSE_RELEASED) {
+ xevent.type = ButtonRelease;
+ xevent.xbutton.button = ui::EventButtonFromNative(xev);
+ } else {
+ xevent.type = MotionNotify;
+ }
+ xevent.xany.display = xev->xgeneric.display;
+ xevent.xany.window = grab_input_window_;
+ // The fields used below are in the same place for all of events
+ // above. Using xmotion from XEvent's unions to avoid repeating
+ // the code.
+ xevent.xmotion.root = DefaultRootWindow(xev->xgeneric.display);
+ xevent.xmotion.time = ui::EventTimeFromNative(xev).InMilliseconds();
+ 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;
+ }
+ default:
+ break;
+ }
+ }
}
- return POST_DISPATCH_NONE;
+ return (event->xany.window == grab_input_window_) ?
+ ui::POST_DISPATCH_NONE : ui::POST_DISPATCH_PERFORM_DEFAULT;
}
-////////////////////////////////////////////////////////////////////////////////
-// DesktopWindowTreeHostLinux, aura::client::WindowMoveClient implementation:
-
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
{
ScopedCapturer capturer(source->GetHost());
- DCHECK(!in_move_loop_); // Can only handle one nested loop at a time.
- in_move_loop_ = true;
-
- XDisplay* display = gfx::GetXDisplay();
-
- grab_input_window_ = CreateDragInputWindow(display);
- if (!drag_image_.isNull() && CheckIfIconValid())
- CreateDragImageWindow();
- base::MessagePumpX11::Current()->AddDispatcherForWindow(
- this, grab_input_window_);
+ 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.
}
// TODO(varkha): Consider integrating GrabPointerAndKeyboard with
// ScopedCapturer to avoid possibility of logically keeping multiple grabs.
- if (!GrabPointerAndKeyboard(cursor))
+ if (!GrabPointerAndKeyboard(cursor)) {
+ XDestroyWindow(gfx::GetXDisplay(), grab_input_window_);
return false;
+ }
+
+ scoped_ptr<ui::ScopedEventDispatcher> old_dispatcher =
+ nested_dispatcher_.Pass();
+ nested_dispatcher_ =
+ ui::PlatformEventSource::GetInstance()->OverrideDispatcher(this);
+ if (!drag_image_.isNull() && CheckIfIconValid())
+ CreateDragImageWindow();
// 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
should_reset_mouse_flags_ = true;
}
+ in_move_loop_ = true;
+ canceled_ = false;
base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
- return true;
+ nested_dispatcher_ = old_dispatcher.Pass();
+ return !canceled_;
}
void X11WholeScreenMoveLoop::UpdateCursor(gfx::NativeCursor cursor) {
// Ungrab before we let go of the window.
XDisplay* display = gfx::GetXDisplay();
- XUngrabPointer(display, CurrentTime);
- XUngrabKeyboard(display, CurrentTime);
+ // Only ungrab pointer if capture was not switched to another window.
+ if (has_grab_) {
+ XUngrabPointer(display, CurrentTime);
+ XUngrabKeyboard(display, CurrentTime);
+ }
- base::MessagePumpX11::Current()->RemoveDispatcherForWindow(
- grab_input_window_);
+ // Restore the previous dispatcher.
+ nested_dispatcher_.reset();
drag_widget_.reset();
delegate_->OnMoveLoopEnded();
XDestroyWindow(display, grab_input_window_);
+ grab_input_window_ = None;
in_move_loop_ = false;
quit_closure_.Run();
DLOG(ERROR) << "Grabbing pointer for dragging failed: "
<< ui::GetX11ErrorString(display, ret);
} else {
+ has_grab_ = true;
XUngrabKeyboard(display, CurrentTime);
ret = XGrabKeyboard(
display,
0, CopyFromParent, InputOnly, CopyFromParent,
attribute_mask, &swa);
XMapRaised(display, window);
- base::MessagePumpX11::Current()->BlockUntilWindowMapped(window);
+ ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(window);
return window;
}
}
bool X11WholeScreenMoveLoop::CheckIfIconValid() {
- // TODO(erg): I've tried at least five different strategies for trying to
- // build a mask based off the alpha channel. While all of them have worked,
- // none of them have been performant and introduced multiple second
- // delays. (I spent a day getting a rectangle segmentation algorithm polished
- // here...and then found that even through I had the rectangle extraction
- // down to mere milliseconds, SkRegion still fell over on the number of
- // rectangles.)
- //
- // Creating a mask here near instantaneously should be possible, as GTK does
- // it, but I've blown days on this and I'm punting now.
-
+ // Because we need a GL context per window, we do a quick check so that we
+ // don't make another context if the window would just be displaying a mostly
+ // transparent image.
const SkBitmap* in_bitmap = drag_image_.bitmap();
SkAutoLockPixels in_lock(*in_bitmap);
for (int y = 0; y < in_bitmap->height(); ++y) {