From 3c12482df08f3bb4b4bc2f2184985790f78d3351 Mon Sep 17 00:00:00 2001
From: Paul Olav Tvete <paul.tvete@theqtcompany.com>
Date: Mon, 22 Jun 2015 12:26:18 +0200
Subject: [PATCH] Fix highdpi drag-n-drop for X11

Task-number: QTBUG-46615
Change-Id: Iad548e62a580d6fbd15b7a826116a53ce23b4b8b
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
---
 src/gui/kernel/qsimpledrag.cpp         | 47 ++++++++++++++++++++++----------
 src/gui/kernel/qsimpledrag_p.h         | 10 ++++---
 src/plugins/platforms/xcb/qxcbdrag.cpp | 50 ++++++++++++++++++----------------
 src/plugins/platforms/xcb/qxcbdrag.h   | 17 ++++++------
 4 files changed, 72 insertions(+), 52 deletions(-)

diff --git a/src/gui/kernel/qsimpledrag.cpp b/src/gui/kernel/qsimpledrag.cpp
index b850f53..6e574d8 100644
--- a/src/gui/kernel/qsimpledrag.cpp
+++ b/src/gui/kernel/qsimpledrag.cpp
@@ -55,6 +55,7 @@
 #include <private/qdnd_p.h>
 
 #include <private/qshapedpixmapdndwindow_p.h>
+#include <private/qhighdpiscaling_p.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -106,6 +107,12 @@ void QBasicDrag::disableEventFilter()
     qApp->removeEventFilter(this);
 }
 
+
+static inline QPoint getNativeMousePos(QEvent *e, QObject *o)
+{
+    return QHighDpi::toNativePixels(static_cast<QMouseEvent *>(e)->globalPos(), qobject_cast<QWindow*>(o));
+}
+
 bool QBasicDrag::eventFilter(QObject *o, QEvent *e)
 {
     Q_UNUSED(o);
@@ -139,19 +146,21 @@ bool QBasicDrag::eventFilter(QObject *o, QEvent *e)
         }
 
         case QEvent::MouseMove:
-            move(static_cast<QMouseEvent *>(e));
+        {
+            QPoint nativePosition = getNativeMousePos(e, o);
+            move(nativePosition);
             return true; // Eat all mouse events
-
+        }
         case QEvent::MouseButtonRelease:
             disableEventFilter();
             if (canDrop()) {
-                drop(static_cast<QMouseEvent *>(e));
+                QPoint nativePosition = getNativeMousePos(e, o);
+                drop(nativePosition);
             } else {
                 cancel();
             }
             exitDndEventLoop();
             return true; // Eat all mouse events
-
         case QEvent::MouseButtonPress:
         case QEvent::MouseButtonDblClick:
         case QEvent::Wheel:
@@ -227,13 +236,18 @@ void QBasicDrag::cancel()
     m_drag_icon_window->setVisible(false);
 }
 
-void QBasicDrag::move(const QMouseEvent *e)
+/*!
+  Move the drag label to \a globalPos, which is
+  interpreted in device independent coordinates. Typically called from reimplementations of move().
+ */
+
+void QBasicDrag::moveShapedPixmapWindow(const QPoint &globalPos)
 {
     if (m_drag)
-        m_drag_icon_window->updateGeometry(e->globalPos());
+        m_drag_icon_window->updateGeometry(globalPos);
 }
 
-void QBasicDrag::drop(const QMouseEvent *)
+void QBasicDrag::drop(const QPoint &)
 {
     disableEventFilter();
     restoreCursor();
@@ -330,14 +344,15 @@ void QSimpleDrag::cancel()
     }
 }
 
-void QSimpleDrag::move(const QMouseEvent *me)
+void QSimpleDrag::move(const QPoint &globalPos)
 {
-    QBasicDrag::move(me);
-    QWindow *window = topLevelAt(me->globalPos());
+    //### not high-DPI aware
+    moveShapedPixmapWindow(globalPos);
+    QWindow *window = topLevelAt(globalPos);
     if (!window)
         return;
 
-    const QPoint pos = me->globalPos() - window->geometry().topLeft();
+    const QPoint pos = globalPos - window->geometry().topLeft();
     const QPlatformDragQtResponse qt_response =
         QWindowSystemInterface::handleDrag(window, drag()->mimeData(), pos, drag()->supportedActions());
 
@@ -345,14 +360,16 @@ void QSimpleDrag::move(const QMouseEvent *me)
     setCanDrop(qt_response.isAccepted());
 }
 
-void QSimpleDrag::drop(const QMouseEvent *me)
+void QSimpleDrag::drop(const QPoint &globalPos)
 {
-    QBasicDrag::drop(me);
-    QWindow *window = topLevelAt(me->globalPos());
+    //### not high-DPI aware
+
+    QBasicDrag::drop(globalPos);
+    QWindow *window = topLevelAt(globalPos);
     if (!window)
         return;
 
-    const QPoint pos = me->globalPos() - window->geometry().topLeft();
+    const QPoint pos = globalPos - window->geometry().topLeft();
     const QPlatformDropQtResponse response =
             QWindowSystemInterface::handleDrop(window, drag()->mimeData(),pos, drag()->supportedActions());
     if (response.isAccepted()) {
diff --git a/src/gui/kernel/qsimpledrag_p.h b/src/gui/kernel/qsimpledrag_p.h
index 7812f8b..a011475 100644
--- a/src/gui/kernel/qsimpledrag_p.h
+++ b/src/gui/kernel/qsimpledrag_p.h
@@ -73,10 +73,12 @@ protected:
 
     virtual void startDrag();
     virtual void cancel();
-    virtual void move(const QMouseEvent *me);
-    virtual void drop(const QMouseEvent *me);
+    virtual void move(const QPoint &globalPos) = 0;
+    virtual void drop(const QPoint &globalPos) = 0;
     virtual void endDrag();
 
+
+    void moveShapedPixmapWindow(const QPoint &deviceIndependentPosition);
     QShapedPixmapWindow *shapedPixmapWindow() const { return m_drag_icon_window; }
     void updateCursor(Qt::DropAction action);
 
@@ -111,8 +113,8 @@ public:
 protected:
     virtual void startDrag() Q_DECL_OVERRIDE;
     virtual void cancel() Q_DECL_OVERRIDE;
-    virtual void move(const QMouseEvent *me) Q_DECL_OVERRIDE;
-    virtual void drop(const QMouseEvent *me) Q_DECL_OVERRIDE;
+    virtual void move(const QPoint &globalPos) Q_DECL_OVERRIDE;
+    virtual void drop(const QPoint &globalPos) Q_DECL_OVERRIDE;
 
 private:
     QWindow *m_current_window;
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index ebbd879..a3e646e 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -51,6 +51,7 @@
 
 #include <private/qshapedpixmapdndwindow_p.h>
 #include <private/qsimpledrag_p.h>
+#include <private/qhighdpiscaling_p.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -71,6 +72,11 @@ QT_BEGIN_NAMESPACE
 
 const int xdnd_version = 5;
 
+static inline xcb_window_t xcb_window(QPlatformWindow *w)
+{
+    return static_cast<QXcbWindow *>(w)->xcb_window();
+}
+
 static inline xcb_window_t xcb_window(QWindow *w)
 {
     return static_cast<QXcbWindow *>(w->handle())->xcb_window();
@@ -296,14 +302,8 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md
     return 0;
 }
 
-void QXcbDrag::move(const QMouseEvent *me)
+void QXcbDrag::move(const QPoint &globalPos)
 {
-    // The mouse event is in the coordinate system of the window that started the drag.
-    // We do not know which window that was at this point, so we just use the device pixel ratio
-    // of the QGuiApplication. This will break once we support screens with different DPR. Fixing
-    // this properly requires some redesign of the drag and drop architecture.
-    QBasicDrag::move(me);
-    QPoint globalPos = me->globalPos();
 
     if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid())
         return;
@@ -317,6 +317,8 @@ void QXcbDrag::move(const QMouseEvent *me)
         }
     }
 
+    QBasicDrag::moveShapedPixmapWindow(QHighDpiScaling::mapPositionFromNative(globalPos, screen));
+
     if (screen != current_screen) {
         // ### need to recreate the shaped pixmap window?
 //    int screen = QCursor::x11Screen();
@@ -442,7 +444,7 @@ void QXcbDrag::move(const QMouseEvent *me)
 
             DEBUG() << "sending Xdnd enter source=" << enter.data.data32[0];
             if (w)
-                handleEnter(w->window(), &enter);
+                handleEnter(w, &enter);
             else if (target)
                 xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&enter);
             waiting_for_status = false;
@@ -470,15 +472,15 @@ void QXcbDrag::move(const QMouseEvent *me)
         source_time = connection()->time();
 
         if (w)
-            handle_xdnd_position(w->window(), &move);
+            handle_xdnd_position(w, &move);
         else
             xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move);
     }
 }
 
-void QXcbDrag::drop(const QMouseEvent *event)
+void QXcbDrag::drop(const QPoint &globalPos)
 {
-    QBasicDrag::drop(event);
+    QBasicDrag::drop(globalPos);
 
     if (!current_target)
         return;
@@ -504,7 +506,7 @@ void QXcbDrag::drop(const QMouseEvent *event)
         connection()->time(),
         current_target,
         current_proxy_target,
-        (w ? w->window() : 0),
+        w,
 //        current_embeddig_widget,
         currentDrag(),
         QTime::currentTime()
@@ -517,7 +519,7 @@ void QXcbDrag::drop(const QMouseEvent *event)
     }
 
     if (w) {
-        handleDrop(w->window(), &drop);
+        handleDrop(w, &drop);
     } else {
         xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&drop);
     }
@@ -663,7 +665,7 @@ static bool checkEmbedded(QWidget* w, const XEvent* xe)
 #endif
 
 
-void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *event)
+void QXcbDrag::handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event)
 {
     Q_UNUSED(window);
     DEBUG() << "handleEnter" << window;
@@ -704,14 +706,14 @@ void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *ev
         DEBUG() << "    " << connection()->atomName(xdnd_types.at(i));
 }
 
-void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *e)
+void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *e)
 {
     QPoint p((e->data.data32[2] & 0xffff0000) >> 16, e->data.data32[2] & 0x0000ffff);
     Q_ASSERT(w);
     QRect geometry = w->geometry();
     p -= geometry.topLeft();
 
-    if (!w || (w->type() == Qt::Desktop))
+    if (!w || !w->window() || (w->window()->type() == Qt::Desktop))
         return;
 
     if (e->data.data32[0] != xdnd_dragsource) {
@@ -720,7 +722,7 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t
     }
 
     currentPosition = p;
-    currentWindow = w;
+    currentWindow = w->window();
 
     // timestamp from the source
     if (e->data.data32[3] != XCB_NONE) {
@@ -737,7 +739,7 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t
         supported_actions = Qt::DropActions(toDropAction(e->data.data32[4]));
     }
 
-    QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(w,dropData,p,supported_actions);
+    QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(w->window(),dropData,p,supported_actions);
     QRect answerRect(p + geometry.topLeft(), QSize(1,1));
     answerRect = qt_response.answerRect().translated(geometry.topLeft()).intersected(geometry);
 
@@ -793,7 +795,7 @@ namespace
     };
 }
 
-void QXcbDrag::handlePosition(QWindow * w, const xcb_client_message_event_t *event)
+void QXcbDrag::handlePosition(QPlatformWindow * w, const xcb_client_message_event_t *event)
 {
     xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event);
     xcb_generic_event_t *nextEvent;
@@ -856,10 +858,10 @@ void QXcbDrag::handleStatus(const xcb_client_message_event_t *event)
     DEBUG("xdndHandleStatus end");
 }
 
-void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event)
+void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event)
 {
     DEBUG("xdnd leave");
-    if (!currentWindow || w != currentWindow.data())
+    if (!currentWindow || w != currentWindow.data()->handle())
         return; // sanity
 
     // ###
@@ -874,7 +876,7 @@ void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event)
         DEBUG("xdnd drag leave from unexpected source (%x not %x", event->data.data32[0], xdnd_dragsource);
     }
 
-    QWindowSystemInterface::handleDrag(w,0,QPoint(),Qt::IgnoreAction);
+    QWindowSystemInterface::handleDrag(w->window(),0,QPoint(),Qt::IgnoreAction);
 
     xdnd_dragsource = 0;
     xdnd_types.clear();
@@ -904,7 +906,7 @@ void QXcbDrag::send_leave()
         w = 0;
 
     if (w)
-        handleLeave(w->window(), (const xcb_client_message_event_t *)&leave);
+        handleLeave(w, (const xcb_client_message_event_t *)&leave);
     else
         xcb_send_event(xcb_connection(), false,current_proxy_target,
                        XCB_EVENT_MASK_NO_EVENT, (const char *)&leave);
@@ -915,7 +917,7 @@ void QXcbDrag::send_leave()
     waiting_for_status = false;
 }
 
-void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event)
+void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event)
 {
     DEBUG("xdndHandleDrop");
     if (!currentWindow) {
diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h
index b307634..699d402 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.h
+++ b/src/plugins/platforms/xcb/qxcbdrag.h
@@ -53,7 +53,6 @@ QT_BEGIN_NAMESPACE
 
 #ifndef QT_NO_DRAGANDDROP
 
-class QMouseEvent;
 class QWindow;
 class QPlatformWindow;
 class QXcbConnection;
@@ -73,14 +72,14 @@ public:
 
     void startDrag() Q_DECL_OVERRIDE;
     void cancel() Q_DECL_OVERRIDE;
-    void move(const QMouseEvent *me) Q_DECL_OVERRIDE;
-    void drop(const QMouseEvent *me) Q_DECL_OVERRIDE;
+    void move(const QPoint &globalPos) Q_DECL_OVERRIDE;
+    void drop(const QPoint &globalPos) Q_DECL_OVERRIDE;
     void endDrag() Q_DECL_OVERRIDE;
 
-    void handleEnter(QWindow *window, const xcb_client_message_event_t *event);
-    void handlePosition(QWindow *w, const xcb_client_message_event_t *event);
-    void handleLeave(QWindow *w, const xcb_client_message_event_t *event);
-    void handleDrop(QWindow *, const xcb_client_message_event_t *event);
+    void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event);
+    void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event);
+    void handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event);
+    void handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event);
 
     void handleStatus(const xcb_client_message_event_t *event);
     void handleSelectionRequest(const xcb_selection_request_event_t *event);
@@ -100,7 +99,7 @@ private:
 
     void init();
 
-    void handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *event);
+    void handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *event);
     void handle_xdnd_status(const xcb_client_message_event_t *event);
     void send_leave();
 
@@ -147,7 +146,7 @@ private:
         xcb_timestamp_t timestamp;
         xcb_window_t target;
         xcb_window_t proxy_target;
-        QWindow *targetWindow;
+        QPlatformWindow *targetWindow;
 //        QWidget *embedding_widget;
         QPointer<QDrag> drag;
         QTime time;
-- 
2.6.2

