summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndre de la Rocha <andre.rocha@qt.io>2018-10-16 15:06:04 +0200
committerAndre de la Rocha <andre.rocha@qt.io>2018-10-18 12:38:37 +0000
commit83d56811ec95dc27d095da0fb55632ea46e4bea3 (patch)
tree8538c9ce1d66098943e37f401aa1c2d481ebfa05
parent57b09b903e015bb192c2d1a0a1eb0fa308483c9c (diff)
Windows QPA: Fix Drag&Drop from touchscreen/pen
The Drag&Drop functionality had stopped working with touchscreen/pen after the WM_POINTER-based input handling was added. The Drag&Drop functionality internally uses the DoDragDrop() WIN32 call which, according to Microsoft docs, is not supported for invocation inside handlers for touch/pen messages, and should be invoked in handlers for mouse messages that are synthesized by the OS afterwards. The result was that when DoDragDrop (which is a blocking function with its own event loop) was called it would hang ignoring all touch/pen messages until a mouse/touchpad message arrived. This change implements a workaround for this issue by enqueuing Qt touch/pen events that would be generated inside the pointer message handler, and that could start a Drag&Drop operation, and only producing them after the OS sends the associated mouse messages. Task-number: QTBUG-70887 Change-Id: Id45e0ecc70358ba250de9b3268856781ed21c9dd Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.cpp4
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.h2
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp149
3 files changed, 145 insertions, 10 deletions
diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp
index b7d225cb00..ee82b2f022 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.cpp
+++ b/src/plugins/platforms/windows/qwindowsdrag.cpp
@@ -652,6 +652,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState,
*/
bool QWindowsDrag::m_canceled = false;
+bool QWindowsDrag::m_dragging = false;
QWindowsDrag::QWindowsDrag() = default;
@@ -699,7 +700,10 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag)
const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x"
<< hex << int(possibleActions) << "effects=0x" << allowedEffects << dec;
+ // Indicate message handlers we are in DoDragDrop() event loop.
+ QWindowsDrag::m_dragging = true;
const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect);
+ QWindowsDrag::m_dragging = false;
const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect();
if (r == DRAGDROP_S_DROP) {
if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
diff --git a/src/plugins/platforms/windows/qwindowsdrag.h b/src/plugins/platforms/windows/qwindowsdrag.h
index f116e50cbf..5f30c59882 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.h
+++ b/src/plugins/platforms/windows/qwindowsdrag.h
@@ -92,6 +92,7 @@ public:
static QWindowsDrag *instance();
void cancelDrag() override { QWindowsDrag::m_canceled = true; }
static bool isCanceled() { return QWindowsDrag::m_canceled; }
+ static bool isDragging() { return QWindowsDrag::m_dragging; }
IDataObject *dropDataObject() const { return m_dropDataObject; }
void setDropDataObject(IDataObject *dataObject) { m_dropDataObject = dataObject; }
@@ -102,6 +103,7 @@ public:
private:
static bool m_canceled;
+ static bool m_dragging;
QWindowsDropMimeData m_dropData;
IDataObject *m_dropDataObject = nullptr;
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
index c5acc38e7c..09eadb247a 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -50,6 +50,9 @@
#include "qwindowswindow.h"
#include "qwindowsintegration.h"
#include "qwindowsscreen.h"
+#if QT_CONFIG(draganddrop)
+# include "qwindowsdrag.h"
+#endif
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/qguiapplication.h>
@@ -60,6 +63,7 @@
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/qqueue.h>
#include <algorithm>
@@ -75,6 +79,111 @@ enum {
QT_PT_TOUCHPAD = 5, // MinGW is missing PT_TOUCHPAD
};
+struct PointerTouchEventInfo {
+ QPointer<QWindow> window;
+ QList<QWindowSystemInterface::TouchPoint> points;
+ Qt::KeyboardModifiers modifiers;
+};
+
+struct PointerTabletEventInfo {
+ QPointer<QWindow> window;
+ QPointF local;
+ QPointF global;
+ int device;
+ int pointerType;
+ Qt::MouseButtons buttons;
+ qreal pressure;
+ int xTilt;
+ int yTilt;
+ qreal tangentialPressure;
+ qreal rotation;
+ int z;
+ qint64 uid;
+ Qt::KeyboardModifiers modifiers;
+};
+
+static QQueue<PointerTouchEventInfo> touchEventQueue;
+static QQueue<PointerTabletEventInfo> tabletEventQueue;
+
+static void enqueueTouchEvent(QWindow *window,
+ const QList<QWindowSystemInterface::TouchPoint> &points,
+ Qt::KeyboardModifiers modifiers)
+{
+ PointerTouchEventInfo eventInfo;
+ eventInfo.window = window;
+ eventInfo.points = points;
+ eventInfo.modifiers = modifiers;
+ touchEventQueue.enqueue(eventInfo);
+}
+
+static void enqueueTabletEvent(QWindow *window, const QPointF &local, const QPointF &global,
+ int device, int pointerType, Qt::MouseButtons buttons, qreal pressure,
+ int xTilt, int yTilt, qreal tangentialPressure, qreal rotation,
+ int z, qint64 uid, Qt::KeyboardModifiers modifiers)
+{
+ PointerTabletEventInfo eventInfo;
+ eventInfo.window = window;
+ eventInfo.local = local;
+ eventInfo.global = global;
+ eventInfo.device = device;
+ eventInfo.pointerType = pointerType;
+ eventInfo.buttons = buttons;
+ eventInfo.pressure = pressure;
+ eventInfo.xTilt = xTilt;
+ eventInfo.yTilt = yTilt;
+ eventInfo.tangentialPressure = tangentialPressure;
+ eventInfo.rotation = rotation;
+ eventInfo.z = z;
+ eventInfo.uid = uid;
+ eventInfo.modifiers = modifiers;
+ tabletEventQueue.enqueue(eventInfo);
+}
+
+static void flushTouchEvents(QTouchDevice *touchDevice)
+{
+ while (!touchEventQueue.isEmpty()) {
+ PointerTouchEventInfo eventInfo = touchEventQueue.dequeue();
+ if (eventInfo.window) {
+ QWindowSystemInterface::handleTouchEvent(eventInfo.window,
+ touchDevice,
+ eventInfo.points,
+ eventInfo.modifiers);
+ }
+ }
+}
+
+static void flushTabletEvents()
+{
+ while (!tabletEventQueue.isEmpty()) {
+ PointerTabletEventInfo eventInfo = tabletEventQueue.dequeue();
+ if (eventInfo.window) {
+ QWindowSystemInterface::handleTabletEvent(eventInfo.window,
+ eventInfo.local,
+ eventInfo.global,
+ eventInfo.device,
+ eventInfo.pointerType,
+ eventInfo.buttons,
+ eventInfo.pressure,
+ eventInfo.xTilt,
+ eventInfo.yTilt,
+ eventInfo.tangentialPressure,
+ eventInfo.rotation,
+ eventInfo.z,
+ eventInfo.uid,
+ eventInfo.modifiers);
+ }
+ }
+}
+
+static bool draggingActive()
+{
+#if QT_CONFIG(draganddrop)
+ return QWindowsDrag::isDragging();
+#else
+ return false;
+#endif
+}
+
bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result)
{
*result = 0;
@@ -426,6 +535,9 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (et & QtWindows::NonClientEventFlag)
return false; // Let DefWindowProc() handle Non Client messages.
+ if (draggingActive())
+ return false; // Let DoDragDrop() loop handle it.
+
if (count < 1)
return false;
@@ -452,6 +564,8 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
QList<QWindowSystemInterface::TouchPoint> touchPoints;
+ bool primaryPointer = false;
+
if (QWindowsContext::verbose > 1)
qCDebug(lcQpaEvents).noquote().nospace() << showbase
<< __FUNCTION__
@@ -494,16 +608,23 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
touchPoint.state = stationaryTouchPoint ? Qt::TouchPointStationary : Qt::TouchPointMoved;
m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition);
}
+ if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY)
+ primaryPointer = true;
+
touchPoints.append(touchPoint);
// Avoid getting repeated messages for this frame if there are multiple pointerIds
QWindowsContext::user32dll.skipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId);
}
-
- QWindowSystemInterface::handleTouchEvent(window, m_touchDevice, touchPoints,
- QWindowsKeyMapper::queryKeyboardModifiers());
-
- return true;
+ if (primaryPointer) {
+ // Postpone event delivery to avoid hanging inside DoDragDrop().
+ // Only the primary pointer will generate mouse messages.
+ enqueueTouchEvent(window, touchPoints, QWindowsKeyMapper::queryKeyboardModifiers());
+ } else {
+ QWindowSystemInterface::handleTouchEvent(window, m_touchDevice, touchPoints,
+ QWindowsKeyMapper::queryKeyboardModifiers());
+ }
+ return false; // Allow mouse messages to be generated.
}
bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et,
@@ -512,6 +633,9 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
if (et & QtWindows::NonClientEventFlag)
return false; // Let DefWindowProc() handle Non Client messages.
+ if (draggingActive())
+ return false; // Let DoDragDrop() loop handle it.
+
POINTER_PEN_INFO *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo);
RECT pRect, dRect;
@@ -592,20 +716,25 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
}
const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
- QWindowSystemInterface::handleTabletEvent(target, localPos, hiResGlobalPos, device, type, mouseButtons,
- pressure, xTilt, yTilt, tangentialPressure, rotation, z,
- pointerId, keyModifiers);
- break;
+ // Postpone event delivery to avoid hanging inside DoDragDrop().
+ enqueueTabletEvent(target, localPos, hiResGlobalPos, device, type, mouseButtons,
+ pressure, xTilt, yTilt, tangentialPressure, rotation, z,
+ pointerId, keyModifiers);
+ return false; // Allow mouse messages to be generated.
}
}
return true;
}
-// SetCursorPos()/TrackMouseEvent() will generate old-style WM_MOUSE messages. Handle them here.
+// Process old-style mouse messages here.
bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result)
{
Q_UNUSED(et);
+ // Generate enqueued events.
+ flushTouchEvents(m_touchDevice);
+ flushTabletEvents();
+
*result = 0;
if (msg.message != WM_MOUSELEAVE && msg.message != WM_MOUSEMOVE)
return false;