2 * Copyright © 2010 Intel Corporation
3 * Copyright © 2011 Benjamin Franzke
4 * Copyright © 2012-2013 Collabora, Ltd.
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.
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
35 #include <linux/input.h>
36 #include <wayland-client.h>
38 #include <wayland-egl.h>
39 #include <GLES2/gl2.h>
45 #define DBG(fmt, ...) \
46 fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__)
48 #define DBG(...) do {} while (0)
51 static int32_t option_red_mode;
52 static int32_t option_triangle_mode;
53 static int32_t option_no_triangle;
54 static int32_t option_help;
56 static 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 },
63 static enum subsurface_mode
64 int_to_mode(int32_t i)
68 return SUBSURFACE_DESYNCHRONIZED;
70 return SUBSURFACE_SYNCHRONIZED;
72 fprintf(stderr, "error: %d is not a valid commit mode.\n", i);
77 static const char help_text[] =
78 "Usage: %s [options]\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"
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"
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"
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"
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"
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"
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"
116 " space - toggle red sub-surface animation loop\n"
117 " up - step window size shorter\n"
118 " down - step window size taller\n"
127 struct triangle_gl_state {
128 GLuint rotation_uniform;
134 struct egl_state *egl;
136 struct wl_surface *wl_surface;
137 struct wl_egl_window *egl_window;
138 EGLSurface egl_surface;
142 struct triangle_gl_state gl;
144 struct widget *widget;
146 struct wl_callback *frame_cb;
149 /******** Pure EGL/GLESv2/libwayland-client component: ***************/
151 static 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"
157 " gl_Position = rotation * pos;\n"
158 " v_color = color;\n"
161 static const char *frag_shader_text =
162 "precision mediump float;\n"
163 "varying vec4 v_color;\n"
165 " gl_FragColor = v_color;\n"
169 egl_print_config_info(struct egl_state *egl)
173 printf("Chosen EGL config details:\n");
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);
182 printf(" unknown\n");
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);
189 printf(" unknown\n");
192 static struct egl_state *
193 egl_state_create(struct wl_display *display)
195 struct egl_state *egl;
197 static const EGLint context_attribs[] = {
198 EGL_CONTEXT_CLIENT_VERSION, 2,
202 EGLint config_attribs[] = {
203 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
208 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
212 EGLint major, minor, n;
215 egl = calloc(1, sizeof *egl);
218 egl->dpy = eglGetDisplay(display);
221 ret = eglInitialize(egl->dpy, &major, &minor);
222 assert(ret == EGL_TRUE);
223 ret = eglBindAPI(EGL_OPENGL_ES_API);
224 assert(ret == EGL_TRUE);
226 ret = eglChooseConfig(egl->dpy, config_attribs, &egl->conf, 1, &n);
227 assert(ret && n == 1);
229 egl->ctx = eglCreateContext(egl->dpy, egl->conf,
230 EGL_NO_CONTEXT, context_attribs);
232 egl_print_config_info(egl);
238 egl_state_destroy(struct egl_state *egl)
240 /* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
241 * on eglReleaseThread(). */
242 eglMakeCurrent(egl->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
245 eglTerminate(egl->dpy);
251 egl_make_swapbuffers_nonblock(struct egl_state *egl)
253 EGLint a = EGL_MIN_SWAP_INTERVAL;
254 EGLint b = EGL_MAX_SWAP_INTERVAL;
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");
261 fprintf(stderr, "warning: minimum swap interval is %d, "
262 "while 0 is required to not deadlock on resize.\n", a);
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.
270 if (!eglSwapInterval(egl->dpy, 0)) {
271 fprintf(stderr, "error: eglSwapInterval() failed.\n");
276 create_shader(const char *source, GLenum shader_type)
281 shader = glCreateShader(shader_type);
284 glShaderSource(shader, 1, (const char **) &source, NULL);
285 glCompileShader(shader);
287 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
291 glGetShaderInfoLog(shader, 1000, &len, log);
292 fprintf(stderr, "Error: compiling %s: %*s\n",
293 shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
302 triangle_init_gl(struct triangle_gl_state *trigl)
308 frag = create_shader(frag_shader_text, GL_FRAGMENT_SHADER);
309 vert = create_shader(vert_shader_text, GL_VERTEX_SHADER);
311 program = glCreateProgram();
312 glAttachShader(program, frag);
313 glAttachShader(program, vert);
314 glLinkProgram(program);
316 glGetProgramiv(program, GL_LINK_STATUS, &status);
320 glGetProgramInfoLog(program, 1000, &len, log);
321 fprintf(stderr, "Error: linking:\n%*s\n", len, log);
325 glUseProgram(program);
330 glBindAttribLocation(program, trigl->pos, "pos");
331 glBindAttribLocation(program, trigl->col, "color");
332 glLinkProgram(program);
334 trigl->rotation_uniform = glGetUniformLocation(program, "rotation");
338 triangle_draw(const struct triangle_gl_state *trigl, uint32_t time)
340 static const GLfloat verts[3][2] = {
345 static const GLfloat colors[3][3] = {
351 GLfloat rotation[4][4] = {
357 static const int32_t speed_div = 5;
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);
365 glUniformMatrix4fv(trigl->rotation_uniform, 1, GL_FALSE,
366 (GLfloat *) rotation);
368 glClearColor(0.0, 0.0, 0.0, 0.5);
369 glClear(GL_COLOR_BUFFER_BIT);
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);
376 glDrawArrays(GL_TRIANGLES, 0, 3);
378 glDisableVertexAttribArray(trigl->pos);
379 glDisableVertexAttribArray(trigl->col);
383 triangle_frame_callback(void *data, struct wl_callback *callback,
386 static const struct wl_callback_listener triangle_frame_listener = {
387 triangle_frame_callback
391 triangle_frame_callback(void *data, struct wl_callback *callback,
394 struct triangle *tri = data;
396 DBG("%stime %u\n", callback ? "" : "artificial ", time);
397 assert(callback == tri->frame_cb);
401 wl_callback_destroy(callback);
403 eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
404 tri->egl_surface, tri->egl->ctx);
406 glViewport(0, 0, tri->width, tri->height);
408 triangle_draw(&tri->gl, tri->time);
410 tri->frame_cb = wl_surface_frame(tri->wl_surface);
411 wl_callback_add_listener(tri->frame_cb, &triangle_frame_listener, tri);
413 eglSwapBuffers(tri->egl->dpy, tri->egl_surface);
417 triangle_create_egl_surface(struct triangle *tri, int width, int height)
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,
425 tri->egl_window, NULL);
427 ret = eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
428 tri->egl_surface, tri->egl->ctx);
429 assert(ret == EGL_TRUE);
431 egl_make_swapbuffers_nonblock(tri->egl);
432 triangle_init_gl(&tri->gl);
435 /********* The widget code interfacing the toolkit agnostic code: **********/
438 triangle_resize_handler(struct widget *widget,
439 int32_t width, int32_t height, void *data)
441 struct triangle *tri = data;
443 DBG("to %dx%d\n", width, height);
445 tri->height = height;
447 if (tri->egl_surface) {
448 wl_egl_window_resize(tri->egl_window, width, height, 0, 0);
450 triangle_create_egl_surface(tri, width, height);
451 triangle_frame_callback(tri, NULL, 0);
456 triangle_redraw_handler(struct widget *widget, void *data)
458 struct triangle *tri = data;
461 wl_egl_window_get_attached_size(tri->egl_window, &w, &h);
463 DBG("previous %dx%d, new %dx%d\n", w, h, tri->width, tri->height);
465 /* If size is not changing, do not redraw ahead of time.
466 * That would risk blocking in eglSwapbuffers().
468 if (w == tri->width && h == tri->height)
472 wl_callback_destroy(tri->frame_cb);
473 tri->frame_cb = NULL;
475 triangle_frame_callback(tri, NULL, tri->time);
479 set_empty_input_region(struct widget *widget, struct display *display)
481 struct wl_compositor *compositor;
482 struct wl_surface *surface;
483 struct wl_region *region;
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);
492 static struct triangle *
493 triangle_create(struct window *window, struct egl_state *egl)
495 struct triangle *tri;
497 tri = xmalloc(sizeof *tri);
498 memset(tri, 0, sizeof *tri);
501 tri->widget = window_add_subsurface(window, tri,
502 int_to_mode(option_triangle_mode));
503 widget_set_use_cairo(tri->widget, 0);
504 widget_set_resize_handler(tri->widget, triangle_resize_handler);
505 widget_set_redraw_handler(tri->widget, triangle_redraw_handler);
507 set_empty_input_region(tri->widget, window_get_display(window));
513 triangle_destroy(struct triangle *tri)
515 if (tri->egl_surface)
516 eglDestroySurface(tri->egl->dpy, tri->egl_surface);
519 wl_egl_window_destroy(tri->egl_window);
521 widget_destroy(tri->widget);
525 /************** The toytoolkit application code: *********************/
528 struct display *display;
529 struct window *window;
530 struct widget *widget;
531 struct widget *subsurface;
533 struct egl_state *egl;
534 struct triangle *triangle;
540 draw_spinner(cairo_t *cr, const struct rectangle *rect, uint32_t time)
542 double cx, cy, r, angle;
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;
549 angle = t * (M_PI / 500.0);
551 cairo_set_line_width(cr, 4.0);
554 cairo_arc(cr, cx, cy, r, 0.0, angle);
556 cairo_arc(cr, cx, cy, r, angle, 0.0);
562 sub_redraw_handler(struct widget *widget, void *data)
564 struct demoapp *app = data;
566 struct rectangle allocation;
569 widget_get_allocation(app->subsurface, &allocation);
571 cr = widget_cairo_create(widget);
572 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
574 /* debug: paint whole surface magenta; no magenta should show */
575 cairo_set_source_rgba(cr, 0.9, 0.0, 0.9, 1.0);
585 cairo_set_source_rgba(cr, 0.8, 0, 0, 0.8);
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);
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);
602 sub_resize_handler(struct widget *widget,
603 int32_t width, int32_t height, void *data)
605 DBG("%dx%d\n", width, height);
606 widget_input_region_add(widget, NULL);
610 redraw_handler(struct widget *widget, void *data)
612 struct demoapp *app = data;
614 struct rectangle allocation;
617 widget_get_allocation(app->widget, &allocation);
619 cr = widget_cairo_create(widget);
620 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
626 cairo_set_source_rgba(cr, 0, 0.8, 0, 0.8);
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);
635 DBG("%dx%d @ %d,%d, last time %u\n",
636 allocation.width, allocation.height,
637 allocation.x, allocation.y, time);
641 resize_handler(struct widget *widget,
642 int32_t width, int32_t height, void *data)
644 struct demoapp *app = data;
645 struct rectangle area;
648 widget_get_allocation(widget, &area);
650 side = area.width < area.height ? area.width / 2 : area.height / 2;
651 h = area.height - side;
653 widget_set_allocation(app->subsurface,
654 area.x + area.width - side,
659 widget_set_allocation(app->triangle->widget,
660 area.x + area.width - side,
665 DBG("green %dx%d, red %dx%d, GL %dx%d\n",
666 area.width, area.height, side, h, side, side);
670 keyboard_focus_handler(struct window *window,
671 struct input *device, void *data)
673 struct demoapp *app = data;
675 window_schedule_redraw(app->window);
679 key_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)
683 struct demoapp *app = data;
684 struct rectangle winrect;
686 if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
691 app->animate = !app->animate;
692 window_schedule_redraw(window);
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);
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);
709 display_exit(app->display);
714 static struct demoapp *
715 demoapp_create(struct display *display)
719 app = xmalloc(sizeof *app);
720 memset(app, 0, sizeof *app);
722 app->egl = egl_state_create(display_get_display(display));
724 app->display = display;
725 display_set_user_data(app->display, app);
727 app->window = window_create(app->display);
728 app->widget = window_frame_create(app->window, app);
729 window_set_title(app->window, "Wayland Sub-surface Demo");
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);
735 widget_set_redraw_handler(app->widget, redraw_handler);
736 widget_set_resize_handler(app->widget, resize_handler);
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);
743 if (app->egl && !option_no_triangle)
744 app->triangle = triangle_create(app->window, app->egl);
747 widget_schedule_resize(app->widget, 100, 100);
750 widget_schedule_resize(app->widget, 400, 300);
758 demoapp_destroy(struct demoapp *app)
761 triangle_destroy(app->triangle);
764 egl_state_destroy(app->egl);
766 widget_destroy(app->subsurface);
767 widget_destroy(app->widget);
768 window_destroy(app->window);
773 main(int argc, char *argv[])
775 struct display *display;
778 if (parse_options(options, ARRAY_LENGTH(options), &argc, argv) > 1
780 printf(help_text, argv[0]);
784 display = display_create(&argc, argv);
785 if (display == NULL) {
786 fprintf(stderr, "failed to create display: %m\n");
790 if (!display_has_subcompositor(display)) {
791 fprintf(stderr, "compositor does not support "
792 "the subcompositor extension\n");
796 app = demoapp_create(display);
798 display_run(display);
800 demoapp_destroy(app);
801 display_destroy(display);