main neuswc / libswc / subsurface.c
  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}