blob: 244bbcae2ae4871e1d6777f583a3d9fc4bce06a9 [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ø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;
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700279 if (!query_buffer(nested->egl_display, buffer_resource,
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400280 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,
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700300 EGL_WAYLAND_BUFFER_WL, buffer_resource,
301 NULL);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400302 if (surface->image == EGL_NO_IMAGE_KHR) {
303 fprintf(stderr, "failed to create img\n");
304 return;
305 }
306
Kristian Høgsberg0d5fe3a2013-08-15 15:17:57 -0700307 query_buffer(nested->egl_display, buffer_resource, EGL_WIDTH, &width);
308 query_buffer(nested->egl_display, buffer_resource, EGL_HEIGHT, &height);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400309
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
Peter Huttererf3d62272013-08-08 11:57:05 +1000407 surface = zalloc(sizeof *surface);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400408 if (surface == NULL) {
409 wl_resource_post_no_memory(resource);
410 return;
411 }
412
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400413 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
Peter Huttererf3d62272013-08-08 11:57:05 +1000570 nested = zalloc(sizeof *nested);
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400571 if (nested == NULL)
572 return nested;
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400573
574 nested->window = window_create(display);
575 nested->widget = frame_create(nested->window, nested);
576 window_set_title(nested->window, "Wayland Nested");
577 nested->display = display;
578
579 window_set_user_data(nested->window, nested);
580 widget_set_redraw_handler(nested->widget, redraw_handler);
581 window_set_keyboard_focus_handler(nested->window,
582 keyboard_focus_handler);
583
584 nested_init_compositor(nested);
585
586 widget_schedule_resize(nested->widget, 400, 400);
587
588 return nested;
589}
590
591static void
592nested_destroy(struct nested *nested)
593{
594 widget_destroy(nested->widget);
595 window_destroy(nested->window);
596 free(nested);
597}
598
599int
600main(int argc, char *argv[])
601{
602 struct display *display;
603 struct nested *nested;
604
605 display = display_create(&argc, argv);
606 if (display == NULL) {
607 fprintf(stderr, "failed to create display: %m\n");
608 return -1;
609 }
610
611 nested = nested_create(display);
612
Kristian Høgsbergcb61dcf2013-08-07 09:50:12 -0700613 launch_client(nested, "weston-nested-client");
Kristian Høgsberg1cc5ac32013-04-11 21:47:41 -0400614
615 display_run(display);
616
617 nested_destroy(nested);
618 display_destroy(display);
619
620 return 0;
621}