Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / views / widget / desktop_aura / x11_whole_screen_move_loop.cc
index 5bbfb29..6c0b3c6 100644 (file)
@@ -10,7 +10,6 @@
 
 #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"
@@ -59,15 +61,14 @@ X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(
       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;
@@ -77,7 +78,23 @@ void X11WholeScreenMoveLoop::DispatchMouseMovement() {
   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
@@ -101,7 +118,7 @@ uint32_t X11WholeScreenMoveLoop::Dispatch(const base::NativeEvent& event) {
             base::Bind(&X11WholeScreenMoveLoop::DispatchMouseMovement,
                        weak_factory_.GetWeakPtr()));
       }
-      break;
+      return ui::POST_DISPATCH_NONE;
     }
     case ButtonRelease: {
       if (xev->xbutton.button == Button1) {
@@ -110,23 +127,61 @@ uint32_t X11WholeScreenMoveLoop::Dispatch(const base::NativeEvent& event) {
         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
@@ -136,24 +191,24 @@ bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source,
   {
     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
@@ -164,12 +219,15 @@ bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source,
     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) {
@@ -201,14 +259,18 @@ void X11WholeScreenMoveLoop::EndMoveLoop() {
 
   // 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();
@@ -242,6 +304,7 @@ bool X11WholeScreenMoveLoop::GrabPointerAndKeyboard(gfx::NativeCursor cursor) {
     DLOG(ERROR) << "Grabbing pointer for dragging failed: "
                 << ui::GetX11ErrorString(display, ret);
   } else {
+    has_grab_ = true;
     XUngrabKeyboard(display, CurrentTime);
     ret = XGrabKeyboard(
         display,
@@ -279,7 +342,7 @@ Window X11WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) {
                                 0, CopyFromParent, InputOnly, CopyFromParent,
                                 attribute_mask, &swa);
   XMapRaised(display, window);
-  base::MessagePumpX11::Current()->BlockUntilWindowMapped(window);
+  ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(window);
   return window;
 }
 
@@ -310,17 +373,9 @@ void X11WholeScreenMoveLoop::CreateDragImageWindow() {
 }
 
 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) {