From 97c95f606f47569cb1be56e5f4c2477095ec7ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20de=20la=20Rocha?= Date: Thu, 13 Oct 2022 20:10:13 +0200 Subject: [PATCH] Windows QPA: Remove tablet->mouse synth and fix Drag&Drop with touch/pen This patch avoids synthesizing mouse messages in the QPA for touch/pen input, and lets the GUI do mouse event synthesis for unhandled touch/pen events, if required, like in other platforms. This requires a workaround to avoid breaking drag and drop with touch/pen (or making it worse). DnD on Windows is based on the DoDragDrop() Win32 API, which does not work with touch/pen input, which in some cases cause a DnD operation started with touch/pen to hang until the mouse is moved. To avoid it we process pointer messages for touch/pen and generate mouse input through SendInput() to trigger DoDragDrop(), which then seems to work as expected. So now we inform QtGui that the Windows platform no longer sends synth-mouse events after tablet events (unsetting the flag added in f931e5e72d4617023bbea46cba2c0d61bb1efa4f); this completes what was attempted in 8ada0633cd58e0608df2e16880f1293286025504. Fixes: QTBUG-106368 Fixes: QTBUG-57577 Fixes: QTBUG-100788 Task-number: QTBUG-77414 Task-number: QTBUG-104594 Change-Id: I46db3c74be2a95cf2d94ba930398e58dc930d2db Reviewed-by: Shawn Rutledge (cherry picked from commit 31e7790102b260344893eaa8bf8b7b1a0f95e3b7) Reviewed-by: Qt Cherry-pick Bot --- .../platforms/windows/qwindowsdrag.cpp | 80 ++++++++++++++++++- .../platforms/windows/qwindowsintegration.cpp | 1 + .../windows/qwindowspointerhandler.cpp | 4 +- 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 33b1cf2abd4..93205259f8e 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -644,6 +644,84 @@ IDropTargetHelper* QWindowsDrag::dropHelper() { return m_cachedDropTargetHelper; } +// Workaround for DoDragDrop() not working with touch/pen input, causing DnD to hang until the mouse is moved. +// We process pointer messages for touch/pen and generate mouse input through SendInput() to trigger DoDragDrop() +static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect) +{ + HWND hwnd = ::GetFocus(); + bool starting = false; + + for (;;) { + MSG msg{}; + if (::GetMessage(&msg, hwnd, 0, 0) > 0) { + + if (msg.message == WM_MOUSEMOVE) { + + // Only consider the first simulated event, or actual mouse messages. + if (!starting && (msg.wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON | MK_XBUTTON1 | MK_XBUTTON2)) == 0) + return E_FAIL; + + return ::DoDragDrop(pDataObj, pDropSource, dwOKEffects, pdwEffect); + } + + if (msg.message == WM_POINTERUPDATE) { + + const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam); + + POINTER_INFO pointerInfo{}; + if (!GetPointerInfo(pointerId, &pointerInfo)) + return E_FAIL; + + if (pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY) { + + DWORD flags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MOVE; + if (IS_POINTER_FIRSTBUTTON_WPARAM(msg.wParam)) + flags |= MOUSEEVENTF_LEFTDOWN; + if (IS_POINTER_SECONDBUTTON_WPARAM(msg.wParam)) + flags |= MOUSEEVENTF_RIGHTDOWN; + if (IS_POINTER_THIRDBUTTON_WPARAM(msg.wParam)) + flags |= MOUSEEVENTF_MIDDLEDOWN; + + if (!starting) { + POINT pt{}; + if (::GetCursorPos(&pt)) { + + // Send mouse input that can generate a WM_MOUSEMOVE message. + if ((flags & MOUSEEVENTF_LEFTDOWN || flags & MOUSEEVENTF_RIGHTDOWN || flags & MOUSEEVENTF_MIDDLEDOWN) + && (pt.x != pointerInfo.ptPixelLocation.x || pt.y != pointerInfo.ptPixelLocation.y)) { + + const int origin_x = ::GetSystemMetrics(SM_XVIRTUALSCREEN); + const int origin_y = ::GetSystemMetrics(SM_YVIRTUALSCREEN); + const int virt_w = ::GetSystemMetrics(SM_CXVIRTUALSCREEN); + const int virt_h = ::GetSystemMetrics(SM_CYVIRTUALSCREEN); + const int virt_x = pointerInfo.ptPixelLocation.x - origin_x; + const int virt_y = pointerInfo.ptPixelLocation.y - origin_y; + + INPUT input{}; + input.type = INPUT_MOUSE; + input.mi.dx = static_cast(virt_x * (65535.0 / virt_w)); + input.mi.dy = static_cast(virt_y * (65535.0 / virt_h)); + input.mi.dwFlags = flags; + + ::SendInput(1, &input, sizeof(input)); + starting = true; + } + } + } + } + } else { + // Handle other messages. + qWindowsWndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + + if (msg.message == WM_POINTERLEAVE) + return E_FAIL; + } + } else { + return E_FAIL; + } + } +} + Qt::DropAction QWindowsDrag::drag(QDrag *drag) { // TODO: Accessibility handling? @@ -661,7 +739,7 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) << Qt::hex << int(possibleActions) << "effects=0x" << allowedEffects << Qt::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 = startDoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect); QWindowsDrag::m_dragging = false; const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect(); if (r == DRAGDROP_S_DROP) { diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 9e284f23663..22de3236ea0 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -223,6 +223,7 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr QCoreApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents); else m_context.initTablet(); + QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false); if (!dpiAwarenessSet) { // Set only once in case of repeated instantiations of QGuiApplication. if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) { diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp index 943eaa2b0a6..e0b2663df55 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp +++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp @@ -798,8 +798,8 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, break; case QT_PT_PEN: #if QT_CONFIG(tabletevent) - if (!m_activeTabletDevice.isNull()) - device = m_activeTabletDevice.data(); + qCDebug(lcQpaTablet) << "ignoring synth-mouse event for tablet event from" << device; + return false; #endif break; }