GCC Code Coverage Report


Directory: ./
File: backends/filer.h
Date: 2023-04-20 22:59:23
Exec Total Coverage
Lines: 201 223 90.1%
Branches: 92 128 71.9%

Line Branch Exec Source
1 /*************************************************************************
2 Copyright © 2020 Roman Gilg <subdiff@gmail.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 #pragma once
19
20 #include "filer_controller.h"
21 #include "filer_helpers.h"
22 #include "output_filer.h"
23
24 #include "logging.h"
25 #include <config.h>
26 #include <output.h>
27 #include <types.h>
28
29 #include <QObject>
30 #include <QStandardPaths>
31 #include <QVariantMap>
32
33 #include <algorithm>
34 #include <memory>
35 #include <string>
36 #include <vector>
37
38 namespace Disman
39 {
40
41 class Filer
42 {
43 public:
44 120 Filer(Disman::ConfigPtr const& config, Filer_controller* controller, std::string suffix = "")
45 120 : m_config{config}
46 120 , m_controller{controller}
47 120 , m_suffix{suffix}
48 {
49 240 m_dir_path = QString(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
50 240 + QStringLiteral("/disman/control/"))
51 120 .toStdString();
52 120 m_read_success = read_file();
53
54
2/2
✓ Branch 8 taken 232 times.
✓ Branch 9 taken 120 times.
352 for (auto const& [key, output] : config->outputs()) {
55 232 m_output_filers.push_back(
56 464 std::unique_ptr<Output_filer>(new Output_filer(output, m_controller, m_dir_path)));
57 120 }
58 120 }
59
60 217 bool get_values(ConfigPtr& config)
61 {
62 217 auto outputs = config->outputs();
63
64
2/2
✓ Branch 7 taken 485 times.
✓ Branch 8 taken 217 times.
702 for (auto& [key, output] : outputs) {
65 //
66 // First we must set the retention for all later calls to get_value().
67 485 auto retention = get_value(
68 output, "retention", static_cast<int>(Output::Retention::Undefined), nullptr);
69 485 output->set_retention(convert_int_to_retention(retention));
70 485 output->set_enabled(get_value(output, "enabled", true, nullptr));
71
72
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 485 times.
485 if (config->supported_features().testFlag(Disman::Config::Feature::PrimaryDisplay)) {
73 if (get_value(output, "primary", false, nullptr)) {
74 config->set_primary_output(output);
75 }
76 }
77
78 970 output->set_position(
79 970 get_value(output, "pos", QPointF(0, 0), nullptr, std::function{get_pos}));
80 485 get_replication_source(output, outputs);
81
82 485 auto ofiler = get_output_filer(output);
83
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 485 times.
485 assert(ofiler);
84
85 485 ofiler->get_global_data(output);
86
87
2/2
✓ Branch 0 taken 297 times.
✓ Branch 1 taken 188 times.
485 if (auto mode = get_value(
88 485 output, "mode", ModePtr(), ofiler, std::function{Output_filer::get_mode})) {
89 297 output->set_mode(mode);
90 } else {
91 // Set an invalid commanded mode.
92 188 output->set_resolution(QSize());
93 188 output->set_refresh_rate(0);
94 485 }
95
96
2/2
✓ Branch 3 taken 367 times.
✓ Branch 4 taken 118 times.
485 if (config->supported_features().testFlag(Disman::Config::Feature::PerOutputScaling)) {
97 367 output->set_scale(get_value(output, "scale", 1., ofiler));
98 }
99
100
2/2
✓ Branch 2 taken 367 times.
✓ Branch 3 taken 118 times.
485 if (output->adaptive_sync_toggle_support()) {
101 367 output->set_adaptive_sync(get_value(output, "adaptive-sync", false, ofiler));
102 }
103 auto const rotation
104 485 = get_value(output, "rotation", static_cast<int>(Output::Rotation::None), ofiler);
105 485 output->set_rotation(Output_filer::convert_int_to_rotation(rotation));
106
107 485 output->set_auto_resolution(get_value(output, "auto-resolution", true, ofiler));
108 485 output->set_auto_refresh_rate(get_value(output, "auto-refresh-rate", true, ofiler));
109
110
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 485 times.
485 if (config->supported_features().testFlag(Disman::Config::Feature::AutoRotation)) {
111 output->set_auto_rotate(get_value(output, "auto-rotate", false, ofiler));
112 if (config->supported_features().testFlag(Disman::Config::Feature::TabletMode)) {
113 output->set_auto_rotate_only_in_tablet_mode(
114 get_value(output, "auto-rotate-tablet-only", false, ofiler));
115 }
116 }
117 }
118
119 434 return m_read_success;
120 217 }
121
122 42 void set_values(ConfigPtr const& config)
123 {
124 42 auto outputs = config->outputs();
125
2/2
✓ Branch 7 taken 104 times.
✓ Branch 8 taken 42 times.
146 for (auto const& [key, output] : outputs) {
126 104 auto const retention = output->retention();
127 104 Output_filer* filer = nullptr;
128
129
1/2
✓ Branch 0 taken 104 times.
✗ Branch 1 not taken.
104 if (retention != Output::Retention::Individual) {
130 104 filer = get_output_filer(output);
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 104 times.
104 assert(filer);
132 }
133
134 104 set_value(output, "retention", static_cast<int>(output->retention()), nullptr);
135 104 set_value(output, "enabled", output->enabled(), nullptr);
136
137
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 104 times.
104 if (config->supported_features().testFlag(Disman::Config::Feature::PrimaryDisplay)) {
138 set_value(
139 output, "primary", config->primary_output().get() == output.get(), nullptr);
140 }
141
142 104 set_replication_source(output, config);
143 104 set_value(output, "pos", output->position(), nullptr, std::function{set_pos});
144
145 104 set_value(output, "mode", output->auto_mode(), filer, std::function{set_mode});
146
147
2/2
✓ Branch 3 taken 92 times.
✓ Branch 4 taken 12 times.
104 if (config->supported_features().testFlag(Disman::Config::Feature::PerOutputScaling)) {
148 92 set_value(output, "scale", output->scale(), filer);
149 }
150 104 set_value(output, "rotation", static_cast<int>(output->rotation()), filer);
151
152
2/2
✓ Branch 2 taken 92 times.
✓ Branch 3 taken 12 times.
104 if (output->adaptive_sync_toggle_support()) {
153 92 set_value(output, "adaptive-sync", output->adaptive_sync(), filer);
154 }
155
156 104 set_value(output, "auto-resolution", output->auto_resolution(), filer);
157 104 set_value(output, "auto-refresh-rate", output->auto_refresh_rate(), filer);
158
159
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 104 times.
104 if (config->supported_features().testFlag(Disman::Config::Feature::AutoRotation)) {
160 set_value(output, "auto-rotate", output->auto_rotate(), filer);
161 if (config->supported_features().testFlag(Disman::Config::Feature::TabletMode)) {
162 set_value(output,
163 "auto-rotate-tablet-only",
164 output->auto_rotate_only_in_tablet_mode(),
165 filer);
166 }
167 }
168 }
169 42 }
170
171 template<typename T>
172 9228 T get_value(
173 OutputPtr const& output,
174 std::string const& id,
175 T default_value,
176 Output_filer* filer,
177 std::function<T(OutputPtr const&, QVariant const&, T)> getter
178 2984 = []([[maybe_unused]] OutputPtr const& output, QVariant const& val, T default_value) {
179 2984 return Filer_helpers::from_variant(val, default_value);
180 }) const
181 {
182
5/6
✓ Branch 0 taken 2674 times.
✓ Branch 1 taken 1940 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2674 times.
✓ Branch 6 taken 1940 times.
✓ Branch 7 taken 2674 times.
9228 if (!filer || output->retention() == Output::Retention::Individual) {
183 3880 auto const outputs_info = get_outputs_info();
184
185
4/4
✓ Branch 4 taken 14644 times.
✓ Branch 5 taken 1060 times.
✓ Branch 8 taken 15704 times.
✓ Branch 9 taken 880 times.
64576 for (auto const& variant_info : outputs_info) {
186 31408 auto const info = variant_info.toMap();
187
2/2
✓ Branch 1 taken 1060 times.
✓ Branch 2 taken 14644 times.
31408 if (is_info_for_output(info, output)) {
188 2120 auto const val = info[QString::fromStdString(id)];
189 2120 return getter(output, val, default_value);
190 }
191 }
192 }
193
194 // Retention is global or info for output not in config control file. If an output filer
195 // was provided we hand over to that, otherwise we can only return the default value.
196
2/2
✓ Branch 0 taken 2674 times.
✓ Branch 1 taken 880 times.
7108 if (filer) {
197 5348 return filer->get_value(id, default_value, getter);
198 }
199 1760 return default_value;
200 }
201
202 template<typename T>
203 2032 void set_value(
204 OutputPtr const& output,
205 std::string const& id,
206 T value,
207 Output_filer* filer,
208 std::function<void(QVariantMap&, std::string const&, T)> setter
209 2608 = [](QVariantMap& info, std::string const& id, T value) {
210 1304 info[QString::fromStdString(id)] = value;
211 })
212 {
213 2032 QList<QVariant>::iterator it;
214 2032 auto outputs_info = get_outputs_info();
215
216 5368 auto set_output_value = [&]() {
217
8/12
✗ Branch 0 not taken.
✓ Branch 1 taken 104 times.
✓ Branch 2 taken 92 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 104 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 104 times.
✓ Branch 8 taken 300 times.
✓ Branch 9 taken 104 times.
✓ Branch 10 taken 104 times.
✓ Branch 11 taken 104 times.
1016 if (filer) {
218 600 filer->set_value(id, value, setter);
219 }
220 };
221
222
4/4
✓ Branch 2 taken 5350 times.
✓ Branch 3 taken 869 times.
✓ Branch 7 taken 6219 times.
✓ Branch 8 taken 147 times.
14470 for (it = outputs_info.begin(); it != outputs_info.end(); ++it) {
223 12438 auto output_info = (*it).toMap();
224
2/2
✓ Branch 1 taken 5350 times.
✓ Branch 2 taken 869 times.
12438 if (!is_info_for_output(output_info, output)) {
225 10700 continue;
226 }
227 1738 setter(output_info, id, value);
228 1738 *it = output_info;
229 1738 set_outputs(outputs_info);
230 1738 set_output_value();
231 1738 return;
232 }
233
234 // No entry yet, create one.
235 294 auto output_info = Output_filer::create_info(output);
236 294 setter(output_info, id, value);
237
238 294 outputs_info << output_info;
239 294 set_outputs(outputs_info);
240 294 set_output_value();
241
2/2
✓ Branch 2 taken 147 times.
✓ Branch 3 taken 869 times.
2032 }
242
243 static QPointF
244 265 get_pos([[maybe_unused]] OutputPtr const& output, QVariant const& val, QPointF default_value)
245 {
246 265 auto const val_map = val.toMap();
247
248 265 bool success = true;
249 1590 auto get_coordinate = [&val_map, &success](QString axis) {
250
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 530 times.
530 if (!val_map.contains(axis)) {
251 success = false;
252 return 0.;
253 }
254 bool ok;
255 530 auto const coord = val_map[axis].toDouble(&ok);
256 530 success &= ok;
257 530 return coord;
258 265 };
259
260 265 auto const x = get_coordinate(QStringLiteral("x"));
261 265 auto const y = get_coordinate(QStringLiteral("y"));
262
263
1/2
✓ Branch 0 taken 265 times.
✗ Branch 1 not taken.
265 return success ? QPointF(x, y) : default_value;
264 265 }
265
266 104 static void set_pos(QVariantMap& info, [[maybe_unused]] std::string const& id, QPointF pos)
267 {
268
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 104 times.
104 assert(id == "pos");
269
270 208 auto pos_info = [&pos]() {
271 104 QVariantMap info;
272 208 info[QStringLiteral("x")] = pos.x();
273 208 info[QStringLiteral("y")] = pos.y();
274 104 return info;
275 104 };
276
277 208 info[QStringLiteral("pos")] = pos_info();
278 104 }
279
280 static void
281 208 set_mode(QVariantMap& info, [[maybe_unused]] std::string const& id, Disman::ModePtr mode)
282 {
283
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 208 times.
208 assert(id == "mode");
284
285 416 auto size_info = [&mode]() {
286 208 QVariantMap info;
287 416 info[QStringLiteral("width")] = mode->size().width();
288 416 info[QStringLiteral("height")] = mode->size().height();
289 208 return info;
290 208 };
291
292 208 auto mode_info = [&mode, size_info]() {
293 208 QVariantMap info;
294 416 info[QStringLiteral("refresh")] = mode->refresh();
295 416 info[QStringLiteral("resolution")] = size_info();
296
297 208 return info;
298 208 };
299
300 416 info[QStringLiteral("mode")] = mode_info();
301 208 }
302
303 485 void get_replication_source(OutputPtr& output, OutputMap const& outputs) const
304 {
305 485 auto replicate_hash = get_value(output, "replicate", QString(), nullptr).toStdString();
306
307 auto replication_source_it
308 485 = std::find_if(outputs.cbegin(), outputs.cend(), [replicate_hash](auto const& out) {
309 1095 return out.second->hash() == replicate_hash;
310 });
311
312
2/2
✓ Branch 2 taken 110 times.
✓ Branch 3 taken 375 times.
485 if (replication_source_it != outputs.cend()) {
313
1/2
✓ Branch 4 taken 110 times.
✗ Branch 5 not taken.
110 if (replicate_hash == output->hash()) {
314
2/2
✓ Branch 11 taken 110 times.
✓ Branch 12 taken 110 times.
220 qCWarning(DISMAN_BACKEND) << "Control file has sets" << output
315 110 << "as its own replica. This is not allowed.";
316 } else {
317 output->set_replication_source(replication_source_it->first);
318 }
319 } else {
320 375 output->set_replication_source(0);
321 }
322 485 }
323
324 104 void set_replication_source(OutputPtr const& output, ConfigPtr const& config)
325 {
326 104 auto const source = config->output(output->replication_source());
327 208 set_value(output,
328 "replicate",
329
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 104 times.
✓ Branch 8 taken 104 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 104 times.
312 source ? QString::fromStdString(source->hash()) : QStringLiteral(""),
330 nullptr);
331 104 }
332
333 222 std::string dir_path() const
334 {
335 222 return m_dir_path + "configs/";
336 }
337
338 222 std::string file_name() const
339 {
340 222 auto file_name = m_config->hash().toStdString();
341
2/2
✓ Branch 1 taken 120 times.
✓ Branch 2 taken 102 times.
222 if (!m_suffix.empty()) {
342 120 file_name += "-" + m_suffix;
343 }
344 222 return file_name;
345 }
346
347 222 QFileInfo file_info() const
348 {
349 222 return Filer_helpers::file_info(dir_path(), file_name());
350 }
351
352 120 bool read_file()
353 {
354 120 return Filer_helpers::read_file(file_info(), m_info);
355 }
356
357 42 bool write(ConfigPtr const& config)
358 {
359 42 set_values(config);
360
361 42 bool success = true;
362
363
2/2
✓ Branch 5 taken 104 times.
✓ Branch 6 taken 42 times.
146 for (auto& output_filer : m_output_filers) {
364 104 auto const output = config->output(output_filer->output()->id());
365
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 104 times.
104 if (!output) {
366 // TODO: fallback or reverse clean up?
367 qCDebug(DISMAN_BACKEND)
368 << "Could not identify output filer" << output_filer->output()->name().c_str();
369 continue;
370 }
371
372
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 104 times.
104 if (output->retention() == Output::Retention::Individual) {
373 continue;
374 }
375 104 success &= output_filer->write_file();
376
1/2
✓ Branch 1 taken 104 times.
✗ Branch 2 not taken.
104 }
377
378 42 success &= Filer_helpers::write_file(m_info, file_info());
379 42 return success;
380 }
381
382 485 static Output::Retention convert_int_to_retention(int val)
383 {
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 485 times.
485 if (val == static_cast<int>(Output::Retention::Global)) {
385 return Output::Retention::Global;
386 }
387
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 485 times.
485 if (val == static_cast<int>(Output::Retention::Individual)) {
388 return Output::Retention::Individual;
389 }
390 485 return Output::Retention::Undefined;
391 }
392
393 201 ConfigPtr config() const
394 {
395 201 return m_config;
396 }
397
398 private:
399 2956 QVariantList get_outputs_info() const
400 {
401 5912 return m_info[QStringLiteral("outputs")].toList();
402 }
403
404 1016 void set_outputs(QVariantList outputsInfo)
405 {
406 2032 m_info[QStringLiteral("outputs")] = outputsInfo;
407 1016 }
408
409 21923 bool is_info_for_output(QVariantMap const& info, OutputPtr const& output) const
410 {
411 21923 auto const output_id_info = info[QStringLiteral("id")].toString().toStdString();
412
413
2/2
✓ Branch 1 taken 18320 times.
✓ Branch 2 taken 3603 times.
21923 if (!output_id_info.size()) {
414 18320 return false;
415 }
416
2/2
✓ Branch 4 taken 1674 times.
✓ Branch 5 taken 1929 times.
3603 if (output->hash() != output_id_info) {
417 1674 return false;
418 }
419
420 1929 return true;
421 21923 }
422
423 589 Output_filer* get_output_filer(OutputPtr const& output) const
424 {
425
1/2
✓ Branch 5 taken 1024 times.
✗ Branch 6 not taken.
1024 for (auto& filer : m_output_filers) {
426
12/14
✓ Branch 5 taken 640 times.
✓ Branch 6 taken 384 times.
✓ Branch 12 taken 589 times.
✓ Branch 13 taken 51 times.
✓ Branch 14 taken 640 times.
✓ Branch 15 taken 384 times.
✓ Branch 17 taken 640 times.
✓ Branch 18 taken 384 times.
✓ Branch 20 taken 1024 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 1024 times.
✗ Branch 24 not taken.
✓ Branch 26 taken 589 times.
✓ Branch 27 taken 435 times.
1024 if (filer->hash() == output->hash() && filer->name() == output->name()) {
427 589 return filer.get();
428 }
429 }
430 return nullptr;
431 }
432
433 ConfigPtr m_config;
434 Filer_controller* m_controller;
435
436 std::vector<std::unique_ptr<Output_filer>> m_output_filers;
437
438 std::string m_dir_path;
439 std::string m_suffix;
440
441 QVariantMap m_info;
442 bool m_read_success{false};
443 };
444
445 }
446