From 9a13743c4988fbf653e20df43e919e836766d3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sat, 15 Apr 2017 11:36:21 +0200 Subject: [PATCH] Don't update the focused pointer Surface if a button is pressed Summary: During pointer motion we already had the condition that an update of focused pointer surface can only happen when no button is pressed. But there are more conditions where we try to update the focused pointer even if a button is pressed. E.g. if the stacking order changes. This happens when trying to move one of Qt's dock widgets: 1. Press inside a dock widget 2. Qt opens another window, which is underneath the cursor 3. KWin sends pointer leave to parent window 4. dock widget movement breaks This change ensures that also this sequence works as expected and the pointer gets only updated when there are no buttons pressed, no matter from where we go into the update code path. BUG: 372876 Test Plan: Dock widgets in Dolphin can be moved now. Reviewers: #kwin, #plasma Subscribers: plasma-devel, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D5461 --- autotests/integration/pointer_input.cpp | 58 +++++++++++++++++++++++++ input.cpp | 5 +-- pointer_input.cpp | 11 +++++ 3 files changed, 70 insertions(+), 4 deletions(-) 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);