blob: bf0b96b7718d65f50c2351d26105d62308428e8b [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"
Bryce Harrington0d1a6222016-02-11 16:42:49 -080045#include "shared/zalloc.h"
Pekka Paalanen7ff7a802013-04-25 13:57:50 +030046#include "window.h"
47
48#if 0
49#define DBG(fmt, ...) \
50 fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__)
51#else
52#define DBG(...) do {} while (0)
53#endif
54
55static int32_t option_red_mode;
56static int32_t option_triangle_mode;
57static int32_t option_no_triangle;
58static int32_t option_help;
59
60static const struct weston_option options[] = {
61 { WESTON_OPTION_INTEGER, "red-mode", 'r', &option_red_mode },
62 { WESTON_OPTION_INTEGER, "triangle-mode", 't', &option_triangle_mode },
63 { WESTON_OPTION_BOOLEAN, "no-triangle", 'n', &option_no_triangle },
64 { WESTON_OPTION_BOOLEAN, "help", 'h', &option_help },
65};
66
67static enum subsurface_mode
68int_to_mode(int32_t i)
69{
70 switch (i) {
71 case 0:
72 return SUBSURFACE_DESYNCHRONIZED;
73 case 1:
74 return SUBSURFACE_SYNCHRONIZED;
75 default:
76 fprintf(stderr, "error: %d is not a valid commit mode.\n", i);
77 exit(1);
78 }
79}
80
81static const char help_text[] =
82"Usage: %s [options]\n"
83"\n"
84" -r, --red-mode=MODE\t\tthe commit mode for the red sub-surface (0)\n"
85" -t, --triangle-mode=MODE\tthe commit mode for the GL sub-surface (0)\n"
86" -n, --no-triangle\t\tDo not create the GL sub-surface.\n"
87"\n"
88"The MODE is the wl_subsurface commit mode used by default for the\n"
89"given sub-surface. Valid values are the integers:\n"
90" 0\tfor desynchronized, i.e. free-running\n"
91" 1\tfor synchronized\n"
92"\n"
93"This program demonstrates sub-surfaces with the toytoolkit.\n"
94"The main surface contains the decorations, a green canvas, and a\n"
95"green spinner. One sub-surface is red with a red spinner. These\n"
96"are rendered with Cairo. The other sub-surface contains a spinning\n"
97"triangle rendered in EGL/GLESv2, without Cairo, i.e. it is a raw GL\n"
98"widget.\n"
99"\n"
100"The GL widget animates on its own. The spinners follow wall clock\n"
101"time and update only when their surface is repainted, so you see\n"
102"which surfaces get redrawn. The red sub-surface animates on its own,\n"
103"but can be toggled with the spacebar.\n"
104"\n"
105"Even though the sub-surfaces attempt to animate on their own, they\n"
106"are subject to the commit mode. If commit mode is synchronized,\n"
107"they will need a commit on the main surface to actually display.\n"
108"You can trigger a main surface repaint, without a resize, by\n"
109"hovering the pointer over the title bar buttons.\n"
110"\n"
111"Resizing will temporarily toggle the commit mode of all sub-surfaces\n"
112"to guarantee synchronized rendering on size changes. It also forces\n"
113"a repaint of all surfaces.\n"
114"\n"
115"Using -t1 -r1 is especially useful for trying to catch inconsistent\n"
116"rendering and deadlocks, since free-running sub-surfaces would\n"
117"immediately hide the problem.\n"
118"\n"
119"Key controls:\n"
120" space - toggle red sub-surface animation loop\n"
121" up - step window size shorter\n"
122" down - step window size taller\n"
123"\n";
124
125struct egl_state {
126 EGLDisplay dpy;
127 EGLContext ctx;
128 EGLConfig conf;
129};
130
131struct triangle_gl_state {
132 GLuint rotation_uniform;
133 GLuint pos;
134 GLuint col;
135};
136
137struct triangle {
138 struct egl_state *egl;
139
140 struct wl_surface *wl_surface;
141 struct wl_egl_window *egl_window;
142 EGLSurface egl_surface;
143 int width;
144 int height;
145
146 struct triangle_gl_state gl;
147
148 struct widget *widget;
149 uint32_t time;
150 struct wl_callback *frame_cb;
151};
152
153/******** Pure EGL/GLESv2/libwayland-client component: ***************/
154
155static const char *vert_shader_text =
156 "uniform mat4 rotation;\n"
157 "attribute vec4 pos;\n"
158 "attribute vec4 color;\n"
159 "varying vec4 v_color;\n"
160 "void main() {\n"
161 " gl_Position = rotation * pos;\n"
162 " v_color = color;\n"
163 "}\n";
164
165static const char *frag_shader_text =
166 "precision mediump float;\n"
167 "varying vec4 v_color;\n"
168 "void main() {\n"
169 " gl_FragColor = v_color;\n"
170 "}\n";
171
172static void
173egl_print_config_info(struct egl_state *egl)
174{
175 EGLint r, g, b, a;
176
177 printf("Chosen EGL config details:\n");
178
179 printf("\tRGBA bits");
180 if (eglGetConfigAttrib(egl->dpy, egl->conf, EGL_RED_SIZE, &r) &&
181 eglGetConfigAttrib(egl->dpy, egl->conf, EGL_GREEN_SIZE, &g) &&
182 eglGetConfigAttrib(egl->dpy, egl->conf, EGL_BLUE_SIZE, &b) &&
183 eglGetConfigAttrib(egl->dpy, egl->conf, EGL_ALPHA_SIZE, &a))
184 printf(": %d %d %d %d\n", r, g, b, a);
185 else
186 printf(" unknown\n");
187
188 printf("\tswap interval range");
189 if (eglGetConfigAttrib(egl->dpy, egl->conf, EGL_MIN_SWAP_INTERVAL, &a) &&
190 eglGetConfigAttrib(egl->dpy, egl->conf, EGL_MAX_SWAP_INTERVAL, &b))
191 printf(": %d - %d\n", a, b);
192 else
193 printf(" unknown\n");
194}
195
196static struct egl_state *
197egl_state_create(struct wl_display *display)
198{
199 struct egl_state *egl;
200
201 static const EGLint context_attribs[] = {
202 EGL_CONTEXT_CLIENT_VERSION, 2,
203 EGL_NONE
204 };
205
206 EGLint config_attribs[] = {
207 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
208 EGL_RED_SIZE, 1,
209 EGL_GREEN_SIZE, 1,
210 EGL_BLUE_SIZE, 1,
211 EGL_ALPHA_SIZE, 1,
212 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
213 EGL_NONE
214 };
215
216 EGLint major, minor, n;
217 EGLBoolean ret;
218
Bryce Harrington0d1a6222016-02-11 16:42:49 -0800219 egl = zalloc(sizeof *egl);
Pekka Paalanen7ff7a802013-04-25 13:57:50 +0300220 assert(egl);
221
Jonny Lamb51a7ae52015-03-20 15:26:51 +0100222 egl->dpy =
223 weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
224 display, NULL);
Pekka Paalanen7ff7a802013-04-25 13:57:50 +0300225 assert(egl->dpy);
226
227 ret = eglInitialize(egl->dpy, &major, &minor);
228 assert(ret == EGL_TRUE);
229 ret = eglBindAPI(EGL_OPENGL_ES_API);
230 assert(ret == EGL_TRUE);
231
232 ret = eglChooseConfig(egl->dpy, config_attribs, &egl->conf, 1, &n);
233 assert(ret && n == 1);
234
235 egl->ctx = eglCreateContext(egl->dpy, egl->conf,
236 EGL_NO_CONTEXT, context_attribs);
237 assert(egl->ctx);
238 egl_print_config_info(egl);
239
240 return egl;
241}
242
243static void
244egl_state_destroy(struct egl_state *egl)
245{
246 /* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
247 * on eglReleaseThread(). */
248 eglMakeCurrent(egl->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
249 EGL_NO_CONTEXT);
250
251 eglTerminate(egl->dpy);
252 eglReleaseThread();
253 free(egl);
254}
255
256static void
257egl_make_swapbuffers_nonblock(struct egl_state *egl)
258{
259 EGLint a = EGL_MIN_SWAP_INTERVAL;
260 EGLint b = EGL_MAX_SWAP_INTERVAL;
261
262 if (!eglGetConfigAttrib(egl->dpy, egl->conf, a, &a) ||
263 !eglGetConfigAttrib(egl->dpy, egl->conf, b, &b)) {
264 fprintf(stderr, "warning: swap interval range unknown\n");
Jonas Ådahl6c3bd9c2015-01-26 18:19:07 +0800265 } else if (a > 0) {
Pekka Paalanen7ff7a802013-04-25 13:57:50 +0300266 fprintf(stderr, "warning: minimum swap interval is %d, "
267 "while 0 is required to not deadlock on resize.\n", a);
268 }
269
270 /*
271 * We rely on the Wayland compositor to sync to vblank anyway.
272 * We just need to be able to call eglSwapBuffers() without the
273 * risk of waiting for a frame callback in it.
274 */
275 if (!eglSwapInterval(egl->dpy, 0)) {
276 fprintf(stderr, "error: eglSwapInterval() failed.\n");
277 }
278}
279
280static GLuint
281create_shader(const char *source, GLenum shader_type)
282{
283 GLuint shader;
284 GLint status;
285
286 shader = glCreateShader(shader_type);
287 assert(shader != 0);
288
289 glShaderSource(shader, 1, (const char **) &source, NULL);
290 glCompileShader(shader);
291
292 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
293 if (!status) {
294 char log[1000];
295 GLsizei len;
296 glGetShaderInfoLog(shader, 1000, &len, log);
297 fprintf(stderr, "Error: compiling %s: %*s\n",
298 shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
299 len, log);
300 exit(1);
301 }
302
303 return shader;
304}
305
306static void
307triangle_init_gl(struct triangle_gl_state *trigl)
308{
309 GLuint frag, vert;
310 GLuint program;
311 GLint status;
312
313 frag = create_shader(frag_shader_text, GL_FRAGMENT_SHADER);
314 vert = create_shader(vert_shader_text, GL_VERTEX_SHADER);
315
316 program = glCreateProgram();
317 glAttachShader(program, frag);
318 glAttachShader(program, vert);
319 glLinkProgram(program);
320
321 glGetProgramiv(program, GL_LINK_STATUS, &status);
322 if (!status) {
323 char log[1000];
324 GLsizei len;
325 glGetProgramInfoLog(program, 1000, &len, log);
326 fprintf(stderr, "Error: linking:\n%*s\n", len, log);
327 exit(1);
328 }
329
330 glUseProgram(program);
331
332 trigl->pos = 0;
333 trigl->col = 1;
334
335 glBindAttribLocation(program, trigl->pos, "pos");
336 glBindAttribLocation(program, trigl->col, "color");
337 glLinkProgram(program);
338
339 trigl->rotation_uniform = glGetUniformLocation(program, "rotation");
340}
341
342static void
343triangle_draw(const struct triangle_gl_state *trigl, uint32_t time)
344{
345 static const GLfloat verts[3][2] = {
346 { -0.5, -0.5 },
347 { 0.5, -0.5 },
348 { 0, 0.5 }
349 };
350 static const GLfloat colors[3][3] = {
351 { 1, 0, 0 },
352 { 0, 1, 0 },
353 { 0, 0, 1 }
354 };
355 GLfloat angle;
356 GLfloat rotation[4][4] = {
357 { 1, 0, 0, 0 },
358 { 0, 1, 0, 0 },
359 { 0, 0, 1, 0 },
360 { 0, 0, 0, 1 }
361 };
362 static const int32_t speed_div = 5;
363
364 angle = (time / speed_div) % 360 * M_PI / 180.0;
365 rotation[0][0] = cos(angle);
366 rotation[0][2] = sin(angle);
367 rotation[2][0] = -sin(angle);
368 rotation[2][2] = cos(angle);
369
370 glUniformMatrix4fv(trigl->rotation_uniform, 1, GL_FALSE,
371 (GLfloat *) rotation);
372
373 glClearColor(0.0, 0.0, 0.0, 0.5);
374 glClear(GL_COLOR_BUFFER_BIT);
375
376 glVertexAttribPointer(trigl->pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
377 glVertexAttribPointer(trigl->col, 3, GL_FLOAT, GL_FALSE, 0, colors);
378 glEnableVertexAttribArray(trigl->pos);
379 glEnableVertexAttribArray(trigl->col);
380
381 glDrawArrays(GL_TRIANGLES, 0, 3);
382
383 glDisableVertexAttribArray(trigl->pos);
384 glDisableVertexAttribArray(trigl->col);
385}
386
387static void
388triangle_frame_callback(void *data, struct wl_callback *callback,
389 uint32_t time);
390
391static const struct wl_callback_listener triangle_frame_listener = {
392 triangle_frame_callback
393};
394
395static void
396triangle_frame_callback(void *data, struct wl_callback *callback,
397 uint32_t time)
398{
399 struct triangle *tri = data;
400
401 DBG("%stime %u\n", callback ? "" : "artificial ", time);
402 assert(callback == tri->frame_cb);
403 tri->time = time;
404
405 if (callback)
406 wl_callback_destroy(callback);
407
Stanislav Vorobiov6346e502013-08-28 10:14:35 +0400408 eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
409 tri->egl_surface, tri->egl->ctx);
410
Pekka Paalanen7ff7a802013-04-25 13:57:50 +0300411 glViewport(0, 0, tri->width, tri->height);
412
413 triangle_draw(&tri->gl, tri->time);
414
415 tri->frame_cb = wl_surface_frame(tri->wl_surface);
416 wl_callback_add_listener(tri->frame_cb, &triangle_frame_listener, tri);
417
418 eglSwapBuffers(tri->egl->dpy, tri->egl_surface);
419}
420
421static void
422triangle_create_egl_surface(struct triangle *tri, int width, int height)
423{
424 EGLBoolean ret;
425
426 tri->wl_surface = widget_get_wl_surface(tri->widget);
427 tri->egl_window = wl_egl_window_create(tri->wl_surface, width, height);
Jonny Lambabff8832015-03-24 13:12:09 +0100428 tri->egl_surface = weston_platform_create_egl_surface(tri->egl->dpy,
429 tri->egl->conf,
430 tri->egl_window, NULL);
Pekka Paalanen7ff7a802013-04-25 13:57:50 +0300431
432 ret = eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
433 tri->egl_surface, tri->egl->ctx);
434 assert(ret == EGL_TRUE);
435
436 egl_make_swapbuffers_nonblock(tri->egl);
437 triangle_init_gl(&tri->gl);
438}
439
440/********* The widget code interfacing the toolkit agnostic code: **********/
441
442static void
443triangle_resize_handler(struct widget *widget,
444 int32_t width, int32_t height, void *data)
445{
446 struct triangle *tri = data;
447
448 DBG("to %dx%d\n", width, height);
449 tri->width = width;
450 tri->height = height;
451
452 if (tri->egl_surface) {
453 wl_egl_window_resize(tri->egl_window, width, height, 0, 0);
454 } else {
455 triangle_create_egl_surface(tri, width, height);
456 triangle_frame_callback(tri, NULL, 0);
457 }
458}
459
460static void
461triangle_redraw_handler(struct widget *widget, void *data)
462{
463 struct triangle *tri = data;
464 int w, h;
465
466 wl_egl_window_get_attached_size(tri->egl_window, &w, &h);
467
468 DBG("previous %dx%d, new %dx%d\n", w, h, tri->width, tri->height);
469
470 /* If size is not changing, do not redraw ahead of time.
471 * That would risk blocking in eglSwapbuffers().
472 */
473 if (w == tri->width && h == tri->height)
474 return;
475
476 if (tri->frame_cb) {
477 wl_callback_destroy(tri->frame_cb);
478 tri->frame_cb = NULL;
479 }
480 triangle_frame_callback(tri, NULL, tri->time);
481}
482
483static void
484set_empty_input_region(struct widget *widget, struct display *display)
485{
486 struct wl_compositor *compositor;
487 struct wl_surface *surface;
488 struct wl_region *region;
489
490 compositor = display_get_compositor(display);
491 surface = widget_get_wl_surface(widget);
492 region = wl_compositor_create_region(compositor);
493 wl_surface_set_input_region(surface, region);
494 wl_region_destroy(region);
495}
496
497static struct triangle *
498triangle_create(struct window *window, struct egl_state *egl)
499{
500 struct triangle *tri;
501
Ryo Munakata5e653ca2015-08-07 20:20:46 +0900502 tri = xzalloc(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
Ryo Munakata5e653ca2015-08-07 20:20:46 +0900723 app = xzalloc(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}