diff --git a/autotests/integration/kwin_wayland_test.h b/autotests/integration/kwin_wayland_test.h index 0d40aef26a..82e0fc25ad 100644 --- a/autotests/integration/kwin_wayland_test.h +++ b/autotests/integration/kwin_wayland_test.h @@ -29,6 +29,7 @@ namespace KWayland { namespace Client { +class AppMenuManager; class ConnectionThread; class Compositor; class IdleInhibitManager; @@ -83,7 +84,8 @@ enum class AdditionalWaylandInterface { PlasmaShell = 1 << 2, WindowManagement = 1 << 3, PointerConstraints = 1 << 4, - IdleInhibition = 1 << 5 + IdleInhibition = 1 << 5, + AppMenu = 1 << 6 }; Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface) /** @@ -112,6 +114,8 @@ KWayland::Client::PlasmaShell *waylandPlasmaShell(); KWayland::Client::PlasmaWindowManagement *waylandWindowManagement(); KWayland::Client::PointerConstraints *waylandPointerConstraints(); KWayland::Client::IdleInhibitManager *waylandIdleInhibitManager(); +KWayland::Client::AppMenuManager *waylandAppMenuManager(); + bool waitForWaylandPointer(); bool waitForWaylandTouch(); diff --git a/autotests/integration/shell_client_test.cpp b/autotests/integration/shell_client_test.cpp index ad4929c99e..d4a0f8fd81 100644 --- a/autotests/integration/shell_client_test.cpp +++ b/autotests/integration/shell_client_test.cpp @@ -33,11 +33,13 @@ along with this program. If not, see . #include #include #include +#include #include #include #include + // system #include #include @@ -84,6 +86,7 @@ private Q_SLOTS: void testUnresponsiveWindow(); void testX11WindowId_data(); void testX11WindowId(); + void testAppMenu(); }; void TestShellClient::initTestCase() @@ -108,7 +111,8 @@ void TestShellClient::initTestCase() void TestShellClient::init() { - QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration)); + QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration | + Test::AdditionalWaylandInterface::AppMenu)); screens()->setCurrent(0); KWin::Cursor::setPos(QPoint(1280, 512)); @@ -955,5 +959,21 @@ void TestShellClient::testX11WindowId() QCOMPARE(c->window(), 0u); } +void TestShellClient::testAppMenu() +{ + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createShellSurface(Test::ShellSurfaceType::XdgShellV6, surface.data())); + auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + QVERIFY(c); + QScopedPointer menu(Test::waylandAppMenuManager()->create(surface.data())); + QSignalSpy spy(c, &ShellClient::hasApplicationMenuChanged); + menu->setAddress("service.name", "object/path"); + spy.wait(); + QCOMPARE(c->hasApplicationMenu(), true); + QCOMPARE(c->applicationMenuServiceName(), "service.name"); + QCOMPARE(c->applicationMenuObjectPath(), "object/path"); +} + + WAYLANDTEST_MAIN(TestShellClient) #include "shell_client_test.moc" diff --git a/autotests/integration/test_helpers.cpp b/autotests/integration/test_helpers.cpp index 7f6fb61e7b..a94f7e8d88 100644 --- a/autotests/integration/test_helpers.cpp +++ b/autotests/integration/test_helpers.cpp @@ -36,6 +36,7 @@ along with this program. If not, see . #include #include #include +#include #include #include @@ -73,6 +74,7 @@ static struct { QThread *thread = nullptr; QVector outputs; IdleInhibitManager *idleInhibit = nullptr; + AppMenuManager *appMenu = nullptr; } s_waylandConnection; bool setupWaylandConnection(AdditionalWaylandInterfaces flags) @@ -196,6 +198,12 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags) return false; } } + if (flags.testFlag(AdditionalWaylandInterface::AppMenu)) { + s_waylandConnection.appMenu = registry->createAppMenuManager(registry->interface(Registry::Interface::AppMenu).name, registry->interface(Registry::Interface::AppMenu).version); + if (!s_waylandConnection.appMenu->isValid()) { + return false; + } + } return true; } @@ -230,6 +238,8 @@ void destroyWaylandConnection() s_waylandConnection.queue = nullptr; delete s_waylandConnection.registry; s_waylandConnection.registry = nullptr; + delete s_waylandConnection.appMenu; + s_waylandConnection.appMenu = nullptr; if (s_waylandConnection.thread) { QSignalSpy spy(s_waylandConnection.connection, &QObject::destroyed); s_waylandConnection.connection->deleteLater(); @@ -294,6 +304,11 @@ IdleInhibitManager *waylandIdleInhibitManager() return s_waylandConnection.idleInhibit; } +AppMenuManager* waylandAppMenuManager() +{ + return s_waylandConnection.appMenu; +} + bool waitForWaylandPointer() { if (!s_waylandConnection.seat) { diff --git a/shell_client.cpp b/shell_client.cpp index 5e4894881a..e9dde7deb3 100644 --- a/shell_client.cpp +++ b/shell_client.cpp @@ -46,6 +46,8 @@ along with this program. If not, see . #include #include #include +#include + #include #include @@ -58,8 +60,6 @@ along with this program. If not, see . using namespace KWayland::Server; static const QByteArray s_schemePropertyName = QByteArrayLiteral("KDE_COLOR_SCHEME_PATH"); -static const QByteArray s_appMenuServiceNamePropertyName = QByteArrayLiteral("KDE_APPMENU_SERVICE_NAME"); -static const QByteArray s_appMenuObjectPathPropertyName = QByteArrayLiteral("KDE_APPMENU_OBJECT_PATH"); static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION"); namespace KWin @@ -336,7 +336,6 @@ void ShellClient::init() } AbstractClient::updateColorScheme(QString()); - updateApplicationMenu(); if (!m_internal) { discardTemporaryRules(); @@ -1374,16 +1373,26 @@ void ShellClient::installQtExtendedSurface(QtExtendedSurfaceInterface *surface) m_qtExtendedSurface->installEventFilter(this); } +void ShellClient::installAppMenu(AppMenuInterface *menu) +{ + m_appMenuInterface = menu; + + auto updateMenu = [this](AppMenuInterface::InterfaceAddress address) { + updateApplicationMenuServiceName(address.serviceName); + updateApplicationMenuObjectPath(address.objectPath); + }; + connect(m_appMenuInterface, &AppMenuInterface::addressChanged, this, [=](AppMenuInterface::InterfaceAddress address) { + updateMenu(address); + }); + updateMenu(menu->address()); +} + bool ShellClient::eventFilter(QObject *watched, QEvent *event) { if (watched == m_qtExtendedSurface.data() && event->type() == QEvent::DynamicPropertyChange) { QDynamicPropertyChangeEvent *pe = static_cast(event); if (pe->propertyName() == s_schemePropertyName) { AbstractClient::updateColorScheme(rules()->checkDecoColor(m_qtExtendedSurface->property(pe->propertyName().constData()).toString())); - } else if (pe->propertyName() == s_appMenuServiceNamePropertyName) { - updateApplicationMenuServiceName(m_qtExtendedSurface->property(pe->propertyName().constData()).toString()); - } else if (pe->propertyName() == s_appMenuObjectPathPropertyName) { - updateApplicationMenuObjectPath(m_qtExtendedSurface->property(pe->propertyName().constData()).toString()); } } if (watched == m_internalWindow && event->type() == QEvent::DynamicPropertyChange) { @@ -1637,14 +1646,6 @@ void ShellClient::killWindow() QTimer::singleShot(5000, c, &ClientConnection::destroy); } -void ShellClient::updateApplicationMenu() -{ - if (m_qtExtendedSurface) { - updateApplicationMenuServiceName(m_qtExtendedSurface->property(s_appMenuObjectPathPropertyName).toString()); - updateApplicationMenuObjectPath(m_qtExtendedSurface->property(s_appMenuServiceNamePropertyName).toString()); - } -} - bool ShellClient::hasPopupGrab() const { return m_hasPopupGrab; diff --git a/shell_client.h b/shell_client.h index da3cd67f01..c2dfa2270a 100644 --- a/shell_client.h +++ b/shell_client.h @@ -29,6 +29,7 @@ namespace Server { class ShellSurfaceInterface; class ServerSideDecorationInterface; +class AppMenuInterface; class PlasmaShellSurfaceInterface; class QtExtendedSurfaceInterface; } @@ -135,6 +136,7 @@ public: void installPlasmaShellSurface(KWayland::Server::PlasmaShellSurfaceInterface *surface); void installQtExtendedSurface(KWayland::Server::QtExtendedSurfaceInterface *surface); void installServerSideDecoration(KWayland::Server::ServerSideDecorationInterface *decoration); + void installAppMenu(KWayland::Server::AppMenuInterface *appmenu); bool isInitialPositionSet() const override; @@ -154,8 +156,6 @@ public: // TODO: const-ref void placeIn(QRect &area); - void updateApplicationMenu(); - bool hasPopupGrab() const override; void popupDone() override; @@ -219,6 +219,7 @@ private: NET::WindowType m_windowType = NET::Normal; QPointer m_plasmaShellSurface; QPointer m_qtExtendedSurface; + QPointer m_appMenuInterface; KWayland::Server::ServerSideDecorationInterface *m_serverDecoration = nullptr; bool m_userNoBorder = false; bool m_fullScreen = false; diff --git a/wayland_server.cpp b/wayland_server.cpp index 7b2d2e8698..6008afc9ef 100644 --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -33,6 +33,7 @@ along with this program. If not, see . #include #include // Server +#include #include #include #include @@ -151,6 +152,9 @@ void WaylandServer::createSurface(T *surface) client->installPlasmaShellSurface(*it); m_plasmaShellSurfaces.erase(it); } + if (auto menu = m_appMenuManager->appMenuForSurface(surface->surface())) { + client->installAppMenu(menu); + } if (client->isInternal()) { m_internalClients << client; } else { @@ -261,6 +265,8 @@ bool WaylandServer::init(const QByteArray &socketName, InitalizationFlags flags) } } ); + + m_qtExtendedSurface = m_display->createQtSurfaceExtension(m_display); m_qtExtendedSurface->create(); connect(m_qtExtendedSurface, &QtSurfaceExtensionInterface::surfaceCreated, @@ -270,6 +276,16 @@ bool WaylandServer::init(const QByteArray &socketName, InitalizationFlags flags) } } ); + m_appMenuManager = m_display->createAppMenuManagerInterface(m_display); + m_appMenuManager->create(); + connect(m_appMenuManager, &AppMenuManagerInterface::appMenuCreated, + [this] (AppMenuInterface *appMenu) { + if (ShellClient *client = findClient(appMenu->surface())) { + client->installAppMenu(appMenu); + } + } + ); + m_windowManagement = m_display->createPlasmaWindowManagement(m_display); m_windowManagement->create(); m_windowManagement->setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState::Disabled); diff --git a/wayland_server.h b/wayland_server.h index d272d9ec25..879bff0b13 100644 --- a/wayland_server.h +++ b/wayland_server.h @@ -40,6 +40,7 @@ class Surface; } namespace Server { +class AppMenuManagerInterface; class ClientConnection; class CompositorInterface; class Display; @@ -219,6 +220,7 @@ private: KWayland::Server::QtSurfaceExtensionInterface *m_qtExtendedSurface = nullptr; KWayland::Server::ServerSideDecorationManagerInterface *m_decorationManager = nullptr; KWayland::Server::OutputManagementInterface *m_outputManagement = nullptr; + KWayland::Server::AppMenuManagerInterface *m_appMenuManager = nullptr; struct { KWayland::Server::ClientConnection *client = nullptr; QMetaObject::Connection destroyConnection;