Skip to content

Commit 933338f

Browse files
author
MohammadHossein Qanbari
committed
Dbus: Enhance process of determining color scheme and contrast
The XDG portal has changed some interfaces since version 2. We already support the changes in version 2. The DBus listener and GNOME theme do not work properly if the machine uses version 1. This patch ensures that the color scheme and contrast preferences work fine with the older version. Regarding contrast preference, the XDG desktop portal introduces the `contrast` interface in version 2. This patch uses the GNOME settings as a fallback method to determine the contrast preference in case the XDG contrast interface is not accessible. The contrast value retrieved from the DBus varies depending on the provider. Regarding the color-scheme interface, the only difference between XDG portal versions 1 and 2 is the method name. The method name to read the color-scheme value from the portal setting changed to `ReadOne` from `Read`. This is also used for reading the contrast value. Change-Id: Id4d81ecf5465bbbab64c41e431a8d4d86e63849a Reviewed-by: Oliver Eftevaag <[email protected]>
1 parent e9eba1e commit 933338f

File tree

3 files changed

+170
-36
lines changed

3 files changed

+170
-36
lines changed

src/gui/platform/unix/qdbuslistener.cpp

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,39 @@ constexpr auto setting() { return "Setting"_L1; }
4848
constexpr auto dbusSignals() { return "DbusSignals"_L1; }
4949
constexpr auto root() { return "Q_L1.qpa.DBusSignals"_L1; }
5050
} // namespace JsonKeys
51-
}
5251

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
83+
} // namespace
5384

5485
void QDBusListener::init(const QString &service, const QString &path,
5586
const QString &interface, const QString &signal)
@@ -209,7 +240,11 @@ void QDBusListener::populateSignalMap()
209240
m_signalMap.insert(DBusKey("org.freedesktop.appearance"_L1, "color-scheme"_L1),
210241
ChangeSignal(Provider::Gnome, Setting::ColorScheme));
211242

212-
m_signalMap.insert(DBusKey("org.freedesktop.appearance"_L1, "contrast"_L1),
243+
m_signalMap.insert(DBusKey(XdgSettings::contrastNamespace, XdgSettings::contrastKey),
244+
ChangeSignal(Provider::Gnome, Setting::Contrast));
245+
// Alternative solution if XDG desktop portal setting is not accessible,
246+
// e.g. when using the XDG portal version 1.
247+
m_signalMap.insert(DBusKey(GSettings::contrastNamespace, GSettings::contrastKey),
213248
ChangeSignal(Provider::Gnome, Setting::Contrast));
214249

215250
const QString &saveJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS_SAVE");
@@ -235,6 +270,24 @@ void QDBusListener::onSettingChanged(const QString &location, const QString &key
235270
if (!sig.has_value())
236271
return;
237272

238-
emit settingChanged(sig.value().provider, sig.value().setting, value.variant());
273+
const Setting setting = sig.value().setting;
274+
QVariant settingValue = value.variant();
275+
276+
switch (setting) {
277+
case Setting::Contrast:
278+
// To unify the value, it's necessary to convert the DBus value to Qt::ContrastPreference.
279+
// Then the users of the value don't need to parse the raw value.
280+
if (key == XdgSettings::contrastKey)
281+
settingValue.setValue(XdgSettings::convertContrastPreference(settingValue));
282+
else if (key == GSettings::contrastKey)
283+
settingValue.setValue(GSettings::convertContrastPreference(settingValue));
284+
else
285+
Q_UNREACHABLE_IMPL();
286+
break;
287+
default:
288+
break;
289+
}
290+
291+
emit settingChanged(sig.value().provider, setting, settingValue);
239292
}
240293
QT_END_NAMESPACE

src/gui/platform/unix/qgnometheme.cpp

Lines changed: 111 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <QtDBus/QDBusMessage>
1414
#include <QtDBus/QDBusPendingCall>
1515
#include <QtDBus/QDBusReply>
16+
#include <QtDBus/QDBusVariant>
1617
#endif
1718
#include <qpa/qwindowsysteminterface.h>
1819

@@ -21,6 +22,8 @@ QT_BEGIN_NAMESPACE
2122
#if QT_CONFIG(dbus)
2223
Q_STATIC_LOGGING_CATEGORY(lcQpaThemeGnome, "qt.qpa.theme.gnome")
2324

25+
using namespace Qt::StringLiterals;
26+
2427
namespace {
2528
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
2629
enum class XDG_ColorScheme : uint { NoPreference, PreferDark, PreferLight };
@@ -54,6 +57,107 @@ constexpr XDG_ColorScheme convertColorScheme(Qt::ColorScheme colorScheme)
5457
break;
5558
}
5659
}
60+
61+
class DBusInterface
62+
{
63+
DBusInterface() = delete;
64+
65+
constexpr static auto Service = "org.freedesktop.portal.Desktop"_L1;
66+
constexpr static auto Path = "/org/freedesktop/portal/desktop"_L1;
67+
68+
public:
69+
static inline QVariant query(QLatin1StringView interface, QLatin1StringView method,
70+
QLatin1StringView name_space, QLatin1StringView key);
71+
static inline uint queryPortalVersion();
72+
static inline QLatin1StringView readOneMethod();
73+
static inline std::optional<Qt::ColorScheme> queryColorScheme();
74+
static inline std::optional<Qt::ContrastPreference> queryContrast();
75+
};
76+
77+
QVariant DBusInterface::query(QLatin1StringView interface, QLatin1StringView method,
78+
QLatin1StringView name_space, QLatin1StringView key)
79+
{
80+
QDBusConnection dbus = QDBusConnection::sessionBus();
81+
if (dbus.isConnected()) {
82+
QDBusMessage message = QDBusMessage::createMethodCall(
83+
DBusInterface::Service, DBusInterface::Path, interface, method);
84+
message << name_space << key;
85+
86+
QDBusReply<QVariant> reply = dbus.call(message);
87+
if (Q_LIKELY(reply.isValid()))
88+
return reply.value();
89+
} else {
90+
qCWarning(lcQpaThemeGnome) << "dbus connection failed. Last error: " << dbus.lastError();
91+
}
92+
93+
return {};
94+
}
95+
96+
uint DBusInterface::queryPortalVersion()
97+
{
98+
constexpr auto interface = "org.freedesktop.DBus.Properties"_L1;
99+
constexpr auto method = "Get"_L1;
100+
constexpr auto name_space = "org.freedesktop.portal.Settings"_L1;
101+
constexpr auto key = "version"_L1;
102+
103+
static uint version = 0; // cached version value
104+
105+
if (version == 0) {
106+
QVariant reply = query(interface, method, name_space, key);
107+
if (reply.isValid())
108+
version = reply.toUInt(); // caches the value for the next calls
109+
}
110+
111+
return version;
112+
}
113+
114+
QLatin1StringView DBusInterface::readOneMethod()
115+
{
116+
// Based on the documentation on flatpak:
117+
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
118+
// The method name "Read" has changed to "ReadOne" since version 2.
119+
const uint version = queryPortalVersion();
120+
if (version == 1)
121+
return "Read"_L1;
122+
return "ReadOne"_L1;
123+
}
124+
125+
std::optional<Qt::ColorScheme> DBusInterface::queryColorScheme()
126+
{
127+
constexpr auto interface = "org.freedesktop.portal.Settings"_L1;
128+
constexpr auto name_space = "org.freedesktop.appearance"_L1;
129+
constexpr auto key = "color-scheme"_L1;
130+
const auto method = readOneMethod();
131+
132+
QVariant reply = query(interface, method, name_space, key);
133+
if (reply.isValid())
134+
return convertColorScheme(
135+
XDG_ColorScheme{ reply.value<QDBusVariant>().variant().toUInt() });
136+
137+
return {};
138+
}
139+
140+
std::optional<Qt::ContrastPreference> DBusInterface::queryContrast()
141+
{
142+
constexpr auto interface = "org.freedesktop.portal.Settings"_L1;
143+
const auto method = readOneMethod();
144+
145+
constexpr auto namespace_xdg_portal = "org.freedesktop.appearance"_L1;
146+
constexpr auto key_xdg_portal = "contrast"_L1;
147+
QVariant reply = query(interface, method, namespace_xdg_portal, key_xdg_portal);
148+
if (reply.isValid())
149+
return static_cast<Qt::ContrastPreference>(reply.toUInt());
150+
151+
// Fall back to desktop-specific methods (GSettings for GNOME)
152+
constexpr auto namespace_gsettings = "org.gnome.desktop.a11y.interface"_L1;
153+
constexpr auto key_gsettings = "high-contrast"_L1;
154+
reply = query(interface, method, namespace_gsettings, key_gsettings);
155+
if (reply.isValid())
156+
return reply.toBool() ? Qt::ContrastPreference::HighContrast
157+
: Qt::ContrastPreference::NoPreference;
158+
159+
return {};
160+
}
57161
} // namespace
58162

59163
#endif // QT_CONFIG(dbus)
@@ -70,41 +174,16 @@ const char *QGnomeTheme::name = "gnome";
70174
QGnomeThemePrivate::QGnomeThemePrivate()
71175
{
72176
#if QT_CONFIG(dbus)
73-
static constexpr QLatin1String appearanceNamespace("org.freedesktop.appearance");
74-
static constexpr QLatin1String colorSchemeKey("color-scheme");
75-
static constexpr QLatin1String contrastKey("contrast");
177+
initDbus();
76178

77-
QDBusConnection dbus = QDBusConnection::sessionBus();
78-
if (dbus.isConnected()) {
79-
// ReadAll appears to omit the contrast setting on Ubuntu.
80-
QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"),
81-
QLatin1String("/org/freedesktop/portal/desktop"),
82-
QLatin1String("org.freedesktop.portal.Settings"),
83-
QLatin1String("ReadOne"));
84-
85-
message << appearanceNamespace << colorSchemeKey;
86-
QDBusReply<QVariant> reply = dbus.call(message);
87-
if (Q_LIKELY(reply.isValid())) {
88-
m_colorScheme = convertColorScheme(XDG_ColorScheme{ reply.value().toUInt() });
89-
QWindowSystemInterface::handleThemeChange();
90-
}
179+
if (auto value = DBusInterface::queryColorScheme(); value.has_value())
180+
updateColorScheme(value.value());
91181

92-
message.setArguments({});
93-
message << appearanceNamespace << contrastKey;
94-
pendingCallWatcher = std::make_unique<QDBusPendingCallWatcher>(dbus.asyncCall(message));
95-
QObject::connect(pendingCallWatcher.get(), &QDBusPendingCallWatcher::finished, pendingCallWatcher.get(), [this](QDBusPendingCallWatcher *watcher) {
96-
if (!watcher->isError()) {
97-
QDBusPendingReply<QVariant> reply = *watcher;
98-
if (Q_LIKELY(reply.isValid()))
99-
updateHighContrast(static_cast<Qt::ContrastPreference>(reply.value().toUInt()));
100-
}
101-
initDbus();
102-
});
103-
} else {
104-
qCWarning(lcQpaThemeGnome) << "dbus connection failed. Last error: " << dbus.lastError();
105-
}
182+
if (auto value = DBusInterface::queryContrast(); value.has_value())
183+
updateHighContrast(value.value());
106184
#endif // QT_CONFIG(dbus)
107185
}
186+
108187
QGnomeThemePrivate::~QGnomeThemePrivate()
109188
{
110189
if (systemFont)
@@ -149,7 +228,7 @@ bool QGnomeThemePrivate::initDbus()
149228
m_themeName = value.toString();
150229
break;
151230
case QDBusListener::Setting::Contrast:
152-
updateHighContrast(static_cast<Qt::ContrastPreference>(value.toUInt()));
231+
updateHighContrast(value.value<Qt::ContrastPreference>());
153232
break;
154233
default:
155234
break;

src/gui/platform/unix/qkdetheme.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@ void QKdeThemePrivate::settingChangedHandler(QDBusListener::Provider provider,
139139
qCDebug(lcQpaThemeKde) << "KDE application style changed to:" << value.toString();
140140
break;
141141
case QDBusListener::Setting::Contrast:
142-
qCDebug(lcQpaThemeKde) << "KDE contrast setting changed to: " << static_cast<Qt::ContrastPreference>(value.toUInt());
142+
qCDebug(lcQpaThemeKde) << "KDE contrast setting changed to: "
143+
<< value.value<Qt::ContrastPreference>();
144+
break;
143145
}
144146

145147
refresh();

0 commit comments

Comments
 (0)