diff --git a/gmenu-dbusmenu-proxy/CMakeLists.txt b/gmenu-dbusmenu-proxy/CMakeLists.txt --- a/gmenu-dbusmenu-proxy/CMakeLists.txt +++ b/gmenu-dbusmenu-proxy/CMakeLists.txt @@ -38,6 +38,7 @@ Qt5::Core Qt5::X11Extras Qt5::DBus + KF5::CoreAddons KF5::ConfigCore KF5::WindowSystem XCB::XCB diff --git a/gmenu-dbusmenu-proxy/menuproxy.h b/gmenu-dbusmenu-proxy/menuproxy.h --- a/gmenu-dbusmenu-proxy/menuproxy.h +++ b/gmenu-dbusmenu-proxy/menuproxy.h @@ -27,6 +27,9 @@ #include class QDBusServiceWatcher; +class QTimer; + +class KDirWatch; class Window; @@ -46,7 +49,15 @@ bool init(); void teardown(); - void setGtkShellShowsMenuBar(bool show); + static QString gtkRc2Path(); + static QString gtk3SettingsIniPath(); + + void enableGtkSettings(bool enabled); + + void writeGtk2Settings(); + void writeGtk3Settings(); + + void addOrRemoveAppMenuGtkModule(QStringList &list); xcb_connection_t *m_xConnection; @@ -58,4 +69,9 @@ QDBusServiceWatcher *m_serviceWatcher; + KDirWatch *m_gtk2RcWatch; + QTimer *m_writeGtk2SettingsTimer; + + bool m_enabled = false; + }; diff --git a/gmenu-dbusmenu-proxy/menuproxy.cpp b/gmenu-dbusmenu-proxy/menuproxy.cpp --- a/gmenu-dbusmenu-proxy/menuproxy.cpp +++ b/gmenu-dbusmenu-proxy/menuproxy.cpp @@ -32,8 +32,10 @@ #include #include #include +#include #include +#include #include #include @@ -59,10 +61,15 @@ static const QByteArray s_kdeNetWmAppMenuServiceName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME"); static const QByteArray s_kdeNetWmAppMenuObjectPath = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH"); +static const QString s_gtkModules = QStringLiteral("gtk-modules"); +static const QString s_appMenuGtkModule = QStringLiteral("appmenu-gtk-module"); + MenuProxy::MenuProxy() : QObject() , m_xConnection(QX11Info::connection()) , m_serviceWatcher(new QDBusServiceWatcher(this)) + , m_gtk2RcWatch(new KDirWatch(this)) + , m_writeGtk2SettingsTimer(new QTimer(this)) { m_serviceWatcher->setConnection(QDBusConnection::sessionBus()); m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration | @@ -88,8 +95,23 @@ qCDebug(DBUSMENUPROXY) << "No global menu service available, waiting for it to start before doing anything"; // be sure when started to restore gtk menus when there's no dbus menu around in case we crashed - setGtkShellShowsMenuBar(false); + enableGtkSettings(false); } + + // kde-gtk-config just deletes and re-creates the gtkrc-2.0, watch this and add out config to it again + m_writeGtk2SettingsTimer->setSingleShot(true); + m_writeGtk2SettingsTimer->setInterval(1000); + connect(m_writeGtk2SettingsTimer, &QTimer::timeout, this, &MenuProxy::writeGtk2Settings); + + auto startGtk2SettingsTimer = [this] { + if (!m_writeGtk2SettingsTimer->isActive()) { + m_writeGtk2SettingsTimer->start(); + } + }; + + connect(m_gtk2RcWatch, &KDirWatch::created, this, startGtk2SettingsTimer); + connect(m_gtk2RcWatch, &KDirWatch::dirty, this, startGtk2SettingsTimer); + m_gtk2RcWatch->addFile(gtkRc2Path()); } MenuProxy::~MenuProxy() @@ -104,7 +126,7 @@ return false; } - setGtkShellShowsMenuBar(true); + enableGtkSettings(true); connect(KWindowSystem::self(), &KWindowSystem::windowAdded, this, &MenuProxy::onWindowAdded); connect(KWindowSystem::self(), &KWindowSystem::windowRemoved, this, &MenuProxy::onWindowRemoved); @@ -123,7 +145,7 @@ void MenuProxy::teardown() { - setGtkShellShowsMenuBar(false); + enableGtkSettings(false); QDBusConnection::sessionBus().unregisterService(s_ourServiceName); @@ -134,30 +156,115 @@ m_windows.clear(); } -void MenuProxy::setGtkShellShowsMenuBar(bool show) +void MenuProxy::enableGtkSettings(bool enable) { - qCDebug(DBUSMENUPROXY) << "Setting gtk-shell-shows-menu-bar to" << show << "which will" << (show ? "hide" : "show") << "menu bars in applications"; + m_enabled = enable; - // mostly taken from kde-gtk-config - QString root = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); - if (root.isEmpty()) { - root = QFileInfo(QDir::home(), QStringLiteral(".config")).absoluteFilePath(); + writeGtk2Settings(); + writeGtk3Settings(); + + // TODO use gconf/dconf directly or at least signal a change somehow? +} + +QString MenuProxy::gtkRc2Path() +{ + return QDir::homePath() + QLatin1String("/.gtkrc-2.0"); +} + +QString MenuProxy::gtk3SettingsIniPath() +{ + return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/gtk-3.0/settings.ini"); +} + +void MenuProxy::writeGtk2Settings() +{ + qCDebug(DBUSMENUPROXY) << "Writing gtkrc-2.0 to" << (m_enabled ? "enable" : "disable") << "global menu support"; + + QFile rcFile(gtkRc2Path()); + if (!rcFile.open(QIODevice::ReadWrite | QIODevice::Text)) { + return; + } + + QByteArray content; + + QStringList gtkModules; + + while (!rcFile.atEnd()) { + const QByteArray rawLine = rcFile.readLine(); + + const QString line = QString::fromUtf8(rawLine.trimmed()); + + if (!line.startsWith(s_gtkModules)) { + // keep line as-is + content += rawLine; + continue; + } + + const int equalSignIdx = line.indexOf(QLatin1Char('=')); + if (equalSignIdx < 1) { + continue; + } + + gtkModules = line.mid(equalSignIdx + 1).split(QLatin1Char(':'), QString::SkipEmptyParts); + + break; } - const QString settingsFilePath = root + QStringLiteral("/gtk-3.0/settings.ini"); + addOrRemoveAppMenuGtkModule(gtkModules); + + if (!gtkModules.isEmpty()) { + content += QStringLiteral("%1=%2").arg(s_gtkModules, gtkModules.join(QLatin1Char(':'))).toUtf8(); + } - auto cfg = KSharedConfig::openConfig(settingsFilePath, KConfig::NoGlobals); + qCDebug(DBUSMENUPROXY) << " gtk-modules:" << gtkModules; + + m_gtk2RcWatch->stopScan(); + + // now write the new contents of the file + rcFile.resize(0); + rcFile.write(content); + rcFile.close(); + + m_gtk2RcWatch->startScan(); +} + +void MenuProxy::writeGtk3Settings() +{ + qCDebug(DBUSMENUPROXY) << "Writing gtk-3.0/settings.ini" << (m_enabled ? "enable" : "disable") << "global menu support"; + + // mostly taken from kde-gtk-config + auto cfg = KSharedConfig::openConfig(gtk3SettingsIniPath(), KConfig::NoGlobals); KConfigGroup group(cfg, "Settings"); - if (show) { + QStringList gtkModules = group.readEntry("gtk-modules", QString()).split(QLatin1Char(':'), QString::SkipEmptyParts); + addOrRemoveAppMenuGtkModule(gtkModules); + + if (!gtkModules.isEmpty()) { + group.writeEntry("gtk-modules", gtkModules.join(QLatin1Char(':'))); + } else { + group.deleteEntry("gtk-modules"); + } + + qCDebug(DBUSMENUPROXY) << " gtk-modules:" << gtkModules; + + if (m_enabled) { group.writeEntry("gtk-shell-shows-menubar", 1); } else { group.deleteEntry("gtk-shell-shows-menubar"); } + qCDebug(DBUSMENUPROXY) << " gtk-shell-shows-menubar:" << (m_enabled ? 1 : 0); + group.sync(); +} - // TODO use gconf/dconf directly or at least signal a change somehow? +void MenuProxy::addOrRemoveAppMenuGtkModule(QStringList &list) +{ + if (m_enabled && !list.contains(s_appMenuGtkModule)) { + list.append(s_appMenuGtkModule); + } else if (!m_enabled) { + list.removeAll(s_appMenuGtkModule); + } } void MenuProxy::onWindowAdded(WId id)