Bug 1218552 - Fix GTK drag-and-drop coordinate scaling on HiDPI displays. r=karlt a=lizzard
--- 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;
};