blob: 096abc55a82322e6d6310f12270df7b97d6894b9 [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 Roberts6bf23872013-09-08 18:49:15 +010070struct nested_buffer {
71 struct wl_resource *resource;
72 struct wl_signal destroy_signal;
73 struct wl_listener destroy_listener;
74 uint32_t busy_count;
75};
76
77struct nested_buffer_reference {
78 struct nested_buffer *buffer;
79 struct wl_listener destroy_listener;
80};
81
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040082struct nested_surface {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040083 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040084 struct nested *nested;
85 EGLImageKHR *image;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040086 struct wl_list link;
Neil Roberts15a8d342013-09-08 19:49:02 +010087
Neil Robertsf9b25412013-09-09 15:13:09 +010088 struct wl_list frame_callback_list;
89
Neil Roberts15a8d342013-09-08 19:49:02 +010090 struct {
91 /* wl_surface.attach */
92 int newly_attached;
93 struct nested_buffer *buffer;
94 struct wl_listener buffer_destroy_listener;
95
96 /* wl_surface.frame */
97 struct wl_list frame_callback_list;
Neil Roberts28557662013-09-08 20:24:14 +010098
99 /* wl_surface.damage */
100 pixman_region32_t damage;
Neil Roberts15a8d342013-09-08 19:49:02 +0100101 } pending;
Neil Roberts47b87d52013-09-08 20:52:36 +0100102
103 void *renderer_data;
104};
105
106/* Data used for the blit renderer */
107struct nested_blit_surface {
108 struct nested_buffer_reference buffer_ref;
109 GLuint texture;
110 cairo_surface_t *cairo_surface;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400111};
112
113struct nested_frame_callback {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400114 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400115 struct wl_list link;
116};
117
Neil Roberts47b87d52013-09-08 20:52:36 +0100118struct nested_renderer {
119 void (* surface_init)(struct nested_surface *surface);
120 void (* surface_fini)(struct nested_surface *surface);
121 void (* render_clients)(struct nested *nested, cairo_t *cr);
122 void (* surface_attach)(struct nested_surface *surface,
123 struct nested_buffer *buffer);
124};
125
126static const struct nested_renderer nested_blit_renderer;
127
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400128static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
129static PFNEGLCREATEIMAGEKHRPROC create_image;
130static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
131static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
132static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
133static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
134
135static void
Neil Roberts6bf23872013-09-08 18:49:15 +0100136nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
137{
138 struct nested_buffer *buffer =
139 container_of(listener, struct nested_buffer, destroy_listener);
140
141 wl_signal_emit(&buffer->destroy_signal, buffer);
142 free(buffer);
143}
144
145static struct nested_buffer *
146nested_buffer_from_resource(struct wl_resource *resource)
147{
148 struct nested_buffer *buffer;
149 struct wl_listener *listener;
150
151 listener =
152 wl_resource_get_destroy_listener(resource,
153 nested_buffer_destroy_handler);
154
155 if (listener)
156 return container_of(listener, struct nested_buffer,
157 destroy_listener);
158
159 buffer = zalloc(sizeof *buffer);
160 if (buffer == NULL)
161 return NULL;
162
163 buffer->resource = resource;
164 wl_signal_init(&buffer->destroy_signal);
165 buffer->destroy_listener.notify = nested_buffer_destroy_handler;
166 wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
167
168 return buffer;
169}
170
171static void
172nested_buffer_reference_handle_destroy(struct wl_listener *listener,
173 void *data)
174{
175 struct nested_buffer_reference *ref =
176 container_of(listener, struct nested_buffer_reference,
177 destroy_listener);
178
179 assert((struct nested_buffer *)data == ref->buffer);
180 ref->buffer = NULL;
181}
182
183static void
184nested_buffer_reference(struct nested_buffer_reference *ref,
185 struct nested_buffer *buffer)
186{
187 if (buffer == ref->buffer)
188 return;
189
190 if (ref->buffer) {
191 ref->buffer->busy_count--;
192 if (ref->buffer->busy_count == 0) {
193 assert(wl_resource_get_client(ref->buffer->resource));
194 wl_resource_queue_event(ref->buffer->resource,
195 WL_BUFFER_RELEASE);
196 }
197 wl_list_remove(&ref->destroy_listener.link);
198 }
199
200 if (buffer) {
201 buffer->busy_count++;
202 wl_signal_add(&buffer->destroy_signal,
203 &ref->destroy_listener);
204
205 ref->destroy_listener.notify =
206 nested_buffer_reference_handle_destroy;
207 }
208
209 ref->buffer = buffer;
210}
211
212static void
Neil Robertsf9b25412013-09-09 15:13:09 +0100213flush_surface_frame_callback_list(struct nested_surface *surface,
214 uint32_t time)
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400215{
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400216 struct nested_frame_callback *nc, *next;
217
Neil Robertsf9b25412013-09-09 15:13:09 +0100218 wl_list_for_each_safe(nc, next, &surface->frame_callback_list, link) {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400219 wl_callback_send_done(nc->resource, time);
220 wl_resource_destroy(nc->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400221 }
Neil Robertsf9b25412013-09-09 15:13:09 +0100222 wl_list_init(&surface->frame_callback_list);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400223
224 /* FIXME: toytoolkit need a pre-block handler where we can
225 * call this. */
Neil Robertsf9b25412013-09-09 15:13:09 +0100226 wl_display_flush_clients(surface->nested->child_display);
227}
228
229static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400230redraw_handler(struct widget *widget, void *data)
231{
232 struct nested *nested = data;
233 cairo_surface_t *surface;
234 cairo_t *cr;
235 struct rectangle allocation;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400236
237 widget_get_allocation(nested->widget, &allocation);
238
239 surface = window_get_surface(nested->window);
240
241 cr = cairo_create(surface);
242 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
243 cairo_rectangle(cr,
244 allocation.x,
245 allocation.y,
246 allocation.width,
247 allocation.height);
248 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
249 cairo_fill(cr);
250
Neil Roberts47b87d52013-09-08 20:52:36 +0100251 nested->renderer->render_clients(nested, cr);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400252
253 cairo_destroy(cr);
254
255 cairo_surface_destroy(surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400256}
257
258static void
259keyboard_focus_handler(struct window *window,
260 struct input *device, void *data)
261{
262 struct nested *nested = data;
263
264 window_schedule_redraw(nested->window);
265}
266
267static void
268handle_child_data(struct task *task, uint32_t events)
269{
270 struct nested *nested = container_of(task, struct nested, child_task);
271 struct wl_event_loop *loop;
272
273 loop = wl_display_get_event_loop(nested->child_display);
274
275 wl_event_loop_dispatch(loop, -1);
276 wl_display_flush_clients(nested->child_display);
277}
278
279struct nested_client {
280 struct wl_client *client;
281 pid_t pid;
282};
283
284static struct nested_client *
285launch_client(struct nested *nested, const char *path)
286{
287 int sv[2];
288 pid_t pid;
289 struct nested_client *client;
290
291 client = malloc(sizeof *client);
292 if (client == NULL)
293 return NULL;
294
295 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
296 fprintf(stderr, "launch_client: "
297 "socketpair failed while launching '%s': %m\n",
298 path);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700299 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400300 return NULL;
301 }
302
303 pid = fork();
304 if (pid == -1) {
305 close(sv[0]);
306 close(sv[1]);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700307 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400308 fprintf(stderr, "launch_client: "
309 "fork failed while launching '%s': %m\n", path);
310 return NULL;
311 }
312
313 if (pid == 0) {
314 int clientfd;
315 char s[32];
316
317 /* SOCK_CLOEXEC closes both ends, so we dup the fd to
318 * get a non-CLOEXEC fd to pass through exec. */
319 clientfd = dup(sv[1]);
320 if (clientfd == -1) {
321 fprintf(stderr, "compositor: dup failed: %m\n");
322 exit(-1);
323 }
324
325 snprintf(s, sizeof s, "%d", clientfd);
326 setenv("WAYLAND_SOCKET", s, 1);
327
328 execl(path, path, NULL);
329
330 fprintf(stderr, "compositor: executing '%s' failed: %m\n",
331 path);
332 exit(-1);
333 }
334
335 close(sv[1]);
336
337 client->client = wl_client_create(nested->child_display, sv[0]);
338 if (!client->client) {
339 close(sv[0]);
Kristian Høgsberg67733942013-10-09 13:30:58 -0700340 free(client);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400341 fprintf(stderr, "launch_client: "
342 "wl_client_create failed while launching '%s'.\n",
343 path);
344 return NULL;
345 }
346
347 client->pid = pid;
348
349 return client;
350}
351
352static void
353destroy_surface(struct wl_resource *resource)
354{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400355 struct nested_surface *surface = wl_resource_get_user_data(resource);
Neil Roberts47b87d52013-09-08 20:52:36 +0100356 struct nested *nested = surface->nested;
Neil Roberts15a8d342013-09-08 19:49:02 +0100357 struct nested_frame_callback *cb, *next;
358
359 wl_list_for_each_safe(cb, next,
Neil Robertsf9b25412013-09-09 15:13:09 +0100360 &surface->frame_callback_list, link)
361 wl_resource_destroy(cb->resource);
362
363 wl_list_for_each_safe(cb, next,
Neil Roberts15a8d342013-09-08 19:49:02 +0100364 &surface->pending.frame_callback_list, link)
365 wl_resource_destroy(cb->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400366
Neil Roberts28557662013-09-08 20:24:14 +0100367 pixman_region32_fini(&surface->pending.damage);
368
Neil Roberts47b87d52013-09-08 20:52:36 +0100369 nested->renderer->surface_fini(surface);
370
Neil Roberts8fbe3a62013-11-22 15:41:53 +0000371 wl_list_remove(&surface->link);
372
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400373 free(surface);
374}
375
376static void
377surface_destroy(struct wl_client *client, struct wl_resource *resource)
378{
379 wl_resource_destroy(resource);
380}
381
382static void
383surface_attach(struct wl_client *client,
384 struct wl_resource *resource,
385 struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
386{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400387 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400388 struct nested *nested = surface->nested;
Neil Roberts15a8d342013-09-08 19:49:02 +0100389 struct nested_buffer *buffer = NULL;
390
391 if (buffer_resource) {
392 int format;
393
394 if (!query_buffer(nested->egl_display, (void *) buffer_resource,
395 EGL_TEXTURE_FORMAT, &format)) {
396 wl_resource_post_error(buffer_resource,
397 WL_DISPLAY_ERROR_INVALID_OBJECT,
398 "attaching non-egl wl_buffer");
399 return;
400 }
401
402 switch (format) {
403 case EGL_TEXTURE_RGB:
404 case EGL_TEXTURE_RGBA:
405 break;
406 default:
407 wl_resource_post_error(buffer_resource,
408 WL_DISPLAY_ERROR_INVALID_OBJECT,
409 "invalid format");
410 return;
411 }
412
413 buffer = nested_buffer_from_resource(buffer_resource);
414 if (buffer == NULL) {
415 wl_client_post_no_memory(client);
416 return;
417 }
418 }
419
420 if (surface->pending.buffer)
421 wl_list_remove(&surface->pending.buffer_destroy_listener.link);
422
423 surface->pending.buffer = buffer;
424 surface->pending.newly_attached = 1;
425 if (buffer) {
426 wl_signal_add(&buffer->destroy_signal,
427 &surface->pending.buffer_destroy_listener);
428 }
429}
430
431static void
432nested_surface_attach(struct nested_surface *surface,
433 struct nested_buffer *buffer)
434{
435 struct nested *nested = surface->nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400436
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400437 if (surface->image != EGL_NO_IMAGE_KHR)
438 destroy_image(nested->egl_display, surface->image);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400439
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400440 surface->image = create_image(nested->egl_display, NULL,
Neil Roberts15a8d342013-09-08 19:49:02 +0100441 EGL_WAYLAND_BUFFER_WL, buffer->resource,
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700442 NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400443 if (surface->image == EGL_NO_IMAGE_KHR) {
444 fprintf(stderr, "failed to create img\n");
445 return;
446 }
447
Neil Roberts47b87d52013-09-08 20:52:36 +0100448 nested->renderer->surface_attach(surface, buffer);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400449}
450
451static void
452surface_damage(struct wl_client *client,
453 struct wl_resource *resource,
454 int32_t x, int32_t y, int32_t width, int32_t height)
455{
Neil Roberts28557662013-09-08 20:24:14 +0100456 struct nested_surface *surface = wl_resource_get_user_data(resource);
457
458 pixman_region32_union_rect(&surface->pending.damage,
459 &surface->pending.damage,
460 x, y, width, height);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400461}
462
463static void
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400464destroy_frame_callback(struct wl_resource *resource)
465{
466 struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
467
468 wl_list_remove(&callback->link);
469 free(callback);
470}
471
472static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400473surface_frame(struct wl_client *client,
474 struct wl_resource *resource, uint32_t id)
475{
476 struct nested_frame_callback *callback;
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400477 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400478
479 callback = malloc(sizeof *callback);
480 if (callback == NULL) {
481 wl_resource_post_no_memory(resource);
482 return;
483 }
484
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400485 callback->resource = wl_resource_create(client,
486 &wl_callback_interface, 1, id);
487 wl_resource_set_implementation(callback->resource, NULL, callback,
488 destroy_frame_callback);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400489
Neil Roberts15a8d342013-09-08 19:49:02 +0100490 wl_list_insert(surface->pending.frame_callback_list.prev,
491 &callback->link);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400492}
493
494static void
495surface_set_opaque_region(struct wl_client *client,
496 struct wl_resource *resource,
497 struct wl_resource *region_resource)
498{
499 fprintf(stderr, "surface_set_opaque_region\n");
500}
501
502static void
503surface_set_input_region(struct wl_client *client,
504 struct wl_resource *resource,
505 struct wl_resource *region_resource)
506{
507 fprintf(stderr, "surface_set_input_region\n");
508}
509
510static void
Neil Roberts28557662013-09-08 20:24:14 +0100511empty_region(pixman_region32_t *region)
512{
513 pixman_region32_fini(region);
514 pixman_region32_init(region);
515}
516
517static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400518surface_commit(struct wl_client *client, struct wl_resource *resource)
519{
Neil Roberts15a8d342013-09-08 19:49:02 +0100520 struct nested_surface *surface = wl_resource_get_user_data(resource);
521 struct nested *nested = surface->nested;
522
523 /* wl_surface.attach */
524 if (surface->pending.newly_attached)
525 nested_surface_attach(surface, surface->pending.buffer);
526
527 if (surface->pending.buffer) {
528 wl_list_remove(&surface->pending.buffer_destroy_listener.link);
529 surface->pending.buffer = NULL;
530 }
531 surface->pending.newly_attached = 0;
532
Neil Roberts28557662013-09-08 20:24:14 +0100533 /* wl_surface.damage */
534 empty_region(&surface->pending.damage);
535
Neil Roberts15a8d342013-09-08 19:49:02 +0100536 /* wl_surface.frame */
Neil Robertsf9b25412013-09-09 15:13:09 +0100537 wl_list_insert_list(&surface->frame_callback_list,
Neil Roberts15a8d342013-09-08 19:49:02 +0100538 &surface->pending.frame_callback_list);
539 wl_list_init(&surface->pending.frame_callback_list);
540
541 window_schedule_redraw(nested->window);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400542}
543
544static void
545surface_set_buffer_transform(struct wl_client *client,
546 struct wl_resource *resource, int transform)
547{
548 fprintf(stderr, "surface_set_buffer_transform\n");
549}
550
551static const struct wl_surface_interface surface_interface = {
552 surface_destroy,
553 surface_attach,
554 surface_damage,
555 surface_frame,
556 surface_set_opaque_region,
557 surface_set_input_region,
558 surface_commit,
559 surface_set_buffer_transform
560};
561
562static void
Neil Roberts15a8d342013-09-08 19:49:02 +0100563surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
564{
565 struct nested_surface *surface =
566 container_of(listener, struct nested_surface,
567 pending.buffer_destroy_listener);
568
569 surface->pending.buffer = NULL;
570}
571
572static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400573compositor_create_surface(struct wl_client *client,
574 struct wl_resource *resource, uint32_t id)
575{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400576 struct nested *nested = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400577 struct nested_surface *surface;
578
Peter Huttererf3d62272013-08-08 11:57:05 +1000579 surface = zalloc(sizeof *surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400580 if (surface == NULL) {
581 wl_resource_post_no_memory(resource);
582 return;
583 }
584
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400585 surface->nested = nested;
586
Neil Robertsf9b25412013-09-09 15:13:09 +0100587 wl_list_init(&surface->frame_callback_list);
588
Neil Roberts15a8d342013-09-08 19:49:02 +0100589 wl_list_init(&surface->pending.frame_callback_list);
590 surface->pending.buffer_destroy_listener.notify =
591 surface_handle_pending_buffer_destroy;
Neil Roberts28557662013-09-08 20:24:14 +0100592 pixman_region32_init(&surface->pending.damage);
Neil Roberts15a8d342013-09-08 19:49:02 +0100593
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400594 display_acquire_window_surface(nested->display,
595 nested->window, NULL);
596
Neil Roberts47b87d52013-09-08 20:52:36 +0100597 nested->renderer->surface_init(surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400598
599 display_release_window_surface(nested->display, nested->window);
600
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400601 surface->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400602 wl_resource_create(client, &wl_surface_interface, 1, id);
603
604 wl_resource_set_implementation(surface->resource,
605 &surface_interface, surface,
606 destroy_surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400607
608 wl_list_insert(nested->surface_list.prev, &surface->link);
609}
610
611static void
612destroy_region(struct wl_resource *resource)
613{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400614 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400615
616 pixman_region32_fini(&region->region);
617 free(region);
618}
619
620static void
621region_destroy(struct wl_client *client, struct wl_resource *resource)
622{
623 wl_resource_destroy(resource);
624}
625
626static void
627region_add(struct wl_client *client, struct wl_resource *resource,
628 int32_t x, int32_t y, int32_t width, int32_t height)
629{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400630 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400631
632 pixman_region32_union_rect(&region->region, &region->region,
633 x, y, width, height);
634}
635
636static void
637region_subtract(struct wl_client *client, struct wl_resource *resource,
638 int32_t x, int32_t y, int32_t width, int32_t height)
639{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400640 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400641 pixman_region32_t rect;
642
643 pixman_region32_init_rect(&rect, x, y, width, height);
644 pixman_region32_subtract(&region->region, &region->region, &rect);
645 pixman_region32_fini(&rect);
646}
647
648static const struct wl_region_interface region_interface = {
649 region_destroy,
650 region_add,
651 region_subtract
652};
653
654static void
655compositor_create_region(struct wl_client *client,
656 struct wl_resource *resource, uint32_t id)
657{
658 struct nested_region *region;
659
660 region = malloc(sizeof *region);
661 if (region == NULL) {
662 wl_resource_post_no_memory(resource);
663 return;
664 }
665
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400666 pixman_region32_init(&region->region);
667
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400668 region->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400669 wl_resource_create(client, &wl_region_interface, 1, id);
670 wl_resource_set_implementation(region->resource, &region_interface,
671 region, destroy_region);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400672}
673
674static const struct wl_compositor_interface compositor_interface = {
675 compositor_create_surface,
676 compositor_create_region
677};
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400678
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400679static void
680compositor_bind(struct wl_client *client,
681 void *data, uint32_t version, uint32_t id)
682{
683 struct nested *nested = data;
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400684 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400685
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400686 resource = wl_resource_create(client, &wl_compositor_interface,
687 MIN(version, 3), id);
688 wl_resource_set_implementation(resource, &compositor_interface,
689 nested, NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400690}
691
692static int
693nested_init_compositor(struct nested *nested)
694{
695 const char *extensions;
696 struct wl_event_loop *loop;
697 int fd, ret;
698
699 wl_list_init(&nested->surface_list);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400700 nested->child_display = wl_display_create();
701 loop = wl_display_get_event_loop(nested->child_display);
702 fd = wl_event_loop_get_fd(loop);
703 nested->child_task.run = handle_child_data;
704 display_watch_fd(nested->display, fd,
705 EPOLLIN, &nested->child_task);
706
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400707 if (!wl_global_create(nested->child_display,
708 &wl_compositor_interface, 1,
709 nested, compositor_bind))
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400710 return -1;
711
712 wl_display_init_shm(nested->child_display);
713
714 nested->egl_display = display_get_egl_display(nested->display);
715 extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
716 if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
717 fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
718 return -1;
719 }
720
721 bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
722 unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
723 create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
724 destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
725 query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
726 image_target_texture_2d =
727 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
728
729 ret = bind_display(nested->egl_display, nested->child_display);
730 if (!ret) {
731 fprintf(stderr, "failed to bind wl_display\n");
732 return -1;
733 }
734
Neil Roberts47b87d52013-09-08 20:52:36 +0100735 nested->renderer = &nested_blit_renderer;
736
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400737 return 0;
738}
739
740static struct nested *
741nested_create(struct display *display)
742{
743 struct nested *nested;
744
Peter Huttererf3d62272013-08-08 11:57:05 +1000745 nested = zalloc(sizeof *nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400746 if (nested == NULL)
747 return nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400748
749 nested->window = window_create(display);
Jason Ekstrandee7fefc2013-10-13 19:08:38 -0500750 nested->widget = window_frame_create(nested->window, nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400751 window_set_title(nested->window, "Wayland Nested");
752 nested->display = display;
753
754 window_set_user_data(nested->window, nested);
755 widget_set_redraw_handler(nested->widget, redraw_handler);
756 window_set_keyboard_focus_handler(nested->window,
757 keyboard_focus_handler);
758
759 nested_init_compositor(nested);
760
761 widget_schedule_resize(nested->widget, 400, 400);
762
763 return nested;
764}
765
766static void
767nested_destroy(struct nested *nested)
768{
769 widget_destroy(nested->widget);
770 window_destroy(nested->window);
771 free(nested);
772}
773
Neil Roberts47b87d52013-09-08 20:52:36 +0100774static void
775blit_surface_init(struct nested_surface *surface)
776{
777 struct nested_blit_surface *blit_surface =
778 zalloc(sizeof *blit_surface);
779
780 glGenTextures(1, &blit_surface->texture);
781 glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
782 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
783 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
784 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
785 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
786
787 surface->renderer_data = blit_surface;
788}
789
790static void
791blit_surface_fini(struct nested_surface *surface)
792{
793 struct nested_blit_surface *blit_surface = surface->renderer_data;
794
795 nested_buffer_reference(&blit_surface->buffer_ref, NULL);
796
797 glDeleteTextures(1, &blit_surface->texture);
798
799 free(blit_surface);
800}
801
802static void
803blit_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
804{
805 struct nested *nested = data;
806 struct nested_surface *surface;
807
808 wl_list_for_each(surface, &nested->surface_list, link)
809 flush_surface_frame_callback_list(surface, time);
810
811 if (callback)
812 wl_callback_destroy(callback);
813}
814
815static const struct wl_callback_listener blit_frame_listener = {
816 blit_frame_callback
817};
818
819static void
820blit_render_clients(struct nested *nested,
821 cairo_t *cr)
822{
823 struct nested_surface *s;
824 struct rectangle allocation;
825 struct wl_callback *callback;
826
827 widget_get_allocation(nested->widget, &allocation);
828
829 wl_list_for_each(s, &nested->surface_list, link) {
830 struct nested_blit_surface *blit_surface = s->renderer_data;
831
832 display_acquire_window_surface(nested->display,
833 nested->window, NULL);
834
835 glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
836 image_target_texture_2d(GL_TEXTURE_2D, s->image);
837
838 display_release_window_surface(nested->display,
839 nested->window);
840
841 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
842 cairo_set_source_surface(cr, blit_surface->cairo_surface,
843 allocation.x + 10,
844 allocation.y + 10);
845 cairo_rectangle(cr, allocation.x + 10,
846 allocation.y + 10,
847 allocation.width - 10,
848 allocation.height - 10);
849
850 cairo_fill(cr);
851 }
852
853 callback = wl_surface_frame(window_get_wl_surface(nested->window));
854 wl_callback_add_listener(callback, &blit_frame_listener, nested);
855}
856
857static void
858blit_surface_attach(struct nested_surface *surface,
859 struct nested_buffer *buffer)
860{
861 struct nested *nested = surface->nested;
862 struct nested_blit_surface *blit_surface = surface->renderer_data;
863 EGLint width, height;
864 cairo_device_t *device;
865
866 nested_buffer_reference(&blit_surface->buffer_ref, buffer);
867
868 if (blit_surface->cairo_surface)
869 cairo_surface_destroy(blit_surface->cairo_surface);
870
871 query_buffer(nested->egl_display, (void *) buffer->resource,
872 EGL_WIDTH, &width);
873 query_buffer(nested->egl_display, (void *) buffer->resource,
874 EGL_HEIGHT, &height);
875
876 device = display_get_cairo_device(nested->display);
877 blit_surface->cairo_surface =
878 cairo_gl_surface_create_for_texture(device,
879 CAIRO_CONTENT_COLOR_ALPHA,
880 blit_surface->texture,
881 width, height);
882}
883
884static const struct nested_renderer
885nested_blit_renderer = {
886 .surface_init = blit_surface_init,
887 .surface_fini = blit_surface_fini,
888 .render_clients = blit_render_clients,
889 .surface_attach = blit_surface_attach
890};
891
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400892int
893main(int argc, char *argv[])
894{
895 struct display *display;
896 struct nested *nested;
897
898 display = display_create(&argc, argv);
899 if (display == NULL) {
900 fprintf(stderr, "failed to create display: %m\n");
901 return -1;
902 }
903
904 nested = nested_create(display);
905
Kristian Høgsbergcb61dcf2013-08-07 09:50:12 -0700906 launch_client(nested, "weston-nested-client");
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400907
908 display_run(display);
909
910 nested_destroy(nested);
911 display_destroy(display);
912
913 return 0;
914}