blob: dfeeb960bd29b7fc0aa45a5117cfc63f41ec2e48 [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) {
140 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
141 cairo_set_source_surface(cr, s->cairo_surface,
142 allocation.x + 10,
143 allocation.y + 10);
144 cairo_rectangle(cr, allocation.x + 10,
145 allocation.y + 10,
146 allocation.width - 10,
147 allocation.height - 10);
148
149 cairo_fill(cr);
150 }
151
152 cairo_destroy(cr);
153
154 cairo_surface_destroy(surface);
155
156 callback = wl_surface_frame(window_get_wl_surface(nested->window));
157 wl_callback_add_listener(callback, &frame_listener, nested);
158}
159
160static void
161keyboard_focus_handler(struct window *window,
162 struct input *device, void *data)
163{
164 struct nested *nested = data;
165
166 window_schedule_redraw(nested->window);
167}
168
169static void
170handle_child_data(struct task *task, uint32_t events)
171{
172 struct nested *nested = container_of(task, struct nested, child_task);
173 struct wl_event_loop *loop;
174
175 loop = wl_display_get_event_loop(nested->child_display);
176
177 wl_event_loop_dispatch(loop, -1);
178 wl_display_flush_clients(nested->child_display);
179}
180
181struct nested_client {
182 struct wl_client *client;
183 pid_t pid;
184};
185
186static struct nested_client *
187launch_client(struct nested *nested, const char *path)
188{
189 int sv[2];
190 pid_t pid;
191 struct nested_client *client;
192
193 client = malloc(sizeof *client);
194 if (client == NULL)
195 return NULL;
196
197 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
198 fprintf(stderr, "launch_client: "
199 "socketpair failed while launching '%s': %m\n",
200 path);
201 return NULL;
202 }
203
204 pid = fork();
205 if (pid == -1) {
206 close(sv[0]);
207 close(sv[1]);
208 fprintf(stderr, "launch_client: "
209 "fork failed while launching '%s': %m\n", path);
210 return NULL;
211 }
212
213 if (pid == 0) {
214 int clientfd;
215 char s[32];
216
217 /* SOCK_CLOEXEC closes both ends, so we dup the fd to
218 * get a non-CLOEXEC fd to pass through exec. */
219 clientfd = dup(sv[1]);
220 if (clientfd == -1) {
221 fprintf(stderr, "compositor: dup failed: %m\n");
222 exit(-1);
223 }
224
225 snprintf(s, sizeof s, "%d", clientfd);
226 setenv("WAYLAND_SOCKET", s, 1);
227
228 execl(path, path, NULL);
229
230 fprintf(stderr, "compositor: executing '%s' failed: %m\n",
231 path);
232 exit(-1);
233 }
234
235 close(sv[1]);
236
237 client->client = wl_client_create(nested->child_display, sv[0]);
238 if (!client->client) {
239 close(sv[0]);
240 fprintf(stderr, "launch_client: "
241 "wl_client_create failed while launching '%s'.\n",
242 path);
243 return NULL;
244 }
245
246 client->pid = pid;
247
248 return client;
249}
250
251static void
252destroy_surface(struct wl_resource *resource)
253{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400254 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400255
256 free(surface);
257}
258
259static void
260surface_destroy(struct wl_client *client, struct wl_resource *resource)
261{
262 wl_resource_destroy(resource);
263}
264
265static void
266surface_attach(struct wl_client *client,
267 struct wl_resource *resource,
268 struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
269{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400270 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400271 struct nested *nested = surface->nested;
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400272 struct wl_buffer *buffer = wl_resource_get_user_data(buffer_resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400273 EGLint format, width, height;
274 cairo_device_t *device;
275
276 if (surface->buffer_resource)
277 wl_buffer_send_release(surface->buffer_resource);
278
279 surface->buffer_resource = buffer_resource;
280 if (!query_buffer(nested->egl_display, buffer,
281 EGL_TEXTURE_FORMAT, &format)) {
282 fprintf(stderr, "attaching non-egl wl_buffer\n");
283 return;
284 }
285
286 if (surface->image != EGL_NO_IMAGE_KHR)
287 destroy_image(nested->egl_display, surface->image);
288 if (surface->cairo_surface)
289 cairo_surface_destroy(surface->cairo_surface);
290
291 switch (format) {
292 case EGL_TEXTURE_RGB:
293 case EGL_TEXTURE_RGBA:
294 break;
295 default:
296 fprintf(stderr, "unhandled format: %x\n", format);
297 return;
298 }
299
300 surface->image = create_image(nested->egl_display, NULL,
301 EGL_WAYLAND_BUFFER_WL, buffer, NULL);
302 if (surface->image == EGL_NO_IMAGE_KHR) {
303 fprintf(stderr, "failed to create img\n");
304 return;
305 }
306
307 query_buffer(nested->egl_display, buffer, EGL_WIDTH, &width);
308 query_buffer(nested->egl_display, buffer, EGL_HEIGHT, &height);
309
310 device = display_get_cairo_device(nested->display);
311 surface->cairo_surface =
312 cairo_gl_surface_create_for_texture(device,
313 CAIRO_CONTENT_COLOR_ALPHA,
314 surface->texture,
315 width, height);
316
317 glBindTexture(GL_TEXTURE_2D, surface->texture);
318 image_target_texture_2d(GL_TEXTURE_2D, surface->image);
319
320 window_schedule_redraw(nested->window);
321}
322
323static void
324surface_damage(struct wl_client *client,
325 struct wl_resource *resource,
326 int32_t x, int32_t y, int32_t width, int32_t height)
327{
328}
329
330static void
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400331destroy_frame_callback(struct wl_resource *resource)
332{
333 struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
334
335 wl_list_remove(&callback->link);
336 free(callback);
337}
338
339static void
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400340surface_frame(struct wl_client *client,
341 struct wl_resource *resource, uint32_t id)
342{
343 struct nested_frame_callback *callback;
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400344 struct nested_surface *surface = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400345 struct nested *nested = surface->nested;
346
347 callback = malloc(sizeof *callback);
348 if (callback == NULL) {
349 wl_resource_post_no_memory(resource);
350 return;
351 }
352
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400353 callback->resource = wl_resource_create(client,
354 &wl_callback_interface, 1, id);
355 wl_resource_set_implementation(callback->resource, NULL, callback,
356 destroy_frame_callback);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400357
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400358 wl_list_insert(nested->frame_callback_list.prev, &callback->link);
359}
360
361static void
362surface_set_opaque_region(struct wl_client *client,
363 struct wl_resource *resource,
364 struct wl_resource *region_resource)
365{
366 fprintf(stderr, "surface_set_opaque_region\n");
367}
368
369static void
370surface_set_input_region(struct wl_client *client,
371 struct wl_resource *resource,
372 struct wl_resource *region_resource)
373{
374 fprintf(stderr, "surface_set_input_region\n");
375}
376
377static void
378surface_commit(struct wl_client *client, struct wl_resource *resource)
379{
380}
381
382static void
383surface_set_buffer_transform(struct wl_client *client,
384 struct wl_resource *resource, int transform)
385{
386 fprintf(stderr, "surface_set_buffer_transform\n");
387}
388
389static const struct wl_surface_interface surface_interface = {
390 surface_destroy,
391 surface_attach,
392 surface_damage,
393 surface_frame,
394 surface_set_opaque_region,
395 surface_set_input_region,
396 surface_commit,
397 surface_set_buffer_transform
398};
399
400static void
401compositor_create_surface(struct wl_client *client,
402 struct wl_resource *resource, uint32_t id)
403{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400404 struct nested *nested = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400405 struct nested_surface *surface;
406
407 surface = malloc(sizeof *surface);
408 if (surface == NULL) {
409 wl_resource_post_no_memory(resource);
410 return;
411 }
412
413 memset(surface, 0, sizeof *surface);
414 surface->nested = nested;
415
416 display_acquire_window_surface(nested->display,
417 nested->window, NULL);
418
419 glGenTextures(1, &surface->texture);
420 glBindTexture(GL_TEXTURE_2D, surface->texture);
421 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
422 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
423 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
424 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
425
426 display_release_window_surface(nested->display, nested->window);
427
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400428 surface->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400429 wl_resource_create(client, &wl_surface_interface, 1, id);
430
431 wl_resource_set_implementation(surface->resource,
432 &surface_interface, surface,
433 destroy_surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400434
435 wl_list_insert(nested->surface_list.prev, &surface->link);
436}
437
438static void
439destroy_region(struct wl_resource *resource)
440{
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400441 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400442
443 pixman_region32_fini(&region->region);
444 free(region);
445}
446
447static void
448region_destroy(struct wl_client *client, struct wl_resource *resource)
449{
450 wl_resource_destroy(resource);
451}
452
453static void
454region_add(struct wl_client *client, struct wl_resource *resource,
455 int32_t x, int32_t y, int32_t width, int32_t height)
456{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400457 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400458
459 pixman_region32_union_rect(&region->region, &region->region,
460 x, y, width, height);
461}
462
463static void
464region_subtract(struct wl_client *client, struct wl_resource *resource,
465 int32_t x, int32_t y, int32_t width, int32_t height)
466{
Kristian Høgsberg1d7d8f02013-06-25 16:15:27 -0400467 struct nested_region *region = wl_resource_get_user_data(resource);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400468 pixman_region32_t rect;
469
470 pixman_region32_init_rect(&rect, x, y, width, height);
471 pixman_region32_subtract(&region->region, &region->region, &rect);
472 pixman_region32_fini(&rect);
473}
474
475static const struct wl_region_interface region_interface = {
476 region_destroy,
477 region_add,
478 region_subtract
479};
480
481static void
482compositor_create_region(struct wl_client *client,
483 struct wl_resource *resource, uint32_t id)
484{
485 struct nested_region *region;
486
487 region = malloc(sizeof *region);
488 if (region == NULL) {
489 wl_resource_post_no_memory(resource);
490 return;
491 }
492
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400493 pixman_region32_init(&region->region);
494
Kristian Høgsberg88dab172013-06-24 16:35:54 -0400495 region->resource =
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400496 wl_resource_create(client, &wl_region_interface, 1, id);
497 wl_resource_set_implementation(region->resource, &region_interface,
498 region, destroy_region);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400499}
500
501static const struct wl_compositor_interface compositor_interface = {
502 compositor_create_surface,
503 compositor_create_region
504};
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400505
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400506static void
507compositor_bind(struct wl_client *client,
508 void *data, uint32_t version, uint32_t id)
509{
510 struct nested *nested = data;
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400511 struct wl_resource *resource;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400512
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400513 resource = wl_resource_create(client, &wl_compositor_interface,
514 MIN(version, 3), id);
515 wl_resource_set_implementation(resource, &compositor_interface,
516 nested, NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400517}
518
519static int
520nested_init_compositor(struct nested *nested)
521{
522 const char *extensions;
523 struct wl_event_loop *loop;
524 int fd, ret;
525
526 wl_list_init(&nested->surface_list);
527 wl_list_init(&nested->frame_callback_list);
528 nested->child_display = wl_display_create();
529 loop = wl_display_get_event_loop(nested->child_display);
530 fd = wl_event_loop_get_fd(loop);
531 nested->child_task.run = handle_child_data;
532 display_watch_fd(nested->display, fd,
533 EPOLLIN, &nested->child_task);
534
Kristian Høgsberg919cddb2013-07-08 19:03:57 -0400535 if (!wl_global_create(nested->child_display,
536 &wl_compositor_interface, 1,
537 nested, compositor_bind))
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400538 return -1;
539
540 wl_display_init_shm(nested->child_display);
541
542 nested->egl_display = display_get_egl_display(nested->display);
543 extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
544 if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
545 fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
546 return -1;
547 }
548
549 bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
550 unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
551 create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
552 destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
553 query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
554 image_target_texture_2d =
555 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
556
557 ret = bind_display(nested->egl_display, nested->child_display);
558 if (!ret) {
559 fprintf(stderr, "failed to bind wl_display\n");
560 return -1;
561 }
562
563 return 0;
564}
565
566static struct nested *
567nested_create(struct display *display)
568{
569 struct nested *nested;
570
571 nested = malloc(sizeof *nested);
572 if (nested == NULL)
573 return nested;
574 memset(nested, 0, sizeof *nested);
575
576 nested->window = window_create(display);
577 nested->widget = frame_create(nested->window, nested);
578 window_set_title(nested->window, "Wayland Nested");
579 nested->display = display;
580
581 window_set_user_data(nested->window, nested);
582 widget_set_redraw_handler(nested->widget, redraw_handler);
583 window_set_keyboard_focus_handler(nested->window,
584 keyboard_focus_handler);
585
586 nested_init_compositor(nested);
587
588 widget_schedule_resize(nested->widget, 400, 400);
589
590 return nested;
591}
592
593static void
594nested_destroy(struct nested *nested)
595{
596 widget_destroy(nested->widget);
597 window_destroy(nested->window);
598 free(nested);
599}
600
601int
602main(int argc, char *argv[])
603{
604 struct display *display;
605 struct nested *nested;
606
607 display = display_create(&argc, argv);
608 if (display == NULL) {
609 fprintf(stderr, "failed to create display: %m\n");
610 return -1;
611 }
612
613 nested = nested_create(display);
614
615 launch_client(nested, "nested-client");
616
617 display_run(display);
618
619 nested_destroy(nested);
620 display_destroy(display);
621
622 return 0;
623}