GCC Code Coverage Report


Directory: ./
File: server/subcompositor.cpp
Date: 2024-01-22 17:25:27
Exec Total Coverage
Lines: 164 175 93.7%
Branches: 57 82 69.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) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
20 #include "subcompositor.h"
21
22 #include "buffer.h"
23 #include "display.h"
24 #include "subsurface_p.h"
25 #include "surface_p.h"
26
27 #include "wayland/global.h"
28 #include "wayland/resource.h"
29
30 #include <cassert>
31 #include <wayland-server.h>
32
33 namespace Wrapland::Server
34 {
35
36 constexpr uint32_t SubcompositorVersion = 1;
37 using SubcompositorGlobal = Wayland::Global<Subcompositor, SubcompositorVersion>;
38 using SubcompositorBind = Wayland::Bind<SubcompositorGlobal>;
39
40 class Subcompositor::Private : public SubcompositorGlobal
41 {
42 public:
43 Private(Subcompositor* q_ptr, Display* display);
44
45 private:
46 static void destroyCallback(SubcompositorBind* bind);
47 static void subsurfaceCallback(SubcompositorBind* bind,
48 uint32_t id,
49 wl_resource* wlSurface,
50 wl_resource* wlParent);
51
52 static const struct wl_subcompositor_interface s_interface;
53 25 };
54 25
55 25 const struct wl_subcompositor_interface Subcompositor::Private::s_interface = {
56 25 resourceDestroyCallback,
57 cb<subsurfaceCallback>,
58 };
59
60 105 Subcompositor::Private::Private(Subcompositor* q_ptr, Display* display)
61 105 : Wayland::Global<Subcompositor>(q_ptr, display, &wl_subcompositor_interface, &s_interface)
62 105 {
63 105 }
64
65 25 void Subcompositor::Private::subsurfaceCallback(SubcompositorBind* bind,
66 uint32_t id,
67 wl_resource* wlSurface,
68 wl_resource* wlParent)
69 {
70 25 auto priv = bind->global()->handle->d_ptr.get();
71
72 25 auto surface = Wayland::Resource<Surface>::get_handle(wlSurface);
73 25 auto parentSurface = Wayland::Resource<Surface>::get_handle(wlParent);
74
75
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 25 times.
25 if (!surface || !parentSurface) {
76 bind->post_error(WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
77 "Surface or parent surface not found.");
78 return;
79 }
80
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 if (surface == parentSurface) {
81 bind->post_error(WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
82 "Cannot subcomposite to same surface.");
83 return;
84 }
85
86 // TODO(romangg): add check that surface is not already used in an interface (e.g. Shell)
87 // TODO(romangg): add check that parentSurface is not a child of surface
88 // TODO(romangg): handle error
89
90 25 auto subsurface
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 = new Subsurface(bind->client->handle, bind->version, id, surface, parentSurface);
92
93 25 Q_EMIT priv->handle->subsurfaceCreated(subsurface);
94 25 }
95
96 105 Subcompositor::Subcompositor(Display* display)
97
2/4
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
105 : d_ptr(new Private(this, display))
98 105 {
99
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 d_ptr->create();
100 105 }
101
102 210 Subcompositor::~Subcompositor() = default;
103
104
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
50 Subsurface::Private::Private(Client* client,
105 uint32_t version,
106 uint32_t id,
107 Surface* surface,
108 Surface* parent,
109 Subsurface* q_ptr)
110 50 : Wayland::Resource<Subsurface>(client,
111 25 version,
112 25 id,
113 &wl_subsurface_interface,
114 &s_interface,
115 25 q_ptr)
116 25 , surface{surface}
117 25 , parent{parent}
118 25 {
119 25 surface->d_ptr->subsurface = q_ptr;
120
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
48 QObject::connect(surface, &Surface::resourceDestroyed, q_ptr, [this, q_ptr] {
122 // From spec: "If the wl_surface associated with the wl_subsurface is destroyed,
123 // the wl_subsurface object becomes inert. Note, that destroying either object
124 // takes effect immediately."
125
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 12 times.
23 if (this->parent) {
126 11 this->parent->d_ptr->removeChild(handle);
127 11 this->parent = nullptr;
128 11 }
129 23 this->surface = nullptr;
130
131 // TODO(romangg): do not use that but an extra signal or automatic with surface.
132 23 Q_EMIT q_ptr->resourceDestroyed();
133 23 });
134 25 }
135
136 25 void Subsurface::Private::init()
137 {
138 25 parent->d_ptr->addChild(handle);
139 25 }
140
141 const struct wl_subsurface_interface Subsurface::Private::s_interface = {
142 destroyCallback,
143 setPositionCallback,
144 placeAboveCallback,
145 placeBelowCallback,
146 setSyncCallback,
147 setDeSyncCallback,
148 };
149
150 67 void Subsurface::Private::applyCached(bool force)
151 {
152
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 assert(surface);
153
154
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 4 times.
67 if (scheduledPosChange) {
155 4 scheduledPosChange = false;
156 4 pos = scheduledPos;
157 4 scheduledPos = QPoint();
158 4 Q_EMIT handle->positionChanged(pos);
159 4 }
160
161
4/4
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 47 times.
✓ Branch 3 taken 15 times.
67 if (force || handle->isSynchronized()) {
162 52 surface->d_ptr->updateCurrentState(cached, true);
163 52 Q_EMIT surface->committed();
164 52 } else {
165
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 15 times.
22 for (auto child : surface->state().children) {
166 // Set at least their positions.
167 7 child->d_ptr->applyCached(false);
168 }
169 }
170 67 }
171
172 19 void Subsurface::Private::commit()
173 {
174
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 assert(surface);
175
176
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 10 times.
19 if (handle->isSynchronized()) {
177 // Sync mode. We cache the pending state and wait for the parent surface to commit.
178 9 cached = std::move(surface->d_ptr->pending);
179 9 surface->d_ptr->pending = SurfaceState();
180 9 surface->d_ptr->pending.pub.children = cached.pub.children;
181
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 if (cached.pub.buffer) {
182 3 cached.pub.buffer->setCommitted();
183 3 }
184 9 return;
185 }
186
187 // Desync mode. We commit the surface directly.
188 10 surface->d_ptr->updateCurrentState(false);
189 10 Q_EMIT surface->committed();
190 19 }
191
192 5 void Subsurface::Private::setPositionCallback([[maybe_unused]] wl_client* wlClient,
193 wl_resource* wlResource,
194 int32_t pos_x,
195 int32_t pos_y)
196 {
197 5 auto priv = get_handle(wlResource)->d_ptr;
198
199 // TODO(unknown author): is this a fixed position?
200 5 priv->setPosition(QPoint(pos_x, pos_y));
201 5 }
202
203 5 void Subsurface::Private::setPosition(QPoint const& pos)
204 {
205
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (this->pos == pos) {
206 return;
207 }
208 5 scheduledPos = pos;
209 5 scheduledPosChange = true;
210 5 }
211
212 5 void Subsurface::Private::placeAboveCallback([[maybe_unused]] wl_client* wlClient,
213 wl_resource* wlResource,
214 wl_resource* wlSibling)
215 {
216 5 auto priv = get_handle(wlResource)->d_ptr;
217 5 auto sibling = Wayland::Resource<Surface>::get_handle(wlSibling);
218 5 priv->placeAbove(sibling);
219 5 }
220
221 5 void Subsurface::Private::placeAbove(Surface* sibling)
222 {
223
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (!parent) {
224 // TODO(unknown author): raise error
225 return;
226 }
227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (!parent->d_ptr->raiseChild(handle, sibling)) {
228 postError(WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Incorrect sibling");
229 }
230 5 }
231
232 5 void Subsurface::Private::placeBelowCallback([[maybe_unused]] wl_client* wlClient,
233 wl_resource* wlResource,
234 wl_resource* wlSibling)
235 {
236 5 auto priv = get_handle(wlResource)->d_ptr;
237 5 auto sibling = Wayland::Resource<Surface>::get_handle(wlSibling);
238 5 priv->placeBelow(sibling);
239 5 }
240
241 5 void Subsurface::Private::placeBelow(Surface* sibling)
242 {
243
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (!parent) {
244 // TODO(unknown author): raise error
245 return;
246 }
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (!parent->d_ptr->lowerChild(handle, sibling)) {
248 postError(WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Incorrect sibling");
249 }
250 5 }
251
252 1 void Subsurface::Private::setSyncCallback([[maybe_unused]] wl_client* wlClient,
253 wl_resource* wlResource)
254 {
255 1 auto priv = get_handle(wlResource)->d_ptr;
256 1 priv->setMode(Mode::Synchronized);
257 1 }
258
259 11 void Subsurface::Private::setDeSyncCallback([[maybe_unused]] wl_client* wlClient,
260 wl_resource* wlResource)
261 {
262 11 auto priv = get_handle(wlResource)->d_ptr;
263 11 priv->setMode(Mode::Desynchronized);
264 11 }
265
266 12 void Subsurface::Private::setMode(Mode mode)
267 {
268
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (this->mode == mode) {
269 return;
270 }
271
272 12 this->mode = mode;
273 12 Q_EMIT handle->modeChanged(mode);
274
275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
17 if (mode == Mode::Desynchronized
276
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 6 times.
12 && (!parent->subsurface() || !parent->subsurface()->isSynchronized())) {
277 // Parent subsurface list must be updated immediately.
278 11 auto& cc = parent->d_ptr->current.pub.children;
279 11 auto subsurface = handle;
280
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 7 times.
11 if (std::find(cc.cbegin(), cc.cend(), subsurface) == cc.cend()) {
281 7 cc.push_back(subsurface);
282 7 }
283 // No longer synchronized, this is like calling commit.
284
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 assert(surface);
285 11 surface->d_ptr->updateCurrentState(cached, false);
286 11 Q_EMIT surface->committed();
287 11 }
288 12 }
289
290 25 Subsurface::Subsurface(Client* client,
291 uint32_t version,
292 uint32_t id,
293 Surface* surface,
294 Surface* parent)
295 25 : QObject(nullptr)
296
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
25 , d_ptr(new Private(client, version, id, surface, parent, this))
297 25 {
298
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 d_ptr->init();
299 25 }
300
301 50 Subsurface::~Subsurface()
302 50 {
303
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 23 times.
25 if (d_ptr->surface) {
304 2 d_ptr->surface->d_ptr->subsurface = nullptr;
305 2 }
306 25 d_ptr->surface = nullptr;
307
308
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 2 times.
25 if (d_ptr->parent) {
309
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 d_ptr->parent->d_ptr->removeChild(this);
310 2 }
311 25 d_ptr->parent = nullptr;
312 50 }
313
314 158 QPoint Subsurface::position() const
315 {
316 158 return d_ptr->pos;
317 }
318
319 236 Surface* Subsurface::surface() const
320 {
321 236 return d_ptr->surface;
322 }
323
324 567 Surface* Subsurface::parentSurface() const
325 {
326 567 return d_ptr->parent;
327 }
328
329 3 Subsurface::Mode Subsurface::mode() const
330 {
331 3 return d_ptr->mode;
332 }
333
334 111 bool Subsurface::isSynchronized() const
335 {
336
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 57 times.
111 if (d_ptr->mode == Mode::Synchronized) {
337 57 return true;
338 }
339
340
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 1 times.
54 if (!d_ptr->parent) {
341 // That shouldn't happen, but let's assume false.
342 1 return false;
343 }
344
345
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 23 times.
53 if (auto parentSub = d_ptr->parent->subsurface()) {
346 // Follow parent's mode.
347 23 return parentSub->isSynchronized();
348 }
349
350 // Parent is no subsurface, thus parent is in desync mode and this surface is in desync mode.
351 30 return false;
352 111 }
353
354 10 Surface* Subsurface::mainSurface() const
355 {
356
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (!d_ptr->parent) {
357 return nullptr;
358 }
359
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if (d_ptr->parent->d_ptr->subsurface) {
360 5 return d_ptr->parent->d_ptr->subsurface->mainSurface();
361 }
362 5 return d_ptr->parent;
363 10 }
364
365 }
366