diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -423,6 +423,7 @@ wayland_server.cpp wayland_cursor_theme.cpp virtualkeyboard.cpp + appmenu.cpp ) if(KWIN_BUILD_TABBOX) @@ -473,6 +474,8 @@ qt5_add_dbus_interface( kwin_KDEINIT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.ScreenSaver.xml screenlocker_interface) +qt5_add_dbus_interface( kwin_KDEINIT_SRCS org.kde.kappmenu.xml appmenu_interface ) + qt5_add_resources( kwin_KDEINIT_SRCS resources.qrc ) ki18n_wrap_ui(kwin_KDEINIT_SRCS diff --git a/abstract_client.h b/abstract_client.h --- a/abstract_client.h +++ b/abstract_client.h @@ -255,6 +255,16 @@ * (e.g. "/opt/kde/share/org.kde.foo.desktop") in case it's not in a standard location. **/ Q_PROPERTY(QByteArray desktopFileName READ desktopFileName NOTIFY desktopFileNameChanged) + + /** + * Whether an application menu is available for this Client + */ + Q_PROPERTY(bool hasApplicationMenu READ hasApplicationMenu NOTIFY hasApplicationMenuChanged) + /** + * Whether the application menu for this Client is currently opened + */ + Q_PROPERTY(bool applicationMenuActive READ applicationMenuActive NOTIFY applicationMenuActiveChanged) + public: virtual ~AbstractClient(); @@ -617,6 +627,21 @@ // TODO: remove boolean trap static bool belongToSameApplication(const AbstractClient* c1, const AbstractClient* c2, bool active_hack = false); + bool hasApplicationMenu() const; + bool applicationMenuActive() const { + return m_applicationMenuActive; + } + void setApplicationMenuActive(bool applicationMenuActive); + + QString applicationMenuServiceName() const { + return m_applicationMenuServiceName; + } + QString applicationMenuObjectPath() const { + return m_applicationMenuObjectPath; + } + + void showApplicationMenu(); + public Q_SLOTS: virtual void closeWindow() = 0; @@ -656,6 +681,8 @@ void shadeableChanged(bool); void maximizeableChanged(bool); void desktopFileNameChanged(); + void hasApplicationMenuChanged(bool); + void applicationMenuActiveChanged(bool); protected: AbstractClient(); @@ -937,6 +964,9 @@ void setDesktopFileName(const QByteArray &name); QString iconFromDesktopFile() const; + void updateApplicationMenuServiceName(const QString &serviceName); + void updateApplicationMenuObjectPath(const QString &objectPath); + private: void handlePaletteChange(); QSharedPointer m_tabBoxClient; @@ -1003,9 +1033,11 @@ QPointer client; QElapsedTimer doubleClickTimer; } m_decoration; - QByteArray m_desktopFileName; + bool m_applicationMenuActive = false; + QString m_applicationMenuServiceName; + QString m_applicationMenuObjectPath; static bool s_haveResizeEffect; }; diff --git a/abstract_client.cpp b/abstract_client.cpp --- a/abstract_client.cpp +++ b/abstract_client.cpp @@ -18,6 +18,8 @@ along with this program. If not, see . *********************************************************************/ #include "abstract_client.h" + +#include "appmenu.h" #include "decorations/decoratedclient.h" #include "decorations/decorationpalette.h" #include "decorations/decorationbridge.h" @@ -69,6 +71,10 @@ connect(this, &AbstractClient::paletteChanged, this, &AbstractClient::triggerDecorationRepaint); connect(Decoration::DecorationBridge::self(), &QObject::destroyed, this, &AbstractClient::destroyDecoration); + + connect(ApplicationMenu::self(), &ApplicationMenu::applicationMenuEnabledChanged, this, [this] { + emit hasApplicationMenuChanged(hasApplicationMenu()); + }); } AbstractClient::~AbstractClient() @@ -1663,4 +1669,53 @@ return df.readIcon(); } +bool AbstractClient::hasApplicationMenu() const +{ + return ApplicationMenu::self()->applicationMenuEnabled() && !m_applicationMenuServiceName.isEmpty() && !m_applicationMenuObjectPath.isEmpty(); +} + +void AbstractClient::updateApplicationMenuServiceName(const QString &serviceName) +{ + const bool old_hasApplicationMenu = hasApplicationMenu(); + + m_applicationMenuServiceName = serviceName; + + const bool new_hasApplicationMenu = hasApplicationMenu(); + + if (old_hasApplicationMenu != new_hasApplicationMenu) { + emit hasApplicationMenuChanged(new_hasApplicationMenu); + } +} + +void AbstractClient::updateApplicationMenuObjectPath(const QString &objectPath) +{ + const bool old_hasApplicationMenu = hasApplicationMenu(); + + m_applicationMenuObjectPath = objectPath; + + const bool new_hasApplicationMenu = hasApplicationMenu(); + + if (old_hasApplicationMenu != new_hasApplicationMenu) { + emit hasApplicationMenuChanged(new_hasApplicationMenu); + } +} + +void AbstractClient::setApplicationMenuActive(bool applicationMenuActive) +{ + if (m_applicationMenuActive != applicationMenuActive) { + m_applicationMenuActive = applicationMenuActive; + emit applicationMenuActiveChanged(applicationMenuActive); + } +} + +void AbstractClient::showApplicationMenu() +{ + if (isDecorated()) { + decoration()->showApplicationMenu(); + } else { + // we don't know where the application menu button will be, show it in the top left corner instead + Workspace::self()->showApplicationMenu(QRect(), this); + } +} + } diff --git a/appmenu.h b/appmenu.h new file mode 100644 --- /dev/null +++ b/appmenu.h @@ -0,0 +1,74 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (c) 2011 Lionel Chauvin +Copyright (c) 2011,2012 Cédric Bellegarde +Copyright (C) 2013 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_APPLICATIONMENU_H +#define KWIN_APPLICATIONMENU_H +// KWin +#include +// Qt +#include +// xcb +#include + +class QPoint; +class OrgKdeKappmenuInterface; +class QDBusObjectPath; + +namespace KWin +{ + +class AbstractClient; + +class ApplicationMenu : public QObject +{ + Q_OBJECT + +public: + ~ApplicationMenu() override; + + void showApplicationMenu(const QPoint &pos, AbstractClient *c); + + bool applicationMenuEnabled() const; + +signals: + void applicationMenuEnabledChanged(bool enabled); + +private Q_SLOTS: + void slotReconfigured(); + void slotShowRequest(const QString &serviceName, const QDBusObjectPath &menuObjectPath); + void slotMenuShown(const QString &serviceName, const QDBusObjectPath &menuObjectPath); + void slotMenuHidden(const QString &serviceName, const QDBusObjectPath &menuObjectPath); + +private: + void updateApplicationMenuEnabled(); + + OrgKdeKappmenuInterface *m_appmenuInterface; + + AbstractClient *findAbstractClientWithApplicationMenu(const QString &serviceName, const QDBusObjectPath &menuObjectPath); + + bool m_applicationMenuEnabled = false; + + KWIN_SINGLETON(ApplicationMenu) +}; + +} + +#endif // KWIN_APPLICATIONMENU_H diff --git a/appmenu.cpp b/appmenu.cpp new file mode 100644 --- /dev/null +++ b/appmenu.cpp @@ -0,0 +1,111 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (c) 2011 Lionel Chauvin +Copyright (c) 2011,2012 Cédric Bellegarde +Copyright (C) 2013 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "appmenu.h" +#include "client.h" +#include "workspace.h" +#include + +#include + +using namespace KWin; + +KWIN_SINGLETON_FACTORY(ApplicationMenu) + +ApplicationMenu::ApplicationMenu(QObject *parent) + : QObject(parent) + , m_appmenuInterface(new OrgKdeKappmenuInterface(QStringLiteral("org.kde.kappmenu"), QStringLiteral("/KAppMenu"), QDBusConnection::sessionBus(), this)) +{ + connect(m_appmenuInterface, &OrgKdeKappmenuInterface::showRequest, this, &ApplicationMenu::slotShowRequest); + connect(m_appmenuInterface, &OrgKdeKappmenuInterface::menuShown, this, &ApplicationMenu::slotMenuShown); + connect(m_appmenuInterface, &OrgKdeKappmenuInterface::menuHidden, this, &ApplicationMenu::slotMenuHidden); + connect(m_appmenuInterface, &OrgKdeKappmenuInterface::reconfigured, this, &ApplicationMenu::slotReconfigured); + + updateApplicationMenuEnabled(); +} + +ApplicationMenu::~ApplicationMenu() +{ + s_self = nullptr; +} + +void ApplicationMenu::slotReconfigured() +{ + updateApplicationMenuEnabled(); +} + +bool ApplicationMenu::applicationMenuEnabled() const +{ + return m_applicationMenuEnabled; +} + +void ApplicationMenu::updateApplicationMenuEnabled() +{ + const bool old_enabled = m_applicationMenuEnabled; + + KConfigGroup config(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), QStringLiteral("Appmenu Style")); + const QString &menuStyle = config.readEntry(QStringLiteral("Style")); + + const bool enabled = (menuStyle == QLatin1String("Decoration")); + + if (old_enabled != enabled) { + m_applicationMenuEnabled = enabled; + emit applicationMenuEnabledChanged(enabled); + } +} + +void ApplicationMenu::slotShowRequest(const QString &serviceName, const QDBusObjectPath &menuObjectPath) +{ + if (AbstractClient *c = findAbstractClientWithApplicationMenu(serviceName, menuObjectPath)) { + c->showApplicationMenu(); + } +} + +void ApplicationMenu::slotMenuShown(const QString &serviceName, const QDBusObjectPath &menuObjectPath) +{ + if (AbstractClient *c = findAbstractClientWithApplicationMenu(serviceName, menuObjectPath)) { + c->setApplicationMenuActive(true); + } +} + +void ApplicationMenu::slotMenuHidden(const QString &serviceName, const QDBusObjectPath &menuObjectPath) +{ + if (AbstractClient *c = findAbstractClientWithApplicationMenu(serviceName, menuObjectPath)) { + c->setApplicationMenuActive(false); + } +} + +void ApplicationMenu::showApplicationMenu(const QPoint &p, AbstractClient *c) +{ + m_appmenuInterface->showMenu(p.x(), p.y(), c->applicationMenuServiceName(), QDBusObjectPath(c->applicationMenuObjectPath())); +} + +AbstractClient *ApplicationMenu::findAbstractClientWithApplicationMenu(const QString &serviceName, const QDBusObjectPath &menuObjectPath) +{ + if (serviceName.isEmpty() || menuObjectPath.path().isEmpty()) { + return nullptr; + } + + return Workspace::self()->findAbstractClient([&](const AbstractClient *c) { + return c->applicationMenuServiceName() == serviceName + && c->applicationMenuObjectPath() == menuObjectPath.path(); + }); +} diff --git a/atoms.h b/atoms.h --- a/atoms.h +++ b/atoms.h @@ -68,6 +68,8 @@ Xcb::Atom kwin_dbus_service; Xcb::Atom utf8_string; Xcb::Atom wl_surface_id; + Xcb::Atom kde_net_wm_appmenu_service_name; + Xcb::Atom kde_net_wm_appmenu_object_path; /** * @internal diff --git a/atoms.cpp b/atoms.cpp --- a/atoms.cpp +++ b/atoms.cpp @@ -59,6 +59,8 @@ , kwin_dbus_service(QByteArrayLiteral("_ORG_KDE_KWIN_DBUS_SERVICE")) , utf8_string(QByteArrayLiteral("UTF8_STRING")) , wl_surface_id(QByteArrayLiteral("WL_SURFACE_ID")) + , kde_net_wm_appmenu_service_name(QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME")) + , kde_net_wm_appmenu_object_path(QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH")) , m_dtSmWindowInfo(QByteArrayLiteral("_DT_SM_WINDOW_INFO")) , m_motifSupport(QByteArrayLiteral("_MOTIF_WM_INFO")) , m_helpersRetrieved(false) diff --git a/client.h b/client.h --- a/client.h +++ b/client.h @@ -331,6 +331,14 @@ **/ void showOnScreenEdge() override; + Xcb::StringProperty fetchApplicationMenuServiceName() const; + void readApplicationMenuServiceName(Xcb::StringProperty &property); + void checkApplicationMenuServiceName(); + + Xcb::StringProperty fetchApplicationMenuObjectPath() const; + void readApplicationMenuObjectPath(Xcb::StringProperty &property); + void checkApplicationMenuObjectPath(); + static void cleanupX11(); public Q_SLOTS: diff --git a/client.cpp b/client.cpp --- a/client.cpp +++ b/client.cpp @@ -2152,5 +2152,37 @@ return m_geometryHints.resizeIncrements(); } +Xcb::StringProperty Client::fetchApplicationMenuServiceName() const +{ + return Xcb::StringProperty(m_client, atoms->kde_net_wm_appmenu_service_name); +} + +void Client::readApplicationMenuServiceName(Xcb::StringProperty &property) +{ + updateApplicationMenuServiceName(QString::fromUtf8(property)); +} + +void Client::checkApplicationMenuServiceName() +{ + Xcb::StringProperty property = fetchApplicationMenuServiceName(); + readApplicationMenuServiceName(property); +} + +Xcb::StringProperty Client::fetchApplicationMenuObjectPath() const +{ + return Xcb::StringProperty(m_client, atoms->kde_net_wm_appmenu_object_path); +} + +void Client::readApplicationMenuObjectPath(Xcb::StringProperty &property) +{ + updateApplicationMenuObjectPath(QString::fromUtf8(property)); +} + +void Client::checkApplicationMenuObjectPath() +{ + Xcb::StringProperty property = fetchApplicationMenuObjectPath(); + readApplicationMenuObjectPath(property); +} + } // namespace diff --git a/decorations/decoratedclient.h b/decorations/decoratedclient.h --- a/decorations/decoratedclient.h +++ b/decorations/decoratedclient.h @@ -69,16 +69,22 @@ Qt::Edges adjacentScreenEdges() const override; + bool hasApplicationMenu() const override; + bool isApplicationMenuActive() const override; + void requestClose() override; void requestContextHelp() override; void requestToggleMaximization(Qt::MouseButtons buttons) override; void requestMinimize() override; void requestShowWindowMenu() override; + void requestShowApplicationMenu(const QRect &rect) override; void requestToggleKeepAbove() override; void requestToggleKeepBelow() override; void requestToggleOnAllDesktops() override; void requestToggleShade() override; + void showApplicationMenu(); + AbstractClient *client() { return m_client; } diff --git a/decorations/decoratedclient.cpp b/decorations/decoratedclient.cpp --- a/decorations/decoratedclient.cpp +++ b/decorations/decoratedclient.cpp @@ -30,8 +30,6 @@ #include #include -#include - namespace KWin { namespace Decoration @@ -110,6 +108,9 @@ connect(client, &AbstractClient::maximizeableChanged, decoratedClient, &KDecoration2::DecoratedClient::maximizeableChanged); connect(client, &AbstractClient::paletteChanged, decoratedClient, &KDecoration2::DecoratedClient::paletteChanged); + + connect(client, &AbstractClient::hasApplicationMenuChanged, decoratedClient, &KDecoration2::DecoratedClient::hasApplicationMenuChanged); + connect(client, &AbstractClient::applicationMenuActiveChanged, decoratedClient, &KDecoration2::DecoratedClient::applicationMenuActiveChanged); } DecoratedClientImpl::~DecoratedClientImpl() = default; @@ -203,6 +204,16 @@ Workspace::self()->showWindowMenu(QRect(Cursor::pos(), Cursor::pos()), m_client); } +void DecoratedClientImpl::requestShowApplicationMenu(const QRect &rect) +{ + Workspace::self()->showApplicationMenu(rect, m_client); +} + +void DecoratedClientImpl::showApplicationMenu() +{ + decoration()->showApplicationMenu(); +} + void DecoratedClientImpl::requestToggleMaximization(Qt::MouseButtons buttons) { QMetaObject::invokeMethod(this, "delayedRequestToggleMaximization", Qt::QueuedConnection, Q_ARG(Options::WindowOperation, options->operationMaxButtonClick(buttons))); @@ -265,6 +276,16 @@ return edges; } +bool DecoratedClientImpl::hasApplicationMenu() const +{ + return m_client->hasApplicationMenu(); +} + +bool DecoratedClientImpl::isApplicationMenuActive() const +{ + return m_client->applicationMenuActive(); +} + void DecoratedClientImpl::createRenderer() { if (Compositor::self()->hasScene()) { diff --git a/events.cpp b/events.cpp --- a/events.cpp +++ b/events.cpp @@ -930,6 +930,10 @@ updateShowOnScreenEdge(); else if (e->atom == atoms->gtk_frame_extents) detectGtkFrameExtents(); + else if (e->atom == atoms->kde_net_wm_appmenu_service_name) + checkApplicationMenuServiceName(); + else if (e->atom == atoms->kde_net_wm_appmenu_object_path) + checkApplicationMenuObjectPath(); break; } } diff --git a/kcmkwin/kwindecoration/declarative-plugin/previewclient.h b/kcmkwin/kwindecoration/declarative-plugin/previewclient.h --- a/kcmkwin/kwindecoration/declarative-plugin/previewclient.h +++ b/kcmkwin/kwindecoration/declarative-plugin/previewclient.h @@ -97,6 +97,9 @@ QColor color(ColorGroup group, ColorRole role) const override; Qt::Edges adjacentScreenEdges() const override; + bool hasApplicationMenu() const override; + bool isApplicationMenuActive() const override; + void requestClose() override; void requestContextHelp() override; void requestToggleMaximization(Qt::MouseButtons buttons) override; @@ -105,8 +108,11 @@ void requestToggleKeepBelow() override; void requestToggleShade() override; void requestShowWindowMenu() override; + void requestShowApplicationMenu(const QRect &rect) override; void requestToggleOnAllDesktops() override; + void showApplicationMenu(); + void setCaption(const QString &caption); void setActive(bool active); void setCloseable(bool closeable); @@ -176,6 +182,7 @@ void bordersBottomEdgeChanged(bool); void showWindowMenuRequested(); + void showApplicationMenuRequested(); void minimizeRequested(); void closeRequested(); diff --git a/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp b/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp --- a/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp +++ b/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp @@ -309,6 +309,16 @@ return edges; } +bool PreviewClient::hasApplicationMenu() const +{ + return false; +} + +bool PreviewClient::isApplicationMenuActive() const +{ + return false; +} + bool PreviewClient::bordersBottomEdge() const { return m_bordersBottomEdge; @@ -408,6 +418,16 @@ emit showWindowMenuRequested(); } +void PreviewClient::requestShowApplicationMenu(const QRect &rect) +{ + Q_UNUSED(rect); +} + +void PreviewClient::showApplicationMenu() +{ + +} + void PreviewClient::requestToggleOnAllDesktops() { setDesktop(isOnAllDesktops() ? 1 : -1); diff --git a/manage.cpp b/manage.cpp --- a/manage.cpp +++ b/manage.cpp @@ -104,6 +104,9 @@ auto firstInTabBoxCookie = fetchFirstInTabBox(); auto transientCookie = fetchTransient(); auto activitiesCookie = fetchActivities(); + auto applicationMenuServiceNameCookie = fetchApplicationMenuServiceName(); + auto applicationMenuObjectPathCookie = fetchApplicationMenuObjectPath(); + m_geometryHints.init(window()); m_motif.init(window()); info = new WinInfo(this, m_client, rootWindow(), properties, properties2); @@ -391,6 +394,9 @@ readColorScheme(colorSchemeCookie); + readApplicationMenuServiceName(applicationMenuServiceNameCookie); + readApplicationMenuObjectPath(applicationMenuObjectPathCookie); + updateDecoration(false); // Also gravitates // TODO: Is CentralGravity right here, when resizing is done after gravitating? plainResize(rules()->checkSize(sizeForClientSize(geom.size()), !isMapped)); diff --git a/org.kde.kappmenu.xml b/org.kde.kappmenu.xml new file mode 100644 --- /dev/null +++ b/org.kde.kappmenu.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shell_client.h b/shell_client.h --- a/shell_client.h +++ b/shell_client.h @@ -135,6 +135,8 @@ // TODO: const-ref void placeIn(QRect &area); + void updateApplicationMenu(); + protected: void addDamage(const QRegion &damage) override; bool belongsToSameApplication(const AbstractClient *other, bool active_hack) const override; diff --git a/shell_client.cpp b/shell_client.cpp --- a/shell_client.cpp +++ b/shell_client.cpp @@ -51,6 +51,8 @@ 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"); namespace KWin { @@ -247,6 +249,7 @@ } updateColorScheme(QString()); + updateApplicationMenu(); } void ShellClient::destroyClient() @@ -1163,6 +1166,10 @@ QDynamicPropertyChangeEvent *pe = static_cast(event); if (pe->propertyName() == s_schemePropertyName) { 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()); } } return false; @@ -1377,4 +1384,12 @@ return false; } +void ShellClient::updateApplicationMenu() +{ + if (m_qtExtendedSurface) { + updateApplicationMenuServiceName(m_qtExtendedSurface->property(s_appMenuObjectPathPropertyName).toString()); + updateApplicationMenuObjectPath(m_qtExtendedSurface->property(s_appMenuServiceNamePropertyName).toString()); + } +} + } diff --git a/useractions.cpp b/useractions.cpp --- a/useractions.cpp +++ b/useractions.cpp @@ -45,6 +45,7 @@ #include "activities.h" #include #endif +#include "appmenu.h" #include @@ -1676,6 +1677,13 @@ m_userActionsMenu->show(pos, cl); } +void Workspace::showApplicationMenu(const QRect &pos, AbstractClient *c) +{ + // FIXME TODO map to global properly, taking into account margins and what not + ApplicationMenu::self()->showApplicationMenu(c->geometry().topLeft() + pos.bottomLeft(), c); + c->setApplicationMenuActive(true); +} + /*! Closes the popup client */ diff --git a/workspace.h b/workspace.h --- a/workspace.h +++ b/workspace.h @@ -285,6 +285,8 @@ return m_userActionsMenu; } + void showApplicationMenu(const QRect &pos, AbstractClient *c); + void updateMinimizedOfTransients(AbstractClient*); void updateOnAllDesktopsOfTransients(AbstractClient*); void checkTransients(xcb_window_t w); diff --git a/workspace.cpp b/workspace.cpp --- a/workspace.cpp +++ b/workspace.cpp @@ -27,6 +27,7 @@ #ifdef KWIN_BUILD_ACTIVITIES #include "activities.h" #endif +#include "appmenu.h" #include "atoms.h" #include "client.h" #include "composite.h" @@ -128,6 +129,8 @@ // If KWin was already running it saved its configuration after loosing the selection -> Reread QFuture reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration); + ApplicationMenu::create(this); + _self = this; // first initialize the extensions