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