GCC Code Coverage Report


Directory: ./
File: lib/configmonitor.cpp
Date: 2023-04-20 22:59:23
Exec Total Coverage
Lines: 51 86 59.3%
Branches: 18 56 32.1%

Line Branch Exec Source
1 /*************************************************************************************
2 * Copyright 2012 - 2014 Daniel Vrátil <dvratil@redhat.com> *
3 * *
4 * This library is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * This library is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with this library; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
17 *************************************************************************************/
18 #include "configmonitor.h"
19 #include "backend.h"
20 #include "backendinterface.h"
21 #include "backendmanager_p.h"
22 #include "configserializer_p.h"
23 #include "disman_debug.h"
24 #include "getconfigoperation.h"
25 #include "output.h"
26
27 #include <QDBusPendingCallWatcher>
28
29 Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)
30
31 using namespace Disman;
32
33 class Q_DECL_HIDDEN ConfigMonitor::Private : public QObject
34 {
35 Q_OBJECT
36
37 public:
38 Private(ConfigMonitor* q);
39
40 void update_configs();
41 void on_backend_ready(org::kwinft::disman::backend* backend);
42 void backend_config_changed(const QVariantMap& configMap);
43 void config_destroyed(QObject* removedConfig);
44 void get_config_finished(ConfigOperation* op);
45 void update_configs(const Disman::ConfigPtr& newConfig);
46 bool has_config(ConfigPtr const& config) const;
47
48 QList<std::weak_ptr<Disman::Config>> watched_configs;
49
50 QPointer<org::kwinft::disman::backend> mBackend;
51 bool mFirstBackend;
52
53 private:
54 ConfigMonitor* q;
55 };
56
57 9 ConfigMonitor::Private::Private(ConfigMonitor* q)
58 : QObject(q)
59 9 , mFirstBackend(true)
60 9 , q(q)
61 {
62 9 }
63
64 void ConfigMonitor::Private::on_backend_ready(org::kwinft::disman::backend* backend)
65 {
66 Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess);
67 if (backend == mBackend) {
68 return;
69 }
70
71 if (mBackend) {
72 disconnect(mBackend.data(),
73 &org::kwinft::disman::backend::configChanged,
74 this,
75 &ConfigMonitor::Private::backend_config_changed);
76 }
77
78 mBackend = QPointer<org::kwinft::disman::backend>(backend);
79 // If we received a new backend interface, then it's very likely that it is
80 // because the backend process has crashed - just to be sure we haven't missed
81 // any change, request the current config now and update our watched configs
82 //
83 // Only request the config if this is not initial backend request, because it
84 // can happen that if a change happened before now, or before we get the config,
85 // the result will be invalid. This can happen when Disman KDED launches and
86 // detects changes need to be done.
87 if (!mFirstBackend && !watched_configs.isEmpty()) {
88 connect(new GetConfigOperation(),
89 &GetConfigOperation::finished,
90 this,
91 &Private::get_config_finished);
92 }
93 mFirstBackend = false;
94
95 connect(mBackend.data(),
96 &org::kwinft::disman::backend::configChanged,
97 this,
98 &ConfigMonitor::Private::backend_config_changed);
99 }
100
101 void ConfigMonitor::Private::get_config_finished(ConfigOperation* op)
102 {
103 Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess);
104 if (op->has_error()) {
105 qCWarning(DISMAN) << "Failed to retrieve current config: " << op->error_string();
106 return;
107 }
108
109 const Disman::ConfigPtr config = qobject_cast<GetConfigOperation*>(op)->config();
110 update_configs(config);
111 }
112
113 void ConfigMonitor::Private::backend_config_changed(const QVariantMap& configMap)
114 {
115 Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess);
116
117 ConfigPtr newConfig = ConfigSerializer::deserialize_config(configMap);
118 if (!newConfig) {
119 qCWarning(DISMAN) << "Failed to deserialize config from DBus change notification";
120 return;
121 }
122 update_configs(newConfig);
123 }
124
125 21 void ConfigMonitor::Private::update_configs(const Disman::ConfigPtr& newConfig)
126 {
127 21 QMutableListIterator<std::weak_ptr<Config>> iter(watched_configs);
128
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 21 times.
47 while (iter.hasNext()) {
129 26 Disman::ConfigPtr config = iter.next().lock();
130
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 17 times.
26 if (!config) {
131 9 iter.remove();
132 9 continue;
133 }
134
135 17 config->apply(newConfig);
136 17 iter.setValue(config);
137
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 9 times.
26 }
138
139 21 Q_EMIT q->configuration_changed();
140 21 }
141
142 13 void ConfigMonitor::Private::config_destroyed(QObject* removedConfig)
143 {
144
2/2
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 13 times.
28 for (auto iter = watched_configs.begin(); iter != watched_configs.end(); ++iter) {
145
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
15 if (iter->lock().get() == removedConfig) {
146 iter = watched_configs.erase(iter);
147 // Iterate over the entire list in case there are duplicates
148 }
149 }
150 13 }
151
152 16 bool ConfigMonitor::Private::has_config(ConfigPtr const& config) const
153 {
154 16 auto& cfgs = watched_configs;
155 16 return std::find_if(cfgs.cbegin(),
156 cfgs.cend(),
157 44 [config](auto const& cfg) { return cfg.lock().get() == config.get(); })
158 32 != cfgs.cend();
159 }
160
161 73 ConfigMonitor* ConfigMonitor::instance()
162 {
163 static ConfigMonitor* s_instance = nullptr;
164
165
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 64 times.
73 if (s_instance == nullptr) {
166 9 s_instance = new ConfigMonitor();
167 }
168
169 73 return s_instance;
170 }
171
172 9 ConfigMonitor::ConfigMonitor()
173 : QObject()
174 9 , d(new Private(this))
175 {
176
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
9 if (BackendManager::instance()->method() == BackendManager::OutOfProcess) {
177 connect(BackendManager::instance(),
178 &BackendManager::backend_ready,
179 d,
180 &ConfigMonitor::Private::on_backend_ready);
181 BackendManager::instance()->request_backend();
182 }
183 9 }
184
185 ConfigMonitor::~ConfigMonitor()
186 {
187 delete d;
188 }
189
190 15 void ConfigMonitor::add_config(const ConfigPtr& config)
191 {
192
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 if (d->has_config(config)) {
193 return;
194 }
195 15 connect(config.get(), &QObject::destroyed, d, &Private::config_destroyed);
196 15 d->watched_configs << config;
197 }
198
199 1 void ConfigMonitor::remove_config(const ConfigPtr& config)
200 {
201
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!d->has_config(config)) {
202 return;
203 }
204
205 1 disconnect(config.get(), &QObject::destroyed, d, &Private::config_destroyed);
206 1 auto& cfgs = d->watched_configs;
207 2 cfgs.erase(
208 std::remove_if(cfgs.begin(),
209 cfgs.end(),
210 3 [config](auto const& cfg) { return cfg.lock().get() == config.get(); }),
211 cfgs.end());
212 }
213
214 58 void ConfigMonitor::connect_in_process_backend(Disman::Backend* backend)
215 {
216
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
58 Q_ASSERT(BackendManager::instance()->method() == BackendManager::InProcess);
217 58 connect(backend, &Backend::config_changed, this, [=](Disman::ConfigPtr config) {
218
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
21 if (!config) {
219 return;
220 }
221
2/2
✓ Branch 11 taken 21 times.
✓ Branch 12 taken 21 times.
42 qCDebug(DISMAN) << "Backend change!" << config;
222 21 BackendManager::instance()->set_config(config);
223 21 d->update_configs(config);
224 });
225 58 }
226
227 #include "configmonitor.moc"
228