main neuswc / libswc / surface.c
  1/* swc: surface.c
  2 *
  3 * Copyright (c) 2013-2020 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 "surface.h"
 25#include "event.h"
 26#include "internal.h"
 27#include "output.h"
 28#include "region.h"
 29#include "screen.h"
 30#include "subsurface.h"
 31#include "util.h"
 32#include "view.h"
 33#include "wayland_buffer.h"
 34
 35#include <stdio.h>
 36#include <stdlib.h>
 37#include <wld/wld.h>
 38
 39/**
 40 * Removes a buffer from a surface state.
 41 */
 42static void
 43handle_buffer_destroy(struct wl_listener *listener, void *data)
 44{
 45	struct surface_state *state;
 46
 47	state = wl_container_of(listener, state, buffer_destroy_listener);
 48	state->buffer = NULL;
 49	state->buffer_resource = NULL;
 50}
 51
 52static void
 53handle_role_destroy(struct wl_listener *listener, void *data)
 54{
 55	struct surface *surface = wl_container_of(listener, surface, role_destroy_listener);
 56
 57	(void)data;
 58
 59	surface->role = NULL;
 60}
 61
 62static void
 63state_initialize(struct surface_state *state)
 64{
 65	state->buffer = NULL;
 66	/*layer-shell rejects surfaces with a pre-existing buffer */
 67	state->buffer_resource = NULL;
 68	state->buffer_destroy_listener.notify = &handle_buffer_destroy;
 69
 70	pixman_region32_init(&state->damage);
 71	pixman_region32_init(&state->opaque);
 72	pixman_region32_init_with_extents(&state->input, &infinite_extents);
 73
 74	wl_list_init(&state->frame_callbacks);
 75	wl_list_init(&state->subsurfaces_below);
 76	wl_list_init(&state->subsurfaces_above);
 77}
 78
 79static void
 80state_finalize(struct surface_state *state)
 81{
 82	struct wl_resource *resource, *tmp;
 83
 84	if (state->buffer) {
 85		wl_list_remove(&state->buffer_destroy_listener.link);
 86	}
 87
 88	pixman_region32_fini(&state->damage);
 89	pixman_region32_fini(&state->opaque);
 90	pixman_region32_fini(&state->input);
 91
 92	/* Remove all leftover callbacks. */
 93	wl_list_for_each_safe(resource, tmp, &state->frame_callbacks, link)
 94	    wl_resource_destroy(resource);
 95}
 96
 97/**
 98 * In order to set the buffer of a surface state (current or pending), we need
 99 * to manage the destroy listeners we have for the new and old buffer.
100 */
101static void
102state_set_buffer(struct surface_state *state, struct wl_resource *resource)
103{
104	struct wld_buffer *buffer = resource ? wayland_buffer_get(resource) : NULL;
105
106	if (state->buffer) {
107		wl_list_remove(&state->buffer_destroy_listener.link);
108	}
109
110	if (buffer) {
111		wl_resource_add_destroy_listener(resource,
112		                                 &state->buffer_destroy_listener);
113	}
114
115	state->buffer = buffer;
116	state->buffer_resource = resource;
117}
118
119static void
120handle_frame(struct view_handler *handler, uint32_t time)
121{
122	struct surface *surface = wl_container_of(handler, surface, view_handler);
123	struct wl_resource *resource, *tmp;
124
125	wl_list_for_each_safe(resource, tmp, &surface->state.frame_callbacks, link)
126	{
127		wl_callback_send_done(resource, time);
128		wl_resource_destroy(resource);
129	}
130
131	wl_list_init(&surface->state.frame_callbacks);
132}
133
134static void
135handle_screens(struct view_handler *handler, uint32_t entered, uint32_t left)
136{
137	struct surface *surface = wl_container_of(handler, surface, view_handler);
138	struct screen *screen;
139	struct output *output;
140	struct wl_client *client;
141	struct wl_resource *resource;
142
143	client = wl_resource_get_client(surface->resource);
144
145	wl_list_for_each(screen, &swc.screens, link)
146	{
147		if (!((entered | left) & screen_mask(screen))) {
148			continue;
149		}
150
151		wl_list_for_each(output, &screen->outputs, link)
152		{
153			resource = wl_resource_find_for_client(&output->resources, client);
154
155			if (resource) {
156				if (entered & screen_mask(screen)) {
157					wl_surface_send_enter(surface->resource, resource);
158				} else if (left & screen_mask(screen)) {
159					wl_surface_send_leave(surface->resource, resource);
160				}
161			}
162		}
163	}
164}
165
166static const struct view_handler_impl view_handler_impl = {
167    .frame = handle_frame,
168    .screens = handle_screens,
169};
170
171static void
172attach(struct wl_client *client, struct wl_resource *resource,
173       struct wl_resource *buffer_resource, int32_t x, int32_t y)
174{
175	struct surface *surface = wl_resource_get_user_data(resource);
176
177	surface->pending.commit |= SURFACE_COMMIT_ATTACH;
178
179	state_set_buffer(&surface->pending.state, buffer_resource);
180	surface->pending.x = x;
181	surface->pending.y = y;
182}
183
184static void
185damage(struct wl_client *client, struct wl_resource *resource, int32_t x,
186       int32_t y, int32_t width, int32_t height)
187{
188	struct surface *surface = wl_resource_get_user_data(resource);
189
190	surface->pending.commit |= SURFACE_COMMIT_DAMAGE;
191	pixman_region32_union_rect(&surface->pending.state.damage,
192	                           &surface->pending.state.damage, x, y, width,
193	                           height);
194}
195
196static void
197frame(struct wl_client *client, struct wl_resource *resource, uint32_t id)
198{
199	struct surface *surface = wl_resource_get_user_data(resource);
200	struct wl_resource *callback_resource;
201
202	callback_resource =
203	    wl_resource_create(client, &wl_callback_interface, 1, id);
204	if (!callback_resource) {
205		wl_resource_post_no_memory(resource);
206		return;
207	}
208	surface->pending.commit |= SURFACE_COMMIT_FRAME;
209	wl_resource_set_implementation(callback_resource, NULL, NULL,
210	                               &remove_resource);
211	wl_list_insert(surface->pending.state.frame_callbacks.prev,
212	               wl_resource_get_link(callback_resource));
213}
214
215static void
216set_opaque_region(struct wl_client *client, struct wl_resource *resource,
217                  struct wl_resource *region_resource)
218{
219	struct surface *surface = wl_resource_get_user_data(resource);
220
221	surface->pending.commit |= SURFACE_COMMIT_OPAQUE;
222
223	if (region_resource) {
224		pixman_region32_t *region = wl_resource_get_user_data(region_resource);
225		pixman_region32_copy(&surface->pending.state.opaque, region);
226	} else {
227		pixman_region32_clear(&surface->pending.state.opaque);
228	}
229}
230
231static void
232set_input_region(struct wl_client *client, struct wl_resource *resource,
233                 struct wl_resource *region_resource)
234{
235	struct surface *surface = wl_resource_get_user_data(resource);
236
237	surface->pending.commit |= SURFACE_COMMIT_INPUT;
238
239	if (region_resource) {
240		pixman_region32_t *region = wl_resource_get_user_data(region_resource);
241		pixman_region32_copy(&surface->pending.state.input, region);
242	} else {
243		pixman_region32_reset(&surface->pending.state.input, &infinite_extents);
244	}
245}
246
247static inline void
248trim_region(pixman_region32_t *region, struct wld_buffer *buffer)
249{
250	pixman_region32_intersect_rect(region, region, 0, 0,
251	                               buffer ? buffer->width : 0,
252	                               buffer ? buffer->height : 0);
253}
254
255static void
256surface_apply_pending(struct surface *surface, bool flush_children)
257{
258	struct wld_buffer *buffer;
259
260	/* Attach */
261	if (surface->pending.commit & SURFACE_COMMIT_ATTACH) {
262		if (surface->state.buffer &&
263		    surface->state.buffer != surface->pending.state.buffer) {
264			wl_buffer_send_release(surface->state.buffer_resource);
265		}
266
267		state_set_buffer(&surface->state,
268		                 surface->pending.state.buffer_resource);
269	}
270
271	buffer = surface->state.buffer;
272
273	/* Damage */
274	if (surface->pending.commit & SURFACE_COMMIT_DAMAGE) {
275		pixman_region32_union(&surface->state.damage, &surface->state.damage,
276		                      &surface->pending.state.damage);
277		pixman_region32_clear(&surface->pending.state.damage);
278	}
279
280	/* Opaque */
281	if (surface->pending.commit & SURFACE_COMMIT_OPAQUE) {
282		pixman_region32_copy(&surface->state.opaque,
283		                     &surface->pending.state.opaque);
284	}
285
286	/* Input */
287	if (surface->pending.commit & SURFACE_COMMIT_INPUT) {
288		pixman_region32_copy(&surface->state.input,
289		                     &surface->pending.state.input);
290	}
291
292	/* Frame */
293	if (surface->pending.commit & SURFACE_COMMIT_FRAME) {
294		wl_list_insert_list(&surface->state.frame_callbacks,
295		                    &surface->pending.state.frame_callbacks);
296		wl_list_init(&surface->pending.state.frame_callbacks);
297	}
298
299	trim_region(&surface->state.damage, buffer);
300	trim_region(&surface->state.opaque, buffer);
301
302	if (surface->view) {
303		if (surface->pending.commit & SURFACE_COMMIT_ATTACH) {
304			view_attach(surface->view, buffer);
305		}
306		view_update(surface->view);
307	}
308
309	surface->pending.commit = 0;
310
311	if (surface->subsurface) {
312		surface->subsurface->pending = false;
313	}
314
315	if (surface->subsurface) {
316		subsurface_update_visibility(surface->subsurface);
317	}
318
319	subsurface_parent_commit(surface);
320	wl_signal_emit(&surface->signal.commit, surface);
321
322	if (flush_children) {
323		struct subsurface *child;
324		wl_list_for_each(child, &surface->subsurfaces, link)
325		{
326			if (!child->pending || !subsurface_is_synchronized(child)) {
327				continue;
328			}
329			if (child->surface) {
330				surface_apply_pending(child->surface, true);
331			}
332		}
333	}
334}
335
336static void
337commit(struct wl_client *client, struct wl_resource *resource)
338{
339	struct surface *surface = wl_resource_get_user_data(resource);
340
341	if (surface->subsurface &&
342	    subsurface_is_synchronized(surface->subsurface)) {
343		surface->subsurface->pending = true;
344		return;
345	}
346
347	surface_apply_pending(surface, true);
348}
349
350static void
351set_buffer_transform(struct wl_client *client, struct wl_resource *surface,
352                     int32_t transform)
353{
354	if (transform != WL_OUTPUT_TRANSFORM_NORMAL) {
355		wl_resource_post_error(surface, WL_SURFACE_ERROR_INVALID_TRANSFORM,
356		                       "buffer transform %" PRId32 " not supported",
357		                       transform);
358	}
359}
360
361static void
362set_buffer_scale(struct wl_client *client, struct wl_resource *surface,
363                 int32_t scale)
364{
365	if (scale != 1) {
366		wl_resource_post_error(surface, WL_SURFACE_ERROR_INVALID_SCALE,
367		                       "buffer scale not supported");
368	}
369}
370
371static void
372damage_buffer(struct wl_client *client, struct wl_resource *surface, int32_t x,
373              int32_t y, int32_t w, int32_t h)
374{
375	damage(client, surface, x, y, w, h);
376}
377
378static const struct wl_surface_interface surface_impl = {
379    .destroy = destroy_resource,
380    .attach = attach,
381    .damage = damage,
382    .frame = frame,
383    .set_opaque_region = set_opaque_region,
384    .set_input_region = set_input_region,
385    .commit = commit,
386    .set_buffer_transform = set_buffer_transform,
387    .set_buffer_scale = set_buffer_scale,
388    .damage_buffer = damage_buffer,
389};
390
391static void
392surface_destroy(struct wl_resource *resource)
393{
394	struct surface *surface = wl_resource_get_user_data(resource);
395
396	state_finalize(&surface->state);
397	state_finalize(&surface->pending.state);
398
399	if (surface->view) {
400		wl_list_remove(&surface->view_handler.link);
401	}
402	if (surface->role) {
403		wl_list_remove(&surface->role_destroy_listener.link);
404	}
405
406	free(surface);
407}
408
409/**
410 * Construct a new surface, adding it to the given client as id.
411 *
412 * The surface will be free'd automatically when its resource is destroyed.
413 *
414 * @return The newly allocated surface.
415 */
416struct surface *
417surface_new(struct wl_client *client, uint32_t version, uint32_t id)
418{
419	struct surface *surface;
420
421	surface = malloc(sizeof(*surface));
422	if (!surface) {
423		goto error0;
424	}
425
426	surface->resource =
427	    wl_resource_create(client, &wl_surface_interface, version, id);
428	if (!surface->resource) {
429		goto error1;
430	}
431	wl_resource_set_implementation(surface->resource, &surface_impl, surface,
432	                               &surface_destroy);
433
434	/* Initialize the surface. */
435	surface->pending.commit = 0;
436	wl_signal_init(&surface->signal.commit);
437	surface->view = NULL;
438	surface->view_handler.impl = &view_handler_impl;
439	surface->role = NULL;
440	surface->role_destroy_listener.notify = handle_role_destroy;
441	surface->subsurface = NULL;
442	wl_list_init(&surface->subsurfaces);
443	surface->has_window_geometry = false;
444	surface->window_x = 0;
445	surface->window_y = 0;
446	surface->window_width = 0;
447	surface->window_height = 0;
448	surface->window_geometry_applied = false;
449
450	state_initialize(&surface->state);
451	state_initialize(&surface->pending.state);
452
453	return surface;
454
455error1:
456	free(surface);
457error0:
458	return NULL;
459}
460
461void
462surface_set_view(struct surface *surface, struct view *view)
463{
464	if (surface->view == view) {
465		return;
466	}
467
468	if (surface->view) {
469		wl_list_remove(&surface->view_handler.link);
470	}
471
472	surface->view = view;
473
474	if (view) {
475		wl_list_insert(&view->handlers, &surface->view_handler.link);
476		view_attach(view, surface->state.buffer);
477		view_update(view);
478	}
479}
480
481bool
482surface_set_role(struct surface *surface, struct wl_resource *role)
483{
484	if (surface->role) {
485		return false;
486	}
487
488	surface->role = role;
489	wl_resource_add_destroy_listener(role, &surface->role_destroy_listener);
490	return true;
491}
492
493bool
494surface_has_buffer(struct surface *surface)
495{
496	return surface->state.buffer_resource ||
497	       ((surface->pending.commit & SURFACE_COMMIT_ATTACH) &&
498	        surface->pending.state.buffer_resource);
499}
500
501void
502surface_commit_pending(struct surface *surface)
503{
504	surface_apply_pending(surface, true);
505}