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