blob: 581e3186d5e899db27fba9e57bce1a1617cf99fe [file] [log] [blame]
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001/*
2 * Copyright © 2013 Intel Corporation
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include <stdint.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <cairo.h>
28#include <math.h>
29#include <assert.h>
30#include <pixman.h>
31#include <sys/epoll.h>
32#include <sys/socket.h>
33#include <unistd.h>
34
35#include <EGL/egl.h>
36#include <EGL/eglext.h>
37#include <GLES2/gl2.h>
38#include <GLES2/gl2ext.h>
39
40#include <cairo-gl.h>
41
42#include <wayland-client.h>
Kristian Høgsberg3c179332013-08-06 19:27:04 -070043#define WL_HIDE_DEPRECATED
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040044#include <wayland-server.h>
45
46#include "window.h"
47
Kristian Høgsberg919cddb2013-07-08 19:03:57 -040048#define MIN(x,y) (((x) < (y)) ? (x) : (y))
49
Neil Robertsa5059eb2013-09-09 00:59:35 +010050static int option_blit;
51
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040052struct nested {
53 struct display *display;
54 struct window *window;
55 struct widget *widget;
56 struct wl_display *child_display;
57 struct task child_task;
58
59 EGLDisplay egl_display;
60 struct program *texture_program;
61
62 struct wl_list surface_list;
Neil Roberts47b87d52013-09-08 20:52:36 +010063
64 const struct nested_renderer *renderer;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040065};
66
67struct nested_region {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040068 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040069 pixman_region32_t region;
70};
71
Neil Roberts1f020a12013-09-09 00:41:29 +010072struct nested_buffer_reference {
73 struct nested_buffer *buffer;
74 struct wl_listener destroy_listener;
75};
76
Neil Roberts6bf23872013-09-08 18:49:15 +010077struct nested_buffer {
78 struct wl_resource *resource;
79 struct wl_signal destroy_signal;
80 struct wl_listener destroy_listener;
81 uint32_t busy_count;
Neil Roberts6bf23872013-09-08 18:49:15 +010082
Neil Roberts1f020a12013-09-09 00:41:29 +010083 /* A buffer in the parent compositor representing the same
84 * data. This is created on-demand when the subsurface
85 * renderer is used */
86 struct wl_buffer *parent_buffer;
87 /* This reference is used to mark when the parent buffer has
88 * been attached to the subsurface. It will be unrefenced when
89 * we receive a buffer release event. That way we won't inform
90 * the client that the buffer is free until the parent
91 * compositor is also finished with it */
92 struct nested_buffer_reference parent_ref;
Neil Roberts6bf23872013-09-08 18:49:15 +010093};
94
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040095struct nested_surface {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040096 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040097 struct nested *nested;
98 EGLImageKHR *image;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040099 struct wl_list link;
Neil Roberts15a8d342013-09-08 19:49:02 +0100100
Neil Robertsf9b25412013-09-09 15:13:09 +0100101 struct wl_list frame_callback_list;
102
Neil Roberts15a8d342013-09-08 19:49:02 +0100103 struct {
104 /* wl_surface.attach */
105 int newly_attached;
106 struct nested_buffer *buffer;
107 struct wl_listener buffer_destroy_listener;
108
109 /* wl_surface.frame */
110 struct wl_list frame_callback_list;
Neil Roberts28557662013-09-08 20:24:14 +0100111
112 /* wl_surface.damage */
113 pixman_region32_t damage;
Neil Roberts15a8d342013-09-08 19:49:02 +0100114 } pending;
Neil Roberts47b87d52013-09-08 20:52:36 +0100115
116 void *renderer_data;
117};
118
119/* Data used for the blit renderer */
120struct nested_blit_surface {
121 struct nested_buffer_reference buffer_ref;
122 GLuint texture;
123 cairo_surface_t *cairo_surface;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400124};
125
Neil Roberts1f020a12013-09-09 00:41:29 +0100126/* Data used for the subsurface renderer */
127struct nested_ss_surface {
128 struct widget *widget;
129 struct wl_surface *surface;
130 struct wl_subsurface *subsurface;
131 struct wl_callback *frame_callback;
132};
133
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400134struct nested_frame_callback {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400135 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400136 struct wl_list link;
137};
138
Neil Roberts47b87d52013-09-08 20:52:36 +0100139struct nested_renderer {
140 void (* surface_init)(struct nested_surface *surface);
141 void (* surface_fini)(struct nested_surface *surface);
142 void (* render_clients)(struct nested *nested, cairo_t *cr);
143 void (* surface_attach)(struct nested_surface *surface,
144 struct nested_buffer *buffer);
145};
146
Neil Robertsa5059eb2013-09-09 00:59:35 +0100147static const struct weston_option nested_options[] = {
148 { WESTON_OPTION_BOOLEAN, "blit", 'b', &option_blit },
149};
150
Neil Roberts47b87d52013-09-08 20:52:36 +0100151static const struct nested_renderer nested_blit_renderer;
Neil Roberts1f020a12013-09-09 00:41:29 +0100152static const struct nested_renderer nested_ss_renderer;
Neil Roberts47b87d52013-09-08 20:52:36 +0100153
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400154static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
155static PFNEGLCREATEIMAGEKHRPROC create_image;
156static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
157static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
158static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
159static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
Neil Roberts1f020a12013-09-09 00:41:29 +0100160static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_wayland_buffer_from_image;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400161
162static void
Neil Roberts6bf23872013-09-08 18:49:15 +0100163nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
164{
165 struct nested_buffer *buffer =
166 container_of(listener, struct nested_buffer, destroy_listener);
167
168 wl_signal_emit(&buffer->destroy_signal, buffer);
Neil Roberts1f020a12013-09-09 00:41:29 +0100169
170 if (buffer->parent_buffer)
171 wl_buffer_destroy(buffer->parent_buffer);
172
Neil Roberts6bf23872013-09-08 18:49:15 +0100173 free(buffer);
174}
175
176static struct nested_buffer *
177nested_buffer_from_resource(struct wl_resource *resource)
178{
179 struct nested_buffer *buffer;
180 struct wl_listener *listener;
181
182 listener =
183 wl_resource_get_destroy_listener(resource,
184 nested_buffer_destroy_handler);
185
186 if (listener)
187 return container_of(listener, struct nested_buffer,
188 destroy_listener);
189
190 buffer = zalloc(sizeof *buffer);
191 if (buffer == NULL)
192 return NULL;
193
194 buffer->resource = resource;
195 wl_signal_init(&buffer->destroy_signal);
196 buffer->destroy_listener.notify = nested_buffer_destroy_handler;
197 wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
198
199 return buffer;
200}
201
202static void
203nested_buffer_reference_handle_destroy(struct wl_listener *listener,
204 void *data)
205{
206 struct nested_buffer_reference *ref =
207 container_of(listener, struct nested_buffer_reference,
208 destroy_listener);
209
210 assert((struct nested_buffer *)data == ref->buffer);
211 ref->buffer = NULL;
212}
213
214static void
215nested_buffer_reference(struct nested_buffer_reference *ref,
216 struct nested_buffer *buffer)
217{
218 if (buffer == ref->buffer)
219 return;
220
221 if (ref->buffer) {
222 ref->buffer->busy_count--;
223 if (ref->buffer->busy_count == 0) {
224 assert(wl_resource_get_client(ref->buffer->resource));
225 wl_resource_queue_event(ref->buffer->resource,
226 WL_BUFFER_RELEASE);
227 }
228 wl_list_remove(&ref->destroy_listener.link);
229 }
230
231 if (buffer) {
232 buffer->busy_count++;
233 wl_signal_add(&buffer->destroy_signal,
234 &ref->destroy_listener);
235
236 ref->destroy_listener.notify =
237 nested_buffer_reference_handle_destroy;
238 }
239
240 ref->buffer = buffer;
241}
242
243static void
Neil Robertsf9b25412013-09-09 15:13:09 +0100244flush_surface_frame_callback_list(struct nested_surface *surface,
245 uint32_t time)
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400246{
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400247 struct nested_frame_callback *nc, *next;
248
Neil Robertsf9b25412013-09-09 15:13:09 +0100249 wl_list_for_each_safe(nc, next, &surface->frame_callback_list, link) {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400250 wl_callback_send_done(nc->resource, time);
251 wl_resource_destroy(nc->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400252 }
Neil Robertsf9b25412013-09-09 15:13:09 +0100253 wl_list_init(&surface->frame_callback_list);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400254
255 /* FIXME: toytoolkit need a pre-block handler where we can
256 * call this. */
Neil Robertsf9b25412013-09-09 15:13:09 +0100257 wl_display_flush_clients(surface->nested->child_display);
258}
259
260static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400261redraw_handler(struct widget *widget, void *data)
262{
263 struct nested *nested = data;
264 cairo_surface_t *surface;
265 cairo_t *cr;
266 struct rectangle allocation;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400267
268 widget_get_allocation(nested->widget, &allocation);
269
270 surface = window_get_surface(nested->window);
271
272 cr = cairo_create(surface);
273 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
274 cairo_rectangle(cr,
275 allocation.x,
276 allocation.y,
277 allocation.width,
278 allocation.height);
279 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
280 cairo_fill(cr);
281
Neil Roberts47b87d52013-09-08 20:52:36 +0100282 nested->renderer->render_clients(nested, cr);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400283
284 cairo_destroy(cr);
285
286 cairo_surface_destroy(surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400287}
288
289static void
290keyboard_focus_handler(struct window *window,
291 struct input *device, void *data)
292{
293 struct nested *nested = data;
294
295 window_schedule_redraw(nested->window);
296}
297
298static void
299handle_child_data(struct task *task, uint32_t events)
300{
301 struct nested *nested = container_of(task, struct nested, child_task);
302 struct wl_event_loop *loop;
303
304 loop = wl_display_get_event_loop(nested->child_display);
305
306 wl_event_loop_dispatch(loop, -1);
307 wl_display_flush_clients(nested->child_display);
308}
309
310struct nested_client {
311 struct wl_client *client;
312 pid_t pid;
313};
314
315static struct nested_client *
316launch_client(struct nested *nested, const char *path)
317{
318 int sv[2];
319 pid_t pid;
320 struct nested_client *client;
321
322 client = malloc(sizeof *client);
323 if (client == NULL)
324 return NULL;
325
326 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
327 fprintf(stderr, "launch_client: "
328 "socketpair failed while launching '%s': %m\n",
329 path);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700330 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400331 return NULL;
332 }
333
334 pid = fork();
335 if (pid == -1) {
336 close(sv[0]);
337 close(sv[1]);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700338 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400339 fprintf(stderr, "launch_client: "
340 "fork failed while launching '%s': %m\n", path);
341 return NULL;
342 }
343
344 if (pid == 0) {
345 int clientfd;
346 char s[32];
347
348 /* SOCK_CLOEXEC closes both ends, so we dup the fd to
349 * get a non-CLOEXEC fd to pass through exec. */
350 clientfd = dup(sv[1]);
351 if (clientfd == -1) {
352 fprintf(stderr, "compositor: dup failed: %m\n");
353 exit(-1);
354 }
355
356 snprintf(s, sizeof s, "%d", clientfd);
357 setenv("WAYLAND_SOCKET", s, 1);
358
359 execl(path, path, NULL);
360
361 fprintf(stderr, "compositor: executing '%s' failed: %m\n",
362 path);
363 exit(-1);
364 }
365
366 close(sv[1]);
367
368 client->client = wl_client_create(nested->child_display, sv[0]);
369 if (!client->client) {
370 close(sv[0]);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700371 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400372 fprintf(stderr, "launch_client: "
373 "wl_client_create failed while launching '%s'.\n",
374 path);
375 return NULL;
376 }
377
378 client->pid = pid;
379
380 return client;
381}
382
383static void
384destroy_surface(struct wl_resource *resource)
385{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400386 struct nested_surface *surface = wl_resource_get_user_data(resource);
Neil Roberts47b87d52013-09-08 20:52:36 +0100387 struct nested *nested = surface->nested;
Neil Roberts15a8d342013-09-08 19:49:02 +0100388 struct nested_frame_callback *cb, *next;
389
390 wl_list_for_each_safe(cb, next,
Neil Robertsf9b25412013-09-09 15:13:09 +0100391 &surface->frame_callback_list, link)
392 wl_resource_destroy(cb->resource);
393
394 wl_list_for_each_safe(cb, next,
Neil Roberts15a8d342013-09-08 19:49:02 +0100395 &surface->pending.frame_callback_list, link)
396 wl_resource_destroy(cb->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400397
Neil Roberts28557662013-09-08 20:24:14 +0100398 pixman_region32_fini(&surface->pending.damage);
399
Neil Roberts47b87d52013-09-08 20:52:36 +0100400 nested->renderer->surface_fini(surface);
401
Neil Roberts8fbe3a62013-11-22 15:41:53 +0000402 wl_list_remove(&surface->link);
403
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400404 free(surface);
405}
406
407static void
408surface_destroy(struct wl_client *client, struct wl_resource *resource)
409{
410 wl_resource_destroy(resource);
411}
412
413static void
414surface_attach(struct wl_client *client,
415 struct wl_resource *resource,
416 struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
417{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400418 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400419 struct nested *nested = surface->nested;
Neil Roberts15a8d342013-09-08 19:49:02 +0100420 struct nested_buffer *buffer = NULL;
421
422 if (buffer_resource) {
423 int format;
424
425 if (!query_buffer(nested->egl_display, (void *) buffer_resource,
426 EGL_TEXTURE_FORMAT, &format)) {
427 wl_resource_post_error(buffer_resource,
428 WL_DISPLAY_ERROR_INVALID_OBJECT,
429 "attaching non-egl wl_buffer");
430 return;
431 }
432
433 switch (format) {
434 case EGL_TEXTURE_RGB:
435 case EGL_TEXTURE_RGBA:
436 break;
437 default:
438 wl_resource_post_error(buffer_resource,
439 WL_DISPLAY_ERROR_INVALID_OBJECT,
440 "invalid format");
441 return;
442 }
443
444 buffer = nested_buffer_from_resource(buffer_resource);
445 if (buffer == NULL) {
446 wl_client_post_no_memory(client);
447 return;
448 }
449 }
450
451 if (surface->pending.buffer)
452 wl_list_remove(&surface->pending.buffer_destroy_listener.link);
453
454 surface->pending.buffer = buffer;
455 surface->pending.newly_attached = 1;
456 if (buffer) {
457 wl_signal_add(&buffer->destroy_signal,
458 &surface->pending.buffer_destroy_listener);
459 }
460}
461
462static void
463nested_surface_attach(struct nested_surface *surface,
464 struct nested_buffer *buffer)
465{
466 struct nested *nested = surface->nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400467
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400468 if (surface->image != EGL_NO_IMAGE_KHR)
469 destroy_image(nested->egl_display, surface->image);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400470
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400471 surface->image = create_image(nested->egl_display, NULL,
Neil Roberts15a8d342013-09-08 19:49:02 +0100472 EGL_WAYLAND_BUFFER_WL, buffer->resource,
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700473 NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400474 if (surface->image == EGL_NO_IMAGE_KHR) {
475 fprintf(stderr, "failed to create img\n");
476 return;
477 }
478
Neil Roberts47b87d52013-09-08 20:52:36 +0100479 nested->renderer->surface_attach(surface, buffer);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400480}
481
482static void
483surface_damage(struct wl_client *client,
484 struct wl_resource *resource,
485 int32_t x, int32_t y, int32_t width, int32_t height)
486{
Neil Roberts28557662013-09-08 20:24:14 +0100487 struct nested_surface *surface = wl_resource_get_user_data(resource);
488
489 pixman_region32_union_rect(&surface->pending.damage,
490 &surface->pending.damage,
491 x, y, width, height);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400492}
493
494static void
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400495destroy_frame_callback(struct wl_resource *resource)
496{
497 struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
498
499 wl_list_remove(&callback->link);
500 free(callback);
501}
502
503static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400504surface_frame(struct wl_client *client,
505 struct wl_resource *resource, uint32_t id)
506{
507 struct nested_frame_callback *callback;
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400508 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400509
510 callback = malloc(sizeof *callback);
511 if (callback == NULL) {
512 wl_resource_post_no_memory(resource);
513 return;
514 }
515
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400516 callback->resource = wl_resource_create(client,
517 &wl_callback_interface, 1, id);
518 wl_resource_set_implementation(callback->resource, NULL, callback,
519 destroy_frame_callback);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400520
Neil Roberts15a8d342013-09-08 19:49:02 +0100521 wl_list_insert(surface->pending.frame_callback_list.prev,
522 &callback->link);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400523}
524
525static void
526surface_set_opaque_region(struct wl_client *client,
527 struct wl_resource *resource,
528 struct wl_resource *region_resource)
529{
530 fprintf(stderr, "surface_set_opaque_region\n");
531}
532
533static void
534surface_set_input_region(struct wl_client *client,
535 struct wl_resource *resource,
536 struct wl_resource *region_resource)
537{
538 fprintf(stderr, "surface_set_input_region\n");
539}
540
541static void
Neil Roberts28557662013-09-08 20:24:14 +0100542empty_region(pixman_region32_t *region)
543{
544 pixman_region32_fini(region);
545 pixman_region32_init(region);
546}
547
548static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400549surface_commit(struct wl_client *client, struct wl_resource *resource)
550{
Neil Roberts15a8d342013-09-08 19:49:02 +0100551 struct nested_surface *surface = wl_resource_get_user_data(resource);
552 struct nested *nested = surface->nested;
553
554 /* wl_surface.attach */
555 if (surface->pending.newly_attached)
556 nested_surface_attach(surface, surface->pending.buffer);
557
558 if (surface->pending.buffer) {
559 wl_list_remove(&surface->pending.buffer_destroy_listener.link);
560 surface->pending.buffer = NULL;
561 }
562 surface->pending.newly_attached = 0;
563
Neil Roberts28557662013-09-08 20:24:14 +0100564 /* wl_surface.damage */
565 empty_region(&surface->pending.damage);
566
Neil Roberts15a8d342013-09-08 19:49:02 +0100567 /* wl_surface.frame */
Neil Robertsf9b25412013-09-09 15:13:09 +0100568 wl_list_insert_list(&surface->frame_callback_list,
Neil Roberts15a8d342013-09-08 19:49:02 +0100569 &surface->pending.frame_callback_list);
570 wl_list_init(&surface->pending.frame_callback_list);
571
Neil Roberts1f020a12013-09-09 00:41:29 +0100572 /* FIXME: For the subsurface renderer we don't need to
573 * actually redraw the window. However we do want to cause a
574 * commit because the subsurface is synchronized. Ideally we
575 * would just queue the commit */
Neil Roberts15a8d342013-09-08 19:49:02 +0100576 window_schedule_redraw(nested->window);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400577}
578
579static void
580surface_set_buffer_transform(struct wl_client *client,
581 struct wl_resource *resource, int transform)
582{
583 fprintf(stderr, "surface_set_buffer_transform\n");
584}
585
586static const struct wl_surface_interface surface_interface = {
587 surface_destroy,
588 surface_attach,
589 surface_damage,
590 surface_frame,
591 surface_set_opaque_region,
592 surface_set_input_region,
593 surface_commit,
594 surface_set_buffer_transform
595};
596
597static void
Neil Roberts15a8d342013-09-08 19:49:02 +0100598surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
599{
600 struct nested_surface *surface =
601 container_of(listener, struct nested_surface,
602 pending.buffer_destroy_listener);
603
604 surface->pending.buffer = NULL;
605}
606
607static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400608compositor_create_surface(struct wl_client *client,
609 struct wl_resource *resource, uint32_t id)
610{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400611 struct nested *nested = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400612 struct nested_surface *surface;
613
Peter Huttererf3d62272013-08-08 11:57:05 +1000614 surface = zalloc(sizeof *surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400615 if (surface == NULL) {
616 wl_resource_post_no_memory(resource);
617 return;
618 }
619
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400620 surface->nested = nested;
621
Neil Robertsf9b25412013-09-09 15:13:09 +0100622 wl_list_init(&surface->frame_callback_list);
623
Neil Roberts15a8d342013-09-08 19:49:02 +0100624 wl_list_init(&surface->pending.frame_callback_list);
625 surface->pending.buffer_destroy_listener.notify =
626 surface_handle_pending_buffer_destroy;
Neil Roberts28557662013-09-08 20:24:14 +0100627 pixman_region32_init(&surface->pending.damage);
Neil Roberts15a8d342013-09-08 19:49:02 +0100628
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400629 display_acquire_window_surface(nested->display,
630 nested->window, NULL);
631
Neil Roberts47b87d52013-09-08 20:52:36 +0100632 nested->renderer->surface_init(surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400633
634 display_release_window_surface(nested->display, nested->window);
635
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400636 surface->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400637 wl_resource_create(client, &wl_surface_interface, 1, id);
638
639 wl_resource_set_implementation(surface->resource,
640 &surface_interface, surface,
641 destroy_surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400642
643 wl_list_insert(nested->surface_list.prev, &surface->link);
644}
645
646static void
647destroy_region(struct wl_resource *resource)
648{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400649 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400650
651 pixman_region32_fini(&region->region);
652 free(region);
653}
654
655static void
656region_destroy(struct wl_client *client, struct wl_resource *resource)
657{
658 wl_resource_destroy(resource);
659}
660
661static void
662region_add(struct wl_client *client, struct wl_resource *resource,
663 int32_t x, int32_t y, int32_t width, int32_t height)
664{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400665 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400666
667 pixman_region32_union_rect(&region->region, &region->region,
668 x, y, width, height);
669}
670
671static void
672region_subtract(struct wl_client *client, struct wl_resource *resource,
673 int32_t x, int32_t y, int32_t width, int32_t height)
674{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400675 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400676 pixman_region32_t rect;
677
678 pixman_region32_init_rect(&rect, x, y, width, height);
679 pixman_region32_subtract(&region->region, &region->region, &rect);
680 pixman_region32_fini(&rect);
681}
682
683static const struct wl_region_interface region_interface = {
684 region_destroy,
685 region_add,
686 region_subtract
687};
688
689static void
690compositor_create_region(struct wl_client *client,
691 struct wl_resource *resource, uint32_t id)
692{
693 struct nested_region *region;
694
695 region = malloc(sizeof *region);
696 if (region == NULL) {
697 wl_resource_post_no_memory(resource);
698 return;
699 }
700
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400701 pixman_region32_init(&region->region);
702
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400703 region->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400704 wl_resource_create(client, &wl_region_interface, 1, id);
705 wl_resource_set_implementation(region->resource, &region_interface,
706 region, destroy_region);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400707}
708
709static const struct wl_compositor_interface compositor_interface = {
710 compositor_create_surface,
711 compositor_create_region
712};
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400713
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400714static void
715compositor_bind(struct wl_client *client,
716 void *data, uint32_t version, uint32_t id)
717{
718 struct nested *nested = data;
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400719 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400720
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400721 resource = wl_resource_create(client, &wl_compositor_interface,
722 MIN(version, 3), id);
723 wl_resource_set_implementation(resource, &compositor_interface,
724 nested, NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400725}
726
727static int
728nested_init_compositor(struct nested *nested)
729{
730 const char *extensions;
731 struct wl_event_loop *loop;
Neil Roberts1f020a12013-09-09 00:41:29 +0100732 int use_ss_renderer = 0;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400733 int fd, ret;
734
735 wl_list_init(&nested->surface_list);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400736 nested->child_display = wl_display_create();
737 loop = wl_display_get_event_loop(nested->child_display);
738 fd = wl_event_loop_get_fd(loop);
739 nested->child_task.run = handle_child_data;
740 display_watch_fd(nested->display, fd,
741 EPOLLIN, &nested->child_task);
742
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400743 if (!wl_global_create(nested->child_display,
744 &wl_compositor_interface, 1,
745 nested, compositor_bind))
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400746 return -1;
747
748 wl_display_init_shm(nested->child_display);
749
750 nested->egl_display = display_get_egl_display(nested->display);
751 extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
752 if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
753 fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
754 return -1;
755 }
756
757 bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
758 unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
759 create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
760 destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
761 query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
762 image_target_texture_2d =
763 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
764
765 ret = bind_display(nested->egl_display, nested->child_display);
766 if (!ret) {
767 fprintf(stderr, "failed to bind wl_display\n");
768 return -1;
769 }
770
Neil Roberts1f020a12013-09-09 00:41:29 +0100771 if (display_has_subcompositor(nested->display)) {
772 const char *func = "eglCreateWaylandBufferFromImageWL";
773 const char *ext = "EGL_WL_create_wayland_buffer_from_image";
774
775 if (strstr(extensions, ext)) {
776 create_wayland_buffer_from_image =
777 (void *) eglGetProcAddress(func);
778 use_ss_renderer = 1;
779 }
780 }
781
Neil Robertsa5059eb2013-09-09 00:59:35 +0100782 if (option_blit)
783 use_ss_renderer = 0;
784
Neil Roberts1f020a12013-09-09 00:41:29 +0100785 if (use_ss_renderer) {
786 printf("Using subsurfaces to render client surfaces\n");
787 nested->renderer = &nested_ss_renderer;
788 } else {
789 printf("Using local compositing with blits to "
790 "render client surfaces\n");
791 nested->renderer = &nested_blit_renderer;
792 }
Neil Roberts47b87d52013-09-08 20:52:36 +0100793
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400794 return 0;
795}
796
797static struct nested *
798nested_create(struct display *display)
799{
800 struct nested *nested;
801
Peter Huttererf3d62272013-08-08 11:57:05 +1000802 nested = zalloc(sizeof *nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400803 if (nested == NULL)
804 return nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400805
806 nested->window = window_create(display);
Jason Ekstrandee7fefc2013-10-13 19:08:38 -0500807 nested->widget = window_frame_create(nested->window, nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400808 window_set_title(nested->window, "Wayland Nested");
809 nested->display = display;
810
811 window_set_user_data(nested->window, nested);
812 widget_set_redraw_handler(nested->widget, redraw_handler);
813 window_set_keyboard_focus_handler(nested->window,
814 keyboard_focus_handler);
815
816 nested_init_compositor(nested);
817
818 widget_schedule_resize(nested->widget, 400, 400);
819
820 return nested;
821}
822
823static void
824nested_destroy(struct nested *nested)
825{
826 widget_destroy(nested->widget);
827 window_destroy(nested->window);
828 free(nested);
829}
830
Neil Roberts1f020a12013-09-09 00:41:29 +0100831/*** blit renderer ***/
832
Neil Roberts47b87d52013-09-08 20:52:36 +0100833static void
834blit_surface_init(struct nested_surface *surface)
835{
836 struct nested_blit_surface *blit_surface =
837 zalloc(sizeof *blit_surface);
838
839 glGenTextures(1, &blit_surface->texture);
840 glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
841 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
842 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
843 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
844 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
845
846 surface->renderer_data = blit_surface;
847}
848
849static void
850blit_surface_fini(struct nested_surface *surface)
851{
852 struct nested_blit_surface *blit_surface = surface->renderer_data;
853
854 nested_buffer_reference(&blit_surface->buffer_ref, NULL);
855
856 glDeleteTextures(1, &blit_surface->texture);
857
858 free(blit_surface);
859}
860
861static void
862blit_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
863{
864 struct nested *nested = data;
865 struct nested_surface *surface;
866
867 wl_list_for_each(surface, &nested->surface_list, link)
868 flush_surface_frame_callback_list(surface, time);
869
870 if (callback)
871 wl_callback_destroy(callback);
872}
873
874static const struct wl_callback_listener blit_frame_listener = {
875 blit_frame_callback
876};
877
878static void
879blit_render_clients(struct nested *nested,
880 cairo_t *cr)
881{
882 struct nested_surface *s;
883 struct rectangle allocation;
884 struct wl_callback *callback;
885
886 widget_get_allocation(nested->widget, &allocation);
887
888 wl_list_for_each(s, &nested->surface_list, link) {
889 struct nested_blit_surface *blit_surface = s->renderer_data;
890
891 display_acquire_window_surface(nested->display,
892 nested->window, NULL);
893
894 glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
895 image_target_texture_2d(GL_TEXTURE_2D, s->image);
896
897 display_release_window_surface(nested->display,
898 nested->window);
899
900 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
901 cairo_set_source_surface(cr, blit_surface->cairo_surface,
902 allocation.x + 10,
903 allocation.y + 10);
904 cairo_rectangle(cr, allocation.x + 10,
905 allocation.y + 10,
906 allocation.width - 10,
907 allocation.height - 10);
908
909 cairo_fill(cr);
910 }
911
912 callback = wl_surface_frame(window_get_wl_surface(nested->window));
913 wl_callback_add_listener(callback, &blit_frame_listener, nested);
914}
915
916static void
917blit_surface_attach(struct nested_surface *surface,
918 struct nested_buffer *buffer)
919{
920 struct nested *nested = surface->nested;
921 struct nested_blit_surface *blit_surface = surface->renderer_data;
922 EGLint width, height;
923 cairo_device_t *device;
924
925 nested_buffer_reference(&blit_surface->buffer_ref, buffer);
926
927 if (blit_surface->cairo_surface)
928 cairo_surface_destroy(blit_surface->cairo_surface);
929
930 query_buffer(nested->egl_display, (void *) buffer->resource,
931 EGL_WIDTH, &width);
932 query_buffer(nested->egl_display, (void *) buffer->resource,
933 EGL_HEIGHT, &height);
934
935 device = display_get_cairo_device(nested->display);
936 blit_surface->cairo_surface =
937 cairo_gl_surface_create_for_texture(device,
938 CAIRO_CONTENT_COLOR_ALPHA,
939 blit_surface->texture,
940 width, height);
941}
942
943static const struct nested_renderer
944nested_blit_renderer = {
945 .surface_init = blit_surface_init,
946 .surface_fini = blit_surface_fini,
947 .render_clients = blit_render_clients,
948 .surface_attach = blit_surface_attach
949};
950
Neil Roberts1f020a12013-09-09 00:41:29 +0100951/*** subsurface renderer ***/
952
953static void
954ss_surface_init(struct nested_surface *surface)
955{
956 struct nested *nested = surface->nested;
957 struct wl_compositor *compositor =
958 display_get_compositor(nested->display);
959 struct nested_ss_surface *ss_surface =
960 zalloc(sizeof *ss_surface);
961 struct rectangle allocation;
962 struct wl_region *region;
963
964 ss_surface->widget =
965 window_add_subsurface(nested->window,
966 nested,
967 SUBSURFACE_SYNCHRONIZED);
968
969 ss_surface->surface = widget_get_wl_surface(ss_surface->widget);
970 ss_surface->subsurface = widget_get_wl_subsurface(ss_surface->widget);
971
972 /* The toy toolkit gets confused about the pointer position
973 * when it gets motion events for a subsurface so we'll just
974 * disable input on it */
975 region = wl_compositor_create_region(compositor);
976 wl_surface_set_input_region(ss_surface->surface, region);
977 wl_region_destroy(region);
978
979 widget_get_allocation(nested->widget, &allocation);
980 wl_subsurface_set_position(ss_surface->subsurface,
981 allocation.x + 10,
982 allocation.y + 10);
983
984 surface->renderer_data = ss_surface;
985}
986
987static void
988ss_surface_fini(struct nested_surface *surface)
989{
990 struct nested_ss_surface *ss_surface = surface->renderer_data;
991
992 widget_destroy(ss_surface->widget);
993
994 if (ss_surface->frame_callback)
995 wl_callback_destroy(ss_surface->frame_callback);
996
997 free(ss_surface);
998}
999
1000static void
1001ss_render_clients(struct nested *nested,
1002 cairo_t *cr)
1003{
1004 /* The clients are composited by the parent compositor so we
1005 * don't need to do anything here */
1006}
1007
1008static void
1009ss_buffer_release(void *data, struct wl_buffer *wl_buffer)
1010{
1011 struct nested_buffer *buffer = data;
1012
1013 nested_buffer_reference(&buffer->parent_ref, NULL);
1014}
1015
1016static struct wl_buffer_listener ss_buffer_listener = {
1017 ss_buffer_release
1018};
1019
1020static void
1021ss_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
1022{
1023 struct nested_surface *surface = data;
1024 struct nested_ss_surface *ss_surface = surface->renderer_data;
1025
1026 flush_surface_frame_callback_list(surface, time);
1027
1028 if (callback)
1029 wl_callback_destroy(callback);
1030
1031 ss_surface->frame_callback = NULL;
1032}
1033
1034static const struct wl_callback_listener ss_frame_listener = {
1035 ss_frame_callback
1036};
1037
1038static void
1039ss_surface_attach(struct nested_surface *surface,
1040 struct nested_buffer *buffer)
1041{
1042 struct nested *nested = surface->nested;
1043 struct nested_ss_surface *ss_surface = surface->renderer_data;
1044 struct wl_buffer *parent_buffer;
1045 const pixman_box32_t *rects;
1046 int n_rects, i;
1047
1048 if (buffer) {
1049 /* Create a representation of the buffer in the parent
1050 * compositor if we haven't already */
1051 if (buffer->parent_buffer == NULL) {
1052 EGLDisplay *edpy = nested->egl_display;
1053 EGLImageKHR image = surface->image;
1054
1055 buffer->parent_buffer =
1056 create_wayland_buffer_from_image(edpy, image);
1057
1058 wl_buffer_add_listener(buffer->parent_buffer,
1059 &ss_buffer_listener,
1060 buffer);
1061 }
1062
1063 parent_buffer = buffer->parent_buffer;
1064
1065 /* We'll take a reference to the buffer while the parent
1066 * compositor is using it so that we won't report the release
1067 * event until the parent has also finished with it */
1068 nested_buffer_reference(&buffer->parent_ref, buffer);
1069 } else {
1070 parent_buffer = NULL;
1071 }
1072
1073 wl_surface_attach(ss_surface->surface, parent_buffer, 0, 0);
1074
1075 rects = pixman_region32_rectangles(&surface->pending.damage, &n_rects);
1076
1077 for (i = 0; i < n_rects; i++) {
1078 const pixman_box32_t *rect = rects + i;
1079 wl_surface_damage(ss_surface->surface,
1080 rect->x1,
1081 rect->y1,
1082 rect->x2 - rect->x1,
1083 rect->y2 - rect->y1);
1084 }
1085
1086 if (ss_surface->frame_callback)
1087 wl_callback_destroy(ss_surface->frame_callback);
1088
1089 ss_surface->frame_callback = wl_surface_frame(ss_surface->surface);
1090 wl_callback_add_listener(ss_surface->frame_callback,
1091 &ss_frame_listener,
1092 surface);
1093
1094 wl_surface_commit(ss_surface->surface);
1095}
1096
1097static const struct nested_renderer
1098nested_ss_renderer = {
1099 .surface_init = ss_surface_init,
1100 .surface_fini = ss_surface_fini,
1101 .render_clients = ss_render_clients,
1102 .surface_attach = ss_surface_attach
1103};
1104
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001105int
1106main(int argc, char *argv[])
1107{
1108 struct display *display;
1109 struct nested *nested;
1110
Neil Robertsa5059eb2013-09-09 00:59:35 +01001111 parse_options(nested_options,
1112 ARRAY_LENGTH(nested_options), &argc, argv);
1113
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001114 display = display_create(&argc, argv);
1115 if (display == NULL) {
1116 fprintf(stderr, "failed to create display: %m\n");
1117 return -1;
1118 }
1119
1120 nested = nested_create(display);
1121
Kristian Høgsbergcb61dcf2013-08-07 09:50:12 -07001122 launch_client(nested, "weston-nested-client");
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001123
1124 display_run(display);
1125
1126 nested_destroy(nested);
1127 display_destroy(display);
1128
1129 return 0;
1130}