nested: Remove the surface from the surface list when destroyed
[profile/ivi/weston-ivi-shell.git] / clients / subsurfaces.c
1 /*
2  * Copyright © 2010 Intel Corporation
3  * Copyright © 2011 Benjamin Franzke
4  * Copyright © 2012-2013 Collabora, Ltd.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that copyright
9  * notice and this permission notice appear in supporting documentation, and
10  * that the name of the copyright holders not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  The copyright holders make no representations
13  * about the suitability of this software for any purpose.  It is provided "as
14  * is" without express or implied warranty.
15  *
16  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22  * OF THIS SOFTWARE.
23  */
24
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <cairo.h>
30 #include <math.h>
31 #include <assert.h>
32
33 #include <linux/input.h>
34 #include <wayland-client.h>
35
36 #include <wayland-egl.h>
37 #include <GLES2/gl2.h>
38 #include <EGL/egl.h>
39
40 #include "window.h"
41
42 #if 0
43 #define DBG(fmt, ...) \
44         fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__)
45 #else
46 #define DBG(...) do {} while (0)
47 #endif
48
49 static int32_t option_red_mode;
50 static int32_t option_triangle_mode;
51 static int32_t option_no_triangle;
52 static int32_t option_help;
53
54 static const struct weston_option options[] = {
55         { WESTON_OPTION_INTEGER, "red-mode", 'r', &option_red_mode },
56         { WESTON_OPTION_INTEGER, "triangle-mode", 't', &option_triangle_mode },
57         { WESTON_OPTION_BOOLEAN, "no-triangle", 'n', &option_no_triangle },
58         { WESTON_OPTION_BOOLEAN, "help", 'h', &option_help },
59 };
60
61 static enum subsurface_mode
62 int_to_mode(int32_t i)
63 {
64         switch (i) {
65         case 0:
66                 return SUBSURFACE_DESYNCHRONIZED;
67         case 1:
68                 return SUBSURFACE_SYNCHRONIZED;
69         default:
70                 fprintf(stderr, "error: %d is not a valid commit mode.\n", i);
71                 exit(1);
72         }
73 }
74
75 static const char help_text[] =
76 "Usage: %s [options]\n"
77 "\n"
78 "  -r, --red-mode=MODE\t\tthe commit mode for the red sub-surface (0)\n"
79 "  -t, --triangle-mode=MODE\tthe commit mode for the GL sub-surface (0)\n"
80 "  -n, --no-triangle\t\tDo not create the GL sub-surface.\n"
81 "\n"
82 "The MODE is the wl_subsurface commit mode used by default for the\n"
83 "given sub-surface. Valid values are the integers:\n"
84 "   0\tfor desynchronized, i.e. free-running\n"
85 "   1\tfor synchronized\n"
86 "\n"
87 "This program demonstrates sub-surfaces with the toytoolkit.\n"
88 "The main surface contains the decorations, a green canvas, and a\n"
89 "green spinner. One sub-surface is red with a red spinner. These\n"
90 "are rendered with Cairo. The other sub-surface contains a spinning\n"
91 "triangle rendered in EGL/GLESv2, without Cairo, i.e. it is a raw GL\n"
92 "widget.\n"
93 "\n"
94 "The GL widget animates on its own. The spinners follow wall clock\n"
95 "time and update only when their surface is repainted, so you see\n"
96 "which surfaces get redrawn. The red sub-surface animates on its own,\n"
97 "but can be toggled with the spacebar.\n"
98 "\n"
99 "Even though the sub-surfaces attempt to animate on their own, they\n"
100 "are subject to the commit mode. If commit mode is synchronized,\n"
101 "they will need a commit on the main surface to actually display.\n"
102 "You can trigger a main surface repaint, without a resize, by\n"
103 "hovering the pointer over the title bar buttons.\n"
104 "\n"
105 "Resizing will temporarily toggle the commit mode of all sub-surfaces\n"
106 "to guarantee synchronized rendering on size changes. It also forces\n"
107 "a repaint of all surfaces.\n"
108 "\n"
109 "Using -t1 -r1 is especially useful for trying to catch inconsistent\n"
110 "rendering and deadlocks, since free-running sub-surfaces would\n"
111 "immediately hide the problem.\n"
112 "\n"
113 "Key controls:\n"
114 "  space - toggle red sub-surface animation loop\n"
115 "  up    - step window size shorter\n"
116 "  down  - step window size taller\n"
117 "\n";
118
119 struct egl_state {
120         EGLDisplay dpy;
121         EGLContext ctx;
122         EGLConfig conf;
123 };
124
125 struct triangle_gl_state {
126         GLuint rotation_uniform;
127         GLuint pos;
128         GLuint col;
129 };
130
131 struct triangle {
132         struct egl_state *egl;
133
134         struct wl_surface *wl_surface;
135         struct wl_egl_window *egl_window;
136         EGLSurface egl_surface;
137         int width;
138         int height;
139
140         struct triangle_gl_state gl;
141
142         struct widget *widget;
143         uint32_t time;
144         struct wl_callback *frame_cb;
145 };
146
147 /******** Pure EGL/GLESv2/libwayland-client component: ***************/
148
149 static const char *vert_shader_text =
150         "uniform mat4 rotation;\n"
151         "attribute vec4 pos;\n"
152         "attribute vec4 color;\n"
153         "varying vec4 v_color;\n"
154         "void main() {\n"
155         "  gl_Position = rotation * pos;\n"
156         "  v_color = color;\n"
157         "}\n";
158
159 static const char *frag_shader_text =
160         "precision mediump float;\n"
161         "varying vec4 v_color;\n"
162         "void main() {\n"
163         "  gl_FragColor = v_color;\n"
164         "}\n";
165
166 static void
167 egl_print_config_info(struct egl_state *egl)
168 {
169         EGLint r, g, b, a;
170
171         printf("Chosen EGL config details:\n");
172
173         printf("\tRGBA bits");
174         if (eglGetConfigAttrib(egl->dpy, egl->conf, EGL_RED_SIZE, &r) &&
175             eglGetConfigAttrib(egl->dpy, egl->conf, EGL_GREEN_SIZE, &g) &&
176             eglGetConfigAttrib(egl->dpy, egl->conf, EGL_BLUE_SIZE, &b) &&
177             eglGetConfigAttrib(egl->dpy, egl->conf, EGL_ALPHA_SIZE, &a))
178                 printf(": %d %d %d %d\n", r, g, b, a);
179         else
180                 printf(" unknown\n");
181
182         printf("\tswap interval range");
183         if (eglGetConfigAttrib(egl->dpy, egl->conf, EGL_MIN_SWAP_INTERVAL, &a) &&
184             eglGetConfigAttrib(egl->dpy, egl->conf, EGL_MAX_SWAP_INTERVAL, &b))
185                 printf(": %d - %d\n", a, b);
186         else
187                 printf(" unknown\n");
188 }
189
190 static struct egl_state *
191 egl_state_create(struct wl_display *display)
192 {
193         struct egl_state *egl;
194
195         static const EGLint context_attribs[] = {
196                 EGL_CONTEXT_CLIENT_VERSION, 2,
197                 EGL_NONE
198         };
199
200         EGLint config_attribs[] = {
201                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
202                 EGL_RED_SIZE, 1,
203                 EGL_GREEN_SIZE, 1,
204                 EGL_BLUE_SIZE, 1,
205                 EGL_ALPHA_SIZE, 1,
206                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
207                 EGL_NONE
208         };
209
210         EGLint major, minor, n;
211         EGLBoolean ret;
212
213         egl = calloc(1, sizeof *egl);
214         assert(egl);
215
216         egl->dpy = eglGetDisplay(display);
217         assert(egl->dpy);
218
219         ret = eglInitialize(egl->dpy, &major, &minor);
220         assert(ret == EGL_TRUE);
221         ret = eglBindAPI(EGL_OPENGL_ES_API);
222         assert(ret == EGL_TRUE);
223
224         ret = eglChooseConfig(egl->dpy, config_attribs, &egl->conf, 1, &n);
225         assert(ret && n == 1);
226
227         egl->ctx = eglCreateContext(egl->dpy, egl->conf,
228                                     EGL_NO_CONTEXT, context_attribs);
229         assert(egl->ctx);
230         egl_print_config_info(egl);
231
232         return egl;
233 }
234
235 static void
236 egl_state_destroy(struct egl_state *egl)
237 {
238         /* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
239          * on eglReleaseThread(). */
240         eglMakeCurrent(egl->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
241                        EGL_NO_CONTEXT);
242
243         eglTerminate(egl->dpy);
244         eglReleaseThread();
245         free(egl);
246 }
247
248 static void
249 egl_make_swapbuffers_nonblock(struct egl_state *egl)
250 {
251         EGLint a = EGL_MIN_SWAP_INTERVAL;
252         EGLint b = EGL_MAX_SWAP_INTERVAL;
253
254         if (!eglGetConfigAttrib(egl->dpy, egl->conf, a, &a) ||
255             !eglGetConfigAttrib(egl->dpy, egl->conf, b, &b)) {
256                 fprintf(stderr, "warning: swap interval range unknown\n");
257         } else
258         if (a > 0) {
259                 fprintf(stderr, "warning: minimum swap interval is %d, "
260                         "while 0 is required to not deadlock on resize.\n", a);
261         }
262
263         /*
264          * We rely on the Wayland compositor to sync to vblank anyway.
265          * We just need to be able to call eglSwapBuffers() without the
266          * risk of waiting for a frame callback in it.
267          */
268         if (!eglSwapInterval(egl->dpy, 0)) {
269                 fprintf(stderr, "error: eglSwapInterval() failed.\n");
270         }
271 }
272
273 static GLuint
274 create_shader(const char *source, GLenum shader_type)
275 {
276         GLuint shader;
277         GLint status;
278
279         shader = glCreateShader(shader_type);
280         assert(shader != 0);
281
282         glShaderSource(shader, 1, (const char **) &source, NULL);
283         glCompileShader(shader);
284
285         glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
286         if (!status) {
287                 char log[1000];
288                 GLsizei len;
289                 glGetShaderInfoLog(shader, 1000, &len, log);
290                 fprintf(stderr, "Error: compiling %s: %*s\n",
291                         shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
292                         len, log);
293                 exit(1);
294         }
295
296         return shader;
297 }
298
299 static void
300 triangle_init_gl(struct triangle_gl_state *trigl)
301 {
302         GLuint frag, vert;
303         GLuint program;
304         GLint status;
305
306         frag = create_shader(frag_shader_text, GL_FRAGMENT_SHADER);
307         vert = create_shader(vert_shader_text, GL_VERTEX_SHADER);
308
309         program = glCreateProgram();
310         glAttachShader(program, frag);
311         glAttachShader(program, vert);
312         glLinkProgram(program);
313
314         glGetProgramiv(program, GL_LINK_STATUS, &status);
315         if (!status) {
316                 char log[1000];
317                 GLsizei len;
318                 glGetProgramInfoLog(program, 1000, &len, log);
319                 fprintf(stderr, "Error: linking:\n%*s\n", len, log);
320                 exit(1);
321         }
322
323         glUseProgram(program);
324
325         trigl->pos = 0;
326         trigl->col = 1;
327
328         glBindAttribLocation(program, trigl->pos, "pos");
329         glBindAttribLocation(program, trigl->col, "color");
330         glLinkProgram(program);
331
332         trigl->rotation_uniform = glGetUniformLocation(program, "rotation");
333 }
334
335 static void
336 triangle_draw(const struct triangle_gl_state *trigl, uint32_t time)
337 {
338         static const GLfloat verts[3][2] = {
339                 { -0.5, -0.5 },
340                 {  0.5, -0.5 },
341                 {  0,    0.5 }
342         };
343         static const GLfloat colors[3][3] = {
344                 { 1, 0, 0 },
345                 { 0, 1, 0 },
346                 { 0, 0, 1 }
347         };
348         GLfloat angle;
349         GLfloat rotation[4][4] = {
350                 { 1, 0, 0, 0 },
351                 { 0, 1, 0, 0 },
352                 { 0, 0, 1, 0 },
353                 { 0, 0, 0, 1 }
354         };
355         static const int32_t speed_div = 5;
356
357         angle = (time / speed_div) % 360 * M_PI / 180.0;
358         rotation[0][0] =  cos(angle);
359         rotation[0][2] =  sin(angle);
360         rotation[2][0] = -sin(angle);
361         rotation[2][2] =  cos(angle);
362
363         glUniformMatrix4fv(trigl->rotation_uniform, 1, GL_FALSE,
364                            (GLfloat *) rotation);
365
366         glClearColor(0.0, 0.0, 0.0, 0.5);
367         glClear(GL_COLOR_BUFFER_BIT);
368
369         glVertexAttribPointer(trigl->pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
370         glVertexAttribPointer(trigl->col, 3, GL_FLOAT, GL_FALSE, 0, colors);
371         glEnableVertexAttribArray(trigl->pos);
372         glEnableVertexAttribArray(trigl->col);
373
374         glDrawArrays(GL_TRIANGLES, 0, 3);
375
376         glDisableVertexAttribArray(trigl->pos);
377         glDisableVertexAttribArray(trigl->col);
378 }
379
380 static void
381 triangle_frame_callback(void *data, struct wl_callback *callback,
382                         uint32_t time);
383
384 static const struct wl_callback_listener triangle_frame_listener = {
385         triangle_frame_callback
386 };
387
388 static void
389 triangle_frame_callback(void *data, struct wl_callback *callback,
390                         uint32_t time)
391 {
392         struct triangle *tri = data;
393
394         DBG("%stime %u\n", callback ? "" : "artificial ", time);
395         assert(callback == tri->frame_cb);
396         tri->time = time;
397
398         if (callback)
399                 wl_callback_destroy(callback);
400
401         eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
402                                    tri->egl_surface, tri->egl->ctx);
403
404         glViewport(0, 0, tri->width, tri->height);
405
406         triangle_draw(&tri->gl, tri->time);
407
408         tri->frame_cb = wl_surface_frame(tri->wl_surface);
409         wl_callback_add_listener(tri->frame_cb, &triangle_frame_listener, tri);
410
411         eglSwapBuffers(tri->egl->dpy, tri->egl_surface);
412 }
413
414 static void
415 triangle_create_egl_surface(struct triangle *tri, int width, int height)
416 {
417         EGLBoolean ret;
418
419         tri->wl_surface = widget_get_wl_surface(tri->widget);
420         tri->egl_window = wl_egl_window_create(tri->wl_surface, width, height);
421         tri->egl_surface = eglCreateWindowSurface(tri->egl->dpy,
422                                                   tri->egl->conf,
423                                                   tri->egl_window, NULL);
424
425         ret = eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
426                              tri->egl_surface, tri->egl->ctx);
427         assert(ret == EGL_TRUE);
428
429         egl_make_swapbuffers_nonblock(tri->egl);
430         triangle_init_gl(&tri->gl);
431 }
432
433 /********* The widget code interfacing the toolkit agnostic code: **********/
434
435 static void
436 triangle_resize_handler(struct widget *widget,
437                         int32_t width, int32_t height, void *data)
438 {
439         struct triangle *tri = data;
440
441         DBG("to %dx%d\n", width, height);
442         tri->width = width;
443         tri->height = height;
444
445         if (tri->egl_surface) {
446                 wl_egl_window_resize(tri->egl_window, width, height, 0, 0);
447         } else {
448                 triangle_create_egl_surface(tri, width, height);
449                 triangle_frame_callback(tri, NULL, 0);
450         }
451 }
452
453 static void
454 triangle_redraw_handler(struct widget *widget, void *data)
455 {
456         struct triangle *tri = data;
457         int w, h;
458
459         wl_egl_window_get_attached_size(tri->egl_window, &w, &h);
460
461         DBG("previous %dx%d, new %dx%d\n", w, h, tri->width, tri->height);
462
463         /* If size is not changing, do not redraw ahead of time.
464          * That would risk blocking in eglSwapbuffers().
465          */
466         if (w == tri->width && h == tri->height)
467                 return;
468
469         if (tri->frame_cb) {
470                 wl_callback_destroy(tri->frame_cb);
471                 tri->frame_cb = NULL;
472         }
473         triangle_frame_callback(tri, NULL, tri->time);
474 }
475
476 static void
477 set_empty_input_region(struct widget *widget, struct display *display)
478 {
479         struct wl_compositor *compositor;
480         struct wl_surface *surface;
481         struct wl_region *region;
482
483         compositor = display_get_compositor(display);
484         surface = widget_get_wl_surface(widget);
485         region = wl_compositor_create_region(compositor);
486         wl_surface_set_input_region(surface, region);
487         wl_region_destroy(region);
488 }
489
490 static struct triangle *
491 triangle_create(struct window *window, struct egl_state *egl)
492 {
493         struct triangle *tri;
494
495         tri = xmalloc(sizeof *tri);
496         memset(tri, 0, sizeof *tri);
497
498         tri->egl = egl;
499         tri->widget = window_add_subsurface(window, tri,
500                 int_to_mode(option_triangle_mode));
501         widget_set_resize_handler(tri->widget, triangle_resize_handler);
502         widget_set_redraw_handler(tri->widget, triangle_redraw_handler);
503
504         set_empty_input_region(tri->widget, window_get_display(window));
505
506         return tri;
507 }
508
509 static void
510 triangle_destroy(struct triangle *tri)
511 {
512         if (tri->egl_surface)
513                 eglDestroySurface(tri->egl->dpy, tri->egl_surface);
514
515         if (tri->egl_window)
516                 wl_egl_window_destroy(tri->egl_window);
517
518         widget_destroy(tri->widget);
519         free(tri);
520 }
521
522 /************** The toytoolkit application code: *********************/
523
524 struct demoapp {
525         struct display *display;
526         struct window *window;
527         struct widget *widget;
528         struct widget *subsurface;
529
530         struct egl_state *egl;
531         struct triangle *triangle;
532
533         int animate;
534 };
535
536 static void
537 draw_spinner(cairo_t *cr, const struct rectangle *rect, uint32_t time)
538 {
539         double cx, cy, r, angle;
540         unsigned t;
541
542         cx = rect->x + rect->width / 2;
543         cy = rect->y + rect->height / 2;
544         r = (rect->width < rect->height ? rect->width : rect->height) * 0.3;
545         t = time % 2000;
546         angle = t * (M_PI / 500.0);
547
548         cairo_set_line_width(cr, 4.0);
549
550         if (t < 1000)
551                 cairo_arc(cr, cx, cy, r, 0.0, angle);
552         else
553                 cairo_arc(cr, cx, cy, r, angle, 0.0);
554
555         cairo_stroke(cr);
556 }
557
558 static void
559 sub_redraw_handler(struct widget *widget, void *data)
560 {
561         struct demoapp *app = data;
562         cairo_t *cr;
563         struct rectangle allocation;
564         uint32_t time;
565
566         widget_get_allocation(app->subsurface, &allocation);
567
568         cr = widget_cairo_create(widget);
569         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
570
571         /* debug: paint whole surface magenta; no magenta should show */
572         cairo_set_source_rgba(cr, 0.9, 0.0, 0.9, 1.0);
573         cairo_paint(cr);
574
575         cairo_rectangle(cr,
576                         allocation.x,
577                         allocation.y,
578                         allocation.width,
579                         allocation.height);
580         cairo_clip(cr);
581
582         cairo_set_source_rgba(cr, 0.8, 0, 0, 0.8);
583         cairo_paint(cr);
584
585         time = widget_get_last_time(widget);
586         cairo_set_source_rgba(cr, 1.0, 0.5, 0.5, 1.0);
587         draw_spinner(cr, &allocation, time);
588
589         cairo_destroy(cr);
590
591         if (app->animate)
592                 widget_schedule_redraw(app->subsurface);
593         DBG("%dx%d @ %d,%d, last time %u\n",
594             allocation.width, allocation.height,
595             allocation.x, allocation.y, time);
596 }
597
598 static void
599 sub_resize_handler(struct widget *widget,
600                    int32_t width, int32_t height, void *data)
601 {
602         DBG("%dx%d\n", width, height);
603         widget_input_region_add(widget, NULL);
604 }
605
606 static void
607 redraw_handler(struct widget *widget, void *data)
608 {
609         struct demoapp *app = data;
610         cairo_t *cr;
611         struct rectangle allocation;
612         uint32_t time;
613
614         widget_get_allocation(app->widget, &allocation);
615
616         cr = widget_cairo_create(widget);
617         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
618         cairo_rectangle(cr,
619                         allocation.x,
620                         allocation.y,
621                         allocation.width,
622                         allocation.height);
623         cairo_set_source_rgba(cr, 0, 0.8, 0, 0.8);
624         cairo_fill(cr);
625
626         time = widget_get_last_time(widget);
627         cairo_set_source_rgba(cr, 0.5, 1.0, 0.5, 1.0);
628         draw_spinner(cr, &allocation, time);
629
630         cairo_destroy(cr);
631
632         DBG("%dx%d @ %d,%d, last time %u\n",
633             allocation.width, allocation.height,
634             allocation.x, allocation.y, time);
635 }
636
637 static void
638 resize_handler(struct widget *widget,
639                int32_t width, int32_t height, void *data)
640 {
641         struct demoapp *app = data;
642         struct rectangle area;
643         int side, h;
644
645         widget_get_allocation(widget, &area);
646
647         side = area.width < area.height ? area.width / 2 : area.height / 2;
648         h = area.height - side;
649
650         widget_set_allocation(app->subsurface,
651                               area.x + area.width - side,
652                               area.y,
653                               side, h);
654
655         if (app->triangle) {
656                 widget_set_allocation(app->triangle->widget,
657                                       area.x + area.width - side,
658                                       area.y + h,
659                                       side, side);
660         }
661
662         DBG("green %dx%d, red %dx%d, GL %dx%d\n",
663             area.width, area.height, side, h, side, side);
664 }
665
666 static void
667 keyboard_focus_handler(struct window *window,
668                        struct input *device, void *data)
669 {
670         struct demoapp *app = data;
671
672         window_schedule_redraw(app->window);
673 }
674
675 static void
676 key_handler(struct window *window, struct input *input, uint32_t time,
677             uint32_t key, uint32_t sym,
678             enum wl_keyboard_key_state state, void *data)
679 {
680         struct demoapp *app = data;
681         struct rectangle winrect;
682
683         if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
684                 return;
685
686         switch (sym) {
687         case XKB_KEY_space:
688                 app->animate = !app->animate;
689                 window_schedule_redraw(window);
690                 break;
691         case XKB_KEY_Up:
692                 window_get_allocation(window, &winrect);
693                 winrect.height -= 100;
694                 if (winrect.height < 150)
695                         winrect.height = 150;
696                 window_schedule_resize(window, winrect.width, winrect.height);
697                 break;
698         case XKB_KEY_Down:
699                 window_get_allocation(window, &winrect);
700                 winrect.height += 100;
701                 if (winrect.height > 600)
702                         winrect.height = 600;
703                 window_schedule_resize(window, winrect.width, winrect.height);
704                 break;
705         case XKB_KEY_Escape:
706                 display_exit(app->display);
707                 break;
708         }
709 }
710
711 static struct demoapp *
712 demoapp_create(struct display *display)
713 {
714         struct demoapp *app;
715
716         app = xmalloc(sizeof *app);
717         memset(app, 0, sizeof *app);
718
719         app->egl = egl_state_create(display_get_display(display));
720
721         app->display = display;
722         display_set_user_data(app->display, app);
723
724         app->window = window_create(app->display);
725         app->widget = window_frame_create(app->window, app);
726         window_set_title(app->window, "Wayland Sub-surface Demo");
727
728         window_set_key_handler(app->window, key_handler);
729         window_set_user_data(app->window, app);
730         window_set_keyboard_focus_handler(app->window, keyboard_focus_handler);
731
732         widget_set_redraw_handler(app->widget, redraw_handler);
733         widget_set_resize_handler(app->widget, resize_handler);
734
735         app->subsurface = window_add_subsurface(app->window, app,
736                 int_to_mode(option_red_mode));
737         widget_set_redraw_handler(app->subsurface, sub_redraw_handler);
738         widget_set_resize_handler(app->subsurface, sub_resize_handler);
739
740         if (app->egl && !option_no_triangle)
741                 app->triangle = triangle_create(app->window, app->egl);
742
743         /* minimum size */
744         widget_schedule_resize(app->widget, 100, 100);
745
746         /* initial size */
747         widget_schedule_resize(app->widget, 400, 300);
748
749         app->animate = 1;
750
751         return app;
752 }
753
754 static void
755 demoapp_destroy(struct demoapp *app)
756 {
757         if (app->triangle)
758                 triangle_destroy(app->triangle);
759
760         if (app->egl)
761                 egl_state_destroy(app->egl);
762
763         widget_destroy(app->subsurface);
764         widget_destroy(app->widget);
765         window_destroy(app->window);
766         free(app);
767 }
768
769 int
770 main(int argc, char *argv[])
771 {
772         struct display *display;
773         struct demoapp *app;
774
775         parse_options(options, ARRAY_LENGTH(options), &argc, argv);
776         if (option_help) {
777                 printf(help_text, argv[0]);
778                 return 0;
779         }
780
781         display = display_create(&argc, argv);
782         if (display == NULL) {
783                 fprintf(stderr, "failed to create display: %m\n");
784                 return -1;
785         }
786
787         if (!display_has_subcompositor(display)) {
788                 fprintf(stderr, "compositor does not support "
789                         "the subcompositor extension\n");
790                 return -1;
791         }
792
793         app = demoapp_create(display);
794
795         display_run(display);
796
797         demoapp_destroy(app);
798         display_destroy(display);
799
800         return 0;
801 }