Bug 1218552 - Fix GTK drag-and-drop coordinate scaling on HiDPI displays. r=karlt a=lizzard
authorBill McCloskey <billm@mozilla.com>
Mon, 26 Oct 2015 13:33:13 -0700
changeset 296728 581b3e8f954f
parent 296727 ec4b13420b71
child 296729 93d92b8c2b6c
push id5310
push usercbook@mozilla.com
push date2015-11-16 07:06 +0000
treeherdermozilla-beta@581b3e8f954f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt, lizzard
bugs1218552
milestone43.0
Bug 1218552 - Fix GTK drag-and-drop coordinate scaling on HiDPI displays. r=karlt a=lizzard
widget/gtk/nsDragService.cpp
widget/gtk/nsDragService.h
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/nsBaseDragService.cpp
widget/nsBaseDragService.h
--- a/widget/gtk/nsDragService.cpp
+++ b/widget/gtk/nsDragService.cpp
@@ -232,18 +232,20 @@ OnSourceGrabEventAfter(GtkWidget *widget
         if (sMotionEvent) {
             gdk_event_free(sMotionEvent);
         }
         sMotionEvent = gdk_event_copy(event);
 
         // Update the cursor position.  The last of these recorded gets used for
         // the eDragEnd event.
         nsDragService *dragService = static_cast<nsDragService*>(user_data);
-        dragService->SetDragEndPoint(nsIntPoint(event->motion.x_root,
-                                                event->motion.y_root));
+        gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
+        LayoutDeviceIntPoint p(floor(event->motion.x_root * scale + 0.5),
+                               floor(event->motion.y_root * scale + 0.5));
+        dragService->SetDragEndPoint(p);
     } else if (sMotionEvent && (event->type == GDK_KEY_PRESS ||
                                 event->type == GDK_KEY_RELEASE)) {
         // Update modifier state from key events.
         sMotionEvent->motion.state = event->key.state;
     } else {
         return;
     }
 
@@ -385,17 +387,17 @@ nsDragService::InvokeDragSession(nsIDOMN
         if (sGrabWidget) {
             g_object_ref(sGrabWidget);
             // Only motion and key events are required but connect to
             // "event-after" as this is never blocked by other handlers.
             g_signal_connect(sGrabWidget, "event-after",
                              G_CALLBACK(OnSourceGrabEventAfter), this);
         }
         // We don't have a drag end point yet.
-        mEndDragPoint = nsIntPoint(-1, -1);
+        mEndDragPoint = LayoutDeviceIntPoint(-1, -1);
     }
     else {
         rv = NS_ERROR_FAILURE;
     }
 
     gtk_target_list_unref(sourceList);
 
     return rv;
@@ -1384,18 +1386,19 @@ nsDragService::SourceEndDragSession(GdkD
         // or SourceEndDragSession on drag-failed
         return;
 
     if (mEndDragPoint.x < 0) {
         // We don't have a drag end point, so guess
         gint x, y;
         GdkDisplay* display = gdk_display_get_default();
         if (display) {
+            gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
             gdk_display_get_pointer(display, nullptr, &x, &y, nullptr);
-            SetDragEndPoint(nsIntPoint(x, y));
+            SetDragEndPoint(LayoutDeviceIntPoint(x * scale, y * scale));
         }
     }
 
     // Either the drag was aborted or the drop occurred outside the app.
     // The dropEffect of mDataTransfer is not updated for motion outside the
     // app, but is needed for the dragend event, so set it now.
 
     uint32_t dropEffect;
@@ -1433,17 +1436,17 @@ nsDragService::SourceEndDragSession(GdkD
         }
     }
 
     if (mDataTransfer) {
         mDataTransfer->SetDropEffectInt(dropEffect);
     }
 
     // Schedule the appropriate drag end dom events.
-    Schedule(eDragTaskSourceEnd, nullptr, nullptr, nsIntPoint(), 0);
+    Schedule(eDragTaskSourceEnd, nullptr, nullptr, LayoutDeviceIntPoint(), 0);
 }
 
 static void
 CreateUriList(nsISupportsArray *items, gchar **text, gint *length)
 {
     uint32_t i, count;
     GString *uriList = g_string_new(nullptr);
 
@@ -1763,17 +1766,17 @@ invisibleSourceDragEnd(GtkWidget        
 //
 // No Gecko drag events are dispatched (during nested event loops) while other
 // Gecko drag events are in flight.  This helps event handlers that may not
 // expect nested events, while accessing an event's dataTransfer for example.
 
 gboolean
 nsDragService::ScheduleMotionEvent(nsWindow *aWindow,
                                    GdkDragContext *aDragContext,
-                                   nsIntPoint aWindowPoint, guint aTime)
+                                   LayoutDeviceIntPoint aWindowPoint, guint aTime)
 {
     if (mScheduledTask == eDragTaskMotion) {
         // The drag source has sent another motion message before we've
         // replied to the previous.  That shouldn't happen with Xdnd.  The
         // spec for Motif drags is less clear, but we'll just update the
         // scheduled task with the new position reply only to the most
         // recent message.
         NS_WARNING("Drag Motion message received before previous reply was sent");
@@ -1786,42 +1789,42 @@ nsDragService::ScheduleMotionEvent(nsWin
 }
 
 void
 nsDragService::ScheduleLeaveEvent()
 {
     // We don't know at this stage whether a drop signal will immediately
     // follow.  If the drop signal gets sent it will happen before we return
     // to the main loop and the scheduled leave task will be replaced.
-    if (!Schedule(eDragTaskLeave, nullptr, nullptr, nsIntPoint(), 0)) {
+    if (!Schedule(eDragTaskLeave, nullptr, nullptr, LayoutDeviceIntPoint(), 0)) {
         NS_WARNING("Drag leave after drop");
     }        
 }
 
 gboolean
 nsDragService::ScheduleDropEvent(nsWindow *aWindow,
                                  GdkDragContext *aDragContext,
-                                 nsIntPoint aWindowPoint, guint aTime)
+                                 LayoutDeviceIntPoint aWindowPoint, guint aTime)
 {
     if (!Schedule(eDragTaskDrop, aWindow,
                   aDragContext, aWindowPoint, aTime)) {
         NS_WARNING("Additional drag drop ignored");
         return FALSE;        
     }
 
-    SetDragEndPoint(aWindowPoint + aWindow->WidgetToScreenOffsetUntyped());
+    SetDragEndPoint(aWindowPoint + aWindow->WidgetToScreenOffset());
 
     // We'll reply with gtk_drag_finish().
     return TRUE;
 }
 
 gboolean
 nsDragService::Schedule(DragTask aTask, nsWindow *aWindow,
                         GdkDragContext *aDragContext,
-                        nsIntPoint aWindowPoint, guint aTime)
+                        LayoutDeviceIntPoint aWindowPoint, guint aTime)
 {
     // If there is an existing leave or motion task scheduled, then that
     // will be replaced.  When the new task is run, it will dispatch
     // any necessary leave or motion events.
 
     // If aTask is eDragTaskSourceEnd, then it will replace even a scheduled
     // drop event (which could happen if the drop event has not been processed
     // within the allowed time).  Otherwise, if we haven't yet run a scheduled
--- a/widget/gtk/nsDragService.h
+++ b/widget/gtk/nsDragService.h
@@ -87,22 +87,22 @@ public:
                                       gint               aX,
                                       gint               aY,
                                       GtkSelectionData  *aSelection_data,
                                       guint              aInfo,
                                       guint32            aTime);
 
     gboolean ScheduleMotionEvent(nsWindow *aWindow,
                                  GdkDragContext *aDragContext,
-                                 nsIntPoint aWindowPoint,
+                                 mozilla::LayoutDeviceIntPoint aWindowPoint,
                                  guint aTime);
     void ScheduleLeaveEvent();
     gboolean ScheduleDropEvent(nsWindow *aWindow,
                                GdkDragContext *aDragContext,
-                               nsIntPoint aWindowPoint,
+                               mozilla::LayoutDeviceIntPoint aWindowPoint,
                                guint aTime);
 
     nsWindow* GetMostRecentDestWindow()
     {
         return mScheduledTask == eDragTaskNone ? mTargetWindow
             : mPendingWindow;
     }
 
@@ -145,25 +145,25 @@ private:
     // target/destination side vars
     // These variables keep track of the state of the current drag.
 
     // mPendingWindow, mPendingWindowPoint, mPendingDragContext, and
     // mPendingTime, carry information from the GTK signal that will be used
     // when the scheduled task is run.  mPendingWindow and mPendingDragContext
     // will be nullptr if the scheduled task is eDragTaskLeave.
     nsRefPtr<nsWindow> mPendingWindow;
-    nsIntPoint mPendingWindowPoint;
+    mozilla::LayoutDeviceIntPoint mPendingWindowPoint;
     nsCountedRef<GdkDragContext> mPendingDragContext;
     guint mPendingTime;
 
     // mTargetWindow and mTargetWindowPoint record the position of the last
     // eDragTaskMotion or eDragTaskDrop task that was run or is still running.
     // mTargetWindow is cleared once the drag has completed or left.
     nsRefPtr<nsWindow> mTargetWindow;
-    nsIntPoint mTargetWindowPoint;
+    mozilla::LayoutDeviceIntPoint mTargetWindowPoint;
     // mTargetWidget and mTargetDragContext are set only while dispatching
     // motion or drop events.  mTime records the corresponding timestamp.
     nsCountedRef<GtkWidget> mTargetWidget;
     nsCountedRef<GdkDragContext> mTargetDragContext;
     // mTargetDragContextForRemote is set while waiting for a reply from
     // a child process.
     nsCountedRef<GdkDragContext> mTargetDragContextForRemote;
     guint           mTargetTime;
@@ -201,17 +201,17 @@ private:
     bool SetAlphaPixmap(SourceSurface *aPixbuf,
                         GdkDragContext  *aContext,
                         int32_t          aXOffset,
                         int32_t          aYOffset,
                         const nsIntRect &dragRect);
 
     gboolean Schedule(DragTask aTask, nsWindow *aWindow,
                       GdkDragContext *aDragContext,
-                      nsIntPoint aWindowPoint, guint aTime);
+                      mozilla::LayoutDeviceIntPoint aWindowPoint, guint aTime);
 
     // Callback for g_idle_add_full() to run mScheduledTask.
     static gboolean TaskDispatchCallback(gpointer data);
     gboolean RunScheduledTask();
     void UpdateDragAction();
     void DispatchMotionEvents();
     void ReplyToDragMotion(GdkDragContext* aDragContext);
     gboolean DispatchDropEvent();
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -3292,26 +3292,26 @@ nsWindow::ThemeChanged()
             win->ThemeChanged();
         }
 
         children = children->next;
     }
 }
 
 void
-nsWindow::DispatchDragEvent(EventMessage aMsg, const nsIntPoint& aRefPoint,
+nsWindow::DispatchDragEvent(EventMessage aMsg, const LayoutDeviceIntPoint& aRefPoint,
                             guint aTime)
 {
     WidgetDragEvent event(true, aMsg, this);
 
     if (aMsg == eDragOver) {
         InitDragEvent(event);
     }
 
-    event.refPoint = LayoutDeviceIntPoint::FromUntyped(aRefPoint);
+    event.refPoint = aRefPoint;
     event.time = aTime;
     event.timeStamp = GetEventTimeStamp(aTime);
 
     DispatchInputEvent(&event);
 }
 
 void
 nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget,
@@ -5855,19 +5855,21 @@ drag_motion_event_cb(GtkWidget *aWidget,
     nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
 
     if (!innerMostWindow) {
         innerMostWindow = window;
     }
 
     LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow));
 
+    LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety });
+
     return nsDragService::GetInstance()->
         ScheduleMotionEvent(innerMostWindow, aDragContext,
-                            nsIntPoint(retx, rety), aTime);
+                            point, aTime);
 }
 
 static void
 drag_leave_event_cb(GtkWidget *aWidget,
                     GdkDragContext *aDragContext,
                     guint aTime,
                     gpointer aData)
 {
@@ -5925,19 +5927,21 @@ drag_drop_event_cb(GtkWidget *aWidget,
     nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
 
     if (!innerMostWindow) {
         innerMostWindow = window;
     }
 
     LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow));
 
+    LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety });
+
     return nsDragService::GetInstance()->
         ScheduleDropEvent(innerMostWindow, aDragContext,
-                          nsIntPoint(retx, rety), aTime);
+                          point, aTime);
 }
 
 static void
 drag_data_received_event_cb(GtkWidget *aWidget,
                             GdkDragContext *aDragContext,
                             gint aX,
                             gint aY,
                             GtkSelectionData  *aSelectionData,
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -255,17 +255,17 @@ public:
     MozContainer*      GetMozContainer() { return mContainer; }
     // GetMozContainerWidget returns the MozContainer even for undestroyed
     // descendant windows
     GtkWidget*         GetMozContainerWidget();
     GdkWindow*         GetGdkWindow() { return mGdkWindow; }
     bool               IsDestroyed() { return mIsDestroyed; }
 
     void               DispatchDragEvent(mozilla::EventMessage aMsg,
-                                         const nsIntPoint& aRefPoint,
+                                         const mozilla::LayoutDeviceIntPoint& aRefPoint,
                                          guint aTime);
     static void        UpdateDragStatus (GdkDragContext *aDragContext,
                                          nsIDragService *aDragService);
     // If this dispatched the keydown event actually, this returns TRUE,
     // otherwise, FALSE.
     bool               DispatchKeyDownEvent(GdkEventKey *aEvent,
                                             bool *aIsCancelled);
     mozilla::TimeStamp GetEventTimeStamp(guint32 aEventTime);
--- a/widget/nsBaseDragService.cpp
+++ b/widget/nsBaseDragService.cpp
@@ -212,17 +212,17 @@ nsBaseDragService::InvokeDragSession(nsI
                                      uint32_t aActionType)
 {
   NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG);
   NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
 
   // stash the document of the dom node
   aDOMNode->GetOwnerDocument(getter_AddRefs(mSourceDocument));
   mSourceNode = aDOMNode;
-  mEndDragPoint = nsIntPoint(0, 0);
+  mEndDragPoint = LayoutDeviceIntPoint(0, 0);
 
   // When the mouse goes down, the selection code starts a mouse
   // capture. However, this gets in the way of determining drag
   // feedback for things like trees because the event coordinates
   // are in the wrong coord system, so turn off mouse capture.
   nsIPresShell::ClearMouseCapture(nullptr);
 
   return NS_OK;
@@ -384,17 +384,17 @@ nsBaseDragService::EndDragSession(bool a
   mHasImage = false;
   mUserCancelled = false;
   mDragPopup = nullptr;
   mImage = nullptr;
   mImageX = 0;
   mImageY = 0;
   mScreenX = -1;
   mScreenY = -1;
-  mEndDragPoint = nsIntPoint(0, 0);
+  mEndDragPoint = LayoutDeviceIntPoint(0, 0);
   mInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage)
 {
--- a/widget/nsBaseDragService.h
+++ b/widget/nsBaseDragService.h
@@ -48,17 +48,24 @@ public:
 
   //nsISupports
   NS_DECL_ISUPPORTS
 
   //nsIDragSession and nsIDragService
   NS_DECL_NSIDRAGSERVICE
   NS_DECL_NSIDRAGSESSION
 
-  void SetDragEndPoint(nsIntPoint aEndDragPoint) { mEndDragPoint = aEndDragPoint; }
+  void SetDragEndPoint(nsIntPoint aEndDragPoint)
+  {
+    mEndDragPoint = mozilla::LayoutDeviceIntPoint::FromUntyped(aEndDragPoint);
+  }
+  void SetDragEndPoint(mozilla::LayoutDeviceIntPoint aEndDragPoint)
+  {
+    mEndDragPoint = aEndDragPoint;
+  }
 
   uint16_t GetInputSource() { return mInputSource; }
 
   int32_t TakeChildProcessDragAction();
 
 protected:
   virtual ~nsBaseDragService();
 
@@ -157,17 +164,17 @@ protected:
 
   // the screen position where drag gesture occurred, used for positioning the
   // drag image when no image is specified. If a value is -1, no event was
   // supplied so the screen position is not known
   int32_t mScreenX;
   int32_t mScreenY;
 
   // the screen position where the drag ended
-  nsIntPoint mEndDragPoint;
+  mozilla::LayoutDeviceIntPoint mEndDragPoint;
 
   uint32_t mSuppressLevel;
 
   // The input source of the drag event. Possible values are from nsIDOMMouseEvent.
   uint16_t mInputSource;
 
   nsTArray<nsRefPtr<mozilla::dom::ContentParent>> mChildProcesses;
 };