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