diff options
Diffstat (limited to 'src/plugins/platforms/windows/qwindowsdrag.cpp')
-rw-r--r-- | src/plugins/platforms/windows/qwindowsdrag.cpp | 107 |
1 files changed, 93 insertions, 14 deletions
diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 33b1cf2abd..b872ef2ad6 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -11,7 +11,7 @@ #include "qwindowsintegration.h" #include "qwindowsdropdataobject.h" #include "qwindowswindow.h" -#include "qwindowsmousehandler.h" +#include "qwindowspointerhandler.h" #include "qwindowscursor.h" #include "qwindowskeymapper.h" @@ -28,6 +28,8 @@ #include <QtCore/qdebug.h> #include <QtCore/qbuffer.h> #include <QtCore/qpoint.h> +#include <QtCore/qpointer.h> +#include <QtCore/private/qcomobject_p.h> #include <shlobj.h> @@ -167,7 +169,7 @@ static Qt::MouseButtons lastButtons = Qt::NoButton; \internal */ -class QWindowsOleDropSource : public QWindowsComBase<IDropSource> +class QWindowsOleDropSource : public QComObject<IDropSource> { public: enum Mode { @@ -339,7 +341,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) // In some rare cases, when a mouse button is released but the mouse is static, // grfKeyState will not be updated with these released buttons until the mouse // is moved. So we use the async key state given by queryMouseButtons() instead. - Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons(); + Qt::MouseButtons buttons = QWindowsPointerHandler::queryMouseButtons(); SCODE result = S_OK; if (fEscapePressed || QWindowsDrag::isCanceled()) { @@ -348,7 +350,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) } else { if (buttons && !m_currentButtons) { m_currentButtons = buttons; - } else if (!(m_currentButtons & buttons)) { // Button changed: Complete Drop operation. + } else if (m_currentButtons != buttons) { // Button changed: Complete Drop operation. result = DRAGDROP_S_DROP; } } @@ -364,7 +366,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) const QPoint localPos = m_windowUnderMouse->handle()->mapFromGlobal(globalPos); QWindowSystemInterface::handleMouseEvent(m_windowUnderMouse.data(), QPointF(localPos), QPointF(globalPos), - QWindowsMouseHandler::queryMouseButtons(), + QWindowsPointerHandler::queryMouseButtons(), Qt::LeftButton, QEvent::MouseButtonRelease); } m_currentButtons = Qt::NoButton; @@ -458,7 +460,7 @@ void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState, const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect); lastModifiers = toQtKeyboardModifiers(grfKeyState); - lastButtons = QWindowsMouseHandler::queryMouseButtons(); + lastButtons = QWindowsPointerHandler::queryMouseButtons(); const QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(), @@ -526,8 +528,9 @@ QWindowsOleDropTarget::DragLeave() qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window; - lastModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); - lastButtons = QWindowsMouseHandler::queryMouseButtons(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + lastModifiers = keyMapper->queryKeyboardModifiers(); + lastButtons = QWindowsPointerHandler::queryMouseButtons(); QWindowSystemInterface::handleDrag(m_window, nullptr, QPoint(), Qt::IgnoreAction, Qt::NoButton, Qt::NoModifier); @@ -556,7 +559,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, QWindowsDrag *windowsDrag = QWindowsDrag::instance(); lastModifiers = toQtKeyboardModifiers(grfKeyState); - lastButtons = QWindowsMouseHandler::queryMouseButtons(); + lastButtons = QWindowsPointerHandler::queryMouseButtons(); const QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_window, windowsDrag->dropData(), @@ -611,7 +614,6 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, */ bool QWindowsDrag::m_canceled = false; -bool QWindowsDrag::m_dragging = false; QWindowsDrag::QWindowsDrag() = default; @@ -644,6 +646,86 @@ 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) +{ + QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse(); + const bool hasMouseCapture = underMouse && static_cast<QWindowsWindow *>(underMouse->handle())->hasMouseCapture(); + const HWND hwnd = hasMouseCapture ? reinterpret_cast<HWND>(underMouse->winId()) : ::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? @@ -659,10 +741,7 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) const DWORD allowedEffects = translateToWinDragEffects(possibleActions); qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x" << 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); - QWindowsDrag::m_dragging = false; + const HRESULT r = startDoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect); const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect(); if (r == DRAGDROP_S_DROP) { if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) { |