File indexing completed on 2024-05-12 15:34:11
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 if (config->name().isEmpty()) { 0059 return; 0060 } 0061 0062 // Watching absolute paths is not supported and also makes no sense. 0063 const bool isAbsolutePath = config->name().at(0) == QLatin1Char('/'); 0064 if (isAbsolutePath) { 0065 qCWarning(KCONFIG_CORE_LOG) << "Watching absolute paths is not supported" << config->name(); 0066 return; 0067 } 0068 0069 qDBusRegisterMetaType<QByteArrayList>(); 0070 qDBusRegisterMetaType<QHash<QString, QByteArrayList>>(); 0071 0072 QStringList watchedPaths = d->m_config->additionalConfigSources(); 0073 for (QString &file : watchedPaths) { 0074 file.prepend(QLatin1Char('/')); 0075 } 0076 watchedPaths.prepend(kconfigDBusSanitizePath(QLatin1Char('/') + d->m_config->name())); 0077 0078 if (d->m_config->openFlags() & KConfig::IncludeGlobals) { 0079 watchedPaths << QStringLiteral("/kdeglobals"); 0080 } 0081 0082 for (const QString &path : std::as_const(watchedPaths)) { 0083 QDBusConnection::sessionBus().connect(QString(), 0084 path, 0085 QStringLiteral("org.kde.kconfig.notify"), 0086 QStringLiteral("ConfigChanged"), 0087 this, 0088 SLOT(onConfigChangeNotification(QHash<QString, QByteArrayList>))); 0089 } 0090 #else 0091 qCWarning(KCONFIG_CORE_LOG) << "Use of KConfigWatcher without DBus support. You will not receive updates"; 0092 #endif 0093 } 0094 0095 KConfigWatcher::~KConfigWatcher() = default; 0096 0097 KSharedConfig::Ptr KConfigWatcher::config() const 0098 { 0099 return d->m_config; 0100 } 0101 0102 void KConfigWatcher::onConfigChangeNotification(const QHash<QString, QByteArrayList> &changes) 0103 { 0104 // should we ever need it we can determine the file changed with QDbusContext::message().path(), but it doesn't seem too useful 0105 0106 d->m_config->reparseConfiguration(); 0107 0108 for (auto it = changes.constBegin(); it != changes.constEnd(); it++) { 0109 KConfigGroup group = d->m_config->group(QString()); // top level group 0110 const auto parts = it.key().split(QLatin1Char('\x1d')); // magic char, see KConfig 0111 for (const QString &groupName : parts) { 0112 group = group.group(groupName); 0113 } 0114 Q_EMIT configChanged(group, it.value()); 0115 } 0116 } 0117 0118 #include "moc_kconfigwatcher.cpp"