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