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