blob: d88b8617cc64cdd13acd59246995ab6cd7e8e3c2 [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)
Emil Velikov00505362016-11-14 17:08:15 +0000521 weston_platform_destroy_egl_surface(tri->egl->dpy,
522 tri->egl_surface);
Pekka Paalanen7ff7a802013-04-25 13:57:50 +0300523
524 if (tri->egl_window)
525 wl_egl_window_destroy(tri->egl_window);
526
527 widget_destroy(tri->widget);
528 free(tri);
529}
530
531/************** The toytoolkit application code: *********************/
532
533struct demoapp {
534 struct display *display;
535 struct window *window;
536 struct widget *widget;
537 struct widget *subsurface;
538
539 struct egl_state *egl;
540 struct triangle *triangle;
541
542 int animate;
543};
544
545static void
546draw_spinner(cairo_t *cr, const struct rectangle *rect, uint32_t time)
547{
548 double cx, cy, r, angle;
549 unsigned t;
550
551 cx = rect->x + rect->width / 2;
552 cy = rect->y + rect->height / 2;
553 r = (rect->width < rect->height ? rect->width : rect->height) * 0.3;
554 t = time % 2000;
555 angle = t * (M_PI / 500.0);
556
557 cairo_set_line_width(cr, 4.0);
558
559 if (t < 1000)
560 cairo_arc(cr, cx, cy, r, 0.0, angle);
561 else
562 cairo_arc(cr, cx, cy, r, angle, 0.0);
563
564 cairo_stroke(cr);
565}
566
567static void
568sub_redraw_handler(struct widget *widget, void *data)
569{
570 struct demoapp *app = data;
571 cairo_t *cr;
572 struct rectangle allocation;
573 uint32_t time;
574
575 widget_get_allocation(app->subsurface, &allocation);
576
577 cr = widget_cairo_create(widget);
578 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
579
580 /* debug: paint whole surface magenta; no magenta should show */
581 cairo_set_source_rgba(cr, 0.9, 0.0, 0.9, 1.0);
582 cairo_paint(cr);
583
584 cairo_rectangle(cr,
585 allocation.x,
586 allocation.y,
587 allocation.width,
588 allocation.height);
589 cairo_clip(cr);
590
591 cairo_set_source_rgba(cr, 0.8, 0, 0, 0.8);
592 cairo_paint(cr);
593
594 time = widget_get_last_time(widget);
595 cairo_set_source_rgba(cr, 1.0, 0.5, 0.5, 1.0);
596 draw_spinner(cr, &allocation, time);
597
598 cairo_destroy(cr);
599
600 if (app->animate)
601 widget_schedule_redraw(app->subsurface);
602 DBG("%dx%d @ %d,%d, last time %u\n",
603 allocation.width, allocation.height,
604 allocation.x, allocation.y, time);
605}
606
607static void
608sub_resize_handler(struct widget *widget,
609 int32_t width, int32_t height, void *data)
610{
611 DBG("%dx%d\n", width, height);
612 widget_input_region_add(widget, NULL);
613}
614
615static void
616redraw_handler(struct widget *widget, void *data)
617{
618 struct demoapp *app = data;
619 cairo_t *cr;
620 struct rectangle allocation;
621 uint32_t time;
622
623 widget_get_allocation(app->widget, &allocation);
624
625 cr = widget_cairo_create(widget);
626 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
627 cairo_rectangle(cr,
628 allocation.x,
629 allocation.y,
630 allocation.width,
631 allocation.height);
632 cairo_set_source_rgba(cr, 0, 0.8, 0, 0.8);
633 cairo_fill(cr);
634
635 time = widget_get_last_time(widget);
636 cairo_set_source_rgba(cr, 0.5, 1.0, 0.5, 1.0);
637 draw_spinner(cr, &allocation, time);
638
639 cairo_destroy(cr);
640
641 DBG("%dx%d @ %d,%d, last time %u\n",
642 allocation.width, allocation.height,
643 allocation.x, allocation.y, time);
644}
645
646static void
647resize_handler(struct widget *widget,
648 int32_t width, int32_t height, void *data)
649{
650 struct demoapp *app = data;
651 struct rectangle area;
652 int side, h;
653
654 widget_get_allocation(widget, &area);
655
656 side = area.width < area.height ? area.width / 2 : area.height / 2;
657 h = area.height - side;
658
659 widget_set_allocation(app->subsurface,
660 area.x + area.width - side,
661 area.y,
662 side, h);
663
664 if (app->triangle) {
665 widget_set_allocation(app->triangle->widget,
666 area.x + area.width - side,
667 area.y + h,
668 side, side);
669 }
670
671 DBG("green %dx%d, red %dx%d, GL %dx%d\n",
672 area.width, area.height, side, h, side, side);
673}
674
675static void
676keyboard_focus_handler(struct window *window,
677 struct input *device, void *data)
678{
679 struct demoapp *app = data;
680
681 window_schedule_redraw(app->window);
682}
683
684static void
685key_handler(struct window *window, struct input *input, uint32_t time,
686 uint32_t key, uint32_t sym,
687 enum wl_keyboard_key_state state, void *data)
688{
689 struct demoapp *app = data;
690 struct rectangle winrect;
691
692 if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
693 return;
694
695 switch (sym) {
696 case XKB_KEY_space:
697 app->animate = !app->animate;
698 window_schedule_redraw(window);
699 break;
700 case XKB_KEY_Up:
701 window_get_allocation(window, &winrect);
702 winrect.height -= 100;
703 if (winrect.height < 150)
704 winrect.height = 150;
705 window_schedule_resize(window, winrect.width, winrect.height);
706 break;
707 case XKB_KEY_Down:
708 window_get_allocation(window, &winrect);
709 winrect.height += 100;
710 if (winrect.height > 600)
711 winrect.height = 600;
712 window_schedule_resize(window, winrect.width, winrect.height);
713 break;
714 case XKB_KEY_Escape:
715 display_exit(app->display);
716 break;
717 }
718}
719
720static struct demoapp *
721demoapp_create(struct display *display)
722{
723 struct demoapp *app;
724
Ryo Munakata5e653ca2015-08-07 20:20:46 +0900725 app = xzalloc(sizeof *app);
Pekka Paalanen7ff7a802013-04-25 13:57:50 +0300726
727 app->egl = egl_state_create(display_get_display(display));
728
729 app->display = display;
730 display_set_user_data(app->display, app);
731
732 app->window = window_create(app->display);
Jason Ekstrandee7fefc2013-10-13 19:08:38 -0500733 app->widget = window_frame_create(app->window, app);
Pekka Paalanen7ff7a802013-04-25 13:57:50 +0300734 window_set_title(app->window, "Wayland Sub-surface Demo");
735
736 window_set_key_handler(app->window, key_handler);
737 window_set_user_data(app->window, app);
738 window_set_keyboard_focus_handler(app->window, keyboard_focus_handler);
739
740 widget_set_redraw_handler(app->widget, redraw_handler);
741 widget_set_resize_handler(app->widget, resize_handler);
742
743 app->subsurface = window_add_subsurface(app->window, app,
744 int_to_mode(option_red_mode));
745 widget_set_redraw_handler(app->subsurface, sub_redraw_handler);
746 widget_set_resize_handler(app->subsurface, sub_resize_handler);
747
748 if (app->egl && !option_no_triangle)
749 app->triangle = triangle_create(app->window, app->egl);
750
751 /* minimum size */
752 widget_schedule_resize(app->widget, 100, 100);
753
754 /* initial size */
755 widget_schedule_resize(app->widget, 400, 300);
756
757 app->animate = 1;
758
759 return app;
760}
761
762static void
763demoapp_destroy(struct demoapp *app)
764{
765 if (app->triangle)
766 triangle_destroy(app->triangle);
767
768 if (app->egl)
769 egl_state_destroy(app->egl);
770
771 widget_destroy(app->subsurface);
772 widget_destroy(app->widget);
773 window_destroy(app->window);
774 free(app);
775}
776
777int
778main(int argc, char *argv[])
779{
780 struct display *display;
781 struct demoapp *app;
782
Bill Spitzak852254a2014-08-08 12:59:53 -0700783 if (parse_options(options, ARRAY_LENGTH(options), &argc, argv) > 1
784 || option_help) {
Pekka Paalanen7ff7a802013-04-25 13:57:50 +0300785 printf(help_text, argv[0]);
786 return 0;
787 }
788
789 display = display_create(&argc, argv);
790 if (display == NULL) {
791 fprintf(stderr, "failed to create display: %m\n");
792 return -1;
793 }
794
Kristian Høgsbergb20b0092013-08-15 11:54:03 -0700795 if (!display_has_subcompositor(display)) {
796 fprintf(stderr, "compositor does not support "
797 "the subcompositor extension\n");
798 return -1;
799 }
800
Pekka Paalanen7ff7a802013-04-25 13:57:50 +0300801 app = demoapp_create(display);
802
803 display_run(display);
804
805 demoapp_destroy(app);
806 display_destroy(display);
807
808 return 0;
809}