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