File indexing completed on 2024-04-28 16:55:48

0001 /*
0002  * SPDX-FileCopyrightText: 2018-2019 Red Hat Inc
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  *
0006  * SPDX-FileCopyrightText: 2018-2019 Jan Grulich <jgrulich@redhat.com>
0007  */
0008 
0009 #include "settings.h"
0010 #include "settings_debug.h"
0011 
0012 #include <QApplication>
0013 
0014 #include <QDBusConnection>
0015 #include <QDBusContext>
0016 #include <QDBusMessage>
0017 #include <QDBusMetaType>
0018 
0019 #include <QPalette>
0020 
0021 #include "dbushelpers.h"
0022 #include <KConfigCore/KConfigGroup>
0023 
0024 /* accent-color */
0025 struct AccentColorArray {
0026     double r = 0.0; // 0-1
0027     double g = 0.0; // 0-1
0028     double b = 0.0; // 0-1
0029 
0030     operator QVariant() const
0031     {
0032         return QVariant::fromValue(*this);
0033     }
0034 };
0035 Q_DECLARE_METATYPE(AccentColorArray)
0036 
0037 QDBusArgument &operator<<(QDBusArgument &argument, const AccentColorArray &item)
0038 {
0039     argument.beginStructure();
0040     argument << item.r << item.g << item.b;
0041     argument.endStructure();
0042     return argument;
0043 }
0044 
0045 const QDBusArgument &operator>>(const QDBusArgument &argument, AccentColorArray &item)
0046 {
0047     argument.beginStructure();
0048     argument >> item.r >> item.g >> item.b;
0049     argument.endStructure();
0050     return argument;
0051 }
0052 
0053 static bool groupMatches(const QString &group, const QStringList &patterns)
0054 {
0055     for (const QString &pattern : patterns) {
0056         if (pattern.isEmpty()) {
0057             return true;
0058         }
0059 
0060         if (pattern == group) {
0061             return true;
0062         }
0063 
0064         if (pattern.endsWith(QLatin1Char('*')) && group.startsWith(pattern.left(pattern.length() - 1))) {
0065             return true;
0066         }
0067     }
0068 
0069     return false;
0070 }
0071 
0072 SettingsPortal::SettingsPortal(QObject *parent)
0073     : QDBusAbstractAdaptor(parent)
0074 {
0075     qDBusRegisterMetaType<VariantMapMap>();
0076     qDBusRegisterMetaType<AccentColorArray>();
0077 
0078     m_kdeglobals = KSharedConfig::openConfig();
0079 
0080     QDBusConnection::sessionBus().connect(QString(),
0081                                           QStringLiteral("/KDEPlatformTheme"),
0082                                           QStringLiteral("org.kde.KDEPlatformTheme"),
0083                                           QStringLiteral("refreshFonts"),
0084                                           this,
0085                                           SLOT(fontChanged()));
0086     QDBusConnection::sessionBus().connect(QString(),
0087                                           QStringLiteral("/KGlobalSettings"),
0088                                           QStringLiteral("org.kde.KGlobalSettings"),
0089                                           QStringLiteral("notifyChange"),
0090                                           this,
0091                                           SLOT(globalSettingChanged(int, int)));
0092     QDBusConnection::sessionBus()
0093         .connect(QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), this, SLOT(toolbarStyleChanged()));
0094 }
0095 
0096 SettingsPortal::~SettingsPortal()
0097 {
0098 }
0099 
0100 void SettingsPortal::ReadAll(const QStringList &groups)
0101 {
0102     qCDebug(XdgDesktopPortalKdeSettings) << "ReadAll called with parameters:";
0103     qCDebug(XdgDesktopPortalKdeSettings) << "    groups: " << groups;
0104 
0105     // FIXME this is super ugly, but I was unable to make it properly return VariantMapMap
0106     QObject *obj = QObject::parent();
0107 
0108     if (!obj) {
0109         qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
0110         return;
0111     }
0112 
0113     void *ptr = obj->qt_metacast("QDBusContext");
0114     QDBusContext *q_ptr = reinterpret_cast<QDBusContext *>(ptr);
0115 
0116     if (!q_ptr) {
0117         qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
0118         return;
0119     }
0120 
0121     VariantMapMap result;
0122 
0123     if (groupMatches(QStringLiteral("org.freedesktop.appearance"), groups)) {
0124         QVariantMap appearanceSettings;
0125         appearanceSettings.insert(QStringLiteral("color-scheme"), readFdoColorScheme().variant());
0126         appearanceSettings.insert(QStringLiteral("accent-color"), readAccentColor().variant());
0127 
0128         result.insert(QStringLiteral("org.freedesktop.appearance"), appearanceSettings);
0129     }
0130 
0131     const auto groupList = m_kdeglobals->groupList();
0132     for (const QString &settingGroupName : groupList) {
0133         // NOTE: use org.kde.kdeglobals prefix
0134 
0135         QString uniqueGroupName = QStringLiteral("org.kde.kdeglobals.") + settingGroupName;
0136 
0137         if (!groupMatches(uniqueGroupName, groups)) {
0138             continue;
0139         }
0140 
0141         QVariantMap map;
0142         KConfigGroup configGroup(m_kdeglobals, settingGroupName);
0143 
0144         const auto keyList = configGroup.keyList();
0145         for (const QString &key : keyList) {
0146             map.insert(key, configGroup.readEntry(key));
0147         }
0148 
0149         result.insert(uniqueGroupName, map);
0150     }
0151 
0152     QDBusMessage message = q_ptr->message();
0153     QDBusMessage reply = message.createReply(QVariant::fromValue(result));
0154     QDBusConnection::sessionBus().send(reply);
0155 }
0156 
0157 void SettingsPortal::Read(const QString &group, const QString &key)
0158 {
0159     qCDebug(XdgDesktopPortalKdeSettings) << "Read called with parameters:";
0160     qCDebug(XdgDesktopPortalKdeSettings) << "    group: " << group;
0161     qCDebug(XdgDesktopPortalKdeSettings) << "    key: " << key;
0162 
0163     // FIXME this is super ugly, but I was unable to make it properly return VariantMapMap
0164     QObject *obj = QObject::parent();
0165 
0166     if (!obj) {
0167         qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
0168         return;
0169     }
0170 
0171     void *ptr = obj->qt_metacast("QDBusContext");
0172     QDBusContext *q_ptr = reinterpret_cast<QDBusContext *>(ptr);
0173 
0174     if (!q_ptr) {
0175         qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
0176         return;
0177     }
0178 
0179     QDBusMessage reply;
0180     QDBusMessage message = q_ptr->message();
0181 
0182     if (group == QLatin1String("org.freedesktop.appearance")) {
0183         if (key == QLatin1String("color-scheme")) {
0184             reply = message.createReply(QVariant::fromValue(readFdoColorScheme()));
0185             QDBusConnection::sessionBus().send(reply);
0186             return;
0187         } else if (key == QLatin1String("accent-color")) {
0188             reply = message.createReply(QVariant::fromValue(readAccentColor()));
0189             QDBusConnection::sessionBus().send(reply);
0190             return;
0191         }
0192     }
0193     // All other namespaces start with this prefix
0194     else if (!group.startsWith(QStringLiteral("org.kde.kdeglobals"))) {
0195         qCWarning(XdgDesktopPortalKdeSettings) << "Namespace " << group << " is not supported";
0196         reply = message.createErrorReply(QDBusError::UnknownProperty, QStringLiteral("Namespace is not supported"));
0197         QDBusConnection::sessionBus().send(reply);
0198         return;
0199     }
0200 
0201     QDBusVariant result = readProperty(group, key);
0202     if (result.variant().isNull()) {
0203         reply = message.createErrorReply(QDBusError::UnknownProperty, QStringLiteral("Property doesn't exist"));
0204     } else {
0205         reply = message.createReply(QVariant::fromValue(result));
0206     }
0207 
0208     QDBusConnection::sessionBus().send(reply);
0209 }
0210 
0211 void SettingsPortal::fontChanged()
0212 {
0213     Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.General"),
0214                           QStringLiteral("font"),
0215                           readProperty(QStringLiteral("org.kde.kdeglobals.General"), QStringLiteral("font")));
0216 }
0217 
0218 void SettingsPortal::globalSettingChanged(int type, int arg)
0219 {
0220     m_kdeglobals->reparseConfiguration();
0221 
0222     // Mostly based on plasma-integration needs
0223     switch (type) {
0224     case PaletteChanged:
0225         // Plasma-integration will be loading whole palette again, there is no reason to try to identify
0226         // particular categories or colors
0227         Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.General"),
0228                               QStringLiteral("ColorScheme"),
0229                               readProperty(QStringLiteral("org.kde.kdeglobals.General"), QStringLiteral("ColorScheme")));
0230 
0231         Q_EMIT SettingChanged(QStringLiteral("org.freedesktop.appearance"), QStringLiteral("color-scheme"), readFdoColorScheme());
0232 
0233         // https://github.com/flatpak/xdg-desktop-portal/pull/815
0234         Q_EMIT SettingChanged(QStringLiteral("org.freedesktop.appearance"), QStringLiteral("accent-color"), readAccentColor());
0235         break;
0236     case FontChanged:
0237         fontChanged();
0238         break;
0239     case StyleChanged:
0240         Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.KDE"),
0241                               QStringLiteral("widgetStyle"),
0242                               readProperty(QStringLiteral("org.kde.kdeglobals.KDE"), QStringLiteral("widgetStyle")));
0243         break;
0244     case SettingsChanged: {
0245         SettingsCategory category = static_cast<SettingsCategory>(arg);
0246         if (category == SETTINGS_QT || category == SETTINGS_MOUSE) {
0247             // TODO
0248         } else if (category == SETTINGS_STYLE) {
0249             // TODO
0250         }
0251         break;
0252     }
0253     case IconChanged:
0254         // we will get notified about each category, but it probably makes sense to send this signal just once
0255         if (arg == 0) { // KIconLoader::Desktop
0256             Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.Icons"),
0257                                   QStringLiteral("Theme"),
0258                                   readProperty(QStringLiteral("org.kde.kdeglobals.Icons"), QStringLiteral("Theme")));
0259         }
0260         break;
0261     case CursorChanged:
0262         // TODO
0263         break;
0264     case ToolbarStyleChanged:
0265         toolbarStyleChanged();
0266         break;
0267     default:
0268         break;
0269     }
0270 }
0271 
0272 void SettingsPortal::toolbarStyleChanged()
0273 {
0274     Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.Toolbar style"),
0275                           QStringLiteral("ToolButtonStyle"),
0276                           readProperty(QStringLiteral("org.kde.kdeglobals.Toolbar style"), QStringLiteral("ToolButtonStyle")));
0277 }
0278 
0279 QDBusVariant SettingsPortal::readProperty(const QString &group, const QString &key)
0280 {
0281     QString groupName = group.right(group.length() - QStringLiteral("org.kde.kdeglobals.").length());
0282 
0283     if (!m_kdeglobals->hasGroup(groupName)) {
0284         qCWarning(XdgDesktopPortalKdeSettings) << "Group " << group << " doesn't exist";
0285         return QDBusVariant();
0286     }
0287 
0288     KConfigGroup configGroup(m_kdeglobals, groupName);
0289 
0290     if (!configGroup.hasKey(key)) {
0291         qCWarning(XdgDesktopPortalKdeSettings) << "Key " << key << " doesn't exist";
0292         return QDBusVariant();
0293     }
0294 
0295     return QDBusVariant(configGroup.readEntry(key));
0296 }
0297 
0298 QDBusVariant SettingsPortal::readFdoColorScheme()
0299 {
0300     const QPalette palette = QApplication::palette();
0301     const int windowBackgroundGray = qGray(palette.window().color().rgb());
0302 
0303     uint result = 0; // no preference
0304 
0305     if (windowBackgroundGray < 192) {
0306         result = 1; // prefer dark
0307     } else {
0308         result = 2; // prefer light
0309     }
0310 
0311     return QDBusVariant(result);
0312 }
0313 
0314 QDBusVariant SettingsPortal::readAccentColor() const
0315 {
0316     const QColor accentColor = qGuiApp->palette().highlight().color();
0317     return QDBusVariant(AccentColorArray{accentColor.redF(), accentColor.greenF(), accentColor.blueF()});
0318 }