From bccb1f4cbaa15b6a0f171c1e11b0da1ad59e0559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sat, 25 Mar 2017 17:42:08 +0100 Subject: [PATCH] Add support for wl_shell_surface::set_popup and popup_done Summary: This extends the client side API to support creating popup ShellSurface windows and the server side API to send out the popup_done request. This is needed to properly support popup windows (e.g. context menus) in KWin. Reviewers: #plasma_on_wayland, #frameworks, #kwin Subscribers: plasma-devel Tags: #plasma_on_wayland, #frameworks Differential Revision: https://phabricator.kde.org/D5174 --- .../autotests/client/test_wayland_shell.cpp | 67 +++++++++++++++++++ src/wayland/server/shell_interface.cpp | 11 +++ src/wayland/server/shell_interface.h | 12 ++++ 3 files changed, 90 insertions(+) diff --git a/src/wayland/autotests/client/test_wayland_shell.cpp b/src/wayland/autotests/client/test_wayland_shell.cpp index 177ce35fd9..508d574246 100644 --- a/src/wayland/autotests/client/test_wayland_shell.cpp +++ b/src/wayland/autotests/client/test_wayland_shell.cpp @@ -55,6 +55,7 @@ private Q_SLOTS: void testToplevel(); void testTransient_data(); void testTransient(); + void testTransientPopup(); void testPing(); void testTitle(); void testWindowClass(); @@ -475,6 +476,72 @@ void TestWaylandShell::testTransient() QVERIFY(serverSurface2->acceptsKeyboardFocus()); } +void TestWaylandShell::testTransientPopup() +{ + using namespace KWayland::Server; + using namespace KWayland::Client; + QScopedPointer s(m_compositor->createSurface()); + QVERIFY(!s.isNull()); + QVERIFY(s->isValid()); + ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); + + QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); + QVERIFY(serverSurfaceSpy.isValid()); + QVERIFY(serverSurfaceSpy.wait()); + ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); + QVERIFY(serverSurface); + QCOMPARE(serverSurface->isToplevel(), true); + QCOMPARE(serverSurface->isPopup(), false); + QCOMPARE(serverSurface->isTransient(), false); + QCOMPARE(serverSurface->transientFor(), QPointer()); + QCOMPARE(serverSurface->transientOffset(), QPoint()); + QVERIFY(serverSurface->acceptsKeyboardFocus()); + + QSignalSpy transientSpy(serverSurface, &ShellSurfaceInterface::transientChanged); + QVERIFY(transientSpy.isValid()); + QSignalSpy transientOffsetSpy(serverSurface, &ShellSurfaceInterface::transientOffsetChanged); + QVERIFY(transientOffsetSpy.isValid()); + QSignalSpy transientForChangedSpy(serverSurface, &ShellSurfaceInterface::transientForChanged); + QVERIFY(transientForChangedSpy.isValid()); + + QScopedPointer s2(m_compositor->createSurface()); + m_shell->createSurface(s2.data(), m_shell); + serverSurfaceSpy.clear(); + QVERIFY(serverSurfaceSpy.wait()); + ShellSurfaceInterface *serverSurface2 = serverSurfaceSpy.first().first().value(); + QVERIFY(serverSurface2 != serverSurface); + QVERIFY(serverSurface2); + QVERIFY(serverSurface2->acceptsKeyboardFocus()); + + // TODO: proper serial checking + surface->setTransientPopup(s2.data(), m_seat, 1, QPoint(10, 20)); + QVERIFY(transientSpy.wait()); + QCOMPARE(transientSpy.count(), 1); + QCOMPARE(transientSpy.first().first().toBool(), true); + QCOMPARE(transientOffsetSpy.count(), 1); + QCOMPARE(transientOffsetSpy.first().first().toPoint(), QPoint(10, 20)); + QCOMPARE(transientForChangedSpy.count(), 1); + QCOMPARE(serverSurface->isToplevel(), false); + QCOMPARE(serverSurface->isPopup(), true); + QCOMPARE(serverSurface->isTransient(), true); + QCOMPARE(serverSurface->transientFor(), QPointer(serverSurface2->surface())); + QCOMPARE(serverSurface->transientOffset(), QPoint(10, 20)); + // TODO: honor the flag + QCOMPARE(serverSurface->acceptsKeyboardFocus(), false); + + QCOMPARE(serverSurface2->isToplevel(), true); + QCOMPARE(serverSurface2->isPopup(), false); + QCOMPARE(serverSurface2->isTransient(), false); + QCOMPARE(serverSurface2->transientFor(), QPointer()); + QCOMPARE(serverSurface2->transientOffset(), QPoint()); + + // send popup done + QSignalSpy popupDoneSpy(surface, &ShellSurface::popupDone); + QVERIFY(popupDoneSpy.isValid()); + serverSurface->popupDone(); + QVERIFY(popupDoneSpy.wait()); +} + void TestWaylandShell::testPing() { using namespace KWayland::Server; diff --git a/src/wayland/server/shell_interface.cpp b/src/wayland/server/shell_interface.cpp index 1fa414a6b4..91a792c746 100644 --- a/src/wayland/server/shell_interface.cpp +++ b/src/wayland/server/shell_interface.cpp @@ -379,6 +379,9 @@ void ShellSurfaceInterface::Private::setPopupCallback(wl_client *client, wl_reso emit s->q_func()->transientChanged(!s->transientFor.isNull()); emit s->q_func()->transientOffsetChanged(s->transientOffset); emit s->q_func()->transientForChanged(); + // we ignore the flags as Qt requests keyboard focus for popups + // if we would honor the flag this could break compositors + // compare QtWayland (5.6), file qwaylandwlshellsurface.cpp:208 s->setAcceptsFocus(WL_SHELL_SURFACE_TRANSIENT_INACTIVE); } @@ -449,6 +452,14 @@ bool ShellSurfaceInterface::acceptsKeyboardFocus() const return d->acceptsKeyboardFocus; } +void ShellSurfaceInterface::popupDone() +{ + Q_D(); + if (isPopup() && d->resource) { + wl_shell_surface_send_popup_done(d->resource); + } +} + QPointer< SurfaceInterface > ShellSurfaceInterface::transientFor() const { Q_D(); diff --git a/src/wayland/server/shell_interface.h b/src/wayland/server/shell_interface.h index 95a0aca093..542e27375a 100644 --- a/src/wayland/server/shell_interface.h +++ b/src/wayland/server/shell_interface.h @@ -231,6 +231,18 @@ public: **/ bool acceptsKeyboardFocus() const; + /** + * Sends a popup done event to the shell surface. + * This is only relevant for popup windows. It indicates that the popup grab + * got canceled. This happens when e.g. the user clicks outside of any surface + * of the same client as this ShellSurfaceInterface. It is the task of the + * compositor to send the popupDone event appropriately. + * + * @see isPopup + * @since 5.33 + **/ + void popupDone(); + Q_SIGNALS: /** * Emitted whenever the title changes.