From 3259d5e11326f5f63845941ac245507a57db7831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 10 Apr 2017 18:19:45 +0200 Subject: [PATCH] Cancel popup if the user clicked window decoration of parent window Summary: So far the window decoration was not considered and e.g. right clicking the window decoration resulted in two open popups - one by KWin and one by the application. This change addresses the problem by ensuring the popup gets cancelled if the decoration is clicked. It's considered not being part of the window. Test Plan: Added test case which fails without the change Reviewers: #kwin, #plasma Subscribers: plasma-devel, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D5388 --- autotests/integration/pointer_input.cpp | 82 ++++++++++++++++++++++++- popup_input_filter.cpp | 8 +++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/autotests/integration/pointer_input.cpp b/autotests/integration/pointer_input.cpp index bfc39a5aae..850822252a 100644 --- a/autotests/integration/pointer_input.cpp +++ b/autotests/integration/pointer_input.cpp @@ -37,6 +37,7 @@ along with this program. If not, see . #include #include #include +#include #include #include @@ -73,6 +74,7 @@ private Q_SLOTS: void testCursorImage(); void testEffectOverrideCursorImage(); void testPopup(); + void testDecoCancelsPopup(); private: void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50)); @@ -108,7 +110,7 @@ void PointerInputTest::initTestCase() void PointerInputTest::init() { - QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat)); + QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::Decoration)); QVERIFY(Test::waitForWaylandPointer()); m_compositor = Test::waylandCompositor(); m_shell = Test::waylandShell(); @@ -1045,6 +1047,84 @@ void PointerInputTest::testPopup() kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++); } +void PointerInputTest::testDecoCancelsPopup() +{ + // this test verifies that clicking the window decoration of parent window + // cancels the popup + + // 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()); + QSignalSpy buttonStateChangedSpy(pointer, &Pointer::buttonStateChanged); + QVERIFY(buttonStateChangedSpy.isValid()); + QSignalSpy motionSpy(pointer, &Pointer::motion); + QVERIFY(motionSpy.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); + + auto deco = Test::waylandServerSideDecoration()->create(surface, surface); + QSignalSpy decoSpy(deco, &ServerSideDecoration::modeChanged); + QVERIFY(decoSpy.isValid()); + QVERIFY(decoSpy.wait()); + deco->requestMode(ServerSideDecoration::Mode::Server); + QVERIFY(decoSpy.wait()); + QCOMPARE(deco->mode(), ServerSideDecoration::Mode::Server); + render(surface); + QVERIFY(clientAddedSpy.wait()); + AbstractClient *window = workspace()->activeClient(); + QVERIFY(window); + QCOMPARE(window->hasPopupGrab(), false); + QVERIFY(window->isDecorated()); + + // move pointer into window + QVERIFY(!window->geometry().contains(QPoint(800, 800))); + Cursor::setPos(window->geometry().center()); + QVERIFY(enteredSpy.wait()); + // click inside window to create serial + quint32 timestamp = 0; + kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++); + kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++); + QVERIFY(buttonStateChangedSpy.wait()); + + // now create the popup surface + Surface *popupSurface = Test::createSurface(m_compositor); + QVERIFY(popupSurface); + ShellSurface *popupShellSurface = Test::createShellSurface(popupSurface, popupSurface); + QVERIFY(popupShellSurface); + QSignalSpy popupDoneSpy(popupShellSurface, &ShellSurface::popupDone); + QVERIFY(popupDoneSpy.isValid()); + // TODO: proper serial + popupShellSurface->setTransientPopup(surface, m_seat, 0, QPoint(80, 20)); + render(popupSurface); + QVERIFY(clientAddedSpy.wait()); + auto popupClient = clientAddedSpy.last().first().value(); + QVERIFY(popupClient); + QVERIFY(popupClient != window); + QCOMPARE(window, workspace()->activeClient()); + QCOMPARE(popupClient->transientFor(), window); + QCOMPARE(popupClient->pos(), window->pos() + window->clientPos() + QPoint(80, 20)); + QCOMPARE(popupClient->hasPopupGrab(), true); + + // let's move the pointer into the center of the deco + Cursor::setPos(window->geometry().center().x(), window->y() + (window->height() - window->clientSize().height()) / 2); + + kwinApp()->platform()->pointerButtonPressed(BTN_RIGHT, timestamp++); + QVERIFY(popupDoneSpy.wait()); + kwinApp()->platform()->pointerButtonReleased(BTN_RIGHT, timestamp++); +} + } WAYLANDTEST_MAIN(KWin::PointerInputTest) diff --git a/popup_input_filter.cpp b/popup_input_filter.cpp index 9d54984da0..fc74540ed6 100644 --- a/popup_input_filter.cpp +++ b/popup_input_filter.cpp @@ -65,6 +65,14 @@ bool PopupInputFilter::pointerEvent(QMouseEvent *event, quint32 nativeButton) // filter out this press return true; } + if (pointerFocus && pointerFocus->isDecorated()) { + // test whether it is on the decoration + const QRect clientRect = QRect(pointerFocus->clientPos(), pointerFocus->clientSize()).translated(pointerFocus->pos()); + if (!clientRect.contains(event->globalPos())) { + cancelPopups(); + return true; + } + } } return false; }