GCC Code Coverage Report


Directory: ./
File: lib/config.cpp
Date: 2023-04-20 22:59:23
Exec Total Coverage
Lines: 233 269 86.6%
Branches: 122 183 66.7%

Line Branch Exec Source
1 /*************************************************************************************
2 * Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org> *
3 * Copyright (C) 2014 by Daniel Vrátil <dvratil@redhat.com> *
4 * *
5 * This library is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU Lesser General Public *
7 * License as published by the Free Software Foundation; either *
8 * version 2.1 of the License, or (at your option) any later version. *
9 * *
10 * This library is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
13 * Lesser General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU Lesser General Public *
16 * License along with this library; if not, write to the Free Software *
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18 *************************************************************************************/
19 #include "config.h"
20 #include "backend.h"
21 #include "backendmanager_p.h"
22 #include "disman_debug.h"
23 #include "output.h"
24
25 #include <QCryptographicHash>
26 #include <QDebug>
27 #include <QRect>
28 #include <QStringList>
29
30 #include <sstream>
31
32 using namespace Disman;
33
34 class Q_DECL_HIDDEN Config::Private : public QObject
35 {
36 Q_OBJECT
37 public:
38 461 Private(Config* parent, Cause cause)
39 461 : QObject(parent)
40 461 , valid(true)
41 461 , supported_features(Config::Feature::None)
42 461 , tablet_mode_available(false)
43 461 , tablet_mode_engaged(false)
44 461 , cause{cause}
45 461 , q(parent)
46 {
47 461 }
48
49 482 auto remove_output(OutputMap::iterator iter)
50 {
51
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 482 times.
482 if (iter == outputs.end()) {
52 return iter;
53 }
54
55 482 OutputPtr output = iter->second;
56
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 482 times.
482 if (!output) {
57 return outputs.erase(iter);
58 }
59
60 482 auto const outputId = iter->first;
61 482 iter = outputs.erase(iter);
62
63
2/2
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 410 times.
482 if (primary_output == output) {
64 72 q->set_primary_output(OutputPtr());
65 }
66 482 output->disconnect(q);
67
68 482 Q_EMIT q->output_removed(outputId);
69
70 482 return iter;
71 482 }
72
73 bool valid;
74 ScreenPtr screen;
75 OutputPtr primary_output;
76 OutputMap outputs;
77 Features supported_features;
78 bool tablet_mode_available;
79 bool tablet_mode_engaged;
80 Cause cause;
81
82 private:
83 Config* q;
84 };
85
86 10 bool Config::can_be_applied(const ConfigPtr& config)
87 {
88 10 return can_be_applied(config, ValidityFlag::None);
89 }
90
91 10 bool Config::can_be_applied(const ConfigPtr& config, ValidityFlags flags)
92 {
93
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9 times.
10 if (!config) {
94
2/2
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
2 qCDebug(DISMAN) << "can_be_applied: Config not available, returning false";
95 1 return false;
96 }
97 9 ConfigPtr currentConfig = BackendManager::instance()->config();
98
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if (!currentConfig) {
99 qCDebug(DISMAN) << "can_be_applied: Current config not available, returning false";
100 return false;
101 }
102
103 9 QRect rect;
104 9 OutputPtr currentOutput;
105 9 int enabledOutputsCount = 0;
106
107
2/2
✓ Branch 9 taken 12 times.
✓ Branch 10 taken 4 times.
16 for (auto const& [key, output] : config->outputs()) {
108
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if (!output->enabled()) {
109 continue;
110 }
111
112 12 ++enabledOutputsCount;
113
114 12 currentOutput = currentConfig->output(output->id());
115 // If there is no such output
116
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 11 times.
12 if (!currentOutput) {
117
2/2
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 1 times.
2 qCDebug(DISMAN) << "can_be_applied: The output:" << output->id() << "does not exists";
118 5 return false;
119 }
120
121 // if there is no currentMode
122
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 11 times.
11 if (!output->auto_mode()) {
123 qCDebug(DISMAN) << "can_be_applied: The output:" << output->id()
124 << "has no currentModeId";
125 return false;
126 }
127 // If the mode is not found in the current output
128
2/2
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 7 times.
11 if (!currentOutput->mode(output->auto_mode()->id())) {
129
2/2
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 4 times.
8 qCDebug(DISMAN) << "can_be_applied: The output:" << output->id()
130 4 << "has no mode:" << output->auto_mode()->id().c_str();
131 4 return false;
132 }
133
134 7 auto const outputSize = output->auto_mode()->size();
135
136
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
7 if (output->position().x() < rect.x()) {
137 rect.setX(output->position().x());
138 }
139
140
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
7 if (output->position().y() < rect.y()) {
141 rect.setY(output->position().y());
142 }
143
144 7 QPoint bottomRight;
145
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 if (output->horizontal()) {
146 7 bottomRight = QPoint(output->position().x() + outputSize.width(),
147 14 output->position().y() + outputSize.height());
148 } else {
149 bottomRight = QPoint(output->position().x() + outputSize.height(),
150 output->position().y() + outputSize.width());
151 }
152
153
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 if (bottomRight.x() > rect.width()) {
154 7 rect.setWidth(bottomRight.x());
155 }
156
157
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 if (bottomRight.y() > rect.height()) {
158 7 rect.setHeight(bottomRight.y());
159 }
160
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 5 times.
9 }
161
162
2/6
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
4 if (flags & ValidityFlag::RequireAtLeastOneEnabledScreen && enabledOutputsCount == 0) {
163 qCDebug(DISMAN) << "canBeAppled: There are no enabled screens, at least one required";
164 return false;
165 }
166
167 4 const int maxEnabledOutputsCount = config->screen()->max_outputs_count();
168
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (enabledOutputsCount > maxEnabledOutputsCount) {
169
2/2
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
2 qCDebug(DISMAN) << "can_be_applied: Too many active screens. Requested: "
170 1 << enabledOutputsCount << ", Max: " << maxEnabledOutputsCount;
171 1 return false;
172 }
173
174 // TODO: Why we do this again? Isn't the screen size determined by the outer rect of all
175 // outputs? At least it's that way in the Wayland case.
176
1/2
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
3 if (rect.width() > config->screen()->max_size().width()) {
177 qCDebug(DISMAN) << "can_be_applied: The configuration is too wide:" << rect.width();
178 return false;
179 }
180
1/2
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
3 if (rect.height() > config->screen()->max_size().height()) {
181 qCDebug(DISMAN) << "can_be_applied: The configuration is too high:" << rect.height();
182 return false;
183 }
184
185 3 return true;
186 9 }
187
188 341 Config::Config()
189 341 : Config(Cause::unknown)
190 {
191 341 }
192
193 461 Config::Config(Cause cause)
194 : QObject(nullptr)
195 461 , d(new Private(this, cause))
196 {
197 461 }
198
199 1174 Config::~Config()
200 {
201
1/2
✓ Branch 0 taken 450 times.
✗ Branch 1 not taken.
900 delete d;
202 1174 }
203
204 120 ConfigPtr Config::clone() const
205 {
206 120 ConfigPtr newConfig(new Config(cause()));
207 120 newConfig->d->screen = d->screen->clone();
208
209
2/2
✓ Branch 7 taken 263 times.
✓ Branch 8 taken 120 times.
383 for (auto const& [key, ourOutput] : d->outputs) {
210 263 auto cloned_output = ourOutput->clone();
211 263 newConfig->add_output(cloned_output);
212
213
2/2
✓ Branch 1 taken 37 times.
✓ Branch 2 taken 226 times.
263 if (d->primary_output == ourOutput) {
214 37 newConfig->set_primary_output(cloned_output);
215 }
216 263 }
217
218 120 newConfig->set_supported_features(supported_features());
219 120 newConfig->set_tablet_mode_available(tablet_mode_available());
220 120 newConfig->set_tablet_mode_engaged(tablet_mode_engaged());
221
222 120 return newConfig;
223 }
224
225 25 bool Config::compare(ConfigPtr config) const
226 {
227
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 18 times.
25 if (!config) {
228 7 return false;
229 }
230
231
2/4
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 18 times.
✗ Branch 8 not taken.
36 auto const simple_data_compare = d->valid == config->d->valid && hash() == config->hash()
232
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 && d->supported_features == config->d->supported_features
233
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 && d->tablet_mode_available == config->d->tablet_mode_available
234
4/8
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 18 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 18 times.
✗ Branch 10 not taken.
36 && d->tablet_mode_engaged == config->d->tablet_mode_engaged && d->cause == config->d->cause;
235
236
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (!simple_data_compare) {
237 return false;
238 }
239
240 18 auto other_screen = config->screen();
241
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 if (d->screen) {
242
4/8
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 18 times.
✓ Branch 8 taken 18 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 18 times.
18 if (!other_screen || !d->screen->compare(other_screen)) {
243 return false;
244 }
245 } else if (other_screen) {
246 return false;
247 }
248
249 18 auto other_primary = config->primary_output();
250
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 12 times.
18 if (d->primary_output) {
251
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6 times.
6 if (!other_primary || d->primary_output->id() != other_primary->id()) {
252 return false;
253 }
254
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 } else if (other_primary) {
255 return false;
256 }
257
258 // Check that we have all outputs of the other config.
259
2/2
✓ Branch 8 taken 47 times.
✓ Branch 9 taken 16 times.
63 for (auto const& [key, output] : config->d->outputs) {
260
2/2
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 45 times.
47 if (!this->output(output->id())) {
261 2 return false;
262 }
263 }
264 // Check that the other config has also all our outputs and that these have the same data.
265
2/2
✓ Branch 7 taken 22 times.
✓ Branch 8 taken 2 times.
24 for (auto const& [key, output] : d->outputs) {
266 22 auto other_output = config->output(output->id());
267
8/8
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 2 times.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 20 times.
✓ Branch 9 taken 2 times.
✓ Branch 11 taken 14 times.
✓ Branch 12 taken 8 times.
22 if (!other_output || !output->compare(other_output)) {
268 14 return false;
269 }
270
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 14 times.
22 }
271 2 return true;
272 18 }
273
274 724 QString Config::hash() const
275 {
276 724 QStringList hashedOutputs;
277
2/2
✓ Branch 6 taken 1674 times.
✓ Branch 7 taken 724 times.
2398 for (auto const& [key, output] : d->outputs) {
278 1674 hashedOutputs << QString::fromStdString(output->hash());
279 }
280
281 724 std::sort(hashedOutputs.begin(), hashedOutputs.end());
282 1448 const auto hash = QCryptographicHash::hash(hashedOutputs.join(QString()).toLatin1(),
283 724 QCryptographicHash::Md5);
284 724 return QString::fromLatin1(hash.toHex());
285 724 }
286
287 577 Config::Cause Config::cause() const
288 {
289 577 return d->cause;
290 }
291
292 194 void Config::set_cause(Cause cause)
293 {
294 194 d->cause = cause;
295 194 }
296
297 357 ScreenPtr Config::screen() const
298 {
299 357 return d->screen;
300 }
301
302 428 void Config::setScreen(const ScreenPtr& screen)
303 {
304 428 d->screen = screen;
305 428 }
306
307 503 OutputPtr Config::output(int outputId) const
308 {
309
2/2
✓ Branch 3 taken 394 times.
✓ Branch 4 taken 109 times.
503 if (auto out = d->outputs.find(outputId); out != d->outputs.end()) {
310 394 return out->second;
311 }
312 109 return nullptr;
313 }
314
315 2055 Config::Features Config::supported_features() const
316 {
317 2055 return d->supported_features;
318 }
319
320 515 void Config::set_supported_features(const Config::Features& features)
321 {
322 515 d->supported_features = features;
323 515 }
324
325 273 bool Config::tablet_mode_available() const
326 {
327 273 return d->tablet_mode_available;
328 }
329
330 120 void Config::set_tablet_mode_available(bool available)
331 {
332 120 d->tablet_mode_available = available;
333 120 }
334
335 120 bool Config::tablet_mode_engaged() const
336 {
337 120 return d->tablet_mode_engaged;
338 }
339
340 120 void Config::set_tablet_mode_engaged(bool engaged)
341 {
342 120 d->tablet_mode_engaged = engaged;
343 120 }
344
345 1782 OutputMap Config::outputs() const
346 {
347 1782 return d->outputs;
348 }
349
350 852 OutputPtr Config::primary_output() const
351 {
352 852 return d->primary_output;
353 }
354
355 351 void Config::set_primary_output(const OutputPtr& newPrimary)
356 {
357
2/2
✓ Branch 1 taken 83 times.
✓ Branch 2 taken 268 times.
351 if (d->primary_output == newPrimary) {
358 83 return;
359 }
360
361 268 d->primary_output = newPrimary;
362 268 Q_EMIT primary_output_changed(newPrimary);
363 }
364
365 OutputPtr Config::replication_source(OutputPtr const& output)
366 {
367 if (auto source_id = output->replication_source()) {
368 for (auto const& [key, output] : d->outputs) {
369 if (output->id() == source_id) {
370 return output;
371 }
372 }
373 }
374 return nullptr;
375 }
376
377 1497 void Config::add_output(const OutputPtr& output)
378 {
379 1497 d->outputs.insert({output->id(), output});
380
381 1497 Q_EMIT output_added(output);
382 1497 }
383
384 2 void Config::remove_output(int outputId)
385 {
386 2 d->remove_output(d->outputs.find(outputId));
387 2 }
388
389 534 void Config::set_outputs(OutputMap const& outputs)
390 {
391 534 auto primary = primary_output();
392
2/2
✓ Branch 3 taken 479 times.
✓ Branch 4 taken 534 times.
1013 for (auto iter = d->outputs.begin(), end = d->outputs.end(); iter != end;) {
393 479 iter = d->remove_output(iter);
394 479 end = d->outputs.end();
395 }
396
397
2/2
✓ Branch 7 taken 1232 times.
✓ Branch 8 taken 534 times.
1766 for (auto const& [key, output] : outputs) {
398 1232 add_output(output);
399
5/6
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 1152 times.
✓ Branch 7 taken 80 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 80 times.
✓ Branch 10 taken 1152 times.
1232 if (primary && primary->id() == output->id()) {
400 80 set_primary_output(output);
401 80 primary = nullptr;
402 }
403 }
404 534 }
405
406 57 bool Config::valid() const
407 {
408 57 return d->valid;
409 }
410
411 428 void Config::set_valid(bool valid)
412 {
413 428 d->valid = valid;
414 428 }
415
416 42 void Config::apply(const ConfigPtr& other)
417 {
418 42 d->screen->apply(other->screen());
419
420 // Remove removed outputs
421
2/2
✓ Branch 3 taken 105 times.
✓ Branch 4 taken 42 times.
147 for (auto iter = d->outputs.begin(), end = d->outputs.end(); iter != end;) {
422
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 104 times.
105 if (other->d->outputs.find(iter->second->id()) == other->d->outputs.end()) {
423 1 iter = d->remove_output(iter);
424 1 end = d->outputs.end();
425 } else {
426 104 iter++;
427 }
428 }
429
430
2/2
✓ Branch 8 taken 105 times.
✓ Branch 9 taken 42 times.
147 for (auto const& [key, otherOutput] : other->d->outputs) {
431 // Add new outputs
432 105 OutputPtr output;
433 auto primary
434
7/8
✓ Branch 3 taken 13 times.
✓ Branch 4 taken 92 times.
✓ Branch 11 taken 8 times.
✓ Branch 12 taken 5 times.
✓ Branch 13 taken 13 times.
✓ Branch 14 taken 92 times.
✓ Branch 16 taken 105 times.
✗ Branch 17 not taken.
105 = other->primary_output() && other->primary_output()->id() == otherOutput->id();
435
436
2/2
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 104 times.
105 if (d->outputs.find(otherOutput->id()) == d->outputs.end()) {
437 1 output = otherOutput->clone();
438 1 add_output(output);
439 } else {
440 // Update existing outputs
441 104 output = d->outputs[otherOutput->id()];
442 104 output->apply(otherOutput);
443 }
444
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 97 times.
105 if (primary) {
445 8 set_primary_output(output);
446 }
447 105 }
448
449 // Update validity
450 42 set_valid(other->valid());
451 42 set_cause(other->cause());
452 42 }
453
454 153 std::string Config::log() const
455 {
456 153 auto print_cause = [](Config::Cause cause) -> std::string {
457
3/5
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 86 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
153 switch (cause) {
458 40 case Config::Cause::unknown:
459 40 return "unknown";
460 27 case Config::Cause::file:
461 27 return "file";
462 86 case Config::Cause::generated:
463 86 return "generated";
464 case Config::Cause::interactive:
465 return "interactive";
466 default:
467 return "ill-defined";
468 }
469 };
470
471 153 std::stringstream ss;
472
473 153 ss << " Disman::Config {";
474 153 ss << " cause: " << print_cause(cause());
475
2/2
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 127 times.
153 if (auto primary = primary_output()) {
476 26 ss << ", primary: " << primary->id();
477 153 }
478
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 153 times.
153 if (tablet_mode_available()) {
479 ss << " tablet-mode: " << (tablet_mode_engaged() ? "engaged" : "disengaged");
480 }
481
2/2
✓ Branch 8 taken 377 times.
✓ Branch 9 taken 153 times.
530 for (auto const& [key, output] : outputs()) {
482 377 auto log = std::istringstream(output->log());
483 377 std::string line;
484
2/2
✓ Branch 2 taken 3393 times.
✓ Branch 3 taken 377 times.
3770 while (std::getline(log, line)) {
485 3393 ss << std::endl << " " << line;
486 }
487 530 }
488 153 ss << std::endl << " }";
489 153 return ss.str();
490 153 }
491
492 153 QDebug operator<<(QDebug dbg, const Disman::ConfigPtr& config)
493 {
494
1/2
✓ Branch 1 taken 153 times.
✗ Branch 2 not taken.
153 if (config) {
495 153 dbg << Qt::endl << config->log().c_str();
496 } else {
497 dbg << "Disman::Config {null}";
498 }
499 153 return dbg;
500 }
501
502 #include "config.moc"
503