blob: f758fdc2c193c509aab2a4a36d4d0e6ab4a595ee [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
Jon Cruz35b2eaa2015-06-15 15:37:08 -070049#include "shared/helpers.h"
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040050#include "window.h"
51
Kristian Høgsberg919cddb2013-07-08 19:03:57 -040052#define MIN(x,y) (((x) < (y)) ? (x) : (y))
53
Kristian Høgsbergc2010b32013-12-17 10:40:01 -080054#ifndef EGL_WL_create_wayland_buffer_from_image
55#define EGL_WL_create_wayland_buffer_from_image 1
56
57#ifdef EGL_EGLEXT_PROTOTYPES
58EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image);
59#endif
60typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (EGLDisplay dpy, EGLImageKHR image);
61
62#endif
63
Neil Robertsa5059eb2013-09-09 00:59:35 +010064static int option_blit;
65
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040066struct nested {
67 struct display *display;
68 struct window *window;
69 struct widget *widget;
70 struct wl_display *child_display;
71 struct task child_task;
72
73 EGLDisplay egl_display;
74 struct program *texture_program;
75
76 struct wl_list surface_list;
Neil Roberts47b87d52013-09-08 20:52:36 +010077
78 const struct nested_renderer *renderer;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040079};
80
81struct nested_region {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040082 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040083 pixman_region32_t region;
84};
85
Neil Roberts1f020a12013-09-09 00:41:29 +010086struct nested_buffer_reference {
87 struct nested_buffer *buffer;
88 struct wl_listener destroy_listener;
89};
90
Neil Roberts6bf23872013-09-08 18:49:15 +010091struct nested_buffer {
92 struct wl_resource *resource;
93 struct wl_signal destroy_signal;
94 struct wl_listener destroy_listener;
95 uint32_t busy_count;
Neil Roberts6bf23872013-09-08 18:49:15 +010096
Neil Roberts1f020a12013-09-09 00:41:29 +010097 /* A buffer in the parent compositor representing the same
98 * data. This is created on-demand when the subsurface
99 * renderer is used */
100 struct wl_buffer *parent_buffer;
101 /* This reference is used to mark when the parent buffer has
102 * been attached to the subsurface. It will be unrefenced when
103 * we receive a buffer release event. That way we won't inform
104 * the client that the buffer is free until the parent
105 * compositor is also finished with it */
106 struct nested_buffer_reference parent_ref;
Neil Roberts6bf23872013-09-08 18:49:15 +0100107};
108
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400109struct nested_surface {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400110 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400111 struct nested *nested;
112 EGLImageKHR *image;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400113 struct wl_list link;
Neil Roberts15a8d342013-09-08 19:49:02 +0100114
Neil Robertsf9b25412013-09-09 15:13:09 +0100115 struct wl_list frame_callback_list;
116
Neil Roberts15a8d342013-09-08 19:49:02 +0100117 struct {
118 /* wl_surface.attach */
119 int newly_attached;
120 struct nested_buffer *buffer;
121 struct wl_listener buffer_destroy_listener;
122
123 /* wl_surface.frame */
124 struct wl_list frame_callback_list;
Neil Roberts28557662013-09-08 20:24:14 +0100125
126 /* wl_surface.damage */
127 pixman_region32_t damage;
Neil Roberts15a8d342013-09-08 19:49:02 +0100128 } pending;
Neil Roberts47b87d52013-09-08 20:52:36 +0100129
130 void *renderer_data;
131};
132
133/* Data used for the blit renderer */
134struct nested_blit_surface {
135 struct nested_buffer_reference buffer_ref;
136 GLuint texture;
137 cairo_surface_t *cairo_surface;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400138};
139
Neil Roberts1f020a12013-09-09 00:41:29 +0100140/* Data used for the subsurface renderer */
141struct nested_ss_surface {
142 struct widget *widget;
143 struct wl_surface *surface;
144 struct wl_subsurface *subsurface;
145 struct wl_callback *frame_callback;
146};
147
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400148struct nested_frame_callback {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400149 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400150 struct wl_list link;
151};
152
Neil Roberts47b87d52013-09-08 20:52:36 +0100153struct nested_renderer {
154 void (* surface_init)(struct nested_surface *surface);
155 void (* surface_fini)(struct nested_surface *surface);
156 void (* render_clients)(struct nested *nested, cairo_t *cr);
157 void (* surface_attach)(struct nested_surface *surface,
158 struct nested_buffer *buffer);
159};
160
Neil Robertsa5059eb2013-09-09 00:59:35 +0100161static const struct weston_option nested_options[] = {
162 { WESTON_OPTION_BOOLEAN, "blit", 'b', &option_blit },
163};
164
Neil Roberts47b87d52013-09-08 20:52:36 +0100165static const struct nested_renderer nested_blit_renderer;
Neil Roberts1f020a12013-09-09 00:41:29 +0100166static const struct nested_renderer nested_ss_renderer;
Neil Roberts47b87d52013-09-08 20:52:36 +0100167
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400168static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
169static PFNEGLCREATEIMAGEKHRPROC create_image;
170static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
171static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
172static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
173static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
Neil Roberts1f020a12013-09-09 00:41:29 +0100174static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_wayland_buffer_from_image;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400175
176static void
Neil Roberts6bf23872013-09-08 18:49:15 +0100177nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
178{
179 struct nested_buffer *buffer =
180 container_of(listener, struct nested_buffer, destroy_listener);
181
182 wl_signal_emit(&buffer->destroy_signal, buffer);
Neil Roberts1f020a12013-09-09 00:41:29 +0100183
184 if (buffer->parent_buffer)
185 wl_buffer_destroy(buffer->parent_buffer);
186
Neil Roberts6bf23872013-09-08 18:49:15 +0100187 free(buffer);
188}
189
190static struct nested_buffer *
191nested_buffer_from_resource(struct wl_resource *resource)
192{
193 struct nested_buffer *buffer;
194 struct wl_listener *listener;
195
196 listener =
197 wl_resource_get_destroy_listener(resource,
198 nested_buffer_destroy_handler);
199
200 if (listener)
201 return container_of(listener, struct nested_buffer,
202 destroy_listener);
203
204 buffer = zalloc(sizeof *buffer);
205 if (buffer == NULL)
206 return NULL;
207
208 buffer->resource = resource;
209 wl_signal_init(&buffer->destroy_signal);
210 buffer->destroy_listener.notify = nested_buffer_destroy_handler;
211 wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
212
213 return buffer;
214}
215
216static void
217nested_buffer_reference_handle_destroy(struct wl_listener *listener,
218 void *data)
219{
220 struct nested_buffer_reference *ref =
221 container_of(listener, struct nested_buffer_reference,
222 destroy_listener);
223
224 assert((struct nested_buffer *)data == ref->buffer);
225 ref->buffer = NULL;
226}
227
228static void
229nested_buffer_reference(struct nested_buffer_reference *ref,
230 struct nested_buffer *buffer)
231{
232 if (buffer == ref->buffer)
233 return;
234
235 if (ref->buffer) {
236 ref->buffer->busy_count--;
237 if (ref->buffer->busy_count == 0) {
238 assert(wl_resource_get_client(ref->buffer->resource));
239 wl_resource_queue_event(ref->buffer->resource,
240 WL_BUFFER_RELEASE);
241 }
242 wl_list_remove(&ref->destroy_listener.link);
243 }
244
245 if (buffer) {
246 buffer->busy_count++;
247 wl_signal_add(&buffer->destroy_signal,
248 &ref->destroy_listener);
249
250 ref->destroy_listener.notify =
251 nested_buffer_reference_handle_destroy;
252 }
253
254 ref->buffer = buffer;
255}
256
257static void
Neil Robertsf9b25412013-09-09 15:13:09 +0100258flush_surface_frame_callback_list(struct nested_surface *surface,
259 uint32_t time)
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400260{
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400261 struct nested_frame_callback *nc, *next;
262
Neil Robertsf9b25412013-09-09 15:13:09 +0100263 wl_list_for_each_safe(nc, next, &surface->frame_callback_list, link) {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400264 wl_callback_send_done(nc->resource, time);
265 wl_resource_destroy(nc->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400266 }
Neil Robertsf9b25412013-09-09 15:13:09 +0100267 wl_list_init(&surface->frame_callback_list);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400268
269 /* FIXME: toytoolkit need a pre-block handler where we can
270 * call this. */
Neil Robertsf9b25412013-09-09 15:13:09 +0100271 wl_display_flush_clients(surface->nested->child_display);
272}
273
274static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400275redraw_handler(struct widget *widget, void *data)
276{
277 struct nested *nested = data;
278 cairo_surface_t *surface;
279 cairo_t *cr;
280 struct rectangle allocation;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400281
282 widget_get_allocation(nested->widget, &allocation);
283
284 surface = window_get_surface(nested->window);
285
286 cr = cairo_create(surface);
287 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
288 cairo_rectangle(cr,
289 allocation.x,
290 allocation.y,
291 allocation.width,
292 allocation.height);
293 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
294 cairo_fill(cr);
295
Neil Roberts47b87d52013-09-08 20:52:36 +0100296 nested->renderer->render_clients(nested, cr);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400297
298 cairo_destroy(cr);
299
300 cairo_surface_destroy(surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400301}
302
303static void
304keyboard_focus_handler(struct window *window,
305 struct input *device, void *data)
306{
307 struct nested *nested = data;
308
309 window_schedule_redraw(nested->window);
310}
311
312static void
313handle_child_data(struct task *task, uint32_t events)
314{
315 struct nested *nested = container_of(task, struct nested, child_task);
316 struct wl_event_loop *loop;
317
318 loop = wl_display_get_event_loop(nested->child_display);
319
320 wl_event_loop_dispatch(loop, -1);
321 wl_display_flush_clients(nested->child_display);
322}
323
324struct nested_client {
325 struct wl_client *client;
326 pid_t pid;
327};
328
329static struct nested_client *
330launch_client(struct nested *nested, const char *path)
331{
332 int sv[2];
333 pid_t pid;
334 struct nested_client *client;
335
336 client = malloc(sizeof *client);
337 if (client == NULL)
338 return NULL;
339
340 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
341 fprintf(stderr, "launch_client: "
342 "socketpair failed while launching '%s': %m\n",
343 path);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700344 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400345 return NULL;
346 }
347
348 pid = fork();
349 if (pid == -1) {
350 close(sv[0]);
351 close(sv[1]);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700352 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400353 fprintf(stderr, "launch_client: "
354 "fork failed while launching '%s': %m\n", path);
355 return NULL;
356 }
357
358 if (pid == 0) {
359 int clientfd;
360 char s[32];
361
362 /* SOCK_CLOEXEC closes both ends, so we dup the fd to
363 * get a non-CLOEXEC fd to pass through exec. */
364 clientfd = dup(sv[1]);
365 if (clientfd == -1) {
366 fprintf(stderr, "compositor: dup failed: %m\n");
367 exit(-1);
368 }
369
370 snprintf(s, sizeof s, "%d", clientfd);
371 setenv("WAYLAND_SOCKET", s, 1);
372
373 execl(path, path, NULL);
374
375 fprintf(stderr, "compositor: executing '%s' failed: %m\n",
376 path);
377 exit(-1);
378 }
379
380 close(sv[1]);
381
382 client->client = wl_client_create(nested->child_display, sv[0]);
383 if (!client->client) {
384 close(sv[0]);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700385 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400386 fprintf(stderr, "launch_client: "
387 "wl_client_create failed while launching '%s'.\n",
388 path);
389 return NULL;
390 }
391
392 client->pid = pid;
393
394 return client;
395}
396
397static void
398destroy_surface(struct wl_resource *resource)
399{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400400 struct nested_surface *surface = wl_resource_get_user_data(resource);
Neil Roberts47b87d52013-09-08 20:52:36 +0100401 struct nested *nested = surface->nested;
Neil Roberts15a8d342013-09-08 19:49:02 +0100402 struct nested_frame_callback *cb, *next;
403
404 wl_list_for_each_safe(cb, next,
Neil Robertsf9b25412013-09-09 15:13:09 +0100405 &surface->frame_callback_list, link)
406 wl_resource_destroy(cb->resource);
407
408 wl_list_for_each_safe(cb, next,
Neil Roberts15a8d342013-09-08 19:49:02 +0100409 &surface->pending.frame_callback_list, link)
410 wl_resource_destroy(cb->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400411
Neil Roberts28557662013-09-08 20:24:14 +0100412 pixman_region32_fini(&surface->pending.damage);
413
Neil Roberts47b87d52013-09-08 20:52:36 +0100414 nested->renderer->surface_fini(surface);
415
Neil Roberts8fbe3a62013-11-22 15:41:53 +0000416 wl_list_remove(&surface->link);
417
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400418 free(surface);
419}
420
421static void
422surface_destroy(struct wl_client *client, struct wl_resource *resource)
423{
424 wl_resource_destroy(resource);
425}
426
427static void
428surface_attach(struct wl_client *client,
429 struct wl_resource *resource,
430 struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
431{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400432 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400433 struct nested *nested = surface->nested;
Neil Roberts15a8d342013-09-08 19:49:02 +0100434 struct nested_buffer *buffer = NULL;
435
436 if (buffer_resource) {
437 int format;
438
439 if (!query_buffer(nested->egl_display, (void *) buffer_resource,
440 EGL_TEXTURE_FORMAT, &format)) {
441 wl_resource_post_error(buffer_resource,
442 WL_DISPLAY_ERROR_INVALID_OBJECT,
443 "attaching non-egl wl_buffer");
444 return;
445 }
446
447 switch (format) {
448 case EGL_TEXTURE_RGB:
449 case EGL_TEXTURE_RGBA:
450 break;
451 default:
452 wl_resource_post_error(buffer_resource,
453 WL_DISPLAY_ERROR_INVALID_OBJECT,
454 "invalid format");
455 return;
456 }
457
458 buffer = nested_buffer_from_resource(buffer_resource);
459 if (buffer == NULL) {
460 wl_client_post_no_memory(client);
461 return;
462 }
463 }
464
465 if (surface->pending.buffer)
466 wl_list_remove(&surface->pending.buffer_destroy_listener.link);
467
468 surface->pending.buffer = buffer;
469 surface->pending.newly_attached = 1;
470 if (buffer) {
471 wl_signal_add(&buffer->destroy_signal,
472 &surface->pending.buffer_destroy_listener);
473 }
474}
475
476static void
477nested_surface_attach(struct nested_surface *surface,
478 struct nested_buffer *buffer)
479{
480 struct nested *nested = surface->nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400481
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400482 if (surface->image != EGL_NO_IMAGE_KHR)
483 destroy_image(nested->egl_display, surface->image);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400484
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400485 surface->image = create_image(nested->egl_display, NULL,
Neil Roberts15a8d342013-09-08 19:49:02 +0100486 EGL_WAYLAND_BUFFER_WL, buffer->resource,
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700487 NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400488 if (surface->image == EGL_NO_IMAGE_KHR) {
489 fprintf(stderr, "failed to create img\n");
490 return;
491 }
492
Neil Roberts47b87d52013-09-08 20:52:36 +0100493 nested->renderer->surface_attach(surface, buffer);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400494}
495
496static void
497surface_damage(struct wl_client *client,
498 struct wl_resource *resource,
499 int32_t x, int32_t y, int32_t width, int32_t height)
500{
Neil Roberts28557662013-09-08 20:24:14 +0100501 struct nested_surface *surface = wl_resource_get_user_data(resource);
502
503 pixman_region32_union_rect(&surface->pending.damage,
504 &surface->pending.damage,
505 x, y, width, height);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400506}
507
508static void
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400509destroy_frame_callback(struct wl_resource *resource)
510{
511 struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
512
513 wl_list_remove(&callback->link);
514 free(callback);
515}
516
517static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400518surface_frame(struct wl_client *client,
519 struct wl_resource *resource, uint32_t id)
520{
521 struct nested_frame_callback *callback;
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400522 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400523
524 callback = malloc(sizeof *callback);
525 if (callback == NULL) {
526 wl_resource_post_no_memory(resource);
527 return;
528 }
529
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400530 callback->resource = wl_resource_create(client,
531 &wl_callback_interface, 1, id);
532 wl_resource_set_implementation(callback->resource, NULL, callback,
533 destroy_frame_callback);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400534
Neil Roberts15a8d342013-09-08 19:49:02 +0100535 wl_list_insert(surface->pending.frame_callback_list.prev,
536 &callback->link);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400537}
538
539static void
540surface_set_opaque_region(struct wl_client *client,
541 struct wl_resource *resource,
542 struct wl_resource *region_resource)
543{
544 fprintf(stderr, "surface_set_opaque_region\n");
545}
546
547static void
548surface_set_input_region(struct wl_client *client,
549 struct wl_resource *resource,
550 struct wl_resource *region_resource)
551{
552 fprintf(stderr, "surface_set_input_region\n");
553}
554
555static void
556surface_commit(struct wl_client *client, struct wl_resource *resource)
557{
Neil Roberts15a8d342013-09-08 19:49:02 +0100558 struct nested_surface *surface = wl_resource_get_user_data(resource);
559 struct nested *nested = surface->nested;
560
561 /* wl_surface.attach */
562 if (surface->pending.newly_attached)
563 nested_surface_attach(surface, surface->pending.buffer);
564
565 if (surface->pending.buffer) {
566 wl_list_remove(&surface->pending.buffer_destroy_listener.link);
567 surface->pending.buffer = NULL;
568 }
569 surface->pending.newly_attached = 0;
570
Neil Roberts28557662013-09-08 20:24:14 +0100571 /* wl_surface.damage */
Jason Ekstrandef540082014-06-26 10:37:36 -0700572 pixman_region32_clear(&surface->pending.damage);
Neil Roberts28557662013-09-08 20:24:14 +0100573
Neil Roberts15a8d342013-09-08 19:49:02 +0100574 /* wl_surface.frame */
Neil Robertsf9b25412013-09-09 15:13:09 +0100575 wl_list_insert_list(&surface->frame_callback_list,
Neil Roberts15a8d342013-09-08 19:49:02 +0100576 &surface->pending.frame_callback_list);
577 wl_list_init(&surface->pending.frame_callback_list);
578
Neil Roberts1f020a12013-09-09 00:41:29 +0100579 /* FIXME: For the subsurface renderer we don't need to
580 * actually redraw the window. However we do want to cause a
581 * commit because the subsurface is synchronized. Ideally we
582 * would just queue the commit */
Neil Roberts15a8d342013-09-08 19:49:02 +0100583 window_schedule_redraw(nested->window);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400584}
585
586static void
587surface_set_buffer_transform(struct wl_client *client,
588 struct wl_resource *resource, int transform)
589{
590 fprintf(stderr, "surface_set_buffer_transform\n");
591}
592
593static const struct wl_surface_interface surface_interface = {
594 surface_destroy,
595 surface_attach,
596 surface_damage,
597 surface_frame,
598 surface_set_opaque_region,
599 surface_set_input_region,
600 surface_commit,
601 surface_set_buffer_transform
602};
603
604static void
Neil Roberts15a8d342013-09-08 19:49:02 +0100605surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
606{
607 struct nested_surface *surface =
608 container_of(listener, struct nested_surface,
609 pending.buffer_destroy_listener);
610
611 surface->pending.buffer = NULL;
612}
613
614static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400615compositor_create_surface(struct wl_client *client,
616 struct wl_resource *resource, uint32_t id)
617{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400618 struct nested *nested = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400619 struct nested_surface *surface;
Michael Vetter2a18a522015-05-15 17:17:47 +0200620
Peter Huttererf3d62272013-08-08 11:57:05 +1000621 surface = zalloc(sizeof *surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400622 if (surface == NULL) {
623 wl_resource_post_no_memory(resource);
624 return;
625 }
626
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400627 surface->nested = nested;
628
Neil Robertsf9b25412013-09-09 15:13:09 +0100629 wl_list_init(&surface->frame_callback_list);
630
Neil Roberts15a8d342013-09-08 19:49:02 +0100631 wl_list_init(&surface->pending.frame_callback_list);
632 surface->pending.buffer_destroy_listener.notify =
633 surface_handle_pending_buffer_destroy;
Neil Roberts28557662013-09-08 20:24:14 +0100634 pixman_region32_init(&surface->pending.damage);
Neil Roberts15a8d342013-09-08 19:49:02 +0100635
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400636 display_acquire_window_surface(nested->display,
637 nested->window, NULL);
638
Neil Roberts47b87d52013-09-08 20:52:36 +0100639 nested->renderer->surface_init(surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400640
641 display_release_window_surface(nested->display, nested->window);
642
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400643 surface->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400644 wl_resource_create(client, &wl_surface_interface, 1, id);
645
646 wl_resource_set_implementation(surface->resource,
647 &surface_interface, surface,
648 destroy_surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400649
650 wl_list_insert(nested->surface_list.prev, &surface->link);
651}
652
653static void
654destroy_region(struct wl_resource *resource)
655{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400656 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400657
658 pixman_region32_fini(&region->region);
659 free(region);
660}
661
662static void
663region_destroy(struct wl_client *client, struct wl_resource *resource)
664{
665 wl_resource_destroy(resource);
666}
667
668static void
669region_add(struct wl_client *client, struct wl_resource *resource,
670 int32_t x, int32_t y, int32_t width, int32_t height)
671{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400672 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400673
674 pixman_region32_union_rect(&region->region, &region->region,
675 x, y, width, height);
676}
677
678static void
679region_subtract(struct wl_client *client, struct wl_resource *resource,
680 int32_t x, int32_t y, int32_t width, int32_t height)
681{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400682 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400683 pixman_region32_t rect;
684
685 pixman_region32_init_rect(&rect, x, y, width, height);
686 pixman_region32_subtract(&region->region, &region->region, &rect);
687 pixman_region32_fini(&rect);
688}
689
690static const struct wl_region_interface region_interface = {
691 region_destroy,
692 region_add,
693 region_subtract
694};
695
696static void
697compositor_create_region(struct wl_client *client,
698 struct wl_resource *resource, uint32_t id)
699{
700 struct nested_region *region;
701
702 region = malloc(sizeof *region);
703 if (region == NULL) {
704 wl_resource_post_no_memory(resource);
705 return;
706 }
707
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400708 pixman_region32_init(&region->region);
709
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400710 region->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400711 wl_resource_create(client, &wl_region_interface, 1, id);
712 wl_resource_set_implementation(region->resource, &region_interface,
713 region, destroy_region);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400714}
715
716static const struct wl_compositor_interface compositor_interface = {
717 compositor_create_surface,
718 compositor_create_region
719};
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400720
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400721static void
722compositor_bind(struct wl_client *client,
723 void *data, uint32_t version, uint32_t id)
724{
725 struct nested *nested = data;
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400726 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400727
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400728 resource = wl_resource_create(client, &wl_compositor_interface,
729 MIN(version, 3), id);
730 wl_resource_set_implementation(resource, &compositor_interface,
731 nested, NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400732}
733
734static int
735nested_init_compositor(struct nested *nested)
736{
737 const char *extensions;
738 struct wl_event_loop *loop;
Neil Roberts1f020a12013-09-09 00:41:29 +0100739 int use_ss_renderer = 0;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400740 int fd, ret;
741
742 wl_list_init(&nested->surface_list);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400743 nested->child_display = wl_display_create();
744 loop = wl_display_get_event_loop(nested->child_display);
745 fd = wl_event_loop_get_fd(loop);
746 nested->child_task.run = handle_child_data;
747 display_watch_fd(nested->display, fd,
748 EPOLLIN, &nested->child_task);
749
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400750 if (!wl_global_create(nested->child_display,
751 &wl_compositor_interface, 1,
752 nested, compositor_bind))
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400753 return -1;
754
755 wl_display_init_shm(nested->child_display);
756
757 nested->egl_display = display_get_egl_display(nested->display);
758 extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
759 if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
760 fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
761 return -1;
762 }
763
764 bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
765 unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
766 create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
767 destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
768 query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
769 image_target_texture_2d =
770 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
771
772 ret = bind_display(nested->egl_display, nested->child_display);
773 if (!ret) {
774 fprintf(stderr, "failed to bind wl_display\n");
775 return -1;
776 }
777
Neil Roberts1f020a12013-09-09 00:41:29 +0100778 if (display_has_subcompositor(nested->display)) {
779 const char *func = "eglCreateWaylandBufferFromImageWL";
780 const char *ext = "EGL_WL_create_wayland_buffer_from_image";
781
782 if (strstr(extensions, ext)) {
783 create_wayland_buffer_from_image =
784 (void *) eglGetProcAddress(func);
785 use_ss_renderer = 1;
786 }
787 }
788
Neil Robertsa5059eb2013-09-09 00:59:35 +0100789 if (option_blit)
790 use_ss_renderer = 0;
791
Neil Roberts1f020a12013-09-09 00:41:29 +0100792 if (use_ss_renderer) {
793 printf("Using subsurfaces to render client surfaces\n");
794 nested->renderer = &nested_ss_renderer;
795 } else {
796 printf("Using local compositing with blits to "
797 "render client surfaces\n");
798 nested->renderer = &nested_blit_renderer;
799 }
Neil Roberts47b87d52013-09-08 20:52:36 +0100800
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400801 return 0;
802}
803
804static struct nested *
805nested_create(struct display *display)
806{
807 struct nested *nested;
808
Peter Huttererf3d62272013-08-08 11:57:05 +1000809 nested = zalloc(sizeof *nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400810 if (nested == NULL)
811 return nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400812
813 nested->window = window_create(display);
Jason Ekstrandee7fefc2013-10-13 19:08:38 -0500814 nested->widget = window_frame_create(nested->window, nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400815 window_set_title(nested->window, "Wayland Nested");
816 nested->display = display;
817
818 window_set_user_data(nested->window, nested);
819 widget_set_redraw_handler(nested->widget, redraw_handler);
820 window_set_keyboard_focus_handler(nested->window,
821 keyboard_focus_handler);
822
823 nested_init_compositor(nested);
824
825 widget_schedule_resize(nested->widget, 400, 400);
826
827 return nested;
828}
829
830static void
831nested_destroy(struct nested *nested)
832{
833 widget_destroy(nested->widget);
834 window_destroy(nested->window);
835 free(nested);
836}
837
Neil Roberts1f020a12013-09-09 00:41:29 +0100838/*** blit renderer ***/
839
Neil Roberts47b87d52013-09-08 20:52:36 +0100840static void
841blit_surface_init(struct nested_surface *surface)
842{
843 struct nested_blit_surface *blit_surface =
U. Artie Eoff5cda4e32014-01-15 11:13:38 -0800844 xzalloc(sizeof *blit_surface);
Neil Roberts47b87d52013-09-08 20:52:36 +0100845
846 glGenTextures(1, &blit_surface->texture);
847 glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
848 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
849 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
850 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
851 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
852
853 surface->renderer_data = blit_surface;
854}
855
856static void
857blit_surface_fini(struct nested_surface *surface)
858{
859 struct nested_blit_surface *blit_surface = surface->renderer_data;
860
861 nested_buffer_reference(&blit_surface->buffer_ref, NULL);
862
863 glDeleteTextures(1, &blit_surface->texture);
864
865 free(blit_surface);
866}
867
868static void
869blit_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
870{
871 struct nested *nested = data;
872 struct nested_surface *surface;
873
874 wl_list_for_each(surface, &nested->surface_list, link)
875 flush_surface_frame_callback_list(surface, time);
876
877 if (callback)
878 wl_callback_destroy(callback);
879}
880
881static const struct wl_callback_listener blit_frame_listener = {
882 blit_frame_callback
883};
884
885static void
886blit_render_clients(struct nested *nested,
887 cairo_t *cr)
888{
889 struct nested_surface *s;
890 struct rectangle allocation;
891 struct wl_callback *callback;
892
893 widget_get_allocation(nested->widget, &allocation);
894
895 wl_list_for_each(s, &nested->surface_list, link) {
896 struct nested_blit_surface *blit_surface = s->renderer_data;
897
898 display_acquire_window_surface(nested->display,
899 nested->window, NULL);
900
901 glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
902 image_target_texture_2d(GL_TEXTURE_2D, s->image);
903
904 display_release_window_surface(nested->display,
905 nested->window);
906
907 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
908 cairo_set_source_surface(cr, blit_surface->cairo_surface,
909 allocation.x + 10,
910 allocation.y + 10);
911 cairo_rectangle(cr, allocation.x + 10,
912 allocation.y + 10,
913 allocation.width - 10,
914 allocation.height - 10);
915
916 cairo_fill(cr);
917 }
918
919 callback = wl_surface_frame(window_get_wl_surface(nested->window));
920 wl_callback_add_listener(callback, &blit_frame_listener, nested);
921}
922
923static void
924blit_surface_attach(struct nested_surface *surface,
925 struct nested_buffer *buffer)
926{
927 struct nested *nested = surface->nested;
928 struct nested_blit_surface *blit_surface = surface->renderer_data;
929 EGLint width, height;
930 cairo_device_t *device;
931
932 nested_buffer_reference(&blit_surface->buffer_ref, buffer);
933
934 if (blit_surface->cairo_surface)
935 cairo_surface_destroy(blit_surface->cairo_surface);
936
937 query_buffer(nested->egl_display, (void *) buffer->resource,
938 EGL_WIDTH, &width);
939 query_buffer(nested->egl_display, (void *) buffer->resource,
940 EGL_HEIGHT, &height);
941
942 device = display_get_cairo_device(nested->display);
943 blit_surface->cairo_surface =
944 cairo_gl_surface_create_for_texture(device,
945 CAIRO_CONTENT_COLOR_ALPHA,
946 blit_surface->texture,
947 width, height);
948}
949
950static const struct nested_renderer
951nested_blit_renderer = {
952 .surface_init = blit_surface_init,
953 .surface_fini = blit_surface_fini,
954 .render_clients = blit_render_clients,
955 .surface_attach = blit_surface_attach
956};
957
Neil Roberts1f020a12013-09-09 00:41:29 +0100958/*** subsurface renderer ***/
959
960static void
961ss_surface_init(struct nested_surface *surface)
962{
963 struct nested *nested = surface->nested;
964 struct wl_compositor *compositor =
965 display_get_compositor(nested->display);
966 struct nested_ss_surface *ss_surface =
U. Artie Eoff5cda4e32014-01-15 11:13:38 -0800967 xzalloc(sizeof *ss_surface);
Neil Roberts1f020a12013-09-09 00:41:29 +0100968 struct rectangle allocation;
969 struct wl_region *region;
970
971 ss_surface->widget =
972 window_add_subsurface(nested->window,
973 nested,
974 SUBSURFACE_SYNCHRONIZED);
975
Neil Roberts40d02282014-02-24 19:07:08 +0000976 widget_set_use_cairo(ss_surface->widget, 0);
977
Neil Roberts1f020a12013-09-09 00:41:29 +0100978 ss_surface->surface = widget_get_wl_surface(ss_surface->widget);
979 ss_surface->subsurface = widget_get_wl_subsurface(ss_surface->widget);
980
981 /* The toy toolkit gets confused about the pointer position
982 * when it gets motion events for a subsurface so we'll just
983 * disable input on it */
984 region = wl_compositor_create_region(compositor);
985 wl_surface_set_input_region(ss_surface->surface, region);
986 wl_region_destroy(region);
987
988 widget_get_allocation(nested->widget, &allocation);
989 wl_subsurface_set_position(ss_surface->subsurface,
990 allocation.x + 10,
991 allocation.y + 10);
992
993 surface->renderer_data = ss_surface;
994}
995
996static void
997ss_surface_fini(struct nested_surface *surface)
998{
999 struct nested_ss_surface *ss_surface = surface->renderer_data;
1000
1001 widget_destroy(ss_surface->widget);
1002
1003 if (ss_surface->frame_callback)
1004 wl_callback_destroy(ss_surface->frame_callback);
1005
1006 free(ss_surface);
1007}
1008
1009static void
1010ss_render_clients(struct nested *nested,
1011 cairo_t *cr)
1012{
1013 /* The clients are composited by the parent compositor so we
1014 * don't need to do anything here */
1015}
1016
1017static void
1018ss_buffer_release(void *data, struct wl_buffer *wl_buffer)
1019{
1020 struct nested_buffer *buffer = data;
1021
1022 nested_buffer_reference(&buffer->parent_ref, NULL);
1023}
1024
1025static struct wl_buffer_listener ss_buffer_listener = {
1026 ss_buffer_release
1027};
1028
1029static void
1030ss_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
1031{
1032 struct nested_surface *surface = data;
1033 struct nested_ss_surface *ss_surface = surface->renderer_data;
1034
1035 flush_surface_frame_callback_list(surface, time);
1036
1037 if (callback)
1038 wl_callback_destroy(callback);
1039
1040 ss_surface->frame_callback = NULL;
1041}
1042
1043static const struct wl_callback_listener ss_frame_listener = {
1044 ss_frame_callback
1045};
1046
1047static void
1048ss_surface_attach(struct nested_surface *surface,
1049 struct nested_buffer *buffer)
1050{
1051 struct nested *nested = surface->nested;
1052 struct nested_ss_surface *ss_surface = surface->renderer_data;
1053 struct wl_buffer *parent_buffer;
1054 const pixman_box32_t *rects;
1055 int n_rects, i;
1056
1057 if (buffer) {
1058 /* Create a representation of the buffer in the parent
1059 * compositor if we haven't already */
1060 if (buffer->parent_buffer == NULL) {
1061 EGLDisplay *edpy = nested->egl_display;
1062 EGLImageKHR image = surface->image;
1063
1064 buffer->parent_buffer =
1065 create_wayland_buffer_from_image(edpy, image);
1066
1067 wl_buffer_add_listener(buffer->parent_buffer,
1068 &ss_buffer_listener,
1069 buffer);
1070 }
1071
1072 parent_buffer = buffer->parent_buffer;
1073
1074 /* We'll take a reference to the buffer while the parent
1075 * compositor is using it so that we won't report the release
1076 * event until the parent has also finished with it */
1077 nested_buffer_reference(&buffer->parent_ref, buffer);
1078 } else {
1079 parent_buffer = NULL;
1080 }
1081
1082 wl_surface_attach(ss_surface->surface, parent_buffer, 0, 0);
1083
1084 rects = pixman_region32_rectangles(&surface->pending.damage, &n_rects);
1085
1086 for (i = 0; i < n_rects; i++) {
1087 const pixman_box32_t *rect = rects + i;
1088 wl_surface_damage(ss_surface->surface,
1089 rect->x1,
1090 rect->y1,
1091 rect->x2 - rect->x1,
1092 rect->y2 - rect->y1);
1093 }
1094
1095 if (ss_surface->frame_callback)
1096 wl_callback_destroy(ss_surface->frame_callback);
1097
1098 ss_surface->frame_callback = wl_surface_frame(ss_surface->surface);
1099 wl_callback_add_listener(ss_surface->frame_callback,
1100 &ss_frame_listener,
1101 surface);
1102
1103 wl_surface_commit(ss_surface->surface);
1104}
1105
1106static const struct nested_renderer
1107nested_ss_renderer = {
1108 .surface_init = ss_surface_init,
1109 .surface_fini = ss_surface_fini,
1110 .render_clients = ss_render_clients,
1111 .surface_attach = ss_surface_attach
1112};
1113
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001114int
1115main(int argc, char *argv[])
1116{
1117 struct display *display;
1118 struct nested *nested;
1119
Bill Spitzak6df71aa2014-08-08 12:59:52 -07001120 if (parse_options(nested_options,
1121 ARRAY_LENGTH(nested_options), &argc, argv) > 1) {
1122 printf("Usage: %s [OPTIONS]\n --blit or -b\n", argv[0]);
1123 exit(1);
1124 }
Neil Robertsa5059eb2013-09-09 00:59:35 +01001125
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001126 display = display_create(&argc, argv);
1127 if (display == NULL) {
1128 fprintf(stderr, "failed to create display: %m\n");
1129 return -1;
1130 }
1131
1132 nested = nested_create(display);
1133
Kristian Høgsbergcb61dcf2013-08-07 09:50:12 -07001134 launch_client(nested, "weston-nested-client");
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -04001135
1136 display_run(display);
1137
1138 nested_destroy(nested);
1139 display_destroy(display);
1140
1141 return 0;
1142}