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 <shawn.rutledge@qt.io> (cherry picked from commit 31e7790102b260344893eaa8bf8b7b1a0f95e3b7) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
1515422d68
commit
97c95f606f
@ -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<DWORD>(virt_x * (65535.0 / virt_w));
|
||||
input.mi.dy = static_cast<DWORD>(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) {
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user