blob: a4e07f7b8f22fb4e9a960c1d41a2dcb384bef9e3 [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>
43#include <wayland-server.h>
44
45#include "window.h"
46
Kristian Høgsberg919cddb2013-07-08 19:03:57 -040047#define MIN(x,y) (((x) < (y)) ? (x) : (y))
48
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040049struct nested {
50 struct display *display;
51 struct window *window;
52 struct widget *widget;
53 struct wl_display *child_display;
54 struct task child_task;
55
56 EGLDisplay egl_display;
57 struct program *texture_program;
58
59 struct wl_list surface_list;
60 struct wl_list frame_callback_list;
61};
62
63struct nested_region {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040064 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040065 pixman_region32_t region;
66};
67
68struct nested_surface {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040069 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040070 struct wl_resource *buffer_resource;
71 struct nested *nested;
72 EGLImageKHR *image;
73 GLuint texture;
74 struct wl_list link;
75 cairo_surface_t *cairo_surface;
76};
77
78struct nested_frame_callback {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040079 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040080 struct wl_list link;
81};
82
83static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
84static PFNEGLCREATEIMAGEKHRPROC create_image;
85static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
86static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
87static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
88static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
89
90static void
91frame_callback(void *data, struct wl_callback *callback, uint32_t time)
92{
93 struct nested *nested = data;
94 struct nested_frame_callback *nc, *next;
95
96 if (callback)
97 wl_callback_destroy(callback);
98
99 wl_list_for_each_safe(nc, next, &nested->frame_callback_list, link) {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400100 wl_callback_send_done(nc->resource, time);
101 wl_resource_destroy(nc->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400102 }
103 wl_list_init(&nested->frame_callback_list);
104
105 /* FIXME: toytoolkit need a pre-block handler where we can
106 * call this. */
107 wl_display_flush_clients(nested->child_display);
108}
109
110static const struct wl_callback_listener frame_listener = {
111 frame_callback
112};
113
114static void
115redraw_handler(struct widget *widget, void *data)
116{
117 struct nested *nested = data;
118 cairo_surface_t *surface;
119 cairo_t *cr;
120 struct rectangle allocation;
121 struct wl_callback *callback;
122 struct nested_surface *s;
123
124 widget_get_allocation(nested->widget, &allocation);
125
126 surface = window_get_surface(nested->window);
127
128 cr = cairo_create(surface);
129 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
130 cairo_rectangle(cr,
131 allocation.x,
132 allocation.y,
133 allocation.width,
134 allocation.height);
135 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
136 cairo_fill(cr);
137
138 wl_list_for_each(s, &nested->surface_list, link) {
139 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
140 cairo_set_source_surface(cr, s->cairo_surface,
141 allocation.x + 10,
142 allocation.y + 10);
143 cairo_rectangle(cr, allocation.x + 10,
144 allocation.y + 10,
145 allocation.width - 10,
146 allocation.height - 10);
147
148 cairo_fill(cr);
149 }
150
151 cairo_destroy(cr);
152
153 cairo_surface_destroy(surface);
154
155 callback = wl_surface_frame(window_get_wl_surface(nested->window));
156 wl_callback_add_listener(callback, &frame_listener, nested);
157}
158
159static void
160keyboard_focus_handler(struct window *window,
161 struct input *device, void *data)
162{
163 struct nested *nested = data;
164
165 window_schedule_redraw(nested->window);
166}
167
168static void
169handle_child_data(struct task *task, uint32_t events)
170{
171 struct nested *nested = container_of(task, struct nested, child_task);
172 struct wl_event_loop *loop;
173
174 loop = wl_display_get_event_loop(nested->child_display);
175
176 wl_event_loop_dispatch(loop, -1);
177 wl_display_flush_clients(nested->child_display);
178}
179
180struct nested_client {
181 struct wl_client *client;
182 pid_t pid;
183};
184
185static struct nested_client *
186launch_client(struct nested *nested, const char *path)
187{
188 int sv[2];
189 pid_t pid;
190 struct nested_client *client;
191
192 client = malloc(sizeof *client);
193 if (client == NULL)
194 return NULL;
195
196 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
197 fprintf(stderr, "launch_client: "
198 "socketpair failed while launching '%s': %m\n",
199 path);
200 return NULL;
201 }
202
203 pid = fork();
204 if (pid == -1) {
205 close(sv[0]);
206 close(sv[1]);
207 fprintf(stderr, "launch_client: "
208 "fork failed while launching '%s': %m\n", path);
209 return NULL;
210 }
211
212 if (pid == 0) {
213 int clientfd;
214 char s[32];
215
216 /* SOCK_CLOEXEC closes both ends, so we dup the fd to
217 * get a non-CLOEXEC fd to pass through exec. */
218 clientfd = dup(sv[1]);
219 if (clientfd == -1) {
220 fprintf(stderr, "compositor: dup failed: %m\n");
221 exit(-1);
222 }
223
224 snprintf(s, sizeof s, "%d", clientfd);
225 setenv("WAYLAND_SOCKET", s, 1);
226
227 execl(path, path, NULL);
228
229 fprintf(stderr, "compositor: executing '%s' failed: %m\n",
230 path);
231 exit(-1);
232 }
233
234 close(sv[1]);
235
236 client->client = wl_client_create(nested->child_display, sv[0]);
237 if (!client->client) {
238 close(sv[0]);
239 fprintf(stderr, "launch_client: "
240 "wl_client_create failed while launching '%s'.\n",
241 path);
242 return NULL;
243 }
244
245 client->pid = pid;
246
247 return client;
248}
249
250static void
251destroy_surface(struct wl_resource *resource)
252{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400253 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400254
255 free(surface);
256}
257
258static void
259surface_destroy(struct wl_client *client, struct wl_resource *resource)
260{
261 wl_resource_destroy(resource);
262}
263
264static void
265surface_attach(struct wl_client *client,
266 struct wl_resource *resource,
267 struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
268{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400269 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400270 struct nested *nested = surface->nested;
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400271 struct wl_buffer *buffer = wl_resource_get_user_data(buffer_resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400272 EGLint format, width, height;
273 cairo_device_t *device;
274
275 if (surface->buffer_resource)
276 wl_buffer_send_release(surface->buffer_resource);
277
278 surface->buffer_resource = buffer_resource;
279 if (!query_buffer(nested->egl_display, buffer,
280 EGL_TEXTURE_FORMAT, &format)) {
281 fprintf(stderr, "attaching non-egl wl_buffer\n");
282 return;
283 }
284
285 if (surface->image != EGL_NO_IMAGE_KHR)
286 destroy_image(nested->egl_display, surface->image);
287 if (surface->cairo_surface)
288 cairo_surface_destroy(surface->cairo_surface);
289
290 switch (format) {
291 case EGL_TEXTURE_RGB:
292 case EGL_TEXTURE_RGBA:
293 break;
294 default:
295 fprintf(stderr, "unhandled format: %x\n", format);
296 return;
297 }
298
299 surface->image = create_image(nested->egl_display, NULL,
300 EGL_WAYLAND_BUFFER_WL, buffer, NULL);
301 if (surface->image == EGL_NO_IMAGE_KHR) {
302 fprintf(stderr, "failed to create img\n");
303 return;
304 }
305
306 query_buffer(nested->egl_display, buffer, EGL_WIDTH, &width);
307 query_buffer(nested->egl_display, buffer, EGL_HEIGHT, &height);
308
309 device = display_get_cairo_device(nested->display);
310 surface->cairo_surface =
311 cairo_gl_surface_create_for_texture(device,
312 CAIRO_CONTENT_COLOR_ALPHA,
313 surface->texture,
314 width, height);
315
316 glBindTexture(GL_TEXTURE_2D, surface->texture);
317 image_target_texture_2d(GL_TEXTURE_2D, surface->image);
318
319 window_schedule_redraw(nested->window);
320}
321
322static void
323surface_damage(struct wl_client *client,
324 struct wl_resource *resource,
325 int32_t x, int32_t y, int32_t width, int32_t height)
326{
327}
328
329static void
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400330destroy_frame_callback(struct wl_resource *resource)
331{
332 struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
333
334 wl_list_remove(&callback->link);
335 free(callback);
336}
337
338static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400339surface_frame(struct wl_client *client,
340 struct wl_resource *resource, uint32_t id)
341{
342 struct nested_frame_callback *callback;
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400343 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400344 struct nested *nested = surface->nested;
345
346 callback = malloc(sizeof *callback);
347 if (callback == NULL) {
348 wl_resource_post_no_memory(resource);
349 return;
350 }
351
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400352 callback->resource = wl_resource_create(client,
353 &wl_callback_interface, 1, id);
354 wl_resource_set_implementation(callback->resource, NULL, callback,
355 destroy_frame_callback);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400356
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400357 wl_list_insert(nested->frame_callback_list.prev, &callback->link);
358}
359
360static void
361surface_set_opaque_region(struct wl_client *client,
362 struct wl_resource *resource,
363 struct wl_resource *region_resource)
364{
365 fprintf(stderr, "surface_set_opaque_region\n");
366}
367
368static void
369surface_set_input_region(struct wl_client *client,
370 struct wl_resource *resource,
371 struct wl_resource *region_resource)
372{
373 fprintf(stderr, "surface_set_input_region\n");
374}
375
376static void
377surface_commit(struct wl_client *client, struct wl_resource *resource)
378{
379}
380
381static void
382surface_set_buffer_transform(struct wl_client *client,
383 struct wl_resource *resource, int transform)
384{
385 fprintf(stderr, "surface_set_buffer_transform\n");
386}
387
388static const struct wl_surface_interface surface_interface = {
389 surface_destroy,
390 surface_attach,
391 surface_damage,
392 surface_frame,
393 surface_set_opaque_region,
394 surface_set_input_region,
395 surface_commit,
396 surface_set_buffer_transform
397};
398
399static void
400compositor_create_surface(struct wl_client *client,
401 struct wl_resource *resource, uint32_t id)
402{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400403 struct nested *nested = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400404 struct nested_surface *surface;
405
406 surface = malloc(sizeof *surface);
407 if (surface == NULL) {
408 wl_resource_post_no_memory(resource);
409 return;
410 }
411
412 memset(surface, 0, sizeof *surface);
413 surface->nested = nested;
414
415 display_acquire_window_surface(nested->display,
416 nested->window, NULL);
417
418 glGenTextures(1, &surface->texture);
419 glBindTexture(GL_TEXTURE_2D, surface->texture);
420 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
421 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
422 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
423 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
424
425 display_release_window_surface(nested->display, nested->window);
426
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400427 surface->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400428 wl_resource_create(client, &wl_surface_interface, 1, id);
429
430 wl_resource_set_implementation(surface->resource,
431 &surface_interface, surface,
432 destroy_surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400433
434 wl_list_insert(nested->surface_list.prev, &surface->link);
435}
436
437static void
438destroy_region(struct wl_resource *resource)
439{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400440 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400441
442 pixman_region32_fini(&region->region);
443 free(region);
444}
445
446static void
447region_destroy(struct wl_client *client, struct wl_resource *resource)
448{
449 wl_resource_destroy(resource);
450}
451
452static void
453region_add(struct wl_client *client, struct wl_resource *resource,
454 int32_t x, int32_t y, int32_t width, int32_t height)
455{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400456 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400457
458 pixman_region32_union_rect(&region->region, &region->region,
459 x, y, width, height);
460}
461
462static void
463region_subtract(struct wl_client *client, struct wl_resource *resource,
464 int32_t x, int32_t y, int32_t width, int32_t height)
465{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400466 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400467 pixman_region32_t rect;
468
469 pixman_region32_init_rect(&rect, x, y, width, height);
470 pixman_region32_subtract(&region->region, &region->region, &rect);
471 pixman_region32_fini(&rect);
472}
473
474static const struct wl_region_interface region_interface = {
475 region_destroy,
476 region_add,
477 region_subtract
478};
479
480static void
481compositor_create_region(struct wl_client *client,
482 struct wl_resource *resource, uint32_t id)
483{
484 struct nested_region *region;
485
486 region = malloc(sizeof *region);
487 if (region == NULL) {
488 wl_resource_post_no_memory(resource);
489 return;
490 }
491
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400492 pixman_region32_init(&region->region);
493
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400494 region->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400495 wl_resource_create(client, &wl_region_interface, 1, id);
496 wl_resource_set_implementation(region->resource, &region_interface,
497 region, destroy_region);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400498}
499
500static const struct wl_compositor_interface compositor_interface = {
501 compositor_create_surface,
502 compositor_create_region
503};
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400504
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400505static void
506compositor_bind(struct wl_client *client,
507 void *data, uint32_t version, uint32_t id)
508{
509 struct nested *nested = data;
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400510 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400511
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400512 resource = wl_resource_create(client, &wl_compositor_interface,
513 MIN(version, 3), id);
514 wl_resource_set_implementation(resource, &compositor_interface,
515 nested, NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400516}
517
518static int
519nested_init_compositor(struct nested *nested)
520{
521 const char *extensions;
522 struct wl_event_loop *loop;
523 int fd, ret;
524
525 wl_list_init(&nested->surface_list);
526 wl_list_init(&nested->frame_callback_list);
527 nested->child_display = wl_display_create();
528 loop = wl_display_get_event_loop(nested->child_display);
529 fd = wl_event_loop_get_fd(loop);
530 nested->child_task.run = handle_child_data;
531 display_watch_fd(nested->display, fd,
532 EPOLLIN, &nested->child_task);
533
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400534 if (!wl_global_create(nested->child_display,
535 &wl_compositor_interface, 1,
536 nested, compositor_bind))
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400537 return -1;
538
539 wl_display_init_shm(nested->child_display);
540
541 nested->egl_display = display_get_egl_display(nested->display);
542 extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
543 if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
544 fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
545 return -1;
546 }
547
548 bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
549 unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
550 create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
551 destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
552 query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
553 image_target_texture_2d =
554 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
555
556 ret = bind_display(nested->egl_display, nested->child_display);
557 if (!ret) {
558 fprintf(stderr, "failed to bind wl_display\n");
559 return -1;
560 }
561
562 return 0;
563}
564
565static struct nested *
566nested_create(struct display *display)
567{
568 struct nested *nested;
569
570 nested = malloc(sizeof *nested);
571 if (nested == NULL)
572 return nested;
573 memset(nested, 0, sizeof *nested);
574
575 nested->window = window_create(display);
576 nested->widget = frame_create(nested->window, nested);
577 window_set_title(nested->window, "Wayland Nested");
578 nested->display = display;
579
580 window_set_user_data(nested->window, nested);
581 widget_set_redraw_handler(nested->widget, redraw_handler);
582 window_set_keyboard_focus_handler(nested->window,
583 keyboard_focus_handler);
584
585 nested_init_compositor(nested);
586
587 widget_schedule_resize(nested->widget, 400, 400);
588
589 return nested;
590}
591
592static void
593nested_destroy(struct nested *nested)
594{
595 widget_destroy(nested->widget);
596 window_destroy(nested->window);
597 free(nested);
598}
599
600int
601main(int argc, char *argv[])
602{
603 struct display *display;
604 struct nested *nested;
605
606 display = display_create(&argc, argv);
607 if (display == NULL) {
608 fprintf(stderr, "failed to create display: %m\n");
609 return -1;
610 }
611
612 nested = nested_create(display);
613
614 launch_client(nested, "nested-client");
615
616 display_run(display);
617
618 nested_destroy(nested);
619 display_destroy(display);
620
621 return 0;
622}