GCC Code Coverage Report


Directory: ./
File: backends/wayland/wayland_interface.cpp
Date: 2023-04-20 22:59:23
Exec Total Coverage
Lines: 139 176 79.0%
Branches: 40 62 64.5%

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 #include "wayland_interface.h"
19
20 #include "waylandbackend.h"
21 #include "waylandoutput.h"
22 #include "waylandscreen.h"
23
24 #include <configmonitor.h>
25 #include <mode.h>
26
27 #include "wayland_logging.h"
28
29 #include <QThread>
30 #include <QTimer>
31 #include <Wrapland/Client/connection_thread.h>
32 #include <Wrapland/Client/event_queue.h>
33 #include <Wrapland/Client/registry.h>
34 #include <Wrapland/Client/wlr_output_manager_v1.h>
35
36 using namespace Disman;
37
38 21 WaylandInterface::WaylandInterface(QThread* thread)
39 21 : m_dismanConfig{new Config}
40 {
41 21 m_connection = new Wrapland::Client::ConnectionThread;
42
43 21 connect(
44 21 m_connection,
45 &Wrapland::Client::ConnectionThread::establishedChanged,
46 this,
47 42 [this](bool established) {
48
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 if (established) {
49 21 setupRegistry();
50 } else {
51 handleDisconnect();
52 }
53 21 },
54 Qt::QueuedConnection);
55
56 21 connect(m_connection, &Wrapland::Client::ConnectionThread::failed, this, [this] {
57 qCWarning(DISMAN_WAYLAND) << "Failed to connect to Wayland server at socket:"
58 << m_connection->socketName();
59 Q_EMIT connectionFailed(m_connection->socketName());
60 });
61
62 21 thread->start();
63 21 m_connection->moveToThread(thread);
64 21 m_connection->establishConnection();
65 21 }
66
67 void WaylandInterface::handleDisconnect()
68 {
69 for (auto& [key, out] : m_outputMap) {
70 delete out;
71 }
72 m_outputMap.clear();
73
74 // Clean up
75 if (m_queue) {
76 delete m_queue;
77 m_queue = nullptr;
78 }
79
80 m_connection->deleteLater();
81 m_connection = nullptr;
82
83 qCWarning(DISMAN_WAYLAND) << "Wayland disconnected, cleaning up.";
84 Q_EMIT config_changed();
85 }
86
87 21 void WaylandInterface::setupRegistry()
88 {
89 21 m_queue = new Wrapland::Client::EventQueue(this);
90 21 m_queue->setup(m_connection);
91
92 21 m_registry = new Wrapland::Client::Registry(this);
93
94 21 connect(m_registry,
95 &Wrapland::Client::Registry::wlrOutputManagerV1Announced,
96 this,
97 84 [this](quint32 name, quint32 version) {
98 21 m_outputManager = m_registry->createWlrOutputManagerV1(name, version, m_registry);
99
100 21 connect(m_outputManager,
101 &Wrapland::Client::WlrOutputManagerV1::head,
102 this,
103 &WaylandInterface::add_output);
104
105 21 connect(m_outputManager,
106 &Wrapland::Client::WlrOutputManagerV1::done,
107 this,
108 &WaylandInterface::handle_wlr_manager_done);
109 21 m_outputManager->setEventQueue(m_queue);
110 21 });
111
112 21 m_registry->setEventQueue(m_queue);
113 21 m_registry->create(m_connection);
114 21 m_registry->setup();
115 21 }
116
117 53 void WaylandInterface::add_output(Wrapland::Client::WlrOutputHeadV1* head)
118 {
119 53 auto output = new WaylandOutput(++m_outputId, *head);
120 53 m_outputMap.insert({output->id, output});
121 53 update.outputs = true;
122 53 update.added.push_back(output);
123
124 54 connect(output, &WaylandOutput::removed, this, [this, output] { removeOutput(output); });
125 53 }
126
127 1 void WaylandInterface::removeOutput(WaylandOutput* output)
128 {
129 1 update.outputs = true;
130 1 m_outputMap.erase(output->id);
131
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 delete output;
132 1 }
133
134 159 void WaylandInterface::handle_wlr_manager_done()
135 {
136
4/4
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 53 times.
✓ Branch 2 taken 53 times.
✓ Branch 3 taken 53 times.
159 if (adaptive_sync_test.output && !adaptive_sync_test.reverted) {
137 // Rollback test change.
138 53 adaptive_sync_test.reverted = true;
139 53 test_toggle_adaptive_sync(adaptive_sync_test.output);
140 53 return;
141 }
142
143 106 adaptive_sync_test = {};
144
145
2/2
✓ Branch 1 taken 53 times.
✓ Branch 2 taken 53 times.
106 if (!update.added.empty()) {
146 53 auto output = update.added.back();
147 53 update.added.pop_back();
148
149 53 test_toggle_adaptive_sync(output);
150 53 return;
151 }
152
153 53 is_initialized = true;
154 53 update.pending = false;
155
156
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 30 times.
53 if (update.outputs) {
157 23 update.outputs = false;
158 23 Q_EMIT outputsChanged();
159 }
160
161 53 Q_EMIT config_changed();
162 53 tryPendingConfig();
163 }
164
165 386 void WaylandInterface::updateConfig(Disman::ConfigPtr& config)
166 {
167 772 config->set_supported_features(Config::Feature::Writable | Config::Feature::PerOutputScaling
168 386 | Config::Feature::AdaptiveSync);
169 386 config->set_valid(m_connection->display());
170
171 // Removing removed outputs
172
2/2
✓ Branch 9 taken 361 times.
✓ Branch 10 taken 386 times.
747 for (auto const& [key, output] : config->outputs()) {
173
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 361 times.
361 if (m_outputMap.find(output->id()) == m_outputMap.end()) {
174 config->remove_output(output->id());
175 }
176 386 }
177
178 // Add Disman::Outputs that aren't in the list yet.
179 386 auto dismanOutputs = config->outputs();
180
181
2/2
✓ Branch 7 taken 996 times.
✓ Branch 8 taken 386 times.
1382 for (auto& [key, output] : m_outputMap) {
182 996 Disman::OutputPtr dismanOutput;
183
184 996 auto it = dismanOutputs.find(output->id);
185
2/2
✓ Branch 2 taken 635 times.
✓ Branch 3 taken 361 times.
996 if (it == dismanOutputs.end()) {
186 635 dismanOutput = output->toDismanOutput();
187 635 dismanOutputs.insert({dismanOutput->id(), dismanOutput});
188 } else {
189 361 dismanOutput = it->second;
190 }
191
192 996 output->updateDismanOutput(dismanOutput);
193 996 }
194 386 config->set_outputs(dismanOutputs);
195 386 }
196
197 44 std::map<int, WaylandOutput*> WaylandInterface::outputMap() const
198 {
199 44 std::map<int, WaylandOutput*> ret;
200
201 44 auto it = m_outputMap.cbegin();
202
2/2
✓ Branch 2 taken 57 times.
✓ Branch 3 taken 44 times.
101 while (it != m_outputMap.cend()) {
203 57 ret[it->first] = it->second;
204 57 ++it;
205 }
206 44 return ret;
207 }
208
209 53 void WaylandInterface::tryPendingConfig()
210 {
211
2/2
✓ Branch 1 taken 52 times.
✓ Branch 2 taken 1 times.
53 if (!m_dismanPendingConfig) {
212 52 return;
213 }
214 1 applyConfig(m_dismanPendingConfig);
215 1 m_dismanPendingConfig = nullptr;
216 }
217
218 36 bool WaylandInterface::applyConfig(const Disman::ConfigPtr& newConfig)
219 {
220 36 return apply_config_impl(newConfig, false);
221 }
222
223 36 bool WaylandInterface::apply_config_impl(const Disman::ConfigPtr& newConfig, bool force)
224 {
225 using namespace Wrapland::Client;
226
227
2/2
✓ Branch 7 taken 36 times.
✓ Branch 8 taken 36 times.
72 qCDebug(DISMAN_WAYLAND) << "Applying config in wlroots backend.";
228
229 // Create a new configuration object
230 36 auto* wlConfig = m_outputManager->createConfiguration();
231 36 wlConfig->setEventQueue(m_queue);
232
233 36 bool changed = false;
234
235
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 35 times.
36 if (update.pending) {
236
2/2
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
2 qCDebug(DISMAN_WAYLAND)
237 1 << "Last apply still pending, remembering new changes and will apply afterwards.";
238 1 m_dismanPendingConfig = newConfig;
239 1 return true;
240 }
241
242
2/2
✓ Branch 8 taken 92 times.
✓ Branch 9 taken 35 times.
127 for (auto const& [key, output] : newConfig->outputs()) {
243 92 changed |= m_outputMap[output->id()]->setWlConfig(wlConfig, output);
244 35 }
245
246
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
35 if (!changed && !force) {
247
2/2
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 5 times.
10 qCDebug(DISMAN_WAYLAND)
248 5 << "New config equals compositor's current data. Aborting apply request.";
249 5 return false;
250 }
251
252 // We now block changes in order to compress events while the compositor is doing its thing
253 // once it's done or failed, we'll trigger config_changed() only once, and not per individual
254 // property change.
255 30 connect(wlConfig, &WlrOutputConfigurationV1::succeeded, this, [this, wlConfig] {
256
2/2
✓ Branch 7 taken 30 times.
✓ Branch 8 taken 30 times.
60 qCDebug(DISMAN_WAYLAND) << "Config applied successfully.";
257 30 wlConfig->deleteLater();
258 30 });
259 30 connect(wlConfig, &WlrOutputConfigurationV1::failed, this, [this, wlConfig] {
260 qCWarning(DISMAN_WAYLAND) << "Applying config failed.";
261 wlConfig->deleteLater();
262 update.pending = false;
263 Q_EMIT config_changed();
264 tryPendingConfig();
265 });
266 30 connect(wlConfig, &WlrOutputConfigurationV1::cancelled, this, [this, newConfig, wlConfig] {
267 // Can occur if serials were not in sync because of some simultaneous change server-side.
268 // We try to apply the current config again as we should have received a done event now.
269 wlConfig->deleteLater();
270 update.pending = false;
271 auto cfg = m_dismanPendingConfig ? m_dismanPendingConfig : newConfig;
272 m_dismanPendingConfig = nullptr;
273 apply_config_impl(cfg, true);
274 });
275
276 30 update.pending = true;
277 30 wlConfig->apply();
278
2/2
✓ Branch 7 taken 30 times.
✓ Branch 8 taken 30 times.
60 qCDebug(DISMAN_WAYLAND) << "Config sent to compositor.";
279 30 return true;
280 }
281
282 106 void WaylandInterface::test_toggle_adaptive_sync(WaylandOutput* output)
283 {
284 106 auto& test = adaptive_sync_test;
285 106 test.output_id = output->id;
286 106 test.output = output;
287
288 106 auto config = std::make_shared<Config>();
289 106 updateConfig(config);
290
291 // Try to toggle adaptive sync. Ensure that the output is enabled for that.
292 106 config->output(output->id)->set_enabled(true);
293 106 config->output(output->id)->set_adaptive_sync(!output->head.adaptive_sync());
294
295 106 test.config.reset(m_outputManager->createConfiguration());
296 106 test.config->setEventQueue(m_queue);
297
298
2/2
✓ Branch 8 taken 274 times.
✓ Branch 9 taken 106 times.
380 for (auto const& [key, output] : config->outputs()) {
299 274 m_outputMap[output->id()]->setWlConfig(test.config.get(), output);
300 106 }
301
302 106 connect(test.config.get(),
303 &Wrapland::Client::WlrOutputConfigurationV1::succeeded,
304 this,
305 106 [this] { adaptive_sync_test.output->supports_adapt_sync_toggle = true; });
306 106 connect(test.config.get(), &Wrapland::Client::WlrOutputConfigurationV1::failed, this, [this] {
307 adaptive_sync_test.output->supports_adapt_sync_toggle = false;
308 handle_wlr_manager_done();
309 });
310 106 connect(
311 106 test.config.get(), &Wrapland::Client::WlrOutputConfigurationV1::cancelled, this, [this] {
312 // Try again if the output still exists.
313 if (m_outputMap.contains(adaptive_sync_test.output_id)) {
314 test_toggle_adaptive_sync(adaptive_sync_test.output);
315 } else {
316 adaptive_sync_test = {};
317 handle_wlr_manager_done();
318 }
319 });
320
321 106 test.config->apply();
322 106 }
323