Skip to content

Commit 2fe9eed

Browse files
author
MohammadHossein Qanbari
committed
QGnomeTheme, QGtk3Theme: Refactor and Simplify DBus Interactions
This patch refactors the DBus integration in both QGnomeTheme and QGtk3Theme to centralize and simplify the portal and settings access logic. Previously, the codebase contained duplicated and scattered DBus logic for querying GNOME/GTK appearance settings, such as color scheme and contrast, which were implemented separately in both QGnomeTheme and QGtk3Theme. The patch introduces a new QGnomePortalInterface class which encapsulates all DBus interactions related to GNOME/GTK appearance settings. The old DBus interface logic is removed from QGtk3Theme and QGnomeTheme, and replaced with calls to the unified QGnomePortalInterface. The update also ensures signal-based updates for theme and appearance changes via the new interface. Change-Id: I5440f7ac00f956b846b18bd890113af0044482f0 Reviewed-by: Oliver Eftevaag <[email protected]> Reviewed-by: Morten Johan Sørvig <[email protected]>
1 parent 933338f commit 2fe9eed

16 files changed

+500
-484
lines changed

src/gui/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,11 @@ qt_internal_extend_target(Gui CONDITION UNIX AND (QT_FEATURE_xcb OR NOT MACOS) A
10591059
platform/unix/qgnometheme_p.h platform/unix/qgnometheme.cpp
10601060
)
10611061

1062+
qt_internal_extend_target(Gui CONDITION UNIX AND QT_FEATURE_dbus AND (QT_FEATURE_xcb OR QT_FEATURE_wayland)
1063+
SOURCES
1064+
platform/unix/qgnomeportalinterface.cpp platform/unix/qgnomeportalinterface_p.h
1065+
)
1066+
10621067
qt_internal_extend_target(Gui CONDITION TARGET Qt::DBus AND UNIX AND (QT_FEATURE_xcb OR NOT MACOS) AND (QT_FEATURE_xcb OR NOT UIKIT)
10631068
SOURCES
10641069
platform/unix/dbusmenu/qdbusmenuadaptor.cpp platform/unix/dbusmenu/qdbusmenuadaptor_p.h
@@ -1068,6 +1073,7 @@ qt_internal_extend_target(Gui CONDITION TARGET Qt::DBus AND UNIX AND (QT_FEATURE
10681073
platform/unix/dbusmenu/qdbusmenutypes.cpp platform/unix/dbusmenu/qdbusmenutypes_p.h
10691074
platform/unix/dbusmenu/qdbusplatformmenu.cpp platform/unix/dbusmenu/qdbusplatformmenu_p.h
10701075
platform/unix/qdbuslistener_p.h platform/unix/qdbuslistener.cpp
1076+
platform/unix/qdbussettings_p.h platform/unix/qdbussettings.cpp
10711077
)
10721078

10731079
qt_internal_extend_target(Gui CONDITION QT_FEATURE_systemtrayicon AND TARGET Qt::DBus AND UNIX AND (QT_FEATURE_xcb OR NOT MACOS) AND (QT_FEATURE_xcb OR NOT UIKIT)

src/gui/platform/unix/qdbuslistener.cpp

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
33

44
#include "qdbuslistener_p.h"
5+
#include "qdbussettings_p.h"
56
#include <private/qguiapplication_p.h>
67
#include <qpa/qplatformintegration.h>
78
#include <qpa/qplatformservices.h>
@@ -48,38 +49,6 @@ constexpr auto setting() { return "Setting"_L1; }
4849
constexpr auto dbusSignals() { return "DbusSignals"_L1; }
4950
constexpr auto root() { return "Q_L1.qpa.DBusSignals"_L1; }
5051
} // namespace JsonKeys
51-
52-
namespace XdgSettings {
53-
// XDG Desktop Portal Settings (Preferred)
54-
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
55-
constexpr auto contrastNamespace = "org.freedesktop.appearance"_L1;
56-
constexpr auto contrastKey = "contrast"_L1;
57-
// XDG portal provides the contrast preference value as uint:
58-
// 0 for no-preference, and, 1 for high-contrast.
59-
Qt::ContrastPreference convertContrastPreference(const QVariant &value)
60-
{
61-
if (!value.isValid())
62-
return Qt::ContrastPreference::NoPreference;
63-
return static_cast<Qt::ContrastPreference>(value.toUInt());
64-
}
65-
} // namespace XdgSettings
66-
67-
namespace GSettings {
68-
// GNOME Destop Settings (Alternative)
69-
// https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2069
70-
// https://gitlab.gnome.org/GNOME/gsettings-desktop-schemas/-/commit/0e97f1f571c495184f80d875c68f241261a50e30
71-
constexpr auto contrastNamespace = "org.gnome.desktop.a11y.interface"_L1;
72-
constexpr auto contrastKey = "high-contrast"_L1;
73-
// GSetting provides the contrast value as boolean:
74-
// true for enabled high-contrast, and, false for disabled high-contrast.
75-
Qt::ContrastPreference convertContrastPreference(const QVariant &value)
76-
{
77-
if (!value.isValid())
78-
return Qt::ContrastPreference::NoPreference;
79-
return value.toBool() ? Qt::ContrastPreference::HighContrast
80-
: Qt::ContrastPreference::NoPreference;
81-
}
82-
} // namespace GSettings
8352
} // namespace
8453

8554
void QDBusListener::init(const QString &service, const QString &path,
@@ -237,14 +206,15 @@ void QDBusListener::populateSignalMap()
237206
m_signalMap.insert(DBusKey("org.gnome.desktop.interface"_L1, "gtk-theme"_L1),
238207
ChangeSignal(Provider::Gtk, Setting::Theme));
239208

240-
m_signalMap.insert(DBusKey("org.freedesktop.appearance"_L1, "color-scheme"_L1),
209+
using namespace QDBusSettings;
210+
m_signalMap.insert(DBusKey(XdgSettings::AppearanceNamespace, XdgSettings::ColorSchemeKey),
241211
ChangeSignal(Provider::Gnome, Setting::ColorScheme));
242212

243-
m_signalMap.insert(DBusKey(XdgSettings::contrastNamespace, XdgSettings::contrastKey),
213+
m_signalMap.insert(DBusKey(XdgSettings::AppearanceNamespace, XdgSettings::ContrastKey),
244214
ChangeSignal(Provider::Gnome, Setting::Contrast));
245215
// Alternative solution if XDG desktop portal setting is not accessible,
246216
// e.g. when using the XDG portal version 1.
247-
m_signalMap.insert(DBusKey(GSettings::contrastNamespace, GSettings::contrastKey),
217+
m_signalMap.insert(DBusKey(GnomeSettings::AllyNamespace, GnomeSettings::ContrastKey),
248218
ChangeSignal(Provider::Gnome, Setting::Contrast));
249219

250220
const QString &saveJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS_SAVE");
@@ -274,13 +244,17 @@ void QDBusListener::onSettingChanged(const QString &location, const QString &key
274244
QVariant settingValue = value.variant();
275245

276246
switch (setting) {
247+
case Setting::ColorScheme:
248+
settingValue.setValue(QDBusSettings::XdgSettings::convertColorScheme(settingValue));
249+
break;
277250
case Setting::Contrast:
251+
using namespace QDBusSettings;
278252
// To unify the value, it's necessary to convert the DBus value to Qt::ContrastPreference.
279253
// Then the users of the value don't need to parse the raw value.
280-
if (key == XdgSettings::contrastKey)
254+
if (key == XdgSettings::ContrastKey)
281255
settingValue.setValue(XdgSettings::convertContrastPreference(settingValue));
282-
else if (key == GSettings::contrastKey)
283-
settingValue.setValue(GSettings::convertContrastPreference(settingValue));
256+
else if (key == GnomeSettings::ContrastKey)
257+
settingValue.setValue(GnomeSettings::convertContrastPreference(settingValue));
284258
else
285259
Q_UNREACHABLE_IMPL();
286260
break;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (C) 2025 The Qt Company Ltd.
2+
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3+
4+
#include "qdbussettings_p.h"
5+
#include <QtCore/qvariant.h>
6+
7+
QT_BEGIN_NAMESPACE
8+
9+
namespace QDBusSettings::XdgSettings {
10+
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
11+
enum class ColorScheme : uint { NoPreference, PreferDark, PreferLight };
12+
} // namespace QDBusSettings::XdgSettings
13+
14+
Qt::ContrastPreference QDBusSettings::XdgSettings::convertContrastPreference(const QVariant &value)
15+
{
16+
// XDG portal provides the contrast preference value as uint:
17+
// 0 for no-preference, and, 1 for high-contrast.
18+
if (!value.isValid())
19+
return Qt::ContrastPreference::NoPreference;
20+
return static_cast<Qt::ContrastPreference>(value.toUInt());
21+
}
22+
23+
Qt::ColorScheme QDBusSettings::XdgSettings::convertColorScheme(const QVariant &value)
24+
{
25+
switch (ColorScheme{ value.toUInt() }) {
26+
case ColorScheme::NoPreference:
27+
return Qt::ColorScheme::Unknown;
28+
case ColorScheme::PreferDark:
29+
return Qt::ColorScheme::Dark;
30+
case ColorScheme::PreferLight:
31+
return Qt::ColorScheme::Light;
32+
}
33+
Q_UNREACHABLE_RETURN(Qt::ColorScheme::Unknown);
34+
}
35+
36+
Qt::ContrastPreference
37+
QDBusSettings::GnomeSettings::convertContrastPreference(const QVariant &value)
38+
{
39+
// GSetting provides the contrast value as boolean:
40+
// true for enabled high-contrast, and, false for disabled high-contrast.
41+
if (!value.isValid())
42+
return Qt::ContrastPreference::NoPreference;
43+
return value.toBool() ? Qt::ContrastPreference::HighContrast
44+
: Qt::ContrastPreference::NoPreference;
45+
}
46+
47+
QT_END_NAMESPACE
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (C) 2025 The Qt Company Ltd.
2+
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3+
4+
#ifndef QDBUSSETTINGS_P_H
5+
#define QDBUSSETTINGS_P_H
6+
7+
#include <QtCore/qnamespace.h>
8+
#include <QtCore/qstring.h>
9+
QT_REQUIRE_CONFIG(dbus);
10+
11+
//
12+
// W A R N I N G
13+
// -------------
14+
//
15+
// This file is not part of the Qt API. It exists purely as an
16+
// implementation detail. This header file may change from version to
17+
// version without notice, or even be removed.
18+
//
19+
// We mean it.
20+
//
21+
22+
QT_BEGIN_NAMESPACE
23+
24+
namespace QDBusSettings {
25+
// XDG Desktop Portal Settings (Preferred)
26+
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
27+
namespace XdgSettings {
28+
static constexpr QLatin1StringView AppearanceNamespace{ "org.freedesktop.appearance" };
29+
static constexpr QLatin1StringView ColorSchemeKey{ "color-scheme" };
30+
static constexpr QLatin1StringView ContrastKey{ "contrast" };
31+
Qt::ContrastPreference convertContrastPreference(const QVariant &value);
32+
Qt::ColorScheme convertColorScheme(const QVariant &value);
33+
} // namespace XdgSettings
34+
35+
namespace GnomeSettings {
36+
// GNOME Destop Settings (Alternative)
37+
// https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2069
38+
// https://gitlab.gnome.org/GNOME/gsettings-desktop-schemas/-/commit/0e97f1f571c495184f80d875c68f241261a50e30
39+
static constexpr QLatin1StringView AllyNamespace{ "org.gnome.desktop.a11y.interface" };
40+
static constexpr QLatin1StringView ContrastKey{ "high-contrast" };
41+
Qt::ContrastPreference convertContrastPreference(const QVariant &value);
42+
} // namespace GnomeSettings
43+
} // namespace QDBusSettings
44+
45+
QT_END_NAMESPACE
46+
#endif // QDBUSSETTINGS_P_H
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright (C) 2025 The Qt Company Ltd.
2+
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3+
4+
#include "qgnomeportalinterface_p.h"
5+
#include "qdbussettings_p.h"
6+
#include <private/qdbusplatformmenu_p.h>
7+
#include <QtDBus/QDBusMessage>
8+
#include <QtDBus/QDBusPendingCall>
9+
#include <QtDBus/QDBusReply>
10+
#include <QtDBus/QDBusVariant>
11+
12+
QT_BEGIN_NAMESPACE
13+
14+
#if QT_CONFIG(dbus)
15+
Q_STATIC_LOGGING_CATEGORY(lcQpaThemeGnome, "qt.qpa.theme.gnome")
16+
#endif // QT_CONFIG(dbus)
17+
18+
using namespace Qt::StringLiterals;
19+
20+
QGnomePortalInterface::QGnomePortalInterface(QObject *parent)
21+
: QObject(parent), m_dbus{ new QDBusListener }
22+
{
23+
QObject::connect(m_dbus.get(), &QDBusListener::settingChanged, this,
24+
&QGnomePortalInterface::dbusSettingChanged);
25+
26+
querySettings();
27+
}
28+
29+
/*!
30+
\internal
31+
\brief QGnomePortalInterface::colorScheme
32+
This is a getter method for the color-scheme loaded from the DBus portal.
33+
\param fallback indicates the fallback color-scheme.
34+
\return returns the cached color-scheme. If the color-scheme was not loaded
35+
it returns the \a fallback color-scheme.
36+
*/
37+
Qt::ColorScheme QGnomePortalInterface::colorScheme(Qt::ColorScheme fallback) const
38+
{
39+
if (!m_colorScheme.has_value())
40+
return fallback;
41+
return m_colorScheme.value();
42+
}
43+
44+
/*!
45+
\internal
46+
\brief QGnomePortalInterface::contrastPreference
47+
This is a getter method for the contrast-preference loaded from the DBus portal.
48+
\param fallback indicates the fallback contrast-preference.
49+
\return returns the cached contrast-preference if it was loaded. Otherwise,
50+
returns the \a fallback contrast-preference.
51+
*/
52+
Qt::ContrastPreference
53+
QGnomePortalInterface::contrastPreference(Qt::ContrastPreference fallback) const
54+
{
55+
if (!m_contrast.has_value())
56+
return fallback;
57+
return m_contrast.value();
58+
}
59+
60+
void QGnomePortalInterface::querySettings()
61+
{
62+
QDBusConnection dbus = QDBusConnection::sessionBus();
63+
if (!dbus.isConnected()) {
64+
qCWarning(lcQpaThemeGnome) << "dbus connection failed. Last error: " << dbus.lastError();
65+
return;
66+
}
67+
68+
constexpr auto method = "ReadAll"_L1;
69+
auto message = QDBusMessage::createMethodCall(s_service, s_path, s_interface, method);
70+
71+
message << QStringList{ QDBusSettings::XdgSettings::AppearanceNamespace,
72+
QDBusSettings::GnomeSettings::AllyNamespace };
73+
74+
QDBusPendingCall pendingCall = dbus.asyncCall(message);
75+
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
76+
77+
auto onWatcherFinished = [&](QDBusPendingCallWatcher *watcher) {
78+
const QDBusMessage reply = watcher->reply();
79+
if (QDBusMessage::ErrorMessage == reply.type()) {
80+
qCWarning(lcQpaThemeGnome)
81+
<< "dbus reply error: [" << reply.errorName() << "]" << reply.errorMessage();
82+
watcher->deleteLater();
83+
return;
84+
}
85+
86+
const auto convertXdgColorScheme = [](const QVariant &value) {
87+
using namespace QDBusSettings::XdgSettings;
88+
return QVariant::fromValue(convertColorScheme(value));
89+
};
90+
91+
constexpr auto XdgContrastKey = QDBusSettings::XdgSettings::ContrastKey;
92+
const auto convertXdgContrast = [](const QVariant &value) {
93+
using namespace QDBusSettings::XdgSettings;
94+
return QVariant::fromValue(convertContrastPreference(value));
95+
};
96+
97+
constexpr auto GnomeContrastKey = QDBusSettings::GnomeSettings::ContrastKey;
98+
const auto convrtGnomeContrast = [](const QVariant &value) {
99+
using namespace QDBusSettings::GnomeSettings;
100+
return QVariant::fromValue(convertContrastPreference(value));
101+
};
102+
103+
const QVariantList &args = reply.arguments();
104+
for (const QVariant &arg_ : args) {
105+
const QMap<QString, QVariantMap> arg = qdbus_cast<QMap<QString, QVariantMap>>(arg_);
106+
for (auto aIt = arg.begin(); aIt != arg.end(); ++aIt) {
107+
const QString &namespace_ = aIt.key();
108+
const QVariantMap &settings = aIt.value();
109+
for (auto sIt = settings.begin(); sIt != settings.end(); ++sIt) {
110+
const QString &key = sIt.key();
111+
const QVariant &value = sIt.value();
112+
if ((key == QDBusSettings::XdgSettings::ColorSchemeKey)
113+
&& (namespace_ == QDBusSettings::XdgSettings::AppearanceNamespace))
114+
dbusSettingChanged(QDBusListener::Provider::Gnome,
115+
QDBusListener::Setting::ColorScheme,
116+
convertXdgColorScheme(value));
117+
else if ((key == XdgContrastKey)
118+
&& (namespace_ == QDBusSettings::XdgSettings::AppearanceNamespace))
119+
dbusSettingChanged(QDBusListener::Provider::Gnome,
120+
QDBusListener::Setting::Contrast,
121+
convertXdgContrast(value));
122+
else if ((key == GnomeContrastKey)
123+
&& (namespace_ == QDBusSettings::GnomeSettings::AllyNamespace))
124+
dbusSettingChanged(QDBusListener::Provider::Gnome,
125+
QDBusListener::Setting::Contrast,
126+
convrtGnomeContrast(value));
127+
}
128+
}
129+
}
130+
watcher->deleteLater();
131+
};
132+
connect(watcher, &QDBusPendingCallWatcher::finished, this, onWatcherFinished);
133+
}
134+
135+
void QGnomePortalInterface::updateColorScheme(Qt::ColorScheme colorScheme)
136+
{
137+
if (m_colorScheme.has_value() && (m_colorScheme.value() == colorScheme))
138+
return;
139+
m_colorScheme = colorScheme;
140+
emit colorSchemeChanged(colorScheme);
141+
}
142+
143+
void QGnomePortalInterface::updateContrast(Qt::ContrastPreference contrast)
144+
{
145+
if (m_contrast.has_value() && (m_contrast.value() == contrast))
146+
return;
147+
m_contrast = contrast;
148+
emit contrastChanged(contrast);
149+
}
150+
151+
void QGnomePortalInterface::dbusSettingChanged(QDBusListener::Provider provider,
152+
QDBusListener::Setting setting,
153+
const QVariant &value)
154+
{
155+
if (provider != QDBusListener::Provider::Gnome && provider != QDBusListener::Provider::Gtk)
156+
return;
157+
158+
switch (setting) {
159+
case QDBusListener::Setting::ColorScheme:
160+
updateColorScheme(value.value<Qt::ColorScheme>());
161+
break;
162+
case QDBusListener::Setting::Contrast:
163+
updateContrast(value.value<Qt::ContrastPreference>());
164+
break;
165+
case QDBusListener::Setting::Theme:
166+
emit themeNameChanged(value.toString());
167+
break;
168+
default:
169+
break;
170+
}
171+
}
172+
173+
QT_END_NAMESPACE
174+
175+
#include "moc_qgnomeportalinterface_p.cpp"

0 commit comments

Comments
 (0)