blob: 67938c0c2f982c63d8ebfef584122037c84e1f22 [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;
61 struct wl_list frame_callback_list;
62};
63
64struct nested_region {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040065 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040066 pixman_region32_t region;
67};
68
69struct nested_surface {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040070 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040071 struct wl_resource *buffer_resource;
72 struct nested *nested;
73 EGLImageKHR *image;
74 GLuint texture;
75 struct wl_list link;
76 cairo_surface_t *cairo_surface;
77};
78
79struct nested_frame_callback {
Kristian Høgsberg88dab172013-06-24 16:35:54 -040080 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -040081 struct wl_list link;
82};
83
84static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
85static PFNEGLCREATEIMAGEKHRPROC create_image;
86static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
87static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
88static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
89static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
90
91static void
92frame_callback(void *data, struct wl_callback *callback, uint32_t time)
93{
94 struct nested *nested = data;
95 struct nested_frame_callback *nc, *next;
96
97 if (callback)
98 wl_callback_destroy(callback);
99
100 wl_list_for_each_safe(nc, next, &nested->frame_callback_list, link) {
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400101 wl_callback_send_done(nc->resource, time);
102 wl_resource_destroy(nc->resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400103 }
104 wl_list_init(&nested->frame_callback_list);
105
106 /* FIXME: toytoolkit need a pre-block handler where we can
107 * call this. */
108 wl_display_flush_clients(nested->child_display);
109}
110
111static const struct wl_callback_listener frame_listener = {
112 frame_callback
113};
114
115static void
116redraw_handler(struct widget *widget, void *data)
117{
118 struct nested *nested = data;
119 cairo_surface_t *surface;
120 cairo_t *cr;
121 struct rectangle allocation;
122 struct wl_callback *callback;
123 struct nested_surface *s;
124
125 widget_get_allocation(nested->widget, &allocation);
126
127 surface = window_get_surface(nested->window);
128
129 cr = cairo_create(surface);
130 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
131 cairo_rectangle(cr,
132 allocation.x,
133 allocation.y,
134 allocation.width,
135 allocation.height);
136 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
137 cairo_fill(cr);
138
139 wl_list_for_each(s, &nested->surface_list, link) {
Ander Conselvan de Oliveirad224bb92013-07-16 14:24:04 +0300140 display_acquire_window_surface(nested->display,
141 nested->window, NULL);
142
143 glBindTexture(GL_TEXTURE_2D, s->texture);
144 image_target_texture_2d(GL_TEXTURE_2D, s->image);
145
146 display_release_window_surface(nested->display,
147 nested->window);
148
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400149 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
150 cairo_set_source_surface(cr, s->cairo_surface,
151 allocation.x + 10,
152 allocation.y + 10);
153 cairo_rectangle(cr, allocation.x + 10,
154 allocation.y + 10,
155 allocation.width - 10,
156 allocation.height - 10);
157
158 cairo_fill(cr);
159 }
160
161 cairo_destroy(cr);
162
163 cairo_surface_destroy(surface);
164
165 callback = wl_surface_frame(window_get_wl_surface(nested->window));
166 wl_callback_add_listener(callback, &frame_listener, nested);
167}
168
169static void
170keyboard_focus_handler(struct window *window,
171 struct input *device, void *data)
172{
173 struct nested *nested = data;
174
175 window_schedule_redraw(nested->window);
176}
177
178static void
179handle_child_data(struct task *task, uint32_t events)
180{
181 struct nested *nested = container_of(task, struct nested, child_task);
182 struct wl_event_loop *loop;
183
184 loop = wl_display_get_event_loop(nested->child_display);
185
186 wl_event_loop_dispatch(loop, -1);
187 wl_display_flush_clients(nested->child_display);
188}
189
190struct nested_client {
191 struct wl_client *client;
192 pid_t pid;
193};
194
195static struct nested_client *
196launch_client(struct nested *nested, const char *path)
197{
198 int sv[2];
199 pid_t pid;
200 struct nested_client *client;
201
202 client = malloc(sizeof *client);
203 if (client == NULL)
204 return NULL;
205
206 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
207 fprintf(stderr, "launch_client: "
208 "socketpair failed while launching '%s': %m\n",
209 path);
210 return NULL;
211 }
212
213 pid = fork();
214 if (pid == -1) {
215 close(sv[0]);
216 close(sv[1]);
217 fprintf(stderr, "launch_client: "
218 "fork failed while launching '%s': %m\n", path);
219 return NULL;
220 }
221
222 if (pid == 0) {
223 int clientfd;
224 char s[32];
225
226 /* SOCK_CLOEXEC closes both ends, so we dup the fd to
227 * get a non-CLOEXEC fd to pass through exec. */
228 clientfd = dup(sv[1]);
229 if (clientfd == -1) {
230 fprintf(stderr, "compositor: dup failed: %m\n");
231 exit(-1);
232 }
233
234 snprintf(s, sizeof s, "%d", clientfd);
235 setenv("WAYLAND_SOCKET", s, 1);
236
237 execl(path, path, NULL);
238
239 fprintf(stderr, "compositor: executing '%s' failed: %m\n",
240 path);
241 exit(-1);
242 }
243
244 close(sv[1]);
245
246 client->client = wl_client_create(nested->child_display, sv[0]);
247 if (!client->client) {
248 close(sv[0]);
249 fprintf(stderr, "launch_client: "
250 "wl_client_create failed while launching '%s'.\n",
251 path);
252 return NULL;
253 }
254
255 client->pid = pid;
256
257 return client;
258}
259
260static void
261destroy_surface(struct wl_resource *resource)
262{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400263 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400264
265 free(surface);
266}
267
268static void
269surface_destroy(struct wl_client *client, struct wl_resource *resource)
270{
271 wl_resource_destroy(resource);
272}
273
274static void
275surface_attach(struct wl_client *client,
276 struct wl_resource *resource,
277 struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
278{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400279 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400280 struct nested *nested = surface->nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400281 EGLint format, width, height;
282 cairo_device_t *device;
283
284 if (surface->buffer_resource)
285 wl_buffer_send_release(surface->buffer_resource);
286
287 surface->buffer_resource = buffer_resource;
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700288 if (!query_buffer(nested->egl_display, buffer_resource,
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400289 EGL_TEXTURE_FORMAT, &format)) {
290 fprintf(stderr, "attaching non-egl wl_buffer\n");
291 return;
292 }
293
294 if (surface->image != EGL_NO_IMAGE_KHR)
295 destroy_image(nested->egl_display, surface->image);
296 if (surface->cairo_surface)
297 cairo_surface_destroy(surface->cairo_surface);
298
299 switch (format) {
300 case EGL_TEXTURE_RGB:
301 case EGL_TEXTURE_RGBA:
302 break;
303 default:
304 fprintf(stderr, "unhandled format: %x\n", format);
305 return;
306 }
307
308 surface->image = create_image(nested->egl_display, NULL,
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700309 EGL_WAYLAND_BUFFER_WL, buffer_resource,
310 NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400311 if (surface->image == EGL_NO_IMAGE_KHR) {
312 fprintf(stderr, "failed to create img\n");
313 return;
314 }
315
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700316 query_buffer(nested->egl_display, buffer_resource, EGL_WIDTH, &width);
317 query_buffer(nested->egl_display, buffer_resource, EGL_HEIGHT, &height);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400318
319 device = display_get_cairo_device(nested->display);
320 surface->cairo_surface =
321 cairo_gl_surface_create_for_texture(device,
322 CAIRO_CONTENT_COLOR_ALPHA,
323 surface->texture,
324 width, height);
325
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400326 window_schedule_redraw(nested->window);
327}
328
329static void
330surface_damage(struct wl_client *client,
331 struct wl_resource *resource,
332 int32_t x, int32_t y, int32_t width, int32_t height)
333{
334}
335
336static void
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400337destroy_frame_callback(struct wl_resource *resource)
338{
339 struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
340
341 wl_list_remove(&callback->link);
342 free(callback);
343}
344
345static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400346surface_frame(struct wl_client *client,
347 struct wl_resource *resource, uint32_t id)
348{
349 struct nested_frame_callback *callback;
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400350 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400351 struct nested *nested = surface->nested;
352
353 callback = malloc(sizeof *callback);
354 if (callback == NULL) {
355 wl_resource_post_no_memory(resource);
356 return;
357 }
358
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400359 callback->resource = wl_resource_create(client,
360 &wl_callback_interface, 1, id);
361 wl_resource_set_implementation(callback->resource, NULL, callback,
362 destroy_frame_callback);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400363
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400364 wl_list_insert(nested->frame_callback_list.prev, &callback->link);
365}
366
367static void
368surface_set_opaque_region(struct wl_client *client,
369 struct wl_resource *resource,
370 struct wl_resource *region_resource)
371{
372 fprintf(stderr, "surface_set_opaque_region\n");
373}
374
375static void
376surface_set_input_region(struct wl_client *client,
377 struct wl_resource *resource,
378 struct wl_resource *region_resource)
379{
380 fprintf(stderr, "surface_set_input_region\n");
381}
382
383static void
384surface_commit(struct wl_client *client, struct wl_resource *resource)
385{
386}
387
388static void
389surface_set_buffer_transform(struct wl_client *client,
390 struct wl_resource *resource, int transform)
391{
392 fprintf(stderr, "surface_set_buffer_transform\n");
393}
394
395static const struct wl_surface_interface surface_interface = {
396 surface_destroy,
397 surface_attach,
398 surface_damage,
399 surface_frame,
400 surface_set_opaque_region,
401 surface_set_input_region,
402 surface_commit,
403 surface_set_buffer_transform
404};
405
406static void
407compositor_create_surface(struct wl_client *client,
408 struct wl_resource *resource, uint32_t id)
409{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400410 struct nested *nested = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400411 struct nested_surface *surface;
412
Peter Huttererf3d62272013-08-08 11:57:05 +1000413 surface = zalloc(sizeof *surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400414 if (surface == NULL) {
415 wl_resource_post_no_memory(resource);
416 return;
417 }
418
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400419 surface->nested = nested;
420
421 display_acquire_window_surface(nested->display,
422 nested->window, NULL);
423
424 glGenTextures(1, &surface->texture);
425 glBindTexture(GL_TEXTURE_2D, surface->texture);
426 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
427 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
428 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
429 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
430
431 display_release_window_surface(nested->display, nested->window);
432
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400433 surface->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400434 wl_resource_create(client, &wl_surface_interface, 1, id);
435
436 wl_resource_set_implementation(surface->resource,
437 &surface_interface, surface,
438 destroy_surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400439
440 wl_list_insert(nested->surface_list.prev, &surface->link);
441}
442
443static void
444destroy_region(struct wl_resource *resource)
445{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400446 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400447
448 pixman_region32_fini(&region->region);
449 free(region);
450}
451
452static void
453region_destroy(struct wl_client *client, struct wl_resource *resource)
454{
455 wl_resource_destroy(resource);
456}
457
458static void
459region_add(struct wl_client *client, struct wl_resource *resource,
460 int32_t x, int32_t y, int32_t width, int32_t height)
461{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400462 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400463
464 pixman_region32_union_rect(&region->region, &region->region,
465 x, y, width, height);
466}
467
468static void
469region_subtract(struct wl_client *client, struct wl_resource *resource,
470 int32_t x, int32_t y, int32_t width, int32_t height)
471{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400472 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400473 pixman_region32_t rect;
474
475 pixman_region32_init_rect(&rect, x, y, width, height);
476 pixman_region32_subtract(&region->region, &region->region, &rect);
477 pixman_region32_fini(&rect);
478}
479
480static const struct wl_region_interface region_interface = {
481 region_destroy,
482 region_add,
483 region_subtract
484};
485
486static void
487compositor_create_region(struct wl_client *client,
488 struct wl_resource *resource, uint32_t id)
489{
490 struct nested_region *region;
491
492 region = malloc(sizeof *region);
493 if (region == NULL) {
494 wl_resource_post_no_memory(resource);
495 return;
496 }
497
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400498 pixman_region32_init(&region->region);
499
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400500 region->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400501 wl_resource_create(client, &wl_region_interface, 1, id);
502 wl_resource_set_implementation(region->resource, &region_interface,
503 region, destroy_region);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400504}
505
506static const struct wl_compositor_interface compositor_interface = {
507 compositor_create_surface,
508 compositor_create_region
509};
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400510
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400511static void
512compositor_bind(struct wl_client *client,
513 void *data, uint32_t version, uint32_t id)
514{
515 struct nested *nested = data;
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400516 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400517
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400518 resource = wl_resource_create(client, &wl_compositor_interface,
519 MIN(version, 3), id);
520 wl_resource_set_implementation(resource, &compositor_interface,
521 nested, NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400522}
523
524static int
525nested_init_compositor(struct nested *nested)
526{
527 const char *extensions;
528 struct wl_event_loop *loop;
529 int fd, ret;
530
531 wl_list_init(&nested->surface_list);
532 wl_list_init(&nested->frame_callback_list);
533 nested->child_display = wl_display_create();
534 loop = wl_display_get_event_loop(nested->child_display);
535 fd = wl_event_loop_get_fd(loop);
536 nested->child_task.run = handle_child_data;
537 display_watch_fd(nested->display, fd,
538 EPOLLIN, &nested->child_task);
539
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400540 if (!wl_global_create(nested->child_display,
541 &wl_compositor_interface, 1,
542 nested, compositor_bind))
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400543 return -1;
544
545 wl_display_init_shm(nested->child_display);
546
547 nested->egl_display = display_get_egl_display(nested->display);
548 extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
549 if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
550 fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
551 return -1;
552 }
553
554 bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
555 unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
556 create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
557 destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
558 query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
559 image_target_texture_2d =
560 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
561
562 ret = bind_display(nested->egl_display, nested->child_display);
563 if (!ret) {
564 fprintf(stderr, "failed to bind wl_display\n");
565 return -1;
566 }
567
568 return 0;
569}
570
571static struct nested *
572nested_create(struct display *display)
573{
574 struct nested *nested;
575
Peter Huttererf3d62272013-08-08 11:57:05 +1000576 nested = zalloc(sizeof *nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400577 if (nested == NULL)
578 return nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400579
580 nested->window = window_create(display);
581 nested->widget = frame_create(nested->window, nested);
582 window_set_title(nested->window, "Wayland Nested");
583 nested->display = display;
584
585 window_set_user_data(nested->window, nested);
586 widget_set_redraw_handler(nested->widget, redraw_handler);
587 window_set_keyboard_focus_handler(nested->window,
588 keyboard_focus_handler);
589
590 nested_init_compositor(nested);
591
592 widget_schedule_resize(nested->widget, 400, 400);
593
594 return nested;
595}
596
597static void
598nested_destroy(struct nested *nested)
599{
600 widget_destroy(nested->widget);
601 window_destroy(nested->window);
602 free(nested);
603}
604
605int
606main(int argc, char *argv[])
607{
608 struct display *display;
609 struct nested *nested;
610
611 display = display_create(&argc, argv);
612 if (display == NULL) {
613 fprintf(stderr, "failed to create display: %m\n");
614 return -1;
615 }
616
617 nested = nested_create(display);
618
Kristian Høgsbergcb61dcf2013-08-07 09:50:12 -0700619 launch_client(nested, "weston-nested-client");
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400620
621 display_run(display);
622
623 nested_destroy(nested);
624 display_destroy(display);
625
626 return 0;
627}