blob: 92720f40ecaa80436142513d41b8700829cf020e [file] [log] [blame]
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001/*
2 * Copyright © 2013 Intel Corporation
3 *
Bryce Harrington1f6b0d12015-06-10 22:48:59 -07004 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040010 *
Bryce Harrington1f6b0d12015-06-10 22:48:59 -070011 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * 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
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040022 */
23
Andrew Wedgbury9cd661e2014-04-07 12:40:35 +010024#include "config.h"
25
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040026#include <stdint.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <cairo.h>
31#include <math.h>
32#include <assert.h>
33#include <pixman.h>
34#include <sys/epoll.h>
35#include <sys/socket.h>
36#include <unistd.h>
37
38#include <EGL/egl.h>
39#include <EGL/eglext.h>
40#include <GLES2/gl2.h>
41#include <GLES2/gl2ext.h>
42
43#include <cairo-gl.h>
44
45#include <wayland-client.h>
Kristian Høgsberg3c179332013-08-06 19:27:04 -070046#define WL_HIDE_DEPRECATED
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040047#include <wayland-server.h>
48
49#include "window.h"
50
Kristian Høgsberg919cddb2013-07-08 19:03:57 -040051#define MIN(x,y) (((x) < (y)) ? (x) : (y))
52
Kristian Høgsbergc2010b32013-12-17 10:40:01 -080053#ifndef EGL_WL_create_wayland_buffer_from_image
54#define EGL_WL_create_wayland_buffer_from_image 1
55
56#ifdef EGL_EGLEXT_PROTOTYPES
57EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image);
58#endif
59typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (EGLDisplay dpy, EGLImageKHR image);
60
61#endif
62
Neil Robertsa5059eb2013-09-09 00:59:35 +010063static int option_blit;
64
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040065struct nested {
66 struct display *display;
67 struct window *window;
68 struct widget *widget;
69 struct wl_display *child_display;
70 struct task child_task;
71
72 EGLDisplay egl_display;
73 struct program *texture_program;
74
75 struct wl_list surface_list;
Neil Roberts47b87d52013-09-08 20:52:36 +010076
77 const struct nested_renderer *renderer;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040078};
79
80struct nested_region {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040081 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040082 pixman_region32_t region;
83};
84
Neil Roberts1f020a12013-09-09 00:41:29 +010085struct nested_buffer_reference {
86 struct nested_buffer *buffer;
87 struct wl_listener destroy_listener;
88};
89
Neil Roberts6bf23872013-09-08 18:49:15 +010090struct nested_buffer {
91 struct wl_resource *resource;
92 struct wl_signal destroy_signal;
93 struct wl_listener destroy_listener;
94 uint32_t busy_count;
Neil Roberts6bf23872013-09-08 18:49:15 +010095
Neil Roberts1f020a12013-09-09 00:41:29 +010096 /* A buffer in the parent compositor representing the same
97 * data. This is created on-demand when the subsurface
98 * renderer is used */
99 struct wl_buffer *parent_buffer;
100 /* This reference is used to mark when the parent buffer has
101 * been attached to the subsurface. It will be unrefenced when
102 * we receive a buffer release event. That way we won't inform
103 * the client that the buffer is free until the parent
104 * compositor is also finished with it */
105 struct nested_buffer_reference parent_ref;
Neil Roberts6bf23872013-09-08 18:49:15 +0100106};
107
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400108struct nested_surface {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400109 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400110 struct nested *nested;
111 EGLImageKHR *image;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400112 struct wl_list link;
Neil Roberts15a8d342013-09-08 19:49:02 +0100113
Neil Robertsf9b25412013-09-09 15:13:09 +0100114 struct wl_list frame_callback_list;
115
Neil Roberts15a8d342013-09-08 19:49:02 +0100116 struct {
117 /* wl_surface.attach */
118 int newly_attached;
119 struct nested_buffer *buffer;
120 struct wl_listener buffer_destroy_listener;
121
122 /* wl_surface.frame */
123 struct wl_list frame_callback_list;
Neil Roberts28557662013-09-08 20:24:14 +0100124
125 /* wl_surface.damage */
126 pixman_region32_t damage;
Neil Roberts15a8d342013-09-08 19:49:02 +0100127 } pending;
Neil Roberts47b87d52013-09-08 20:52:36 +0100128
129 void *renderer_data;
130};
131
132/* Data used for the blit renderer */
133struct nested_blit_surface {
134 struct nested_buffer_reference buffer_ref;
135 GLuint texture;
136 cairo_surface_t *cairo_surface;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400137};
138
Neil Roberts1f020a12013-09-09 00:41:29 +0100139/* Data used for the subsurface renderer */
140struct nested_ss_surface {
141 struct widget *widget;
142 struct wl_surface *surface;
143 struct wl_subsurface *subsurface;
144 struct wl_callback *frame_callback;
145};
146
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400147struct nested_frame_callback {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400148 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400149 struct wl_list link;
150};
151
Neil Roberts47b87d52013-09-08 20:52:36 +0100152struct nested_renderer {
153 void (* surface_init)(struct nested_surface *surface);
154 void (* surface_fini)(struct nested_surface *surface);
155 void (* render_clients)(struct nested *nested, cairo_t *cr);
156 void (* surface_attach)(struct nested_surface *surface,
157 struct nested_buffer *buffer);
158};
159
Neil Robertsa5059eb2013-09-09 00:59:35 +0100160static const struct weston_option nested_options[] = {
161 { WESTON_OPTION_BOOLEAN, "blit", 'b', &option_blit },
162};
163
Neil Roberts47b87d52013-09-08 20:52:36 +0100164static const struct nested_renderer nested_blit_renderer;
Neil Roberts1f020a12013-09-09 00:41:29 +0100165static const struct nested_renderer nested_ss_renderer;
Neil Roberts47b87d52013-09-08 20:52:36 +0100166
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400167static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
168static PFNEGLCREATEIMAGEKHRPROC create_image;
169static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
170static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
171static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
172static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
Neil Roberts1f020a12013-09-09 00:41:29 +0100173static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_wayland_buffer_from_image;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400174
175static void
Neil Roberts6bf23872013-09-08 18:49:15 +0100176nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
177{
178 struct nested_buffer *buffer =
179 container_of(listener, struct nested_buffer, destroy_listener);
180
181 wl_signal_emit(&buffer->destroy_signal, buffer);
Neil Roberts1f020a12013-09-09 00:41:29 +0100182
183 if (buffer->parent_buffer)
184 wl_buffer_destroy(buffer->parent_buffer);
185
Neil Roberts6bf23872013-09-08 18:49:15 +0100186 free(buffer);
187}
188
189static struct nested_buffer *
190nested_buffer_from_resource(struct wl_resource *resource)
191{
192 struct nested_buffer *buffer;
193 struct wl_listener *listener;
194
195 listener =
196 wl_resource_get_destroy_listener(resource,
197 nested_buffer_destroy_handler);
198
199 if (listener)
200 return container_of(listener, struct nested_buffer,
201 destroy_listener);
202
203 buffer = zalloc(sizeof *buffer);
204 if (buffer == NULL)
205 return NULL;
206
207 buffer->resource = resource;
208 wl_signal_init(&buffer->destroy_signal);
209 buffer->destroy_listener.notify = nested_buffer_destroy_handler;
210 wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
211
212 return buffer;
213}
214
215static void
216nested_buffer_reference_handle_destroy(struct wl_listener *listener,
217 void *data)
218{
219 struct nested_buffer_reference *ref =
220 container_of(listener, struct nested_buffer_reference,
221 destroy_listener);
222
223 assert((struct nested_buffer *)data == ref->buffer);
224 ref->buffer = NULL;
225}
226
227static void
228nested_buffer_reference(struct nested_buffer_reference *ref,
229 struct nested_buffer *buffer)
230{
231 if (buffer == ref->buffer)
232 return;
233
234 if (ref->buffer) {
235 ref->buffer->busy_count--;
236 if (ref->buffer->busy_count == 0) {
237 assert(wl_resource_get_client(ref->buffer->resource));
238 wl_resource_queue_event(ref->buffer->resource,
239 WL_BUFFER_RELEASE);
240 }
241 wl_list_remove(&ref->destroy_listener.link);
242 }
243
244 if (buffer) {
245 buffer->busy_count++;
246 wl_signal_add(&buffer->destroy_signal,
247 &ref->destroy_listener);
248
249 ref->destroy_listener.notify =
250 nested_buffer_reference_handle_destroy;
251 }
252
253 ref->buffer = buffer;
254}
255
256static void
Neil Robertsf9b25412013-09-09 15:13:09 +0100257flush_surface_frame_callback_list(struct nested_surface *surface,
258 uint32_t time)
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400259{
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400260 struct nested_frame_callback *nc, *next;
261
Neil Robertsf9b25412013-09-09 15:13:09 +0100262 wl_list_for_each_safe(nc, next, &surface->frame_callback_list, link) {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400263 wl_callback_send_done(nc->resource, time);
264 wl_resource_destroy(nc->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400265 }
Neil Robertsf9b25412013-09-09 15:13:09 +0100266 wl_list_init(&surface->frame_callback_list);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400267
268 /* FIXME: toytoolkit need a pre-block handler where we can
269 * call this. */
Neil Robertsf9b25412013-09-09 15:13:09 +0100270 wl_display_flush_clients(surface->nested->child_display);
271}
272
273static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400274redraw_handler(struct widget *widget, void *data)
275{
276 struct nested *nested = data;
277 cairo_surface_t *surface;
278 cairo_t *cr;
279 struct rectangle allocation;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400280
281 widget_get_allocation(nested->widget, &allocation);
282
283 surface = window_get_surface(nested->window);
284
285 cr = cairo_create(surface);
286 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
287 cairo_rectangle(cr,
288 allocation.x,
289 allocation.y,
290 allocation.width,
291 allocation.height);
292 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
293 cairo_fill(cr);
294
Neil Roberts47b87d52013-09-08 20:52:36 +0100295 nested->renderer->render_clients(nested, cr);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400296
297 cairo_destroy(cr);
298
299 cairo_surface_destroy(surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400300}
301
302static void
303keyboard_focus_handler(struct window *window,
304 struct input *device, void *data)
305{
306 struct nested *nested = data;
307
308 window_schedule_redraw(nested->window);
309}
310
311static void
312handle_child_data(struct task *task, uint32_t events)
313{
314 struct nested *nested = container_of(task, struct nested, child_task);
315 struct wl_event_loop *loop;
316
317 loop = wl_display_get_event_loop(nested->child_display);
318
319 wl_event_loop_dispatch(loop, -1);
320 wl_display_flush_clients(nested->child_display);
321}
322
323struct nested_client {
324 struct wl_client *client;
325 pid_t pid;
326};
327
328static struct nested_client *
329launch_client(struct nested *nested, const char *path)
330{
331 int sv[2];
332 pid_t pid;
333 struct nested_client *client;
334
335 client = malloc(sizeof *client);
336 if (client == NULL)
337 return NULL;
338
339 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
340 fprintf(stderr, "launch_client: "
341 "socketpair failed while launching '%s': %m\n",
342 path);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700343 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400344 return NULL;
345 }
346
347 pid = fork();
348 if (pid == -1) {
349 close(sv[0]);
350 close(sv[1]);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700351 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400352 fprintf(stderr, "launch_client: "
353 "fork failed while launching '%s': %m\n", path);
354 return NULL;
355 }
356
357 if (pid == 0) {
358 int clientfd;
359 char s[32];
360
361 /* SOCK_CLOEXEC closes both ends, so we dup the fd to
362 * get a non-CLOEXEC fd to pass through exec. */
363 clientfd = dup(sv[1]);
364 if (clientfd == -1) {
365 fprintf(stderr, "compositor: dup failed: %m\n");
366 exit(-1);
367 }
368
369 snprintf(s, sizeof s, "%d", clientfd);
370 setenv("WAYLAND_SOCKET", s, 1);
371
372 execl(path, path, NULL);
373
374 fprintf(stderr, "compositor: executing '%s' failed: %m\n",
375 path);
376 exit(-1);
377 }
378
379 close(sv[1]);
380
381 client->client = wl_client_create(nested->child_display, sv[0]);
382 if (!client->client) {
383 close(sv[0]);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700384 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400385 fprintf(stderr, "launch_client: "
386 "wl_client_create failed while launching '%s'.\n",
387 path);
388 return NULL;
389 }
390
391 client->pid = pid;
392
393 return client;
394}
395
396static void
397destroy_surface(struct wl_resource *resource)
398{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400399 struct nested_surface *surface = wl_resource_get_user_data(resource);
Neil Roberts47b87d52013-09-08 20:52:36 +0100400 struct nested *nested = surface->nested;
Neil Roberts15a8d342013-09-08 19:49:02 +0100401 struct nested_frame_callback *cb, *next;
402
403 wl_list_for_each_safe(cb, next,
Neil Robertsf9b25412013-09-09 15:13:09 +0100404 &surface->frame_callback_list, link)
405 wl_resource_destroy(cb->resource);
406
407 wl_list_for_each_safe(cb, next,
Neil Roberts15a8d342013-09-08 19:49:02 +0100408 &surface->pending.frame_callback_list, link)
409 wl_resource_destroy(cb->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400410
Neil Roberts28557662013-09-08 20:24:14 +0100411 pixman_region32_fini(&surface->pending.damage);
412
Neil Roberts47b87d52013-09-08 20:52:36 +0100413 nested->renderer->surface_fini(surface);
414
Neil Roberts8fbe3a62013-11-22 15:41:53 +0000415 wl_list_remove(&surface->link);
416
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400417 free(surface);
418}
419
420static void
421surface_destroy(struct wl_client *client, struct wl_resource *resource)
422{
423 wl_resource_destroy(resource);
424}
425
426static void
427surface_attach(struct wl_client *client,
428 struct wl_resource *resource,
429 struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
430{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400431 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400432 struct nested *nested = surface->nested;
Neil Roberts15a8d342013-09-08 19:49:02 +0100433 struct nested_buffer *buffer = NULL;
434
435 if (buffer_resource) {
436 int format;
437
438 if (!query_buffer(nested->egl_display, (void *) buffer_resource,
439 EGL_TEXTURE_FORMAT, &format)) {
440 wl_resource_post_error(buffer_resource,
441 WL_DISPLAY_ERROR_INVALID_OBJECT,
442 "attaching non-egl wl_buffer");
443 return;
444 }
445
446 switch (format) {
447 case EGL_TEXTURE_RGB:
448 case EGL_TEXTURE_RGBA:
449 break;
450 default:
451 wl_resource_post_error(buffer_resource,
452 WL_DISPLAY_ERROR_INVALID_OBJECT,
453 "invalid format");
454 return;
455 }
456
457 buffer = nested_buffer_from_resource(buffer_resource);
458 if (buffer == NULL) {
459 wl_client_post_no_memory(client);
460 return;
461 }
462 }
463
464 if (surface->pending.buffer)
465 wl_list_remove(&surface->pending.buffer_destroy_listener.link);
466
467 surface->pending.buffer = buffer;
468 surface->pending.newly_attached = 1;
469 if (buffer) {
470 wl_signal_add(&buffer->destroy_signal,
471 &surface->pending.buffer_destroy_listener);
472 }
473}
474
475static void
476nested_surface_attach(struct nested_surface *surface,
477 struct nested_buffer *buffer)
478{
479 struct nested *nested = surface->nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400480
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400481 if (surface->image != EGL_NO_IMAGE_KHR)
482 destroy_image(nested->egl_display, surface->image);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400483
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400484 surface->image = create_image(nested->egl_display, NULL,
Neil Roberts15a8d342013-09-08 19:49:02 +0100485 EGL_WAYLAND_BUFFER_WL, buffer->resource,
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700486 NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400487 if (surface->image == EGL_NO_IMAGE_KHR) {
488 fprintf(stderr, "failed to create img\n");
489 return;
490 }
491
Neil Roberts47b87d52013-09-08 20:52:36 +0100492 nested->renderer->surface_attach(surface, buffer);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400493}
494
495static void
496surface_damage(struct wl_client *client,
497 struct wl_resource *resource,
498 int32_t x, int32_t y, int32_t width, int32_t height)
499{
Neil Roberts28557662013-09-08 20:24:14 +0100500 struct nested_surface *surface = wl_resource_get_user_data(resource);
501
502 pixman_region32_union_rect(&surface->pending.damage,
503 &surface->pending.damage,
504 x, y, width, height);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400505}
506
507static void
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400508destroy_frame_callback(struct wl_resource *resource)
509{
510 struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
511
512 wl_list_remove(&callback->link);
513 free(callback);
514}
515
516static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400517surface_frame(struct wl_client *client,
518 struct wl_resource *resource, uint32_t id)
519{
520 struct nested_frame_callback *callback;
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400521 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400522
523 callback = malloc(sizeof *callback);
524 if (callback == NULL) {
525 wl_resource_post_no_memory(resource);
526 return;
527 }
528
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400529 callback->resource = wl_resource_create(client,
530 &wl_callback_interface, 1, id);
531 wl_resource_set_implementation(callback->resource, NULL, callback,
532 destroy_frame_callback);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400533
Neil Roberts15a8d342013-09-08 19:49:02 +0100534 wl_list_insert(surface->pending.frame_callback_list.prev,
535 &callback->link);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400536}
537
538static void
539surface_set_opaque_region(struct wl_client *client,
540 struct wl_resource *resource,
541 struct wl_resource *region_resource)
542{
543 fprintf(stderr, "surface_set_opaque_region\n");
544}
545
546static void
547surface_set_input_region(struct wl_client *client,
548 struct wl_resource *resource,
549 struct wl_resource *region_resource)
550{
551 fprintf(stderr, "surface_set_input_region\n");
552}
553
554static void
555surface_commit(struct wl_client *client, struct wl_resource *resource)
556{
Neil Roberts15a8d342013-09-08 19:49:02 +0100557 struct nested_surface *surface = wl_resource_get_user_data(resource);
558 struct nested *nested = surface->nested;
559
560 /* wl_surface.attach */
561 if (surface->pending.newly_attached)
562 nested_surface_attach(surface, surface->pending.buffer);
563
564 if (surface->pending.buffer) {
565 wl_list_remove(&surface->pending.buffer_destroy_listener.link);
566 surface->pending.buffer = NULL;
567 }
568 surface->pending.newly_attached = 0;
569
Neil Roberts28557662013-09-08 20:24:14 +0100570 /* wl_surface.damage */
Jason Ekstrandef540082014-06-26 10:37:36 -0700571 pixman_region32_clear(&surface->pending.damage);
Neil Roberts28557662013-09-08 20:24:14 +0100572
Neil Roberts15a8d342013-09-08 19:49:02 +0100573 /* wl_surface.frame */
Neil Robertsf9b25412013-09-09 15:13:09 +0100574 wl_list_insert_list(&surface->frame_callback_list,
Neil Roberts15a8d342013-09-08 19:49:02 +0100575 &surface->pending.frame_callback_list);
576 wl_list_init(&surface->pending.frame_callback_list);
577
Neil Roberts1f020a12013-09-09 00:41:29 +0100578 /* FIXME: For the subsurface renderer we don't need to
579 * actually redraw the window. However we do want to cause a
580 * commit because the subsurface is synchronized. Ideally we
581 * would just queue the commit */
Neil Roberts15a8d342013-09-08 19:49:02 +0100582 window_schedule_redraw(nested->window);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400583}
584
585static void
586surface_set_buffer_transform(struct wl_client *client,
587 struct wl_resource *resource, int transform)
588{
589 fprintf(stderr, "surface_set_buffer_transform\n");
590}
591
592static const struct wl_surface_interface surface_interface = {
593 surface_destroy,
594 surface_attach,
595 surface_damage,
596 surface_frame,
597 surface_set_opaque_region,
598 surface_set_input_region,
599 surface_commit,
600 surface_set_buffer_transform
601};
602
603static void
Neil Roberts15a8d342013-09-08 19:49:02 +0100604surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
605{
606 struct nested_surface *surface =
607 container_of(listener, struct nested_surface,
608 pending.buffer_destroy_listener);
609
610 surface->pending.buffer = NULL;
611}
612
613static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400614compositor_create_surface(struct wl_client *client,
615 struct wl_resource *resource, uint32_t id)
616{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400617 struct nested *nested = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400618 struct nested_surface *surface;
Michael Vetter2a18a522015-05-15 17:17:47 +0200619
Peter Huttererf3d62272013-08-08 11:57:05 +1000620 surface = zalloc(sizeof *surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400621 if (surface == NULL) {
622 wl_resource_post_no_memory(resource);
623 return;
624 }
625
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400626 surface->nested = nested;
627
Neil Robertsf9b25412013-09-09 15:13:09 +0100628 wl_list_init(&surface->frame_callback_list);
629
Neil Roberts15a8d342013-09-08 19:49:02 +0100630 wl_list_init(&surface->pending.frame_callback_list);
631 surface->pending.buffer_destroy_listener.notify =
632 surface_handle_pending_buffer_destroy;
Neil Roberts28557662013-09-08 20:24:14 +0100633 pixman_region32_init(&surface->pending.damage);
Neil Roberts15a8d342013-09-08 19:49:02 +0100634
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400635 display_acquire_window_surface(nested->display,
636 nested->window, NULL);
637
Neil Roberts47b87d52013-09-08 20:52:36 +0100638 nested->renderer->surface_init(surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400639
640 display_release_window_surface(nested->display, nested->window);
641
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400642 surface->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400643 wl_resource_create(client, &wl_surface_interface, 1, id);
644
645 wl_resource_set_implementation(surface->resource,
646 &surface_interface, surface,
647 destroy_surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400648
649 wl_list_insert(nested->surface_list.prev, &surface->link);
650}
651
652static void
653destroy_region(struct wl_resource *resource)
654{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400655 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400656
657 pixman_region32_fini(&region->region);
658 free(region);
659}
660
661static void
662region_destroy(struct wl_client *client, struct wl_resource *resource)
663{
664 wl_resource_destroy(resource);
665}
666
667static void
668region_add(struct wl_client *client, struct wl_resource *resource,
669 int32_t x, int32_t y, int32_t width, int32_t height)
670{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400671 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400672
673 pixman_region32_union_rect(&region->region, &region->region,
674 x, y, width, height);
675}
676
677static void
678region_subtract(struct wl_client *client, struct wl_resource *resource,
679 int32_t x, int32_t y, int32_t width, int32_t height)
680{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400681 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400682 pixman_region32_t rect;
683
684 pixman_region32_init_rect(&rect, x, y, width, height);
685 pixman_region32_subtract(&region->region, &region->region, &rect);
686 pixman_region32_fini(&rect);
687}
688
689static const struct wl_region_interface region_interface = {
690 region_destroy,
691 region_add,
692 region_subtract
693};
694
695static void
696compositor_create_region(struct wl_client *client,
697 struct wl_resource *resource, uint32_t id)
698{
699 struct nested_region *region;
700
701 region = malloc(sizeof *region);
702 if (region == NULL) {
703 wl_resource_post_no_memory(resource);
704 return;
705 }
706
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400707 pixman_region32_init(&region->region);
708
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400709 region->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400710 wl_resource_create(client, &wl_region_interface, 1, id);
711 wl_resource_set_implementation(region->resource, &region_interface,
712 region, destroy_region);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400713}
714
715static const struct wl_compositor_interface compositor_interface = {
716 compositor_create_surface,
717 compositor_create_region
718};
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400719
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400720static void
721compositor_bind(struct wl_client *client,
722 void *data, uint32_t version, uint32_t id)
723{
724 struct nested *nested = data;
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400725 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400726
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400727 resource = wl_resource_create(client, &wl_compositor_interface,
728 MIN(version, 3), id);
729 wl_resource_set_implementation(resource, &compositor_interface,
730 nested, NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400731}
732
733static int
734nested_init_compositor(struct nested *nested)
735{
736 const char *extensions;
737 struct wl_event_loop *loop;
Neil Roberts1f020a12013-09-09 00:41:29 +0100738 int use_ss_renderer = 0;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400739 int fd, ret;
740
741 wl_list_init(&nested->surface_list);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400742 nested->child_display = wl_display_create();
743 loop = wl_display_get_event_loop(nested->child_display);
744 fd = wl_event_loop_get_fd(loop);
745 nested->child_task.run = handle_child_data;
746 display_watch_fd(nested->display, fd,
747 EPOLLIN, &nested->child_task);
748
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400749 if (!wl_global_create(nested->child_display,
750 &wl_compositor_interface, 1,
751 nested, compositor_bind))
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400752 return -1;
753
754 wl_display_init_shm(nested->child_display);
755
756 nested->egl_display = display_get_egl_display(nested->display);
757 extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
758 if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
759 fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
760 return -1;
761 }
762
763 bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
764 unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
765 create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
766 destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
767 query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
768 image_target_texture_2d =
769 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
770
771 ret = bind_display(nested->egl_display, nested->child_display);
772 if (!ret) {
773 fprintf(stderr, "failed to bind wl_display\n");
774 return -1;
775 }
776
Neil Roberts1f020a12013-09-09 00:41:29 +0100777 if (display_has_subcompositor(nested->display)) {
778 const char *func = "eglCreateWaylandBufferFromImageWL";
779 const char *ext = "EGL_WL_create_wayland_buffer_from_image";
780
781 if (strstr(extensions, ext)) {
782 create_wayland_buffer_from_image =
783 (void *) eglGetProcAddress(func);
784 use_ss_renderer = 1;
785 }
786 }
787
Neil Robertsa5059eb2013-09-09 00:59:35 +0100788 if (option_blit)
789 use_ss_renderer = 0;
790
Neil Roberts1f020a12013-09-09 00:41:29 +0100791 if (use_ss_renderer) {
792 printf("Using subsurfaces to render client surfaces\n");
793 nested->renderer = &nested_ss_renderer;
794 } else {
795 printf("Using local compositing with blits to "
796 "render client surfaces\n");
797 nested->renderer = &nested_blit_renderer;
798 }
Neil Roberts47b87d52013-09-08 20:52:36 +0100799
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400800 return 0;
801}
802
803static struct nested *
804nested_create(struct display *display)
805{
806 struct nested *nested;
807
Peter Huttererf3d62272013-08-08 11:57:05 +1000808 nested = zalloc(sizeof *nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400809 if (nested == NULL)
810 return nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400811
812 nested->window = window_create(display);
Jason Ekstrandee7fefc2013-10-13 19:08:38 -0500813 nested->widget = window_frame_create(nested->window, nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400814 window_set_title(nested->window, "Wayland Nested");
815 nested->display = display;
816
817 window_set_user_data(nested->window, nested);
818 widget_set_redraw_handler(nested->widget, redraw_handler);
819 window_set_keyboard_focus_handler(nested->window,
820 keyboard_focus_handler);
821
822 nested_init_compositor(nested);
823
824 widget_schedule_resize(nested->widget, 400, 400);
825
826 return nested;
827}
828
829static void
830nested_destroy(struct nested *nested)
831{
832 widget_destroy(nested->widget);
833 window_destroy(nested->window);
834 free(nested);
835}
836
Neil Roberts1f020a12013-09-09 00:41:29 +0100837/*** blit renderer ***/
838
Neil Roberts47b87d52013-09-08 20:52:36 +0100839static void
840blit_surface_init(struct nested_surface *surface)
841{
842 struct nested_blit_surface *blit_surface =
U. Artie Eoff5cda4e32014-01-15 11:13:38 -0800843 xzalloc(sizeof *blit_surface);
Neil Roberts47b87d52013-09-08 20:52:36 +0100844
845 glGenTextures(1, &blit_surface->texture);
846 glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
847 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
848 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
849 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
850 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
851
852 surface->renderer_data = blit_surface;
853}
854
855static void
856blit_surface_fini(struct nested_surface *surface)
857{
858 struct nested_blit_surface *blit_surface = surface->renderer_data;
859
860 nested_buffer_reference(&blit_surface->buffer_ref, NULL);
861
862 glDeleteTextures(1, &blit_surface->texture);
863
864 free(blit_surface);
865}
866
867static void
868blit_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
869{
870 struct nested *nested = data;
871 struct nested_surface *surface;
872
873 wl_list_for_each(surface, &nested->surface_list, link)
874 flush_surface_frame_callback_list(surface, time);
875
876 if (callback)
877 wl_callback_destroy(callback);
878}
879
880static const struct wl_callback_listener blit_frame_listener = {
881 blit_frame_callback
882};
883
884static void
885blit_render_clients(struct nested *nested,
886 cairo_t *cr)
887{
888 struct nested_surface *s;
889 struct rectangle allocation;
890 struct wl_callback *callback;
891
892 widget_get_allocation(nested->widget, &allocation);
893
894 wl_list_for_each(s, &nested->surface_list, link) {
895 struct nested_blit_surface *blit_surface = s->renderer_data;
896
897 display_acquire_window_surface(nested->display,
898 nested->window, NULL);
899
900 glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
901 image_target_texture_2d(GL_TEXTURE_2D, s->image);
902
903 display_release_window_surface(nested->display,
904 nested->window);
905
906 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
907 cairo_set_source_surface(cr, blit_surface->cairo_surface,
908 allocation.x + 10,
909 allocation.y + 10);
910 cairo_rectangle(cr, allocation.x + 10,
911 allocation.y + 10,
912 allocation.width - 10,
913 allocation.height - 10);
914
915 cairo_fill(cr);
916 }
917
918 callback = wl_surface_frame(window_get_wl_surface(nested->window));
919 wl_callback_add_listener(callback, &blit_frame_listener, nested);
920}
921
922static void
923blit_surface_attach(struct nested_surface *surface,
924 struct nested_buffer *buffer)
925{
926 struct nested *nested = surface->nested;
927 struct nested_blit_surface *blit_surface = surface->renderer_data;
928 EGLint width, height;
929 cairo_device_t *device;
930
931 nested_buffer_reference(&blit_surface->buffer_ref, buffer);
932
933 if (blit_surface->cairo_surface)
934 cairo_surface_destroy(blit_surface->cairo_surface);
935
936 query_buffer(nested->egl_display, (void *) buffer->resource,
937 EGL_WIDTH, &width);
938 query_buffer(nested->egl_display, (void *) buffer->resource,
939 EGL_HEIGHT, &height);
940
941 device = display_get_cairo_device(nested->display);
942 blit_surface->cairo_surface =
943 cairo_gl_surface_create_for_texture(device,
944 CAIRO_CONTENT_COLOR_ALPHA,
945 blit_surface->texture,
946 width, height);
947}
948
949static const struct nested_renderer
950nested_blit_renderer = {
951 .surface_init = blit_surface_init,
952 .surface_fini = blit_surface_fini,
953 .render_clients = blit_render_clients,
954 .surface_attach = blit_surface_attach
955};
956
Neil Roberts1f020a12013-09-09 00:41:29 +0100957/*** subsurface renderer ***/
958
959static void
960ss_surface_init(struct nested_surface *surface)
961{
962 struct nested *nested = surface->nested;
963 struct wl_compositor *compositor =
964 display_get_compositor(nested->display);
965 struct nested_ss_surface *ss_surface =
U. Artie Eoff5cda4e32014-01-15 11:13:38 -0800966 xzalloc(sizeof *ss_surface);
Neil Roberts1f020a12013-09-09 00:41:29 +0100967 struct rectangle allocation;
968 struct wl_region *region;
969
970 ss_surface->widget =
971 window_add_subsurface(nested->window,
972 nested,
973 SUBSURFACE_SYNCHRONIZED);
974
Neil Roberts40d02282014-02-24 19:07:08 +0000975 widget_set_use_cairo(ss_surface->widget, 0);
976
Neil Roberts1f020a12013-09-09 00:41:29 +0100977 ss_surface->surface = widget_get_wl_surface(ss_surface->widget);
978 ss_surface->subsurface = widget_get_wl_subsurface(ss_surface->widget);
979
980 /* The toy toolkit gets confused about the pointer position
981 * when it gets motion events for a subsurface so we'll just
982 * disable input on it */
983 region = wl_compositor_create_region(compositor);
984 wl_surface_set_input_region(ss_surface->surface, region);
985 wl_region_destroy(region);
986
987 widget_get_allocation(nested->widget, &allocation);
988 wl_subsurface_set_position(ss_surface->subsurface,
989 allocation.x + 10,
990 allocation.y + 10);
991
992 surface->renderer_data = ss_surface;
993}
994
995static void
996ss_surface_fini(struct nested_surface *surface)
997{
998 struct nested_ss_surface *ss_surface = surface->renderer_data;
999
1000 widget_destroy(ss_surface->widget);
1001
1002 if (ss_surface->frame_callback)
1003 wl_callback_destroy(ss_surface->frame_callback);
1004
1005 free(ss_surface);
1006}
1007
1008static void
1009ss_render_clients(struct nested *nested,
1010 cairo_t *cr)
1011{
1012 /* The clients are composited by the parent compositor so we
1013 * don't need to do anything here */
1014}
1015
1016static void
1017ss_buffer_release(void *data, struct wl_buffer *wl_buffer)
1018{
1019 struct nested_buffer *buffer = data;
1020
1021 nested_buffer_reference(&buffer->parent_ref, NULL);
1022}
1023
1024static struct wl_buffer_listener ss_buffer_listener = {
1025 ss_buffer_release
1026};
1027
1028static void
1029ss_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
1030{
1031 struct nested_surface *surface = data;
1032 struct nested_ss_surface *ss_surface = surface->renderer_data;
1033
1034 flush_surface_frame_callback_list(surface, time);
1035
1036 if (callback)
1037 wl_callback_destroy(callback);
1038
1039 ss_surface->frame_callback = NULL;
1040}
1041
1042static const struct wl_callback_listener ss_frame_listener = {
1043 ss_frame_callback
1044};
1045
1046static void
1047ss_surface_attach(struct nested_surface *surface,
1048 struct nested_buffer *buffer)
1049{
1050 struct nested *nested = surface->nested;
1051 struct nested_ss_surface *ss_surface = surface->renderer_data;
1052 struct wl_buffer *parent_buffer;
1053 const pixman_box32_t *rects;
1054 int n_rects, i;
1055
1056 if (buffer) {
1057 /* Create a representation of the buffer in the parent
1058 * compositor if we haven't already */
1059 if (buffer->parent_buffer == NULL) {
1060 EGLDisplay *edpy = nested->egl_display;
1061 EGLImageKHR image = surface->image;
1062
1063 buffer->parent_buffer =
1064 create_wayland_buffer_from_image(edpy, image);
1065
1066 wl_buffer_add_listener(buffer->parent_buffer,
1067 &ss_buffer_listener,
1068 buffer);
1069 }
1070
1071 parent_buffer = buffer->parent_buffer;
1072
1073 /* We'll take a reference to the buffer while the parent
1074 * compositor is using it so that we won't report the release
1075 * event until the parent has also finished with it */
1076 nested_buffer_reference(&buffer->parent_ref, buffer);
1077 } else {
1078 parent_buffer = NULL;
1079 }
1080
1081 wl_surface_attach(ss_surface->surface, parent_buffer, 0, 0);
1082
1083 rects = pixman_region32_rectangles(&surface->pending.damage, &n_rects);
1084
1085 for (i = 0; i < n_rects; i++) {
1086 const pixman_box32_t *rect = rects + i;
1087 wl_surface_damage(ss_surface->surface,
1088 rect->x1,
1089 rect->y1,
1090 rect->x2 - rect->x1,
1091 rect->y2 - rect->y1);
1092 }
1093
1094 if (ss_surface->frame_callback)
1095 wl_callback_destroy(ss_surface->frame_callback);
1096
1097 ss_surface->frame_callback = wl_surface_frame(ss_surface->surface);
1098 wl_callback_add_listener(ss_surface->frame_callback,
1099 &ss_frame_listener,
1100 surface);
1101
1102 wl_surface_commit(ss_surface->surface);
1103}
1104
1105static const struct nested_renderer
1106nested_ss_renderer = {
1107 .surface_init = ss_surface_init,
1108 .surface_fini = ss_surface_fini,
1109 .render_clients = ss_render_clients,
1110 .surface_attach = ss_surface_attach
1111};
1112
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001113int
1114main(int argc, char *argv[])
1115{
1116 struct display *display;
1117 struct nested *nested;
1118
Bill Spitzak6df71aa2014-08-08 12:59:52 -07001119 if (parse_options(nested_options,
1120 ARRAY_LENGTH(nested_options), &argc, argv) > 1) {
1121 printf("Usage: %s [OPTIONS]\n --blit or -b\n", argv[0]);
1122 exit(1);
1123 }
Neil Robertsa5059eb2013-09-09 00:59:35 +01001124
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001125 display = display_create(&argc, argv);
1126 if (display == NULL) {
1127 fprintf(stderr, "failed to create display: %m\n");
1128 return -1;
1129 }
1130
1131 nested = nested_create(display);
1132
Kristian Høgsbergcb61dcf2013-08-07 09:50:12 -07001133 launch_client(nested, "weston-nested-client");
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001134
1135 display_run(display);
1136
1137 nested_destroy(nested);
1138 display_destroy(display);
1139
1140 return 0;
1141}