File indexing completed on 2024-05-12 03:54:26
0001 /* 0002 SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org> 0003 SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kconfigwatcher.h" 0009 0010 #include "config-kconfig.h" 0011 #include "kconfig_core_log_settings.h" 0012 0013 #if KCONFIG_USE_DBUS 0014 #include <QDBusConnection> 0015 #include <QDBusMessage> 0016 #include <QDBusMetaType> 0017 0018 #include "dbussanitizer_p.h" 0019 #endif 0020 0021 #include <QDebug> 0022 #include <QHash> 0023 #include <QThreadStorage> 0024 0025 class KConfigWatcherPrivate 0026 { 0027 public: 0028 KSharedConfig::Ptr m_config; 0029 }; 0030 0031 KConfigWatcher::Ptr KConfigWatcher::create(const KSharedConfig::Ptr &config) 0032 { 0033 static QThreadStorage<QHash<KSharedConfig *, QWeakPointer<KConfigWatcher>>> watcherList; 0034 0035 auto c = config.data(); 0036 KConfigWatcher::Ptr watcher; 0037 0038 if (!watcherList.localData().contains(c)) { 0039 watcher = KConfigWatcher::Ptr(new KConfigWatcher(config)); 0040 0041 watcherList.localData().insert(c, watcher.toWeakRef()); 0042 0043 QObject::connect(watcher.data(), &QObject::destroyed, [c]() { 0044 watcherList.localData().remove(c); 0045 }); 0046 } 0047 return watcherList.localData().value(c).toStrongRef(); 0048 } 0049 0050 KConfigWatcher::KConfigWatcher(const KSharedConfig::Ptr &config) 0051 : QObject(nullptr) 0052 , d(new KConfigWatcherPrivate) 0053 { 0054 Q_ASSERT(config); 0055 d->m_config = config; 0056 0057 #if KCONFIG_USE_DBUS 0058 0059 if (config->name().isEmpty()) { 0060 return; 0061 } 0062 0063 // Watching absolute paths is not supported and also makes no sense. 0064 const bool isAbsolutePath = config->name().at(0) == QLatin1Char('/'); 0065 if (isAbsolutePath) { 0066 qCWarning(KCONFIG_CORE_LOG) << "Watching absolute paths is not supported" << config->name(); 0067 return; 0068 } 0069 0070 qDBusRegisterMetaType<QByteArrayList>(); 0071 qDBusRegisterMetaType<QHash<QString, QByteArrayList>>(); 0072 0073 QStringList watchedPaths = d->m_config->additionalConfigSources(); 0074 for (QString &file : watchedPaths) { 0075 file.prepend(QLatin1Char('/')); 0076 } 0077 watchedPaths.prepend(kconfigDBusSanitizePath(QLatin1Char('/') + d->m_config->name())); 0078 0079 if (d->m_config->openFlags() & KConfig::IncludeGlobals) { 0080 watchedPaths << QStringLiteral("/kdeglobals"); 0081 } 0082 0083 for (const QString &path : std::as_const(watchedPaths)) { 0084 QDBusConnection::sessionBus().connect(QString(), 0085 path, 0086 QStringLiteral("org.kde.kconfig.notify"), 0087 QStringLiteral("ConfigChanged"), 0088 this, 0089 // clang-format off 0090 SLOT(onConfigChangeNotification(QHash<QString,QByteArrayList>)) 0091 // clang-format on 0092 ); 0093 } 0094 #else 0095 qCWarning(KCONFIG_CORE_LOG) << "Use of KConfigWatcher without DBus support. You will not receive updates"; 0096 #endif 0097 } 0098 0099 KConfigWatcher::~KConfigWatcher() = default; 0100 0101 KSharedConfig::Ptr KConfigWatcher::config() const 0102 { 0103 return d->m_config; 0104 } 0105 0106 void KConfigWatcher::onConfigChangeNotification(const QHash<QString, QByteArrayList> &changes) 0107 { 0108 // should we ever need it we can determine the file changed with QDbusContext::message().path(), but it doesn't seem too useful 0109 0110 d->m_config->reparseConfiguration(); 0111 0112 for (auto it = changes.constBegin(); it != changes.constEnd(); it++) { 0113 KConfigGroup group = d->m_config->group(QString()); // top level group 0114 const auto parts = it.key().split(QLatin1Char('\x1d')); // magic char, see KConfig 0115 for (const QString &groupName : parts) { 0116 group = group.group(groupName); 0117 } 0118 Q_EMIT configChanged(group, it.value()); 0119 } 0120 } 0121 0122 #include "moc_kconfigwatcher.cpp"