diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2017-12-20 14:07:17 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2018-02-14 07:13:39 +0000 |
commit | bb45b75f3d0fc745fcac03ab330d3a9f147bfe9c (patch) | |
tree | 0eaa2f4dd02d0013e05d1a1367cc6cdabc211758 | |
parent | d08e0e861acadf3a354d3833dd5623ef71fa7a4b (diff) |
Windows QPA: Discard spurious mouse move events
Windows sends a mouse move with no buttons pressed to signal "Enter"
when a window is shown over the cursor. Discard the event and only
use it for generating QEvent::Enter as not to confuse tests.
This is preparing for the use of the new QPA API for mouse events.
Change-Id: I3eb7f3dad82d27d0b425c7eaf34b1eee11592074
Reviewed-by: Gatis Paeglis <gatis.paeglis@qt.io>
-rw-r--r-- | src/plugins/platforms/windows/qwindowsmousehandler.cpp | 37 | ||||
-rw-r--r-- | tests/auto/gui/kernel/qwindow/tst_qwindow.cpp | 53 |
2 files changed, 80 insertions, 10 deletions
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index 814291c54a..f17eded2c7 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -178,6 +178,8 @@ Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons() return result; } +static QPoint lastMouseMovePos; + bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result) @@ -192,6 +194,29 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, if (et == QtWindows::MouseWheelEvent) return translateMouseWheelEvent(window, hwnd, msg, result); + const QPoint winEventPosition(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); + QPoint clientPosition; + QPoint globalPosition; + if (et & QtWindows::NonClientEventFlag) { + globalPosition = winEventPosition; + clientPosition = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPosition); + } else { + clientPosition = winEventPosition; + globalPosition = QWindowsGeometryHint::mapToGlobal(hwnd, winEventPosition); + } + + // Windows sends a mouse move with no buttons pressed to signal "Enter" + // when a window is shown over the cursor. Discard the event and only use + // it for generating QEvent::Enter to be consistent with other platforms - + // X11 and macOS. + bool discardEvent = false; + if (msg.message == WM_MOUSEMOVE) { + const bool samePosition = globalPosition == lastMouseMovePos; + lastMouseMovePos = globalPosition; + if (msg.wParam == 0 && (m_windowUnderMouse.isNull() || samePosition)) + discardEvent = true; + } + Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; // Check for events synthesized from touch. Lower byte is touch index, 0 means pen. @@ -210,10 +235,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, } } - const QPoint winEventPosition(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); if (et & QtWindows::NonClientEventFlag) { - const QPoint globalPosition = winEventPosition; - const QPoint clientPosition = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPosition); const Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons(); QWindowSystemInterface::handleFrameStrutMouseEvent(window, clientPosition, globalPosition, buttons, @@ -269,7 +291,6 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, } } - const QPoint globalPosition = QWindowsGeometryHint::mapToGlobal(hwnd, winEventPosition); // In this context, neither an invisible nor a transparent window (transparent regarding mouse // events, "click-through") can be considered as the window under mouse. QWindow *currentWindowUnderMouse = platformWindow->hasMouseCapture() ? @@ -369,9 +390,11 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, m_windowUnderMouse = currentWindowUnderMouse; } - QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition, buttons, - QWindowsKeyMapper::queryKeyboardModifiers(), - source); + if (!discardEvent) { + QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition, buttons, + QWindowsKeyMapper::queryKeyboardModifiers(), + source); + } m_previousCaptureWindow = hasCapture ? window : 0; // QTBUG-48117, force synchronous handling for the extra buttons so that WM_APPCOMMAND // is sent for unhandled WM_XBUTTONDOWN. diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp index 18723458f6..8dbdd0dec3 100644 --- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp @@ -97,6 +97,7 @@ private slots: void modalWindowPosition(); #ifndef QT_NO_CURSOR void modalWindowEnterEventOnHide_QTBUG35109(); + void spuriousMouseMove(); #endif void windowsTransientChildren(); void requestUpdate(); @@ -887,7 +888,7 @@ void tst_QWindow::isActive() QVERIFY(child.isActive()); } -class InputTestWindow : public QWindow +class InputTestWindow : public ColoredWindow { public: void keyPressEvent(QKeyEvent *event) { @@ -981,7 +982,9 @@ public: enterEventCount = leaveEventCount = 0; } - InputTestWindow() { + explicit InputTestWindow(const QColor &color = Qt::white, QWindow *parent = nullptr) + : ColoredWindow(color, parent) + { keyPressCode = keyReleaseCode = 0; mousePressButton = mouseReleaseButton = mouseMoveButton = 0; ignoreMouse = ignoreTouch = false; @@ -2202,7 +2205,51 @@ void tst_QWindow::modalWindowEnterEventOnHide_QTBUG35109() QTRY_COMPARE(root.enterEventCount, 1); } } -#endif + +// Verify that no spurious mouse move events are received. On Windows, there is +// no enter event, the OS sends mouse move events instead. Test that the QPA +// plugin properly suppresses those since they can interfere with tests. +// Simulate a main window setup with a modal dialog on top, keep the cursor +// in the center and check that no mouse events are recorded. +void tst_QWindow::spuriousMouseMove() +{ + const QString &platformName = QGuiApplication::platformName(); + if (platformName == QLatin1String("offscreen") || platformName == QLatin1String("cocoa")) + QSKIP("No enter events sent"); + const QRect screenGeometry = QGuiApplication::primaryScreen()->geometry(); + const QPoint center = screenGeometry.center(); + QCursor::setPos(center); + QRect windowGeometry(QPoint(), 2 * m_testWindowSize); + windowGeometry.moveCenter(center); + QTRY_COMPARE(QCursor::pos(), center); + InputTestWindow topLevel; + topLevel.setTitle(QTest::currentTestFunction()); + topLevel.setGeometry(windowGeometry); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QTRY_VERIFY(topLevel.enterEventCount > 0); + InputTestWindow dialog(Qt::yellow); + dialog.setTransientParent(&topLevel); + dialog.setTitle("Dialog " + topLevel.title()); + dialog.setModality(Qt::ApplicationModal); + windowGeometry.setSize(m_testWindowSize); + windowGeometry.moveCenter(center); + dialog.setGeometry(windowGeometry); + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + QTRY_VERIFY(dialog.enterEventCount > 0); + dialog.setVisible(false); + QCOMPARE(dialog.mousePressedCount, 0); + QCOMPARE(dialog.mouseReleasedCount, 0); + QCOMPARE(dialog.mouseMovedCount, 0); + QCOMPARE(dialog.mouseDoubleClickedCount, 0); + topLevel.setVisible(false); + QCOMPARE(topLevel.mousePressedCount, 0); + QCOMPARE(topLevel.mouseReleasedCount, 0); + QCOMPARE(topLevel.mouseMovedCount, 0); + QCOMPARE(topLevel.mouseDoubleClickedCount, 0); +} +#endif // !QT_NO_CURSOR static bool isNativeWindowVisible(const QWindow *window) { |