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