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 |