1 /*
2  * SPDX-FileCopyrightText: 2018-2019 Red Hat Inc
3  *
4  * SPDX-License-Identifier: LGPL-2.0-or-later
5  *
6  * SPDX-FileCopyrightText: 2018-2019 Jan Grulich <jgrulich@redhat.com>
7  */
8 
9 #include "settings.h"
10 
11 #include <QDBusConnection>
12 #include <QDBusContext>
13 #include <QDBusMessage>
14 #include <QDBusMetaType>
15 
16 #include <QLoggingCategory>
17 
18 #include <KConfigCore/KConfigGroup>
19 
20 Q_LOGGING_CATEGORY(XdgDesktopPortalKdeSettings, "xdp-kde-settings")
21 
22 Q_DECLARE_METATYPE(SettingsPortal::VariantMapMap)
23 
24 QDBusArgument &operator<<(QDBusArgument &argument, const SettingsPortal::VariantMapMap &mymap)
25 {
26     argument.beginMap(QVariant::String, QVariant::Map);
27 
28     QMapIterator<QString, QVariantMap> i(mymap);
29     while (i.hasNext()) {
30         i.next();
31         argument.beginMapEntry();
32         argument << i.key() << i.value();
33         argument.endMapEntry();
34     }
35     argument.endMap();
36     return argument;
37 }
38 
operator >>(const QDBusArgument & argument,SettingsPortal::VariantMapMap & mymap)39 const QDBusArgument &operator>>(const QDBusArgument &argument, SettingsPortal::VariantMapMap &mymap)
40 {
41     argument.beginMap();
42     mymap.clear();
43 
44     while (!argument.atEnd()) {
45         QString key;
46         QVariantMap value;
47         argument.beginMapEntry();
48         argument >> key >> value;
49         argument.endMapEntry();
50         mymap.insert(key, value);
51     }
52 
53     argument.endMap();
54     return argument;
55 }
56 
groupMatches(const QString & group,const QStringList & patterns)57 static bool groupMatches(const QString &group, const QStringList &patterns)
58 {
59     for (const QString &pattern : patterns) {
60         if (pattern.isEmpty()) {
61             return true;
62         }
63 
64         if (pattern == group) {
65             return true;
66         }
67 
68         if (pattern.endsWith(QLatin1Char('*')) && group.startsWith(pattern.left(pattern.length() - 1))) {
69             return true;
70         }
71     }
72 
73     return false;
74 }
75 
SettingsPortal(QObject * parent)76 SettingsPortal::SettingsPortal(QObject *parent)
77     : QDBusAbstractAdaptor(parent)
78 {
79     qDBusRegisterMetaType<VariantMapMap>();
80 
81     m_kdeglobals = KSharedConfig::openConfig();
82 
83     QDBusConnection::sessionBus().connect(QString(),
84                                           QStringLiteral("/KDEPlatformTheme"),
85                                           QStringLiteral("org.kde.KDEPlatformTheme"),
86                                           QStringLiteral("refreshFonts"),
87                                           this,
88                                           SLOT(fontChanged()));
89     QDBusConnection::sessionBus().connect(QString(),
90                                           QStringLiteral("/KGlobalSettings"),
91                                           QStringLiteral("org.kde.KGlobalSettings"),
92                                           QStringLiteral("notifyChange"),
93                                           this,
94                                           SLOT(globalSettingChanged(int, int)));
95     QDBusConnection::sessionBus()
96         .connect(QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), this, SLOT(toolbarStyleChanged()));
97 }
98 
~SettingsPortal()99 SettingsPortal::~SettingsPortal()
100 {
101 }
102 
ReadAll(const QStringList & groups)103 void SettingsPortal::ReadAll(const QStringList &groups)
104 {
105     qCDebug(XdgDesktopPortalKdeSettings) << "ReadAll called with parameters:";
106     qCDebug(XdgDesktopPortalKdeSettings) << "    groups: " << groups;
107 
108     // FIXME this is super ugly, but I was unable to make it properly return VariantMapMap
109     QObject *obj = QObject::parent();
110 
111     if (!obj) {
112         qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
113         return;
114     }
115 
116     void *ptr = obj->qt_metacast("QDBusContext");
117     QDBusContext *q_ptr = reinterpret_cast<QDBusContext *>(ptr);
118 
119     if (!q_ptr) {
120         qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
121         return;
122     }
123 
124     VariantMapMap result;
125     const auto groupList = m_kdeglobals->groupList();
126     for (const QString &settingGroupName : groupList) {
127         // NOTE: use org.kde.kdeglobals prefix
128 
129         QString uniqueGroupName = QStringLiteral("org.kde.kdeglobals.") + settingGroupName;
130 
131         if (!groupMatches(uniqueGroupName, groups)) {
132             continue;
133         }
134 
135         QVariantMap map;
136         KConfigGroup configGroup(m_kdeglobals, settingGroupName);
137 
138         const auto keyList = configGroup.keyList();
139         for (const QString &key : keyList) {
140             map.insert(key, configGroup.readEntry(key));
141         }
142 
143         result.insert(uniqueGroupName, map);
144     }
145 
146     QDBusMessage message = q_ptr->message();
147     QDBusMessage reply = message.createReply(QVariant::fromValue(result));
148     QDBusConnection::sessionBus().send(reply);
149 }
150 
Read(const QString & group,const QString & key)151 void SettingsPortal::Read(const QString &group, const QString &key)
152 {
153     qCDebug(XdgDesktopPortalKdeSettings) << "Read called with parameters:";
154     qCDebug(XdgDesktopPortalKdeSettings) << "    group: " << group;
155     qCDebug(XdgDesktopPortalKdeSettings) << "    key: " << key;
156 
157     // FIXME this is super ugly, but I was unable to make it properly return VariantMapMap
158     QObject *obj = QObject::parent();
159 
160     if (!obj) {
161         qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
162         return;
163     }
164 
165     void *ptr = obj->qt_metacast("QDBusContext");
166     QDBusContext *q_ptr = reinterpret_cast<QDBusContext *>(ptr);
167 
168     if (!q_ptr) {
169         qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
170         return;
171     }
172 
173     QDBusMessage reply;
174     QDBusMessage message = q_ptr->message();
175 
176     // All our namespaces start with this prefix
177     if (!group.startsWith(QStringLiteral("org.kde.kdeglobals"))) {
178         qCWarning(XdgDesktopPortalKdeSettings) << "Namespace " << group << " is not supported";
179         reply = message.createErrorReply(QDBusError::UnknownProperty, QStringLiteral("Namespace is not supported"));
180         QDBusConnection::sessionBus().send(reply);
181         return;
182     }
183 
184     QDBusVariant result = readProperty(group, key);
185     if (result.variant().isNull()) {
186         reply = message.createErrorReply(QDBusError::UnknownProperty, QStringLiteral("Property doesn't exist"));
187     } else {
188         reply = message.createReply(QVariant::fromValue(result));
189     }
190 
191     QDBusConnection::sessionBus().send(reply);
192 }
193 
fontChanged()194 void SettingsPortal::fontChanged()
195 {
196     Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.General"),
197                           QStringLiteral("font"),
198                           readProperty(QStringLiteral("org.kde.kdeglobals.General"), QStringLiteral("font")));
199 }
200 
globalSettingChanged(int type,int arg)201 void SettingsPortal::globalSettingChanged(int type, int arg)
202 {
203     m_kdeglobals->reparseConfiguration();
204 
205     // Mostly based on plasma-integration needs
206     switch (type) {
207     case PaletteChanged:
208         // Plasma-integration will be loading whole palette again, there is no reason to try to identify
209         // particular categories or colors
210         Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.General"),
211                               QStringLiteral("ColorScheme"),
212                               readProperty(QStringLiteral("org.kde.kdeglobals.General"), QStringLiteral("ColorScheme")));
213         break;
214     case FontChanged:
215         fontChanged();
216         break;
217     case StyleChanged:
218         Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.KDE"),
219                               QStringLiteral("widgetStyle"),
220                               readProperty(QStringLiteral("org.kde.kdeglobals.KDE"), QStringLiteral("widgetStyle")));
221         break;
222     case SettingsChanged: {
223         SettingsCategory category = static_cast<SettingsCategory>(arg);
224         if (category == SETTINGS_QT || category == SETTINGS_MOUSE) {
225             // TODO
226         } else if (category == SETTINGS_STYLE) {
227             // TODO
228         }
229         break;
230     }
231     case IconChanged:
232         // we will get notified about each category, but it probably makes sense to send this signal just once
233         if (arg == 0) { // KIconLoader::Desktop
234             Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.Icons"),
235                                   QStringLiteral("Theme"),
236                                   readProperty(QStringLiteral("org.kde.kdeglobals.Icons"), QStringLiteral("Theme")));
237         }
238         break;
239     case CursorChanged:
240         // TODO
241         break;
242     case ToolbarStyleChanged:
243         toolbarStyleChanged();
244         break;
245     default:
246         break;
247     }
248 }
249 
toolbarStyleChanged()250 void SettingsPortal::toolbarStyleChanged()
251 {
252     Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.Toolbar style"),
253                           QStringLiteral("ToolButtonStyle"),
254                           readProperty(QStringLiteral("org.kde.kdeglobals.Toolbar style"), QStringLiteral("ToolButtonStyle")));
255 }
256 
readProperty(const QString & group,const QString & key)257 QDBusVariant SettingsPortal::readProperty(const QString &group, const QString &key)
258 {
259     QString groupName = group.right(group.length() - QStringLiteral("org.kde.kdeglobals.").length());
260 
261     if (!m_kdeglobals->hasGroup(groupName)) {
262         qCWarning(XdgDesktopPortalKdeSettings) << "Group " << group << " doesn't exist";
263         return QDBusVariant();
264     }
265 
266     KConfigGroup configGroup(m_kdeglobals, groupName);
267 
268     if (!configGroup.hasKey(key)) {
269         qCWarning(XdgDesktopPortalKdeSettings) << "Key " << key << " doesn't exist";
270         return QDBusVariant();
271     }
272 
273     return QDBusVariant(configGroup.readEntry(key));
274 }
275