diff --git a/autotests/integration/pointer_input.cpp b/autotests/integration/pointer_input.cpp index 850822252a..fa11fba41c 100644 --- a/autotests/integration/pointer_input.cpp +++ b/autotests/integration/pointer_input.cpp @@ -75,6 +75,7 @@ private Q_SLOTS: void testEffectOverrideCursorImage(); void testPopup(); void testDecoCancelsPopup(); + void testWindowUnderCursorWhileButtonPressed(); private: void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50)); @@ -1125,6 +1126,63 @@ void PointerInputTest::testDecoCancelsPopup() kwinApp()->platform()->pointerButtonReleased(BTN_RIGHT, timestamp++); } +void PointerInputTest::testWindowUnderCursorWhileButtonPressed() +{ + // this test verifies that opening a window underneath the mouse cursor does not + // trigger a leave event if a button is pressed + // see BUG: 372876 + + // first create a parent surface + using namespace KWayland::Client; + auto pointer = m_seat->createPointer(m_seat); + QVERIFY(pointer); + QVERIFY(pointer->isValid()); + QSignalSpy enteredSpy(pointer, &Pointer::entered); + QVERIFY(enteredSpy.isValid()); + QSignalSpy leftSpy(pointer, &Pointer::left); + QVERIFY(leftSpy.isValid()); + + Cursor::setPos(800, 800); + QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QVERIFY(clientAddedSpy.isValid()); + Surface *surface = Test::createSurface(m_compositor); + QVERIFY(surface); + ShellSurface *shellSurface = Test::createShellSurface(surface, surface); + QVERIFY(shellSurface); + render(surface); + QVERIFY(clientAddedSpy.wait()); + AbstractClient *window = workspace()->activeClient(); + QVERIFY(window); + + // move cursor over window + QVERIFY(!window->geometry().contains(QPoint(800, 800))); + Cursor::setPos(window->geometry().center()); + QVERIFY(enteredSpy.wait()); + // click inside window + quint32 timestamp = 0; + kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++); + + // now create a second window as transient + Surface *popupSurface = Test::createSurface(m_compositor); + QVERIFY(popupSurface); + ShellSurface *popupShellSurface = Test::createShellSurface(popupSurface, popupSurface); + QVERIFY(popupShellSurface); + popupShellSurface->setTransient(surface, QPoint(0, 0)); + render(popupSurface); + QVERIFY(clientAddedSpy.wait()); + auto popupClient = clientAddedSpy.last().first().value(); + QVERIFY(popupClient); + QVERIFY(popupClient != window); + QCOMPARE(window->geometry(), popupClient->geometry()); + QVERIFY(!leftSpy.wait()); + + kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++); + // now that the button is no longer pressed we should get the leave event + QVERIFY(leftSpy.wait()); + QCOMPARE(leftSpy.count(), 1); + QCOMPARE(enteredSpy.count(), 2); +} + } WAYLANDTEST_MAIN(KWin::PointerInputTest) diff --git a/input.cpp b/input.cpp index 9d8c39cd0d..6b3bdb90ff 100644 --- a/input.cpp +++ b/input.cpp @@ -223,10 +223,7 @@ public: auto seat = waylandServer()->seat(); seat->setTimestamp(event->timestamp()); if (event->type() == QEvent::MouseMove) { - if (event->buttons() == Qt::NoButton) { - // update pointer window only if no button is pressed - input()->pointer()->update(); - } + input()->pointer()->update(); if (pointerSurfaceAllowed()) { seat->setPointerPos(event->screenPos().toPoint()); } diff --git a/pointer_input.cpp b/pointer_input.cpp index 65f28fecbe..da59ec259e 100644 --- a/pointer_input.cpp +++ b/pointer_input.cpp @@ -436,6 +436,17 @@ void PointerInputRedirection::update() if (input()->isSelectingWindow()) { return; } + auto areButtonsPressed = [this] { + for (auto state : qAsConst(m_buttons)) { + if (state == InputRedirection::PointerButtonPressed) { + return true; + } + } + return false; + }; + if (areButtonsPressed()) { + return; + } Toplevel *t = m_input->findToplevel(m_pos.toPoint()); const auto oldDeco = m_decoration; updateInternalWindow(m_pos);