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
12QT_BEGIN_NAMESPACE
13
14#if QT_CONFIG(dbus)
15Q_STATIC_LOGGING_CATEGORY(lcQpaThemeGnome, "qt.qpa.theme.gnome")
16#endif // QT_CONFIG(dbus)
17
18using namespace Qt::StringLiterals;
19
20QGnomePortalInterface::QGnomePortalInterface(QObject *parent)
21 : QObject(parent), m_dbus{ new QDBusListener }
22{
23 QObject::connect(sender: m_dbus.get(), signal: &QDBusListener::settingChanged, context: this,
24 slot: &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 */
37Qt::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 */
52Qt::ContrastPreference
53QGnomePortalInterface::contrastPreference(Qt::ContrastPreference fallback) const
54{
55 if (!m_contrast.has_value())
56 return fallback;
57 return m_contrast.value();
58}
59
60void 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(destination: s_service, path: s_path, interface: 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(value: 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(value: 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(value: 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>>(v: 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 value: convertXdgColorScheme(value));
117 else if ((key == XdgContrastKey)
118 && (namespace_ == QDBusSettings::XdgSettings::AppearanceNamespace))
119 dbusSettingChanged(QDBusListener::Provider::Gnome,
120 QDBusListener::Setting::Contrast,
121 value: convertXdgContrast(value));
122 else if ((key == GnomeContrastKey)
123 && (namespace_ == QDBusSettings::GnomeSettings::AllyNamespace))
124 dbusSettingChanged(QDBusListener::Provider::Gnome,
125 QDBusListener::Setting::Contrast,
126 value: convrtGnomeContrast(value));
127 }
128 }
129 }
130 watcher->deleteLater();
131 };
132 connect(sender: watcher, signal: &QDBusPendingCallWatcher::finished, context: this, slot&: onWatcherFinished);
133}
134
135void 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
143void 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
151void 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(colorScheme: value.value<Qt::ColorScheme>());
161 break;
162 case QDBusListener::Setting::Contrast:
163 updateContrast(contrast: value.value<Qt::ContrastPreference>());
164 break;
165 case QDBusListener::Setting::Theme:
166 emit themeNameChanged(themeName: value.toString());
167 break;
168 default:
169 break;
170 }
171}
172
173QT_END_NAMESPACE
174
175#include "moc_qgnomeportalinterface_p.cpp"
176

source code of qtbase/src/gui/platform/unix/qgnomeportalinterface.cpp