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;
}