1 | /* |
2 | SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org> |
3 | SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #include "kconfigwatcher.h" |
9 | |
10 | #include "config-kconfig.h" |
11 | #include "kconfig_core_log_settings.h" |
12 | |
13 | #if KCONFIG_USE_DBUS |
14 | #include <QDBusConnection> |
15 | #include <QDBusMessage> |
16 | #include <QDBusMetaType> |
17 | |
18 | #include "dbussanitizer_p.h" |
19 | #endif |
20 | |
21 | #include <QDebug> |
22 | #include <QHash> |
23 | #include <QThreadStorage> |
24 | |
25 | class KConfigWatcherPrivate |
26 | { |
27 | public: |
28 | KSharedConfig::Ptr m_config; |
29 | }; |
30 | |
31 | KConfigWatcher::Ptr KConfigWatcher::create(const KSharedConfig::Ptr &config) |
32 | { |
33 | static QThreadStorage<QHash<KSharedConfig *, QWeakPointer<KConfigWatcher>>> watcherList; |
34 | |
35 | auto c = config.data(); |
36 | KConfigWatcher::Ptr watcher; |
37 | |
38 | if (!watcherList.localData().contains(key: c)) { |
39 | watcher = KConfigWatcher::Ptr(new KConfigWatcher(config)); |
40 | |
41 | watcherList.localData().insert(key: c, value: watcher.toWeakRef()); |
42 | |
43 | QObject::connect(sender: watcher.data(), signal: &QObject::destroyed, slot: [c]() { |
44 | watcherList.localData().remove(key: c); |
45 | }); |
46 | } |
47 | return watcherList.localData().value(key: c).toStrongRef(); |
48 | } |
49 | |
50 | KConfigWatcher::KConfigWatcher(const KSharedConfig::Ptr &config) |
51 | : QObject(nullptr) |
52 | , d(new KConfigWatcherPrivate) |
53 | { |
54 | Q_ASSERT(config); |
55 | d->m_config = config; |
56 | |
57 | #if KCONFIG_USE_DBUS |
58 | |
59 | if (config->name().isEmpty()) { |
60 | return; |
61 | } |
62 | |
63 | // Watching absolute paths is not supported and also makes no sense. |
64 | const bool isAbsolutePath = config->name().at(i: 0) == QLatin1Char('/'); |
65 | if (isAbsolutePath) { |
66 | qCWarning(KCONFIG_CORE_LOG) << "Watching absolute paths is not supported" << config->name(); |
67 | return; |
68 | } |
69 | |
70 | qDBusRegisterMetaType<QByteArrayList>(); |
71 | qDBusRegisterMetaType<QHash<QString, QByteArrayList>>(); |
72 | |
73 | QStringList watchedPaths = d->m_config->additionalConfigSources(); |
74 | for (QString &file : watchedPaths) { |
75 | file.prepend(c: QLatin1Char('/')); |
76 | } |
77 | watchedPaths.prepend(t: kconfigDBusSanitizePath(path: QLatin1Char('/') + d->m_config->name())); |
78 | |
79 | if (d->m_config->openFlags() & KConfig::IncludeGlobals) { |
80 | watchedPaths << QStringLiteral("/kdeglobals" ); |
81 | } |
82 | |
83 | for (const QString &path : std::as_const(t&: watchedPaths)) { |
84 | QDBusConnection::sessionBus().connect(service: QString(), |
85 | path, |
86 | QStringLiteral("org.kde.kconfig.notify" ), |
87 | QStringLiteral("ConfigChanged" ), |
88 | receiver: this, |
89 | // clang-format off |
90 | SLOT(onConfigChangeNotification(QHash<QString,QByteArrayList>)) |
91 | // clang-format on |
92 | ); |
93 | } |
94 | #else |
95 | qCWarning(KCONFIG_CORE_LOG) << "Use of KConfigWatcher without DBus support. You will not receive updates" ; |
96 | #endif |
97 | } |
98 | |
99 | KConfigWatcher::~KConfigWatcher() = default; |
100 | |
101 | KSharedConfig::Ptr KConfigWatcher::config() const |
102 | { |
103 | return d->m_config; |
104 | } |
105 | |
106 | void KConfigWatcher::onConfigChangeNotification(const QHash<QString, QByteArrayList> &changes) |
107 | { |
108 | // should we ever need it we can determine the file changed with QDbusContext::message().path(), but it doesn't seem too useful |
109 | |
110 | d->m_config->reparseConfiguration(); |
111 | |
112 | for (auto it = changes.constBegin(); it != changes.constEnd(); it++) { |
113 | KConfigGroup group = d->m_config->group(group: QString()); // top level group |
114 | const auto parts = it.key().split(sep: QLatin1Char('\x1d')); // magic char, see KConfig |
115 | for (const QString &groupName : parts) { |
116 | group = group.group(group: groupName); |
117 | } |
118 | Q_EMIT configChanged(group, names: it.value()); |
119 | } |
120 | } |
121 | |
122 | #include "moc_kconfigwatcher.cpp" |
123 | |