client: Fix shell unstable version check
[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_use_cairo(tri->widget, 0);
502         widget_set_resize_handler(tri->widget, triangle_resize_handler);
503         widget_set_redraw_handler(tri->widget, triangle_redraw_handler);
504
505         set_empty_input_region(tri->widget, window_get_display(window));
506
507         return tri;
508 }
509
510 static void
511 triangle_destroy(struct triangle *tri)
512 {
513         if (tri->egl_surface)
514                 eglDestroySurface(tri->egl->dpy, tri->egl_surface);
515
516         if (tri->egl_window)
517                 wl_egl_window_destroy(tri->egl_window);
518
519         widget_destroy(tri->widget);
520         free(tri);
521 }
522
523 /************** The toytoolkit application code: *********************/
524
525 struct demoapp {
526         struct display *display;
527         struct window *window;
528         struct widget *widget;
529         struct widget *subsurface;
530
531         struct egl_state *egl;
532         struct triangle *triangle;
533
534         int animate;
535 };
536
537 static void
538 draw_spinner(cairo_t *cr, const struct rectangle *rect, uint32_t time)
539 {
540         double cx, cy, r, angle;
541         unsigned t;
542
543         cx = rect->x + rect->width / 2;
544         cy = rect->y + rect->height / 2;
545         r = (rect->width < rect->height ? rect->width : rect->height) * 0.3;
546         t = time % 2000;
547         angle = t * (M_PI / 500.0);
548
549         cairo_set_line_width(cr, 4.0);
550
551         if (t < 1000)
552                 cairo_arc(cr, cx, cy, r, 0.0, angle);
553         else
554                 cairo_arc(cr, cx, cy, r, angle, 0.0);
555
556         cairo_stroke(cr);
557 }
558
559 static void
560 sub_redraw_handler(struct widget *widget, void *data)
561 {
562         struct demoapp *app = data;
563         cairo_t *cr;
564         struct rectangle allocation;
565         uint32_t time;
566
567         widget_get_allocation(app->subsurface, &allocation);
568
569         cr = widget_cairo_create(widget);
570         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
571
572         /* debug: paint whole surface magenta; no magenta should show */
573         cairo_set_source_rgba(cr, 0.9, 0.0, 0.9, 1.0);
574         cairo_paint(cr);
575
576         cairo_rectangle(cr,
577                         allocation.x,
578                         allocation.y,
579                         allocation.width,
580                         allocation.height);
581         cairo_clip(cr);
582
583         cairo_set_source_rgba(cr, 0.8, 0, 0, 0.8);
584         cairo_paint(cr);
585
586         time = widget_get_last_time(widget);
587         cairo_set_source_rgba(cr, 1.0, 0.5, 0.5, 1.0);
588         draw_spinner(cr, &allocation, time);
589
590         cairo_destroy(cr);
591
592         if (app->animate)
593                 widget_schedule_redraw(app->subsurface);
594         DBG("%dx%d @ %d,%d, last time %u\n",
595             allocation.width, allocation.height,
596             allocation.x, allocation.y, time);
597 }
598
599 static void
600 sub_resize_handler(struct widget *widget,
601                    int32_t width, int32_t height, void *data)
602 {
603         DBG("%dx%d\n", width, height);
604         widget_input_region_add(widget, NULL);
605 }
606
607 static void
608 redraw_handler(struct widget *widget, void *data)
609 {
610         struct demoapp *app = data;
611         cairo_t *cr;
612         struct rectangle allocation;
613         uint32_t time;
614
615         widget_get_allocation(app->widget, &allocation);
616
617         cr = widget_cairo_create(widget);
618         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
619         cairo_rectangle(cr,
620                         allocation.x,
621                         allocation.y,
622                         allocation.width,
623                         allocation.height);
624         cairo_set_source_rgba(cr, 0, 0.8, 0, 0.8);
625         cairo_fill(cr);
626
627         time = widget_get_last_time(widget);
628         cairo_set_source_rgba(cr, 0.5, 1.0, 0.5, 1.0);
629         draw_spinner(cr, &allocation, time);
630
631         cairo_destroy(cr);
632
633         DBG("%dx%d @ %d,%d, last time %u\n",
634             allocation.width, allocation.height,
635             allocation.x, allocation.y, time);
636 }
637
638 static void
639 resize_handler(struct widget *widget,
640                int32_t width, int32_t height, void *data)
641 {
642         struct demoapp *app = data;
643         struct rectangle area;
644         int side, h;
645
646         widget_get_allocation(widget, &area);
647
648         side = area.width < area.height ? area.width / 2 : area.height / 2;
649         h = area.height - side;
650
651         widget_set_allocation(app->subsurface,
652                               area.x + area.width - side,
653                               area.y,
654                               side, h);
655
656         if (app->triangle) {
657                 widget_set_allocation(app->triangle->widget,
658                                       area.x + area.width - side,
659                                       area.y + h,
660                                       side, side);
661         }
662
663         DBG("green %dx%d, red %dx%d, GL %dx%d\n",
664             area.width, area.height, side, h, side, side);
665 }
666
667 static void
668 keyboard_focus_handler(struct window *window,
669                        struct input *device, void *data)
670 {
671         struct demoapp *app = data;
672
673         window_schedule_redraw(app->window);
674 }
675
676 static void
677 key_handler(struct window *window, struct input *input, uint32_t time,
678             uint32_t key, uint32_t sym,
679             enum wl_keyboard_key_state state, void *data)
680 {
681         struct demoapp *app = data;
682         struct rectangle winrect;
683
684         if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
685                 return;
686
687         switch (sym) {
688         case XKB_KEY_space:
689                 app->animate = !app->animate;
690                 window_schedule_redraw(window);
691                 break;
692         case XKB_KEY_Up:
693                 window_get_allocation(window, &winrect);
694                 winrect.height -= 100;
695                 if (winrect.height < 150)
696                         winrect.height = 150;
697                 window_schedule_resize(window, winrect.width, winrect.height);
698                 break;
699         case XKB_KEY_Down:
700                 window_get_allocation(window, &winrect);
701                 winrect.height += 100;
702                 if (winrect.height > 600)
703                         winrect.height = 600;
704                 window_schedule_resize(window, winrect.width, winrect.height);
705                 break;
706         case XKB_KEY_Escape:
707                 display_exit(app->display);
708                 break;
709         }
710 }
711
712 static struct demoapp *
713 demoapp_create(struct display *display)
714 {
715         struct demoapp *app;
716
717         app = xmalloc(sizeof *app);
718         memset(app, 0, sizeof *app);
719
720         app->egl = egl_state_create(display_get_display(display));
721
722         app->display = display;
723         display_set_user_data(app->display, app);
724
725         app->window = window_create(app->display);
726         app->widget = window_frame_create(app->window, app);
727         window_set_title(app->window, "Wayland Sub-surface Demo");
728
729         window_set_key_handler(app->window, key_handler);
730         window_set_user_data(app->window, app);
731         window_set_keyboard_focus_handler(app->window, keyboard_focus_handler);
732
733         widget_set_redraw_handler(app->widget, redraw_handler);
734         widget_set_resize_handler(app->widget, resize_handler);
735
736         app->subsurface = window_add_subsurface(app->window, app,
737                 int_to_mode(option_red_mode));
738         widget_set_redraw_handler(app->subsurface, sub_redraw_handler);
739         widget_set_resize_handler(app->subsurface, sub_resize_handler);
740
741         if (app->egl && !option_no_triangle)
742                 app->triangle = triangle_create(app->window, app->egl);
743
744         /* minimum size */
745         widget_schedule_resize(app->widget, 100, 100);
746
747         /* initial size */
748         widget_schedule_resize(app->widget, 400, 300);
749
750         app->animate = 1;
751
752         return app;
753 }
754
755 static void
756 demoapp_destroy(struct demoapp *app)
757 {
758         if (app->triangle)
759                 triangle_destroy(app->triangle);
760
761         if (app->egl)
762                 egl_state_destroy(app->egl);
763
764         widget_destroy(app->subsurface);
765         widget_destroy(app->widget);
766         window_destroy(app->window);
767         free(app);
768 }
769
770 int
771 main(int argc, char *argv[])
772 {
773         struct display *display;
774         struct demoapp *app;
775
776         parse_options(options, ARRAY_LENGTH(options), &argc, argv);
777         if (option_help) {
778                 printf(help_text, argv[0]);
779                 return 0;
780         }
781
782         display = display_create(&argc, argv);
783         if (display == NULL) {
784                 fprintf(stderr, "failed to create display: %m\n");
785                 return -1;
786         }
787
788         if (!display_has_subcompositor(display)) {
789                 fprintf(stderr, "compositor does not support "
790                         "the subcompositor extension\n");
791                 return -1;
792         }
793
794         app = demoapp_create(display);
795
796         display_run(display);
797
798         demoapp_destroy(app);
799         display_destroy(display);
800
801         return 0;
802 }