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>
This commit is contained in:
parent
57b09b903e
commit
83d56811ec
@ -652,6 +652,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
bool QWindowsDrag::m_canceled = false;
|
bool QWindowsDrag::m_canceled = false;
|
||||||
|
bool QWindowsDrag::m_dragging = false;
|
||||||
|
|
||||||
QWindowsDrag::QWindowsDrag() = default;
|
QWindowsDrag::QWindowsDrag() = default;
|
||||||
|
|
||||||
@ -699,7 +700,10 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag)
|
|||||||
const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
|
const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
|
||||||
qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x"
|
qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x"
|
||||||
<< hex << int(possibleActions) << "effects=0x" << allowedEffects << dec;
|
<< 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);
|
const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect);
|
||||||
|
QWindowsDrag::m_dragging = false;
|
||||||
const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect();
|
const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect();
|
||||||
if (r == DRAGDROP_S_DROP) {
|
if (r == DRAGDROP_S_DROP) {
|
||||||
if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
|
if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
|
||||||
|
@ -92,6 +92,7 @@ public:
|
|||||||
static QWindowsDrag *instance();
|
static QWindowsDrag *instance();
|
||||||
void cancelDrag() override { QWindowsDrag::m_canceled = true; }
|
void cancelDrag() override { QWindowsDrag::m_canceled = true; }
|
||||||
static bool isCanceled() { return QWindowsDrag::m_canceled; }
|
static bool isCanceled() { return QWindowsDrag::m_canceled; }
|
||||||
|
static bool isDragging() { return QWindowsDrag::m_dragging; }
|
||||||
|
|
||||||
IDataObject *dropDataObject() const { return m_dropDataObject; }
|
IDataObject *dropDataObject() const { return m_dropDataObject; }
|
||||||
void setDropDataObject(IDataObject *dataObject) { m_dropDataObject = dataObject; }
|
void setDropDataObject(IDataObject *dataObject) { m_dropDataObject = dataObject; }
|
||||||
@ -102,6 +103,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static bool m_canceled;
|
static bool m_canceled;
|
||||||
|
static bool m_dragging;
|
||||||
|
|
||||||
QWindowsDropMimeData m_dropData;
|
QWindowsDropMimeData m_dropData;
|
||||||
IDataObject *m_dropDataObject = nullptr;
|
IDataObject *m_dropDataObject = nullptr;
|
||||||
|
@ -50,6 +50,9 @@
|
|||||||
#include "qwindowswindow.h"
|
#include "qwindowswindow.h"
|
||||||
#include "qwindowsintegration.h"
|
#include "qwindowsintegration.h"
|
||||||
#include "qwindowsscreen.h"
|
#include "qwindowsscreen.h"
|
||||||
|
#if QT_CONFIG(draganddrop)
|
||||||
|
# include "qwindowsdrag.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <qpa/qwindowsysteminterface.h>
|
#include <qpa/qwindowsysteminterface.h>
|
||||||
#include <QtGui/qguiapplication.h>
|
#include <QtGui/qguiapplication.h>
|
||||||
@ -60,6 +63,7 @@
|
|||||||
#include <QtCore/qvarlengtharray.h>
|
#include <QtCore/qvarlengtharray.h>
|
||||||
#include <QtCore/qloggingcategory.h>
|
#include <QtCore/qloggingcategory.h>
|
||||||
#include <QtCore/qoperatingsystemversion.h>
|
#include <QtCore/qoperatingsystemversion.h>
|
||||||
|
#include <QtCore/qqueue.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@ -75,6 +79,111 @@ enum {
|
|||||||
QT_PT_TOUCHPAD = 5, // MinGW is missing PT_TOUCHPAD
|
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)
|
bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result)
|
||||||
{
|
{
|
||||||
*result = 0;
|
*result = 0;
|
||||||
@ -426,6 +535,9 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
|
|||||||
if (et & QtWindows::NonClientEventFlag)
|
if (et & QtWindows::NonClientEventFlag)
|
||||||
return false; // Let DefWindowProc() handle Non Client messages.
|
return false; // Let DefWindowProc() handle Non Client messages.
|
||||||
|
|
||||||
|
if (draggingActive())
|
||||||
|
return false; // Let DoDragDrop() loop handle it.
|
||||||
|
|
||||||
if (count < 1)
|
if (count < 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -452,6 +564,8 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
|
|||||||
|
|
||||||
QList<QWindowSystemInterface::TouchPoint> touchPoints;
|
QList<QWindowSystemInterface::TouchPoint> touchPoints;
|
||||||
|
|
||||||
|
bool primaryPointer = false;
|
||||||
|
|
||||||
if (QWindowsContext::verbose > 1)
|
if (QWindowsContext::verbose > 1)
|
||||||
qCDebug(lcQpaEvents).noquote().nospace() << showbase
|
qCDebug(lcQpaEvents).noquote().nospace() << showbase
|
||||||
<< __FUNCTION__
|
<< __FUNCTION__
|
||||||
@ -494,16 +608,23 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
|
|||||||
touchPoint.state = stationaryTouchPoint ? Qt::TouchPointStationary : Qt::TouchPointMoved;
|
touchPoint.state = stationaryTouchPoint ? Qt::TouchPointStationary : Qt::TouchPointMoved;
|
||||||
m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition);
|
m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition);
|
||||||
}
|
}
|
||||||
|
if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY)
|
||||||
|
primaryPointer = true;
|
||||||
|
|
||||||
touchPoints.append(touchPoint);
|
touchPoints.append(touchPoint);
|
||||||
|
|
||||||
// Avoid getting repeated messages for this frame if there are multiple pointerIds
|
// Avoid getting repeated messages for this frame if there are multiple pointerIds
|
||||||
QWindowsContext::user32dll.skipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId);
|
QWindowsContext::user32dll.skipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId);
|
||||||
}
|
}
|
||||||
|
if (primaryPointer) {
|
||||||
QWindowSystemInterface::handleTouchEvent(window, m_touchDevice, touchPoints,
|
// Postpone event delivery to avoid hanging inside DoDragDrop().
|
||||||
QWindowsKeyMapper::queryKeyboardModifiers());
|
// Only the primary pointer will generate mouse messages.
|
||||||
|
enqueueTouchEvent(window, touchPoints, QWindowsKeyMapper::queryKeyboardModifiers());
|
||||||
return true;
|
} 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,
|
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)
|
if (et & QtWindows::NonClientEventFlag)
|
||||||
return false; // Let DefWindowProc() handle Non Client messages.
|
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);
|
POINTER_PEN_INFO *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo);
|
||||||
|
|
||||||
RECT pRect, dRect;
|
RECT pRect, dRect;
|
||||||
@ -592,20 +716,25 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
|
|||||||
}
|
}
|
||||||
const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
|
const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
|
||||||
|
|
||||||
QWindowSystemInterface::handleTabletEvent(target, localPos, hiResGlobalPos, device, type, mouseButtons,
|
// Postpone event delivery to avoid hanging inside DoDragDrop().
|
||||||
pressure, xTilt, yTilt, tangentialPressure, rotation, z,
|
enqueueTabletEvent(target, localPos, hiResGlobalPos, device, type, mouseButtons,
|
||||||
pointerId, keyModifiers);
|
pressure, xTilt, yTilt, tangentialPressure, rotation, z,
|
||||||
break;
|
pointerId, keyModifiers);
|
||||||
|
return false; // Allow mouse messages to be generated.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
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)
|
bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result)
|
||||||
{
|
{
|
||||||
Q_UNUSED(et);
|
Q_UNUSED(et);
|
||||||
|
|
||||||
|
// Generate enqueued events.
|
||||||
|
flushTouchEvents(m_touchDevice);
|
||||||
|
flushTabletEvents();
|
||||||
|
|
||||||
*result = 0;
|
*result = 0;
|
||||||
if (msg.message != WM_MOUSELEAVE && msg.message != WM_MOUSEMOVE)
|
if (msg.message != WM_MOUSELEAVE && msg.message != WM_MOUSEMOVE)
|
||||||
return false;
|
return false;
|
||||||
|
Loading…
Reference in New Issue
Block a user