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>
This commit is contained in:
Friedemann Kleint 2017-12-20 14:07:17 +01:00
parent d08e0e861a
commit bb45b75f3d
2 changed files with 80 additions and 10 deletions

View File

@ -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.

View File

@ -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)
{