blob: c0e1935056ff88b32ab26a5c39549d1cbadfd2c1 [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
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040050struct nested {
51 struct display *display;
52 struct window *window;
53 struct widget *widget;
54 struct wl_display *child_display;
55 struct task child_task;
56
57 EGLDisplay egl_display;
58 struct program *texture_program;
59
60 struct wl_list surface_list;
Neil Roberts47b87d52013-09-08 20:52:36 +010061
62 const struct nested_renderer *renderer;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040063};
64
65struct nested_region {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040066 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040067 pixman_region32_t region;
68};
69
Neil Roberts1f020a12013-09-09 00:41:29 +010070struct nested_buffer_reference {
71 struct nested_buffer *buffer;
72 struct wl_listener destroy_listener;
73};
74
Neil Roberts6bf23872013-09-08 18:49:15 +010075struct nested_buffer {
76 struct wl_resource *resource;
77 struct wl_signal destroy_signal;
78 struct wl_listener destroy_listener;
79 uint32_t busy_count;
Neil Roberts6bf23872013-09-08 18:49:15 +010080
Neil Roberts1f020a12013-09-09 00:41:29 +010081 /* A buffer in the parent compositor representing the same
82 * data. This is created on-demand when the subsurface
83 * renderer is used */
84 struct wl_buffer *parent_buffer;
85 /* This reference is used to mark when the parent buffer has
86 * been attached to the subsurface. It will be unrefenced when
87 * we receive a buffer release event. That way we won't inform
88 * the client that the buffer is free until the parent
89 * compositor is also finished with it */
90 struct nested_buffer_reference parent_ref;
Neil Roberts6bf23872013-09-08 18:49:15 +010091};
92
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040093struct nested_surface {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040094 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040095 struct nested *nested;
96 EGLImageKHR *image;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040097 struct wl_list link;
Neil Roberts15a8d342013-09-08 19:49:02 +010098
Neil Robertsf9b25412013-09-09 15:13:09 +010099 struct wl_list frame_callback_list;
100
Neil Roberts15a8d342013-09-08 19:49:02 +0100101 struct {
102 /* wl_surface.attach */
103 int newly_attached;
104 struct nested_buffer *buffer;
105 struct wl_listener buffer_destroy_listener;
106
107 /* wl_surface.frame */
108 struct wl_list frame_callback_list;
Neil Roberts28557662013-09-08 20:24:14 +0100109
110 /* wl_surface.damage */
111 pixman_region32_t damage;
Neil Roberts15a8d342013-09-08 19:49:02 +0100112 } pending;
Neil Roberts47b87d52013-09-08 20:52:36 +0100113
114 void *renderer_data;
115};
116
117/* Data used for the blit renderer */
118struct nested_blit_surface {
119 struct nested_buffer_reference buffer_ref;
120 GLuint texture;
121 cairo_surface_t *cairo_surface;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400122};
123
Neil Roberts1f020a12013-09-09 00:41:29 +0100124/* Data used for the subsurface renderer */
125struct nested_ss_surface {
126 struct widget *widget;
127 struct wl_surface *surface;
128 struct wl_subsurface *subsurface;
129 struct wl_callback *frame_callback;
130};
131
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400132struct nested_frame_callback {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400133 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400134 struct wl_list link;
135};
136
Neil Roberts47b87d52013-09-08 20:52:36 +0100137struct nested_renderer {
138 void (* surface_init)(struct nested_surface *surface);
139 void (* surface_fini)(struct nested_surface *surface);
140 void (* render_clients)(struct nested *nested, cairo_t *cr);
141 void (* surface_attach)(struct nested_surface *surface,
142 struct nested_buffer *buffer);
143};
144
145static const struct nested_renderer nested_blit_renderer;
Neil Roberts1f020a12013-09-09 00:41:29 +0100146static const struct nested_renderer nested_ss_renderer;
Neil Roberts47b87d52013-09-08 20:52:36 +0100147
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400148static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
149static PFNEGLCREATEIMAGEKHRPROC create_image;
150static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
151static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
152static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
153static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
Neil Roberts1f020a12013-09-09 00:41:29 +0100154static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_wayland_buffer_from_image;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400155
156static void
Neil Roberts6bf23872013-09-08 18:49:15 +0100157nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
158{
159 struct nested_buffer *buffer =
160 container_of(listener, struct nested_buffer, destroy_listener);
161
162 wl_signal_emit(&buffer->destroy_signal, buffer);
Neil Roberts1f020a12013-09-09 00:41:29 +0100163
164 if (buffer->parent_buffer)
165 wl_buffer_destroy(buffer->parent_buffer);
166
Neil Roberts6bf23872013-09-08 18:49:15 +0100167 free(buffer);
168}
169
170static struct nested_buffer *
171nested_buffer_from_resource(struct wl_resource *resource)
172{
173 struct nested_buffer *buffer;
174 struct wl_listener *listener;
175
176 listener =
177 wl_resource_get_destroy_listener(resource,
178 nested_buffer_destroy_handler);
179
180 if (listener)
181 return container_of(listener, struct nested_buffer,
182 destroy_listener);
183
184 buffer = zalloc(sizeof *buffer);
185 if (buffer == NULL)
186 return NULL;
187
188 buffer->resource = resource;
189 wl_signal_init(&buffer->destroy_signal);
190 buffer->destroy_listener.notify = nested_buffer_destroy_handler;
191 wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
192
193 return buffer;
194}
195
196static void
197nested_buffer_reference_handle_destroy(struct wl_listener *listener,
198 void *data)
199{
200 struct nested_buffer_reference *ref =
201 container_of(listener, struct nested_buffer_reference,
202 destroy_listener);
203
204 assert((struct nested_buffer *)data == ref->buffer);
205 ref->buffer = NULL;
206}
207
208static void
209nested_buffer_reference(struct nested_buffer_reference *ref,
210 struct nested_buffer *buffer)
211{
212 if (buffer == ref->buffer)
213 return;
214
215 if (ref->buffer) {
216 ref->buffer->busy_count--;
217 if (ref->buffer->busy_count == 0) {
218 assert(wl_resource_get_client(ref->buffer->resource));
219 wl_resource_queue_event(ref->buffer->resource,
220 WL_BUFFER_RELEASE);
221 }
222 wl_list_remove(&ref->destroy_listener.link);
223 }
224
225 if (buffer) {
226 buffer->busy_count++;
227 wl_signal_add(&buffer->destroy_signal,
228 &ref->destroy_listener);
229
230 ref->destroy_listener.notify =
231 nested_buffer_reference_handle_destroy;
232 }
233
234 ref->buffer = buffer;
235}
236
237static void
Neil Robertsf9b25412013-09-09 15:13:09 +0100238flush_surface_frame_callback_list(struct nested_surface *surface,
239 uint32_t time)
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400240{
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400241 struct nested_frame_callback *nc, *next;
242
Neil Robertsf9b25412013-09-09 15:13:09 +0100243 wl_list_for_each_safe(nc, next, &surface->frame_callback_list, link) {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400244 wl_callback_send_done(nc->resource, time);
245 wl_resource_destroy(nc->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400246 }
Neil Robertsf9b25412013-09-09 15:13:09 +0100247 wl_list_init(&surface->frame_callback_list);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400248
249 /* FIXME: toytoolkit need a pre-block handler where we can
250 * call this. */
Neil Robertsf9b25412013-09-09 15:13:09 +0100251 wl_display_flush_clients(surface->nested->child_display);
252}
253
254static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400255redraw_handler(struct widget *widget, void *data)
256{
257 struct nested *nested = data;
258 cairo_surface_t *surface;
259 cairo_t *cr;
260 struct rectangle allocation;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400261
262 widget_get_allocation(nested->widget, &allocation);
263
264 surface = window_get_surface(nested->window);
265
266 cr = cairo_create(surface);
267 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
268 cairo_rectangle(cr,
269 allocation.x,
270 allocation.y,
271 allocation.width,
272 allocation.height);
273 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
274 cairo_fill(cr);
275
Neil Roberts47b87d52013-09-08 20:52:36 +0100276 nested->renderer->render_clients(nested, cr);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400277
278 cairo_destroy(cr);
279
280 cairo_surface_destroy(surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400281}
282
283static void
284keyboard_focus_handler(struct window *window,
285 struct input *device, void *data)
286{
287 struct nested *nested = data;
288
289 window_schedule_redraw(nested->window);
290}
291
292static void
293handle_child_data(struct task *task, uint32_t events)
294{
295 struct nested *nested = container_of(task, struct nested, child_task);
296 struct wl_event_loop *loop;
297
298 loop = wl_display_get_event_loop(nested->child_display);
299
300 wl_event_loop_dispatch(loop, -1);
301 wl_display_flush_clients(nested->child_display);
302}
303
304struct nested_client {
305 struct wl_client *client;
306 pid_t pid;
307};
308
309static struct nested_client *
310launch_client(struct nested *nested, const char *path)
311{
312 int sv[2];
313 pid_t pid;
314 struct nested_client *client;
315
316 client = malloc(sizeof *client);
317 if (client == NULL)
318 return NULL;
319
320 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
321 fprintf(stderr, "launch_client: "
322 "socketpair failed while launching '%s': %m\n",
323 path);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700324 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400325 return NULL;
326 }
327
328 pid = fork();
329 if (pid == -1) {
330 close(sv[0]);
331 close(sv[1]);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700332 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400333 fprintf(stderr, "launch_client: "
334 "fork failed while launching '%s': %m\n", path);
335 return NULL;
336 }
337
338 if (pid == 0) {
339 int clientfd;
340 char s[32];
341
342 /* SOCK_CLOEXEC closes both ends, so we dup the fd to
343 * get a non-CLOEXEC fd to pass through exec. */
344 clientfd = dup(sv[1]);
345 if (clientfd == -1) {
346 fprintf(stderr, "compositor: dup failed: %m\n");
347 exit(-1);
348 }
349
350 snprintf(s, sizeof s, "%d", clientfd);
351 setenv("WAYLAND_SOCKET", s, 1);
352
353 execl(path, path, NULL);
354
355 fprintf(stderr, "compositor: executing '%s' failed: %m\n",
356 path);
357 exit(-1);
358 }
359
360 close(sv[1]);
361
362 client->client = wl_client_create(nested->child_display, sv[0]);
363 if (!client->client) {
364 close(sv[0]);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700365 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400366 fprintf(stderr, "launch_client: "
367 "wl_client_create failed while launching '%s'.\n",
368 path);
369 return NULL;
370 }
371
372 client->pid = pid;
373
374 return client;
375}
376
377static void
378destroy_surface(struct wl_resource *resource)
379{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400380 struct nested_surface *surface = wl_resource_get_user_data(resource);
Neil Roberts47b87d52013-09-08 20:52:36 +0100381 struct nested *nested = surface->nested;
Neil Roberts15a8d342013-09-08 19:49:02 +0100382 struct nested_frame_callback *cb, *next;
383
384 wl_list_for_each_safe(cb, next,
Neil Robertsf9b25412013-09-09 15:13:09 +0100385 &surface->frame_callback_list, link)
386 wl_resource_destroy(cb->resource);
387
388 wl_list_for_each_safe(cb, next,
Neil Roberts15a8d342013-09-08 19:49:02 +0100389 &surface->pending.frame_callback_list, link)
390 wl_resource_destroy(cb->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400391
Neil Roberts28557662013-09-08 20:24:14 +0100392 pixman_region32_fini(&surface->pending.damage);
393
Neil Roberts47b87d52013-09-08 20:52:36 +0100394 nested->renderer->surface_fini(surface);
395
Neil Roberts8fbe3a62013-11-22 15:41:53 +0000396 wl_list_remove(&surface->link);
397
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400398 free(surface);
399}
400
401static void
402surface_destroy(struct wl_client *client, struct wl_resource *resource)
403{
404 wl_resource_destroy(resource);
405}
406
407static void
408surface_attach(struct wl_client *client,
409 struct wl_resource *resource,
410 struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
411{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400412 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400413 struct nested *nested = surface->nested;
Neil Roberts15a8d342013-09-08 19:49:02 +0100414 struct nested_buffer *buffer = NULL;
415
416 if (buffer_resource) {
417 int format;
418
419 if (!query_buffer(nested->egl_display, (void *) buffer_resource,
420 EGL_TEXTURE_FORMAT, &format)) {
421 wl_resource_post_error(buffer_resource,
422 WL_DISPLAY_ERROR_INVALID_OBJECT,
423 "attaching non-egl wl_buffer");
424 return;
425 }
426
427 switch (format) {
428 case EGL_TEXTURE_RGB:
429 case EGL_TEXTURE_RGBA:
430 break;
431 default:
432 wl_resource_post_error(buffer_resource,
433 WL_DISPLAY_ERROR_INVALID_OBJECT,
434 "invalid format");
435 return;
436 }
437
438 buffer = nested_buffer_from_resource(buffer_resource);
439 if (buffer == NULL) {
440 wl_client_post_no_memory(client);
441 return;
442 }
443 }
444
445 if (surface->pending.buffer)
446 wl_list_remove(&surface->pending.buffer_destroy_listener.link);
447
448 surface->pending.buffer = buffer;
449 surface->pending.newly_attached = 1;
450 if (buffer) {
451 wl_signal_add(&buffer->destroy_signal,
452 &surface->pending.buffer_destroy_listener);
453 }
454}
455
456static void
457nested_surface_attach(struct nested_surface *surface,
458 struct nested_buffer *buffer)
459{
460 struct nested *nested = surface->nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400461
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400462 if (surface->image != EGL_NO_IMAGE_KHR)
463 destroy_image(nested->egl_display, surface->image);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400464
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400465 surface->image = create_image(nested->egl_display, NULL,
Neil Roberts15a8d342013-09-08 19:49:02 +0100466 EGL_WAYLAND_BUFFER_WL, buffer->resource,
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700467 NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400468 if (surface->image == EGL_NO_IMAGE_KHR) {
469 fprintf(stderr, "failed to create img\n");
470 return;
471 }
472
Neil Roberts47b87d52013-09-08 20:52:36 +0100473 nested->renderer->surface_attach(surface, buffer);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400474}
475
476static void
477surface_damage(struct wl_client *client,
478 struct wl_resource *resource,
479 int32_t x, int32_t y, int32_t width, int32_t height)
480{
Neil Roberts28557662013-09-08 20:24:14 +0100481 struct nested_surface *surface = wl_resource_get_user_data(resource);
482
483 pixman_region32_union_rect(&surface->pending.damage,
484 &surface->pending.damage,
485 x, y, width, height);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400486}
487
488static void
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400489destroy_frame_callback(struct wl_resource *resource)
490{
491 struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
492
493 wl_list_remove(&callback->link);
494 free(callback);
495}
496
497static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400498surface_frame(struct wl_client *client,
499 struct wl_resource *resource, uint32_t id)
500{
501 struct nested_frame_callback *callback;
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400502 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400503
504 callback = malloc(sizeof *callback);
505 if (callback == NULL) {
506 wl_resource_post_no_memory(resource);
507 return;
508 }
509
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400510 callback->resource = wl_resource_create(client,
511 &wl_callback_interface, 1, id);
512 wl_resource_set_implementation(callback->resource, NULL, callback,
513 destroy_frame_callback);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400514
Neil Roberts15a8d342013-09-08 19:49:02 +0100515 wl_list_insert(surface->pending.frame_callback_list.prev,
516 &callback->link);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400517}
518
519static void
520surface_set_opaque_region(struct wl_client *client,
521 struct wl_resource *resource,
522 struct wl_resource *region_resource)
523{
524 fprintf(stderr, "surface_set_opaque_region\n");
525}
526
527static void
528surface_set_input_region(struct wl_client *client,
529 struct wl_resource *resource,
530 struct wl_resource *region_resource)
531{
532 fprintf(stderr, "surface_set_input_region\n");
533}
534
535static void
Neil Roberts28557662013-09-08 20:24:14 +0100536empty_region(pixman_region32_t *region)
537{
538 pixman_region32_fini(region);
539 pixman_region32_init(region);
540}
541
542static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400543surface_commit(struct wl_client *client, struct wl_resource *resource)
544{
Neil Roberts15a8d342013-09-08 19:49:02 +0100545 struct nested_surface *surface = wl_resource_get_user_data(resource);
546 struct nested *nested = surface->nested;
547
548 /* wl_surface.attach */
549 if (surface->pending.newly_attached)
550 nested_surface_attach(surface, surface->pending.buffer);
551
552 if (surface->pending.buffer) {
553 wl_list_remove(&surface->pending.buffer_destroy_listener.link);
554 surface->pending.buffer = NULL;
555 }
556 surface->pending.newly_attached = 0;
557
Neil Roberts28557662013-09-08 20:24:14 +0100558 /* wl_surface.damage */
559 empty_region(&surface->pending.damage);
560
Neil Roberts15a8d342013-09-08 19:49:02 +0100561 /* wl_surface.frame */
Neil Robertsf9b25412013-09-09 15:13:09 +0100562 wl_list_insert_list(&surface->frame_callback_list,
Neil Roberts15a8d342013-09-08 19:49:02 +0100563 &surface->pending.frame_callback_list);
564 wl_list_init(&surface->pending.frame_callback_list);
565
Neil Roberts1f020a12013-09-09 00:41:29 +0100566 /* FIXME: For the subsurface renderer we don't need to
567 * actually redraw the window. However we do want to cause a
568 * commit because the subsurface is synchronized. Ideally we
569 * would just queue the commit */
Neil Roberts15a8d342013-09-08 19:49:02 +0100570 window_schedule_redraw(nested->window);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400571}
572
573static void
574surface_set_buffer_transform(struct wl_client *client,
575 struct wl_resource *resource, int transform)
576{
577 fprintf(stderr, "surface_set_buffer_transform\n");
578}
579
580static const struct wl_surface_interface surface_interface = {
581 surface_destroy,
582 surface_attach,
583 surface_damage,
584 surface_frame,
585 surface_set_opaque_region,
586 surface_set_input_region,
587 surface_commit,
588 surface_set_buffer_transform
589};
590
591static void
Neil Roberts15a8d342013-09-08 19:49:02 +0100592surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
593{
594 struct nested_surface *surface =
595 container_of(listener, struct nested_surface,
596 pending.buffer_destroy_listener);
597
598 surface->pending.buffer = NULL;
599}
600
601static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400602compositor_create_surface(struct wl_client *client,
603 struct wl_resource *resource, uint32_t id)
604{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400605 struct nested *nested = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400606 struct nested_surface *surface;
607
Peter Huttererf3d62272013-08-08 11:57:05 +1000608 surface = zalloc(sizeof *surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400609 if (surface == NULL) {
610 wl_resource_post_no_memory(resource);
611 return;
612 }
613
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400614 surface->nested = nested;
615
Neil Robertsf9b25412013-09-09 15:13:09 +0100616 wl_list_init(&surface->frame_callback_list);
617
Neil Roberts15a8d342013-09-08 19:49:02 +0100618 wl_list_init(&surface->pending.frame_callback_list);
619 surface->pending.buffer_destroy_listener.notify =
620 surface_handle_pending_buffer_destroy;
Neil Roberts28557662013-09-08 20:24:14 +0100621 pixman_region32_init(&surface->pending.damage);
Neil Roberts15a8d342013-09-08 19:49:02 +0100622
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400623 display_acquire_window_surface(nested->display,
624 nested->window, NULL);
625
Neil Roberts47b87d52013-09-08 20:52:36 +0100626 nested->renderer->surface_init(surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400627
628 display_release_window_surface(nested->display, nested->window);
629
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400630 surface->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400631 wl_resource_create(client, &wl_surface_interface, 1, id);
632
633 wl_resource_set_implementation(surface->resource,
634 &surface_interface, surface,
635 destroy_surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400636
637 wl_list_insert(nested->surface_list.prev, &surface->link);
638}
639
640static void
641destroy_region(struct wl_resource *resource)
642{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400643 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400644
645 pixman_region32_fini(&region->region);
646 free(region);
647}
648
649static void
650region_destroy(struct wl_client *client, struct wl_resource *resource)
651{
652 wl_resource_destroy(resource);
653}
654
655static void
656region_add(struct wl_client *client, struct wl_resource *resource,
657 int32_t x, int32_t y, int32_t width, int32_t height)
658{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400659 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400660
661 pixman_region32_union_rect(&region->region, &region->region,
662 x, y, width, height);
663}
664
665static void
666region_subtract(struct wl_client *client, struct wl_resource *resource,
667 int32_t x, int32_t y, int32_t width, int32_t height)
668{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400669 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400670 pixman_region32_t rect;
671
672 pixman_region32_init_rect(&rect, x, y, width, height);
673 pixman_region32_subtract(&region->region, &region->region, &rect);
674 pixman_region32_fini(&rect);
675}
676
677static const struct wl_region_interface region_interface = {
678 region_destroy,
679 region_add,
680 region_subtract
681};
682
683static void
684compositor_create_region(struct wl_client *client,
685 struct wl_resource *resource, uint32_t id)
686{
687 struct nested_region *region;
688
689 region = malloc(sizeof *region);
690 if (region == NULL) {
691 wl_resource_post_no_memory(resource);
692 return;
693 }
694
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400695 pixman_region32_init(&region->region);
696
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400697 region->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400698 wl_resource_create(client, &wl_region_interface, 1, id);
699 wl_resource_set_implementation(region->resource, &region_interface,
700 region, destroy_region);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400701}
702
703static const struct wl_compositor_interface compositor_interface = {
704 compositor_create_surface,
705 compositor_create_region
706};
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400707
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400708static void
709compositor_bind(struct wl_client *client,
710 void *data, uint32_t version, uint32_t id)
711{
712 struct nested *nested = data;
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400713 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400714
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400715 resource = wl_resource_create(client, &wl_compositor_interface,
716 MIN(version, 3), id);
717 wl_resource_set_implementation(resource, &compositor_interface,
718 nested, NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400719}
720
721static int
722nested_init_compositor(struct nested *nested)
723{
724 const char *extensions;
725 struct wl_event_loop *loop;
Neil Roberts1f020a12013-09-09 00:41:29 +0100726 int use_ss_renderer = 0;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400727 int fd, ret;
728
729 wl_list_init(&nested->surface_list);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400730 nested->child_display = wl_display_create();
731 loop = wl_display_get_event_loop(nested->child_display);
732 fd = wl_event_loop_get_fd(loop);
733 nested->child_task.run = handle_child_data;
734 display_watch_fd(nested->display, fd,
735 EPOLLIN, &nested->child_task);
736
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400737 if (!wl_global_create(nested->child_display,
738 &wl_compositor_interface, 1,
739 nested, compositor_bind))
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400740 return -1;
741
742 wl_display_init_shm(nested->child_display);
743
744 nested->egl_display = display_get_egl_display(nested->display);
745 extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
746 if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
747 fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
748 return -1;
749 }
750
751 bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
752 unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
753 create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
754 destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
755 query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
756 image_target_texture_2d =
757 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
758
759 ret = bind_display(nested->egl_display, nested->child_display);
760 if (!ret) {
761 fprintf(stderr, "failed to bind wl_display\n");
762 return -1;
763 }
764
Neil Roberts1f020a12013-09-09 00:41:29 +0100765 if (display_has_subcompositor(nested->display)) {
766 const char *func = "eglCreateWaylandBufferFromImageWL";
767 const char *ext = "EGL_WL_create_wayland_buffer_from_image";
768
769 if (strstr(extensions, ext)) {
770 create_wayland_buffer_from_image =
771 (void *) eglGetProcAddress(func);
772 use_ss_renderer = 1;
773 }
774 }
775
776 if (use_ss_renderer) {
777 printf("Using subsurfaces to render client surfaces\n");
778 nested->renderer = &nested_ss_renderer;
779 } else {
780 printf("Using local compositing with blits to "
781 "render client surfaces\n");
782 nested->renderer = &nested_blit_renderer;
783 }
Neil Roberts47b87d52013-09-08 20:52:36 +0100784
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400785 return 0;
786}
787
788static struct nested *
789nested_create(struct display *display)
790{
791 struct nested *nested;
792
Peter Huttererf3d62272013-08-08 11:57:05 +1000793 nested = zalloc(sizeof *nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400794 if (nested == NULL)
795 return nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400796
797 nested->window = window_create(display);
Jason Ekstrandee7fefc2013-10-13 19:08:38 -0500798 nested->widget = window_frame_create(nested->window, nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400799 window_set_title(nested->window, "Wayland Nested");
800 nested->display = display;
801
802 window_set_user_data(nested->window, nested);
803 widget_set_redraw_handler(nested->widget, redraw_handler);
804 window_set_keyboard_focus_handler(nested->window,
805 keyboard_focus_handler);
806
807 nested_init_compositor(nested);
808
809 widget_schedule_resize(nested->widget, 400, 400);
810
811 return nested;
812}
813
814static void
815nested_destroy(struct nested *nested)
816{
817 widget_destroy(nested->widget);
818 window_destroy(nested->window);
819 free(nested);
820}
821
Neil Roberts1f020a12013-09-09 00:41:29 +0100822/*** blit renderer ***/
823
Neil Roberts47b87d52013-09-08 20:52:36 +0100824static void
825blit_surface_init(struct nested_surface *surface)
826{
827 struct nested_blit_surface *blit_surface =
828 zalloc(sizeof *blit_surface);
829
830 glGenTextures(1, &blit_surface->texture);
831 glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
832 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
833 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
834 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
835 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
836
837 surface->renderer_data = blit_surface;
838}
839
840static void
841blit_surface_fini(struct nested_surface *surface)
842{
843 struct nested_blit_surface *blit_surface = surface->renderer_data;
844
845 nested_buffer_reference(&blit_surface->buffer_ref, NULL);
846
847 glDeleteTextures(1, &blit_surface->texture);
848
849 free(blit_surface);
850}
851
852static void
853blit_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
854{
855 struct nested *nested = data;
856 struct nested_surface *surface;
857
858 wl_list_for_each(surface, &nested->surface_list, link)
859 flush_surface_frame_callback_list(surface, time);
860
861 if (callback)
862 wl_callback_destroy(callback);
863}
864
865static const struct wl_callback_listener blit_frame_listener = {
866 blit_frame_callback
867};
868
869static void
870blit_render_clients(struct nested *nested,
871 cairo_t *cr)
872{
873 struct nested_surface *s;
874 struct rectangle allocation;
875 struct wl_callback *callback;
876
877 widget_get_allocation(nested->widget, &allocation);
878
879 wl_list_for_each(s, &nested->surface_list, link) {
880 struct nested_blit_surface *blit_surface = s->renderer_data;
881
882 display_acquire_window_surface(nested->display,
883 nested->window, NULL);
884
885 glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
886 image_target_texture_2d(GL_TEXTURE_2D, s->image);
887
888 display_release_window_surface(nested->display,
889 nested->window);
890
891 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
892 cairo_set_source_surface(cr, blit_surface->cairo_surface,
893 allocation.x + 10,
894 allocation.y + 10);
895 cairo_rectangle(cr, allocation.x + 10,
896 allocation.y + 10,
897 allocation.width - 10,
898 allocation.height - 10);
899
900 cairo_fill(cr);
901 }
902
903 callback = wl_surface_frame(window_get_wl_surface(nested->window));
904 wl_callback_add_listener(callback, &blit_frame_listener, nested);
905}
906
907static void
908blit_surface_attach(struct nested_surface *surface,
909 struct nested_buffer *buffer)
910{
911 struct nested *nested = surface->nested;
912 struct nested_blit_surface *blit_surface = surface->renderer_data;
913 EGLint width, height;
914 cairo_device_t *device;
915
916 nested_buffer_reference(&blit_surface->buffer_ref, buffer);
917
918 if (blit_surface->cairo_surface)
919 cairo_surface_destroy(blit_surface->cairo_surface);
920
921 query_buffer(nested->egl_display, (void *) buffer->resource,
922 EGL_WIDTH, &width);
923 query_buffer(nested->egl_display, (void *) buffer->resource,
924 EGL_HEIGHT, &height);
925
926 device = display_get_cairo_device(nested->display);
927 blit_surface->cairo_surface =
928 cairo_gl_surface_create_for_texture(device,
929 CAIRO_CONTENT_COLOR_ALPHA,
930 blit_surface->texture,
931 width, height);
932}
933
934static const struct nested_renderer
935nested_blit_renderer = {
936 .surface_init = blit_surface_init,
937 .surface_fini = blit_surface_fini,
938 .render_clients = blit_render_clients,
939 .surface_attach = blit_surface_attach
940};
941
Neil Roberts1f020a12013-09-09 00:41:29 +0100942/*** subsurface renderer ***/
943
944static void
945ss_surface_init(struct nested_surface *surface)
946{
947 struct nested *nested = surface->nested;
948 struct wl_compositor *compositor =
949 display_get_compositor(nested->display);
950 struct nested_ss_surface *ss_surface =
951 zalloc(sizeof *ss_surface);
952 struct rectangle allocation;
953 struct wl_region *region;
954
955 ss_surface->widget =
956 window_add_subsurface(nested->window,
957 nested,
958 SUBSURFACE_SYNCHRONIZED);
959
960 ss_surface->surface = widget_get_wl_surface(ss_surface->widget);
961 ss_surface->subsurface = widget_get_wl_subsurface(ss_surface->widget);
962
963 /* The toy toolkit gets confused about the pointer position
964 * when it gets motion events for a subsurface so we'll just
965 * disable input on it */
966 region = wl_compositor_create_region(compositor);
967 wl_surface_set_input_region(ss_surface->surface, region);
968 wl_region_destroy(region);
969
970 widget_get_allocation(nested->widget, &allocation);
971 wl_subsurface_set_position(ss_surface->subsurface,
972 allocation.x + 10,
973 allocation.y + 10);
974
975 surface->renderer_data = ss_surface;
976}
977
978static void
979ss_surface_fini(struct nested_surface *surface)
980{
981 struct nested_ss_surface *ss_surface = surface->renderer_data;
982
983 widget_destroy(ss_surface->widget);
984
985 if (ss_surface->frame_callback)
986 wl_callback_destroy(ss_surface->frame_callback);
987
988 free(ss_surface);
989}
990
991static void
992ss_render_clients(struct nested *nested,
993 cairo_t *cr)
994{
995 /* The clients are composited by the parent compositor so we
996 * don't need to do anything here */
997}
998
999static void
1000ss_buffer_release(void *data, struct wl_buffer *wl_buffer)
1001{
1002 struct nested_buffer *buffer = data;
1003
1004 nested_buffer_reference(&buffer->parent_ref, NULL);
1005}
1006
1007static struct wl_buffer_listener ss_buffer_listener = {
1008 ss_buffer_release
1009};
1010
1011static void
1012ss_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
1013{
1014 struct nested_surface *surface = data;
1015 struct nested_ss_surface *ss_surface = surface->renderer_data;
1016
1017 flush_surface_frame_callback_list(surface, time);
1018
1019 if (callback)
1020 wl_callback_destroy(callback);
1021
1022 ss_surface->frame_callback = NULL;
1023}
1024
1025static const struct wl_callback_listener ss_frame_listener = {
1026 ss_frame_callback
1027};
1028
1029static void
1030ss_surface_attach(struct nested_surface *surface,
1031 struct nested_buffer *buffer)
1032{
1033 struct nested *nested = surface->nested;
1034 struct nested_ss_surface *ss_surface = surface->renderer_data;
1035 struct wl_buffer *parent_buffer;
1036 const pixman_box32_t *rects;
1037 int n_rects, i;
1038
1039 if (buffer) {
1040 /* Create a representation of the buffer in the parent
1041 * compositor if we haven't already */
1042 if (buffer->parent_buffer == NULL) {
1043 EGLDisplay *edpy = nested->egl_display;
1044 EGLImageKHR image = surface->image;
1045
1046 buffer->parent_buffer =
1047 create_wayland_buffer_from_image(edpy, image);
1048
1049 wl_buffer_add_listener(buffer->parent_buffer,
1050 &ss_buffer_listener,
1051 buffer);
1052 }
1053
1054 parent_buffer = buffer->parent_buffer;
1055
1056 /* We'll take a reference to the buffer while the parent
1057 * compositor is using it so that we won't report the release
1058 * event until the parent has also finished with it */
1059 nested_buffer_reference(&buffer->parent_ref, buffer);
1060 } else {
1061 parent_buffer = NULL;
1062 }
1063
1064 wl_surface_attach(ss_surface->surface, parent_buffer, 0, 0);
1065
1066 rects = pixman_region32_rectangles(&surface->pending.damage, &n_rects);
1067
1068 for (i = 0; i < n_rects; i++) {
1069 const pixman_box32_t *rect = rects + i;
1070 wl_surface_damage(ss_surface->surface,
1071 rect->x1,
1072 rect->y1,
1073 rect->x2 - rect->x1,
1074 rect->y2 - rect->y1);
1075 }
1076
1077 if (ss_surface->frame_callback)
1078 wl_callback_destroy(ss_surface->frame_callback);
1079
1080 ss_surface->frame_callback = wl_surface_frame(ss_surface->surface);
1081 wl_callback_add_listener(ss_surface->frame_callback,
1082 &ss_frame_listener,
1083 surface);
1084
1085 wl_surface_commit(ss_surface->surface);
1086}
1087
1088static const struct nested_renderer
1089nested_ss_renderer = {
1090 .surface_init = ss_surface_init,
1091 .surface_fini = ss_surface_fini,
1092 .render_clients = ss_render_clients,
1093 .surface_attach = ss_surface_attach
1094};
1095
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001096int
1097main(int argc, char *argv[])
1098{
1099 struct display *display;
1100 struct nested *nested;
1101
1102 display = display_create(&argc, argv);
1103 if (display == NULL) {
1104 fprintf(stderr, "failed to create display: %m\n");
1105 return -1;
1106 }
1107
1108 nested = nested_create(display);
1109
Kristian Høgsbergcb61dcf2013-08-07 09:50:12 -07001110 launch_client(nested, "weston-nested-client");
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001111
1112 display_run(display);
1113
1114 nested_destroy(nested);
1115 display_destroy(display);
1116
1117 return 0;
1118}