Commit 3954e7e0 authored by Eike Hein's avatar Eike Hein

Set gtk-modules to auto-load appmenu-gtk-module

Summary:
Makes it work out of the box for both GTK 2 and GTK 3 applications.
Since both krdb and kde-gt-config overwrite their respective `gtkrc-2.0` we use a watcher to re-add our config key afterwards.

Test Plan:
Tested Pluma (Gtk3), now has global menu
Tested Gimp and Inkscape, now have global menu
When module isn't installed, prints a warning on console, shouldn't be too bad

Reviewers: #plasma, davidedmundson, fvogt, hein

Reviewed By: #plasma, hein

Subscribers: cgiboudeaux, plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D15315
parent 2b4d96bc
......@@ -38,6 +38,7 @@ target_link_libraries(gmenudbusmenuproxy
Qt5::Core
Qt5::X11Extras
Qt5::DBus
KF5::CoreAddons
KF5::ConfigCore
KF5::WindowSystem
XCB::XCB
......
......@@ -32,8 +32,10 @@
#include <QFileInfo>
#include <QHash>
#include <QStandardPaths>
#include <QTimer>
#include <KConfigGroup>
#include <KDirWatch>
#include <KSharedConfig>
#include <KWindowSystem>
......@@ -59,10 +61,15 @@ static const QByteArray s_gtkAppMenuObjectPath = QByteArrayLiteral("_GTK_APP_MEN
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 @@ MenuProxy::MenuProxy()
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 @@ bool MenuProxy::init()
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 @@ bool MenuProxy::init()
void MenuProxy::teardown()
{
setGtkShellShowsMenuBar(false);
enableGtkSettings(false);
QDBusConnection::sessionBus().unregisterService(s_ourServiceName);
......@@ -134,30 +156,115 @@ void MenuProxy::teardown()
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)
......
......@@ -27,6 +27,9 @@
#include <xcb/xcb_atom.h>
class QDBusServiceWatcher;
class QTimer;
class KDirWatch;
class Window;
......@@ -46,7 +49,15 @@ private:
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 @@ private:
QDBusServiceWatcher *m_serviceWatcher;
KDirWatch *m_gtk2RcWatch;
QTimer *m_writeGtk2SettingsTimer;
bool m_enabled = false;
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment