1 /*
2 SPDX-FileCopyrightText: 2020 Roman Gilg <subdiff@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
5 */
6 #include "backend_impl.h"
7
8 #include "device.h"
9 #include "filer_controller.h"
10 #include "generator.h"
11 #include "logging.h"
12 #include "output.h"
13
14 #include <QRectF>
15
16 namespace Disman
17 {
18
BackendImpl()19 BackendImpl::BackendImpl()
20 : Backend()
21 , m_device{new Device}
22 , m_filer_controller{new Filer_controller(m_device.get())}
23 {
24 connect(m_device.get(), &Device::lid_open_changed, this, &BackendImpl::load_lid_config);
25 }
26
27 BackendImpl::~BackendImpl() = default;
28
init(QVariantMap const & arguments)29 void BackendImpl::init([[maybe_unused]] QVariantMap const& arguments)
30 {
31 // noop, maybe overridden in individual backends.
32 }
33
config() const34 Disman::ConfigPtr BackendImpl::config() const
35 {
36 m_config_initialized = true;
37
38 auto config = config_impl();
39
40 if (config->cause() == Config::Cause::unknown && m_config) {
41 config->set_cause(m_config->cause());
42 }
43
44 return config;
45 }
46
config_impl() const47 Disman::ConfigPtr BackendImpl::config_impl() const
48 {
49 auto config = std::make_shared<Config>();
50
51 // We update from the windowing system first so the controller knows about the current
52 // configuration and then update one more time so the windowing system can override values
53 // it provides itself.
54 update_config(config);
55 m_filer_controller->read(config);
56 update_config(config);
57
58 return config;
59 }
60
set_config(Disman::ConfigPtr const & config)61 void BackendImpl::set_config(Disman::ConfigPtr const& config)
62 {
63 if (!config || config->compare(m_config)) {
64 // No change by new config. Do nothing.
65 return;
66 }
67
68 if (!set_config_impl(config)) {
69 // No change to the system but other changes that need to be synced with other Disman
70 // clients so emit a config_changed signal directly.
71 m_config = config;
72 Q_EMIT config_changed(config);
73 }
74 }
75
set_config_impl(Disman::ConfigPtr const & config)76 bool BackendImpl::set_config_impl(Disman::ConfigPtr const& config)
77 {
78 if (QLoggingCategory category("disman.backend"); category.isEnabled(QtDebugMsg)) {
79 qCDebug(DISMAN_BACKEND) << "About to set config."
80 << "\nPrevious config:" << this->config()
81 << "\nNew config:" << config;
82 }
83
84 m_filer_controller->write(config);
85
86 if (config->supported_features().testFlag(Config::Feature::OutputReplication)) {
87 for (auto const& [key, output] : config->outputs()) {
88 if (auto source_id = output->replication_source()) {
89 auto source = config->output(source_id);
90 output->set_position(source->position());
91 output->force_geometry(source->geometry());
92 }
93 }
94 }
95
96 return set_config_system(config);
97 }
98
handle_config_change()99 bool BackendImpl::handle_config_change()
100 {
101 // We need the config with its own cause, so we call config_impl here.
102 auto cfg = config_impl();
103
104 if (!m_config || m_config->hash() != cfg->hash()) {
105 qCDebug(DISMAN_BACKEND) << "Config with new output pattern received:" << cfg;
106
107 if (cfg->cause() == Config::Cause::unknown) {
108 qCDebug(DISMAN_BACKEND)
109 << "Config received that is unknown. Creating an optimized config now.";
110 Generator generator(cfg);
111 generator.optimize();
112 cfg = generator.config();
113 } else {
114 // We set the windowing system to our saved values. They were overriden before so
115 // re-read them.
116 m_filer_controller->read(cfg);
117 }
118
119 m_config = cfg;
120
121 if (set_config_impl(cfg)) {
122 qCDebug(DISMAN_BACKEND) << "Config for new output pattern sent.";
123 return false;
124 }
125 }
126
127 Q_EMIT config_changed(cfg);
128 return true;
129 }
130
load_lid_config()131 void BackendImpl::load_lid_config()
132 {
133 if (!m_config_initialized) {
134 qCWarning(DISMAN_BACKEND) << "Lid open state changed but first config has not yet been "
135 "initialized. Doing nothing.";
136 return;
137 }
138
139 auto cfg = config();
140 if (cfg->outputs().size() == 1) {
141 // Open-lid configuration is only relevant with more than one output.
142 return;
143 }
144
145 if (m_device->lid_open()) {
146 // The lid has been opnened. Try to load the open-lid file.
147 if (!m_filer_controller->load_lid_file(cfg)) {
148 qCWarning(DISMAN_BACKEND)
149 << "Loading open-lid file failed. Generating an optimal config instead.";
150 return;
151 }
152 qCDebug(DISMAN_BACKEND) << "Loaded lid-open file on lid being opened.";
153 } else {
154 // The lid has been closed, we write the current config as open-lid-config and then generate
155 // an optimized one with the embedded display disabled that gets applied.
156 Generator generator(cfg);
157 qCDebug(DISMAN_BACKEND) << "Lid closed, trying to disable embedded display.";
158
159 if (!generator.disable_embedded()) {
160 // Alternative config could not be generated.
161 qCWarning(DISMAN_BACKEND) << "Embedded display could not be disabled.";
162 return;
163 }
164 if (!m_filer_controller->save_lid_file(cfg)) {
165 qCWarning(DISMAN_BACKEND) << "Failed to save open-lid file.";
166 return;
167 }
168 cfg = generator.config();
169 }
170
171 set_config_impl(cfg);
172 }
173
174 }
175