blob: baaff64a08d497623c352ab928ddd300a5a31229 [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 {
62 struct wl_resource resource;
63 pixman_region32_t region;
64};
65
66struct nested_surface {
67 struct wl_resource resource;
68 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 {
77 struct wl_resource resource;
78 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) {
98 wl_callback_send_done(&nc->resource, time);
99 wl_resource_destroy(&nc->resource);
100 }
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{
251 struct nested_surface *surface =
252 container_of(resource, struct nested_surface, resource);
253
254 free(surface);
255}
256
257static void
258surface_destroy(struct wl_client *client, struct wl_resource *resource)
259{
260 wl_resource_destroy(resource);
261}
262
263static void
264surface_attach(struct wl_client *client,
265 struct wl_resource *resource,
266 struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
267{
268 struct nested_surface *surface = resource->data;
269 struct nested *nested = surface->nested;
270 struct wl_buffer *buffer = buffer_resource->data;
271 EGLint format, width, height;
272 cairo_device_t *device;
273
274 if (surface->buffer_resource)
275 wl_buffer_send_release(surface->buffer_resource);
276
277 surface->buffer_resource = buffer_resource;
278 if (!query_buffer(nested->egl_display, buffer,
279 EGL_TEXTURE_FORMAT, &format)) {
280 fprintf(stderr, "attaching non-egl wl_buffer\n");
281 return;
282 }
283
284 if (surface->image != EGL_NO_IMAGE_KHR)
285 destroy_image(nested->egl_display, surface->image);
286 if (surface->cairo_surface)
287 cairo_surface_destroy(surface->cairo_surface);
288
289 switch (format) {
290 case EGL_TEXTURE_RGB:
291 case EGL_TEXTURE_RGBA:
292 break;
293 default:
294 fprintf(stderr, "unhandled format: %x\n", format);
295 return;
296 }
297
298 surface->image = create_image(nested->egl_display, NULL,
299 EGL_WAYLAND_BUFFER_WL, buffer, NULL);
300 if (surface->image == EGL_NO_IMAGE_KHR) {
301 fprintf(stderr, "failed to create img\n");
302 return;
303 }
304
305 query_buffer(nested->egl_display, buffer, EGL_WIDTH, &width);
306 query_buffer(nested->egl_display, buffer, EGL_HEIGHT, &height);
307
308 device = display_get_cairo_device(nested->display);
309 surface->cairo_surface =
310 cairo_gl_surface_create_for_texture(device,
311 CAIRO_CONTENT_COLOR_ALPHA,
312 surface->texture,
313 width, height);
314
315 glBindTexture(GL_TEXTURE_2D, surface->texture);
316 image_target_texture_2d(GL_TEXTURE_2D, surface->image);
317
318 window_schedule_redraw(nested->window);
319}
320
321static void
322surface_damage(struct wl_client *client,
323 struct wl_resource *resource,
324 int32_t x, int32_t y, int32_t width, int32_t height)
325{
326}
327
328static void
329surface_frame(struct wl_client *client,
330 struct wl_resource *resource, uint32_t id)
331{
332 struct nested_frame_callback *callback;
333 struct nested_surface *surface = resource->data;
334 struct nested *nested = surface->nested;
335
336 callback = malloc(sizeof *callback);
337 if (callback == NULL) {
338 wl_resource_post_no_memory(resource);
339 return;
340 }
341
342 wl_resource_init(&callback->resource, &wl_callback_interface,
343 NULL, id, callback);
344
345 wl_client_add_resource(client, &callback->resource);
346 wl_list_insert(nested->frame_callback_list.prev, &callback->link);
347}
348
349static void
350surface_set_opaque_region(struct wl_client *client,
351 struct wl_resource *resource,
352 struct wl_resource *region_resource)
353{
354 fprintf(stderr, "surface_set_opaque_region\n");
355}
356
357static void
358surface_set_input_region(struct wl_client *client,
359 struct wl_resource *resource,
360 struct wl_resource *region_resource)
361{
362 fprintf(stderr, "surface_set_input_region\n");
363}
364
365static void
366surface_commit(struct wl_client *client, struct wl_resource *resource)
367{
368}
369
370static void
371surface_set_buffer_transform(struct wl_client *client,
372 struct wl_resource *resource, int transform)
373{
374 fprintf(stderr, "surface_set_buffer_transform\n");
375}
376
377static const struct wl_surface_interface surface_interface = {
378 surface_destroy,
379 surface_attach,
380 surface_damage,
381 surface_frame,
382 surface_set_opaque_region,
383 surface_set_input_region,
384 surface_commit,
385 surface_set_buffer_transform
386};
387
388static void
389compositor_create_surface(struct wl_client *client,
390 struct wl_resource *resource, uint32_t id)
391{
392 struct nested *nested = resource->data;
393 struct nested_surface *surface;
394
395 surface = malloc(sizeof *surface);
396 if (surface == NULL) {
397 wl_resource_post_no_memory(resource);
398 return;
399 }
400
401 memset(surface, 0, sizeof *surface);
402 surface->nested = nested;
403
404 display_acquire_window_surface(nested->display,
405 nested->window, NULL);
406
407 glGenTextures(1, &surface->texture);
408 glBindTexture(GL_TEXTURE_2D, surface->texture);
409 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
410 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
411 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
412 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
413
414 display_release_window_surface(nested->display, nested->window);
415
416 wl_resource_init(&surface->resource, &wl_surface_interface,
417 &surface_interface, id, surface);
418 surface->resource.destroy = destroy_surface;
419
420 wl_client_add_resource(client, &surface->resource);
421
422 wl_list_insert(nested->surface_list.prev, &surface->link);
423}
424
425static void
426destroy_region(struct wl_resource *resource)
427{
428 struct nested_region *region =
429 container_of(resource, struct nested_region, resource);
430
431 pixman_region32_fini(&region->region);
432 free(region);
433}
434
435static void
436region_destroy(struct wl_client *client, struct wl_resource *resource)
437{
438 wl_resource_destroy(resource);
439}
440
441static void
442region_add(struct wl_client *client, struct wl_resource *resource,
443 int32_t x, int32_t y, int32_t width, int32_t height)
444{
445 struct nested_region *region = resource->data;
446
447 pixman_region32_union_rect(&region->region, &region->region,
448 x, y, width, height);
449}
450
451static void
452region_subtract(struct wl_client *client, struct wl_resource *resource,
453 int32_t x, int32_t y, int32_t width, int32_t height)
454{
455 struct nested_region *region = resource->data;
456 pixman_region32_t rect;
457
458 pixman_region32_init_rect(&rect, x, y, width, height);
459 pixman_region32_subtract(&region->region, &region->region, &rect);
460 pixman_region32_fini(&rect);
461}
462
463static const struct wl_region_interface region_interface = {
464 region_destroy,
465 region_add,
466 region_subtract
467};
468
469static void
470compositor_create_region(struct wl_client *client,
471 struct wl_resource *resource, uint32_t id)
472{
473 struct nested_region *region;
474
475 region = malloc(sizeof *region);
476 if (region == NULL) {
477 wl_resource_post_no_memory(resource);
478 return;
479 }
480
481 wl_resource_init(&region->resource, &wl_region_interface,
482 &region_interface, id, region);
483 region->resource.destroy = destroy_region;
484
485 pixman_region32_init(&region->region);
486
487 wl_client_add_resource(client, &region->resource);
488}
489
490static const struct wl_compositor_interface compositor_interface = {
491 compositor_create_surface,
492 compositor_create_region
493};
494static void
495compositor_bind(struct wl_client *client,
496 void *data, uint32_t version, uint32_t id)
497{
498 struct nested *nested = data;
499
500 wl_client_add_object(client, &wl_compositor_interface,
501 &compositor_interface, id, nested);
502}
503
504static int
505nested_init_compositor(struct nested *nested)
506{
507 const char *extensions;
508 struct wl_event_loop *loop;
509 int fd, ret;
510
511 wl_list_init(&nested->surface_list);
512 wl_list_init(&nested->frame_callback_list);
513 nested->child_display = wl_display_create();
514 loop = wl_display_get_event_loop(nested->child_display);
515 fd = wl_event_loop_get_fd(loop);
516 nested->child_task.run = handle_child_data;
517 display_watch_fd(nested->display, fd,
518 EPOLLIN, &nested->child_task);
519
520 if (!wl_display_add_global(nested->child_display,
521 &wl_compositor_interface,
522 nested, compositor_bind))
523 return -1;
524
525 wl_display_init_shm(nested->child_display);
526
527 nested->egl_display = display_get_egl_display(nested->display);
528 extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
529 if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
530 fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
531 return -1;
532 }
533
534 bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
535 unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
536 create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
537 destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
538 query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
539 image_target_texture_2d =
540 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
541
542 ret = bind_display(nested->egl_display, nested->child_display);
543 if (!ret) {
544 fprintf(stderr, "failed to bind wl_display\n");
545 return -1;
546 }
547
548 return 0;
549}
550
551static struct nested *
552nested_create(struct display *display)
553{
554 struct nested *nested;
555
556 nested = malloc(sizeof *nested);
557 if (nested == NULL)
558 return nested;
559 memset(nested, 0, sizeof *nested);
560
561 nested->window = window_create(display);
562 nested->widget = frame_create(nested->window, nested);
563 window_set_title(nested->window, "Wayland Nested");
564 nested->display = display;
565
566 window_set_user_data(nested->window, nested);
567 widget_set_redraw_handler(nested->widget, redraw_handler);
568 window_set_keyboard_focus_handler(nested->window,
569 keyboard_focus_handler);
570
571 nested_init_compositor(nested);
572
573 widget_schedule_resize(nested->widget, 400, 400);
574
575 return nested;
576}
577
578static void
579nested_destroy(struct nested *nested)
580{
581 widget_destroy(nested->widget);
582 window_destroy(nested->window);
583 free(nested);
584}
585
586int
587main(int argc, char *argv[])
588{
589 struct display *display;
590 struct nested *nested;
591
592 display = display_create(&argc, argv);
593 if (display == NULL) {
594 fprintf(stderr, "failed to create display: %m\n");
595 return -1;
596 }
597
598 nested = nested_create(display);
599
600 launch_client(nested, "nested-client");
601
602 display_run(display);
603
604 nested_destroy(nested);
605 display_destroy(display);
606
607 return 0;
608}