nested: Remove the surface from the surface list when destroyed
[profile/ivi/weston-ivi-shell.git] / clients / nested.c
1 /*
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 #define WL_HIDE_DEPRECATED
44 #include <wayland-server.h>
45
46 #include "window.h"
47
48 #define MIN(x,y) (((x) < (y)) ? (x) : (y))
49
50 struct 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
64 struct nested_region {
65         struct wl_resource *resource;
66         pixman_region32_t region;
67 };
68
69 struct nested_surface {
70         struct wl_resource *resource;
71         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
79 struct nested_frame_callback {
80         struct wl_resource *resource;
81         struct wl_list link;
82 };
83
84 static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
85 static PFNEGLCREATEIMAGEKHRPROC create_image;
86 static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
87 static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
88 static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
89 static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
90
91 static void
92 frame_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) {
101                 wl_callback_send_done(nc->resource, time);
102                 wl_resource_destroy(nc->resource);
103         }
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
111 static const struct wl_callback_listener frame_listener = {
112         frame_callback
113 };
114
115 static void
116 redraw_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                 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
149                 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
169 static void
170 keyboard_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
178 static void
179 handle_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
190 struct nested_client {
191         struct wl_client *client;
192         pid_t pid;
193 };
194
195 static struct nested_client *
196 launch_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                 free(client);
211                 return NULL;
212         }
213
214         pid = fork();
215         if (pid == -1) {
216                 close(sv[0]);
217                 close(sv[1]);
218                 free(client);
219                 fprintf(stderr, "launch_client: "
220                         "fork failed while launching '%s': %m\n", path);
221                 return NULL;
222         }
223
224         if (pid == 0) {
225                 int clientfd;
226                 char s[32];
227
228                 /* SOCK_CLOEXEC closes both ends, so we dup the fd to
229                  * get a non-CLOEXEC fd to pass through exec. */
230                 clientfd = dup(sv[1]);
231                 if (clientfd == -1) {
232                         fprintf(stderr, "compositor: dup failed: %m\n");
233                         exit(-1);
234                 }
235
236                 snprintf(s, sizeof s, "%d", clientfd);
237                 setenv("WAYLAND_SOCKET", s, 1);
238
239                 execl(path, path, NULL);
240
241                 fprintf(stderr, "compositor: executing '%s' failed: %m\n",
242                         path);
243                 exit(-1);
244         }
245
246         close(sv[1]);
247
248         client->client = wl_client_create(nested->child_display, sv[0]);
249         if (!client->client) {
250                 close(sv[0]);
251                 free(client);
252                 fprintf(stderr, "launch_client: "
253                         "wl_client_create failed while launching '%s'.\n",
254                         path);
255                 return NULL;
256         }
257
258         client->pid = pid;
259
260         return client;
261 }
262
263 static void
264 destroy_surface(struct wl_resource *resource)
265 {
266         struct nested_surface *surface = wl_resource_get_user_data(resource);
267
268         wl_list_remove(&surface->link);
269
270         free(surface);
271 }
272
273 static void
274 surface_destroy(struct wl_client *client, struct wl_resource *resource)
275 {
276         wl_resource_destroy(resource);
277 }
278
279 static void
280 surface_attach(struct wl_client *client,
281                struct wl_resource *resource,
282                struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
283 {
284         struct nested_surface *surface = wl_resource_get_user_data(resource);
285         struct nested *nested = surface->nested;
286         EGLint format, width, height;
287         cairo_device_t *device;
288
289         if (surface->buffer_resource)
290                 wl_buffer_send_release(surface->buffer_resource);
291
292         surface->buffer_resource = buffer_resource;
293         if (!query_buffer(nested->egl_display, (void *) buffer_resource,
294                           EGL_TEXTURE_FORMAT, &format)) {
295                 fprintf(stderr, "attaching non-egl wl_buffer\n");
296                 return;
297         }
298
299         if (surface->image != EGL_NO_IMAGE_KHR)
300                 destroy_image(nested->egl_display, surface->image);
301         if (surface->cairo_surface)
302                 cairo_surface_destroy(surface->cairo_surface);
303
304         switch (format) {
305         case EGL_TEXTURE_RGB:
306         case EGL_TEXTURE_RGBA:
307                 break;
308         default:
309                 fprintf(stderr, "unhandled format: %x\n", format);
310                 return;
311         }
312
313         surface->image = create_image(nested->egl_display, NULL,
314                                       EGL_WAYLAND_BUFFER_WL, buffer_resource,
315                                       NULL);
316         if (surface->image == EGL_NO_IMAGE_KHR) {
317                 fprintf(stderr, "failed to create img\n");
318                 return;
319         }
320
321         query_buffer(nested->egl_display,
322                      (void *) buffer_resource, EGL_WIDTH, &width);
323         query_buffer(nested->egl_display,
324                      (void *) buffer_resource, EGL_HEIGHT, &height);
325
326         device = display_get_cairo_device(nested->display);
327         surface->cairo_surface = 
328                 cairo_gl_surface_create_for_texture(device,
329                                                     CAIRO_CONTENT_COLOR_ALPHA,
330                                                     surface->texture,
331                                                     width, height);
332
333         window_schedule_redraw(nested->window);
334 }
335
336 static void
337 surface_damage(struct wl_client *client,
338                struct wl_resource *resource,
339                int32_t x, int32_t y, int32_t width, int32_t height)
340 {
341 }
342
343 static void
344 destroy_frame_callback(struct wl_resource *resource)
345 {
346         struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
347
348         wl_list_remove(&callback->link);
349         free(callback);
350 }
351
352 static void
353 surface_frame(struct wl_client *client,
354               struct wl_resource *resource, uint32_t id)
355 {
356         struct nested_frame_callback *callback;
357         struct nested_surface *surface = wl_resource_get_user_data(resource);
358         struct nested *nested = surface->nested;
359
360         callback = malloc(sizeof *callback);
361         if (callback == NULL) {
362                 wl_resource_post_no_memory(resource);
363                 return;
364         }
365
366         callback->resource = wl_resource_create(client,
367                                                 &wl_callback_interface, 1, id);
368         wl_resource_set_implementation(callback->resource, NULL, callback,
369                                        destroy_frame_callback);
370
371         wl_list_insert(nested->frame_callback_list.prev, &callback->link);
372 }
373
374 static void
375 surface_set_opaque_region(struct wl_client *client,
376                           struct wl_resource *resource,
377                           struct wl_resource *region_resource)
378 {
379         fprintf(stderr, "surface_set_opaque_region\n");
380 }
381
382 static void
383 surface_set_input_region(struct wl_client *client,
384                          struct wl_resource *resource,
385                          struct wl_resource *region_resource)
386 {
387         fprintf(stderr, "surface_set_input_region\n");
388 }
389
390 static void
391 surface_commit(struct wl_client *client, struct wl_resource *resource)
392 {
393 }
394
395 static void
396 surface_set_buffer_transform(struct wl_client *client,
397                              struct wl_resource *resource, int transform)
398 {
399         fprintf(stderr, "surface_set_buffer_transform\n");
400 }
401
402 static const struct wl_surface_interface surface_interface = {
403         surface_destroy,
404         surface_attach,
405         surface_damage,
406         surface_frame,
407         surface_set_opaque_region,
408         surface_set_input_region,
409         surface_commit,
410         surface_set_buffer_transform
411 };
412
413 static void
414 compositor_create_surface(struct wl_client *client,
415                           struct wl_resource *resource, uint32_t id)
416 {
417         struct nested *nested = wl_resource_get_user_data(resource);
418         struct nested_surface *surface;
419         
420         surface = zalloc(sizeof *surface);
421         if (surface == NULL) {
422                 wl_resource_post_no_memory(resource);
423                 return;
424         }
425
426         surface->nested = nested;
427
428         display_acquire_window_surface(nested->display,
429                                        nested->window, NULL);
430
431         glGenTextures(1, &surface->texture);
432         glBindTexture(GL_TEXTURE_2D, surface->texture);
433         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
434         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
435         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
436         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
437
438         display_release_window_surface(nested->display, nested->window);
439
440         surface->resource =
441                 wl_resource_create(client, &wl_surface_interface, 1, id);
442
443         wl_resource_set_implementation(surface->resource,
444                                        &surface_interface, surface,
445                                        destroy_surface);
446
447         wl_list_insert(nested->surface_list.prev, &surface->link);
448 }
449
450 static void
451 destroy_region(struct wl_resource *resource)
452 {
453         struct nested_region *region = wl_resource_get_user_data(resource);
454
455         pixman_region32_fini(&region->region);
456         free(region);
457 }
458
459 static void
460 region_destroy(struct wl_client *client, struct wl_resource *resource)
461 {
462         wl_resource_destroy(resource);
463 }
464
465 static void
466 region_add(struct wl_client *client, struct wl_resource *resource,
467            int32_t x, int32_t y, int32_t width, int32_t height)
468 {
469         struct nested_region *region = wl_resource_get_user_data(resource);
470
471         pixman_region32_union_rect(&region->region, &region->region,
472                                    x, y, width, height);
473 }
474
475 static void
476 region_subtract(struct wl_client *client, struct wl_resource *resource,
477                 int32_t x, int32_t y, int32_t width, int32_t height)
478 {
479         struct nested_region *region = wl_resource_get_user_data(resource);
480         pixman_region32_t rect;
481
482         pixman_region32_init_rect(&rect, x, y, width, height);
483         pixman_region32_subtract(&region->region, &region->region, &rect);
484         pixman_region32_fini(&rect);
485 }
486
487 static const struct wl_region_interface region_interface = {
488         region_destroy,
489         region_add,
490         region_subtract
491 };
492
493 static void
494 compositor_create_region(struct wl_client *client,
495                          struct wl_resource *resource, uint32_t id)
496 {
497         struct nested_region *region;
498
499         region = malloc(sizeof *region);
500         if (region == NULL) {
501                 wl_resource_post_no_memory(resource);
502                 return;
503         }
504
505         pixman_region32_init(&region->region);
506
507         region->resource =
508                 wl_resource_create(client, &wl_region_interface, 1, id);
509         wl_resource_set_implementation(region->resource, &region_interface,
510                                        region, destroy_region);
511 }
512
513 static const struct wl_compositor_interface compositor_interface = {
514         compositor_create_surface,
515         compositor_create_region
516 };
517
518 static void
519 compositor_bind(struct wl_client *client,
520                 void *data, uint32_t version, uint32_t id)
521 {
522         struct nested *nested = data;
523         struct wl_resource *resource;
524
525         resource = wl_resource_create(client, &wl_compositor_interface,
526                                       MIN(version, 3), id);
527         wl_resource_set_implementation(resource, &compositor_interface,
528                                        nested, NULL);
529 }
530
531 static int
532 nested_init_compositor(struct nested *nested)
533 {
534         const char *extensions;
535         struct wl_event_loop *loop;
536         int fd, ret;
537
538         wl_list_init(&nested->surface_list);
539         wl_list_init(&nested->frame_callback_list);
540         nested->child_display = wl_display_create();
541         loop = wl_display_get_event_loop(nested->child_display);
542         fd = wl_event_loop_get_fd(loop);
543         nested->child_task.run = handle_child_data;
544         display_watch_fd(nested->display, fd,
545                          EPOLLIN, &nested->child_task);
546
547         if (!wl_global_create(nested->child_display,
548                               &wl_compositor_interface, 1,
549                               nested, compositor_bind))
550                 return -1;
551
552         wl_display_init_shm(nested->child_display);
553
554         nested->egl_display = display_get_egl_display(nested->display);
555         extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
556         if (strstr(extensions, "EGL_WL_bind_wayland_display") == NULL) {
557                 fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
558                 return -1;
559         }
560
561         bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
562         unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
563         create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
564         destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
565         query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
566         image_target_texture_2d =
567                 (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
568
569         ret = bind_display(nested->egl_display, nested->child_display);
570         if (!ret) {
571                 fprintf(stderr, "failed to bind wl_display\n");
572                 return -1;
573         }
574
575         return 0;
576 }
577
578 static struct nested *
579 nested_create(struct display *display)
580 {
581         struct nested *nested;
582
583         nested = zalloc(sizeof *nested);
584         if (nested == NULL)
585                 return nested;
586
587         nested->window = window_create(display);
588         nested->widget = window_frame_create(nested->window, nested);
589         window_set_title(nested->window, "Wayland Nested");
590         nested->display = display;
591
592         window_set_user_data(nested->window, nested);
593         widget_set_redraw_handler(nested->widget, redraw_handler);
594         window_set_keyboard_focus_handler(nested->window,
595                                           keyboard_focus_handler);
596
597         nested_init_compositor(nested);
598
599         widget_schedule_resize(nested->widget, 400, 400);
600
601         return nested;
602 }
603
604 static void
605 nested_destroy(struct nested *nested)
606 {
607         widget_destroy(nested->widget);
608         window_destroy(nested->window);
609         free(nested);
610 }
611
612 int
613 main(int argc, char *argv[])
614 {
615         struct display *display;
616         struct nested *nested;
617
618         display = display_create(&argc, argv);
619         if (display == NULL) {
620                 fprintf(stderr, "failed to create display: %m\n");
621                 return -1;
622         }
623
624         nested = nested_create(display);
625
626         launch_client(nested, "weston-nested-client");
627
628         display_run(display);
629
630         nested_destroy(nested);
631         display_destroy(display);
632
633         return 0;
634 }