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