1/* swc: libswc/subsurface.c
2 *
3 * Copyright (c) 2015-2019 Michael Forney
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include "subsurface.h"
25#include "compositor.h"
26#include "surface.h"
27#include "util.h"
28#include "view.h"
29
30#include <stdlib.h>
31#include <wayland-server.h>
32
33bool
34subsurface_is_synchronized(const struct subsurface *subsurface)
35{
36 while (subsurface) {
37 if (subsurface->sync) {
38 return true;
39 }
40 if (!subsurface->parent) {
41 return false;
42 }
43 subsurface = subsurface->parent->subsurface;
44 }
45
46 return false;
47}
48
49static void
50subsurface_update_position(struct subsurface *subsurface)
51{
52 struct compositor_view *parent_view;
53 struct compositor_view *view;
54
55 if (!subsurface->surface || !subsurface->parent) {
56 return;
57 }
58
59 view = compositor_view(subsurface->surface->view);
60 parent_view = compositor_view(subsurface->parent->view);
61 if (!view || !parent_view) {
62 return;
63 }
64
65 view_move(&view->base,
66 parent_view->base.geometry.x + subsurface->x -
67 parent_view->buffer_offset_x,
68 parent_view->base.geometry.y + subsurface->y -
69 parent_view->buffer_offset_y);
70}
71
72static void
73list_remove_if_linked(struct wl_list *link)
74{
75 if (!wl_list_empty(link)) {
76 wl_list_remove(link);
77 wl_list_init(link);
78 }
79}
80
81void
82subsurface_update_visibility(struct subsurface *subsurface)
83{
84 struct compositor_view *view;
85 struct compositor_view *parent_view;
86
87 if (!subsurface || !subsurface->surface || !subsurface->parent) {
88 return;
89 }
90
91 view = compositor_view(subsurface->surface->view);
92 parent_view = compositor_view(subsurface->parent->view);
93 if (!view || !parent_view) {
94 return;
95 }
96
97 if (subsurface->added && parent_view->visible &&
98 subsurface->surface->state.buffer) {
99 compositor_view_show(view);
100 } else {
101 compositor_view_hide(view);
102 }
103}
104
105static void
106handle_parent_view_change(struct view_handler *handler)
107{
108 struct subsurface *subsurface =
109 wl_container_of(handler, subsurface, parent_view_handler);
110 subsurface_update_position(subsurface);
111}
112
113static void
114handle_parent_view_resize(struct view_handler *handler, uint32_t old_width,
115 uint32_t old_height)
116{
117 (void)old_width;
118 (void)old_height;
119 handle_parent_view_change(handler);
120}
121
122static const struct view_handler_impl parent_view_handler_impl = {
123 .attach = handle_parent_view_change,
124 .move = handle_parent_view_change,
125 .resize = handle_parent_view_resize,
126};
127
128static struct subsurface *
129subsurface_find_sibling(struct subsurface *subsurface, struct surface *surface)
130{
131 struct surface *parent = subsurface->parent;
132 struct subsurface *sibling;
133
134 if (!parent) {
135 return NULL;
136 }
137
138 wl_list_for_each(sibling, &parent->pending.state.subsurfaces_below,
139 pending_link)
140 {
141 if (sibling->surface == surface && sibling != subsurface) {
142 return sibling;
143 }
144 }
145
146 wl_list_for_each(sibling, &parent->pending.state.subsurfaces_above,
147 pending_link)
148 {
149 if (sibling->surface == surface && sibling != subsurface) {
150 return sibling;
151 }
152 }
153
154 return NULL;
155}
156
157static bool
158is_valid_sibling(struct subsurface *subsurface, struct surface *sibling_surface,
159 struct subsurface **sibling_subsurface)
160{
161 struct subsurface *sibling;
162
163 if (!subsurface->parent || !sibling_surface ||
164 sibling_surface == subsurface->surface) {
165 return false;
166 }
167
168 if (sibling_surface == subsurface->parent) {
169 *sibling_subsurface = NULL;
170 return true;
171 }
172
173 sibling = subsurface_find_sibling(subsurface, sibling_surface);
174 if (!sibling) {
175 return false;
176 }
177
178 *sibling_subsurface = sibling;
179 return true;
180}
181
182static void
183handle_surface_destroy(struct wl_listener *listener, void *data)
184{
185 (void)data;
186 struct subsurface *subsurface =
187 wl_container_of(listener, subsurface, surface_destroy_listener);
188 if (subsurface->resource) {
189 wl_resource_destroy(subsurface->resource);
190 }
191}
192
193static void
194handle_parent_destroy(struct wl_listener *listener, void *data)
195{
196 (void)data;
197 struct subsurface *subsurface =
198 wl_container_of(listener, subsurface, parent_destroy_listener);
199 struct compositor_view *view = NULL;
200
201 if (subsurface->surface && subsurface->surface->view) {
202 view = compositor_view(subsurface->surface->view);
203 }
204
205 if (view) {
206 view->parent = NULL;
207 compositor_view_hide(view);
208 }
209
210 if (!wl_list_empty(&subsurface->parent_view_handler.link)) {
211 wl_list_remove(&subsurface->parent_view_handler.link);
212 wl_list_init(&subsurface->parent_view_handler.link);
213 }
214
215 if (!wl_list_empty(&subsurface->link)) {
216 wl_list_remove(&subsurface->link);
217 wl_list_init(&subsurface->link);
218 }
219
220 list_remove_if_linked(&subsurface->pending_link);
221 list_remove_if_linked(&subsurface->current_link);
222
223 subsurface->parent = NULL;
224}
225
226static void
227set_position(struct wl_client *client, struct wl_resource *resource, int32_t x,
228 int32_t y)
229{
230 (void)client;
231 struct subsurface *subsurface = wl_resource_get_user_data(resource);
232
233 subsurface->pending_x = x;
234 subsurface->pending_y = y;
235 subsurface->pending_position = true;
236}
237
238static void
239place_above(struct wl_client *client, struct wl_resource *resource,
240 struct wl_resource *sibling_resource)
241{
242 (void)client;
243 struct subsurface *subsurface = wl_resource_get_user_data(resource);
244 struct surface *sibling_surface =
245 wl_resource_get_user_data(sibling_resource);
246 struct subsurface *sibling_subsurface;
247
248 if (!is_valid_sibling(subsurface, sibling_surface, &sibling_subsurface)) {
249 wl_resource_post_error(resource, WL_SUBSURFACE_ERROR_BAD_SURFACE,
250 "invalid sibling surface");
251 return;
252 }
253
254 if (!sibling_subsurface) {
255 wl_list_remove(&subsurface->pending_link);
256 wl_list_insert(&subsurface->parent->pending.state.subsurfaces_above,
257 &subsurface->pending_link);
258 } else {
259 wl_list_remove(&subsurface->pending_link);
260 wl_list_insert(&sibling_subsurface->pending_link,
261 &subsurface->pending_link);
262 }
263}
264
265static void
266place_below(struct wl_client *client, struct wl_resource *resource,
267 struct wl_resource *sibling_resource)
268{
269 (void)client;
270 struct subsurface *subsurface = wl_resource_get_user_data(resource);
271 struct surface *sibling_surface =
272 wl_resource_get_user_data(sibling_resource);
273 struct subsurface *sibling_subsurface;
274
275 if (!is_valid_sibling(subsurface, sibling_surface, &sibling_subsurface)) {
276 wl_resource_post_error(resource, WL_SUBSURFACE_ERROR_BAD_SURFACE,
277 "invalid sibling surface");
278 return;
279 }
280
281 if (!sibling_subsurface) {
282 wl_list_remove(&subsurface->pending_link);
283 wl_list_insert(subsurface->parent->pending.state.subsurfaces_below.prev,
284 &subsurface->pending_link);
285 } else {
286 wl_list_remove(&subsurface->pending_link);
287 wl_list_insert(sibling_subsurface->pending_link.prev,
288 &subsurface->pending_link);
289 }
290}
291
292static void
293set_sync(struct wl_client *client, struct wl_resource *resource)
294{
295 (void)client;
296 struct subsurface *subsurface = wl_resource_get_user_data(resource);
297 subsurface->sync = true;
298}
299
300static void
301set_desync(struct wl_client *client, struct wl_resource *resource)
302{
303 (void)client;
304 struct subsurface *subsurface = wl_resource_get_user_data(resource);
305 bool synchronized = subsurface_is_synchronized(subsurface);
306
307 subsurface->sync = false;
308
309 if (synchronized && !subsurface_is_synchronized(subsurface) &&
310 subsurface->pending && subsurface->surface) {
311 surface_commit_pending(subsurface->surface);
312 }
313}
314
315void
316subsurface_parent_commit(struct surface *parent)
317{
318 struct subsurface *child;
319 struct compositor_view *parent_view;
320 struct compositor_view *reference;
321 struct compositor_view *child_view;
322
323 if (!parent) {
324 return;
325 }
326
327 wl_list_for_each(child, &parent->subsurfaces, link)
328 list_remove_if_linked(&child->current_link);
329
330 wl_list_init(&parent->state.subsurfaces_below);
331 wl_list_init(&parent->state.subsurfaces_above);
332
333 wl_list_for_each(child, &parent->pending.state.subsurfaces_below,
334 pending_link)
335 wl_list_insert(parent->state.subsurfaces_below.prev,
336 &child->current_link);
337
338 wl_list_for_each(child, &parent->pending.state.subsurfaces_above,
339 pending_link)
340 wl_list_insert(parent->state.subsurfaces_above.prev,
341 &child->current_link);
342
343 parent_view = parent->view ? compositor_view(parent->view) : NULL;
344 if (parent_view) {
345 reference = parent_view;
346 wl_list_for_each_reverse(child, &parent->state.subsurfaces_below,
347 current_link)
348 {
349 if (!child->surface || !child->surface->view) {
350 continue;
351 }
352
353 child_view = compositor_view(child->surface->view);
354 if (!child_view) {
355 continue;
356 }
357
358 compositor_view_restack(child_view, reference, false);
359 reference = child_view;
360 }
361
362 reference = parent_view;
363 wl_list_for_each(child, &parent->state.subsurfaces_above, current_link)
364 {
365 if (!child->surface || !child->surface->view) {
366 continue;
367 }
368
369 child_view = compositor_view(child->surface->view);
370 if (!child_view) {
371 continue;
372 }
373
374 compositor_view_restack(child_view, reference, true);
375 reference = child_view;
376 }
377 }
378
379 wl_list_for_each(child, &parent->subsurfaces, link)
380 {
381 if (!child->pending_position) {
382 continue;
383 }
384
385 child->x = child->pending_x;
386 child->y = child->pending_y;
387 child->pending_position = false;
388 subsurface_update_position(child);
389 }
390
391 wl_list_for_each(child, &parent->state.subsurfaces_below, current_link)
392 {
393 if (!child->added) {
394 child->added = true;
395 }
396 subsurface_update_visibility(child);
397 }
398 wl_list_for_each(child, &parent->state.subsurfaces_above, current_link)
399 {
400 if (!child->added) {
401 child->added = true;
402 }
403 subsurface_update_visibility(child);
404 }
405}
406
407static const struct wl_subsurface_interface subsurface_impl = {
408 .destroy = destroy_resource,
409 .set_position = set_position,
410 .place_above = place_above,
411 .place_below = place_below,
412 .set_sync = set_sync,
413 .set_desync = set_desync,
414};
415
416static void
417subsurface_destroy(struct wl_resource *resource)
418{
419 struct subsurface *subsurface = wl_resource_get_user_data(resource);
420
421 if (subsurface->surface) {
422 if (subsurface->surface->subsurface == subsurface) {
423 subsurface->surface->subsurface = NULL;
424 }
425 }
426
427 if (!wl_list_empty(&subsurface->parent_destroy_listener.link)) {
428 wl_list_remove(&subsurface->parent_destroy_listener.link);
429 wl_list_init(&subsurface->parent_destroy_listener.link);
430 }
431 if (!wl_list_empty(&subsurface->surface_destroy_listener.link)) {
432 wl_list_remove(&subsurface->surface_destroy_listener.link);
433 wl_list_init(&subsurface->surface_destroy_listener.link);
434 }
435
436 if (!wl_list_empty(&subsurface->parent_view_handler.link)) {
437 wl_list_remove(&subsurface->parent_view_handler.link);
438 wl_list_init(&subsurface->parent_view_handler.link);
439 }
440
441 if (!wl_list_empty(&subsurface->link)) {
442 wl_list_remove(&subsurface->link);
443 wl_list_init(&subsurface->link);
444 }
445
446 list_remove_if_linked(&subsurface->pending_link);
447 list_remove_if_linked(&subsurface->current_link);
448
449 if (subsurface->surface && subsurface->surface->view) {
450 struct compositor_view *view =
451 compositor_view(subsurface->surface->view);
452 if (view && !view->window) {
453 compositor_view_destroy(view);
454 }
455 }
456
457 free(subsurface);
458}
459
460struct subsurface *
461subsurface_new(struct wl_client *client, uint32_t version, uint32_t id,
462 struct surface *surface, struct surface *parent)
463{
464 struct subsurface *subsurface;
465 struct compositor_view *parent_view;
466 struct compositor_view *view;
467
468 if (!(subsurface = malloc(sizeof(*subsurface)))) {
469 goto error0;
470 }
471
472 subsurface->resource =
473 wl_resource_create(client, &wl_subsurface_interface, version, id);
474
475 if (!subsurface->resource) {
476 goto error1;
477 }
478
479 wl_resource_set_implementation(subsurface->resource, &subsurface_impl,
480 subsurface, &subsurface_destroy);
481
482 subsurface->surface = surface;
483 subsurface->parent = parent;
484 subsurface->x = 0;
485 subsurface->y = 0;
486 subsurface->pending_x = 0;
487 subsurface->pending_y = 0;
488 subsurface->pending_position = false;
489 subsurface->sync = true;
490 subsurface->pending = false;
491 subsurface->added = false;
492
493 subsurface->parent_view_handler.impl = &parent_view_handler_impl;
494 wl_list_init(&subsurface->parent_view_handler.link);
495 wl_list_init(&subsurface->surface_destroy_listener.link);
496 wl_list_init(&subsurface->parent_destroy_listener.link);
497 wl_list_init(&subsurface->link);
498 wl_list_init(&subsurface->pending_link);
499 wl_list_init(&subsurface->current_link);
500
501 if (!surface->view) {
502 compositor_create_view(surface);
503 }
504 if (!parent->view) {
505 compositor_create_view(parent);
506 }
507
508 parent_view = compositor_view(parent->view);
509 view = compositor_view(surface->view);
510 if (!parent_view || !view) {
511 goto error2;
512 }
513
514 compositor_view_set_parent(view, parent_view);
515 wl_list_remove(&view->link);
516 wl_list_insert(parent_view->link.prev, &view->link);
517
518 wl_list_insert(&parent_view->base.handlers,
519 &subsurface->parent_view_handler.link);
520 subsurface_update_position(subsurface);
521 wl_list_insert(&parent->subsurfaces, &subsurface->link);
522 wl_list_insert(parent->pending.state.subsurfaces_above.prev,
523 &subsurface->pending_link);
524 subsurface_update_visibility(subsurface);
525
526 subsurface->surface_destroy_listener.notify = handle_surface_destroy;
527 wl_resource_add_destroy_listener(surface->resource,
528 &subsurface->surface_destroy_listener);
529 subsurface->parent_destroy_listener.notify = handle_parent_destroy;
530 wl_resource_add_destroy_listener(parent->resource,
531 &subsurface->parent_destroy_listener);
532
533 return subsurface;
534
535error2:
536 wl_resource_destroy(subsurface->resource);
537 return NULL;
538error1:
539 free(subsurface);
540error0:
541 return NULL;
542}