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