diff --git a/applets/appmenu/plugin/appmenumodel.h b/applets/appmenu/plugin/appmenumodel.h --- a/applets/appmenu/plugin/appmenumodel.h +++ b/applets/appmenu/plugin/appmenumodel.h @@ -20,6 +20,7 @@ ******************************************************************/ #include +#include #include #include #include @@ -30,7 +31,7 @@ class QDBusServiceWatcher; class KDBusMenuImporter; -class AppMenuModel : public QAbstractListModel +class AppMenuModel : public QAbstractListModel, public QAbstractNativeEventFilter { Q_OBJECT @@ -57,6 +58,9 @@ signals: void requestActivateIndex(int index); +protected: + bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override; + private Q_SLOTS: void onActiveWindowChanged(WId id); void update(); @@ -68,6 +72,8 @@ private: bool m_menuAvailable; + WId m_currentWindowId = 0; + QPointer m_menu; QStringList m_activeMenu; QList m_activeActions; diff --git a/applets/appmenu/plugin/appmenumodel.cpp b/applets/appmenu/plugin/appmenumodel.cpp --- a/applets/appmenu/plugin/appmenumodel.cpp +++ b/applets/appmenu/plugin/appmenumodel.cpp @@ -31,6 +31,7 @@ #endif #include +#include #include #include #include @@ -42,6 +43,10 @@ static const QByteArray s_x11AppMenuServiceNamePropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME"); static const QByteArray s_x11AppMenuObjectPathPropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH"); +#if HAVE_X11 +static QHash s_atoms; +#endif + class KDBusMenuImporter : public DBusMenuImporter { @@ -123,12 +128,18 @@ void AppMenuModel::onActiveWindowChanged(WId id) { + qApp->removeNativeEventFilter(this); + + if (!id) { + setMenuAvailable(false); + emit modelNeedsUpdate(); + return; + } + #if HAVE_X11 if (KWindowSystem::isPlatformX11()) { auto *c = QX11Info::connection(); - static QHash s_atoms; - auto getWindowPropertyString = [c, this](WId id, const QByteArray &name) -> QByteArray { QByteArray value; if (!s_atoms.contains(name)) { @@ -193,6 +204,11 @@ return; } + // monitor whether an app menu becomes available later + // this can happen when an app starts, shows its window, and only later announces global menu (e.g. Firefox) + qApp->installNativeEventFilter(this); + m_currentWindowId = id; + //no menu found, set it to unavailable setMenuAvailable(false); emit modelNeedsUpdate(); @@ -274,3 +290,36 @@ }); } +bool AppMenuModel::nativeEventFilter(const QByteArray &eventType, void *message, long *result) +{ + Q_UNUSED(result); + + if (!KWindowSystem::isPlatformX11() || eventType != "xcb_generic_event_t") { + return false; + } + +#if HAVE_X11 + auto e = static_cast(message); + const uint8_t type = e->response_type & ~0x80; + if (type == XCB_PROPERTY_NOTIFY) { + auto *event = reinterpret_cast(e); + if (event->window == m_currentWindowId) { + + auto serviceNameAtom = s_atoms.value(s_x11AppMenuServiceNamePropertyName); + auto objectPathAtom = s_atoms.value(s_x11AppMenuObjectPathPropertyName); + + if (serviceNameAtom != XCB_ATOM_NONE && objectPathAtom != XCB_ATOM_NONE) { // shouldn't happen + if (event->atom == serviceNameAtom || event->atom == objectPathAtom) { + // see if we now have a menu + onActiveWindowChanged(KWindowSystem::activeWindow()); + } + } + } + } +#else + Q_UNUSED(message); +#endif + + return false; +} +