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
33 #include <linux/input.h>
34 #include <wayland-client.h>
36 #include <wayland-egl.h>
37 #include <GLES2/gl2.h>
43 #define DBG(fmt, ...) \
44 fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__)
46 #define DBG(...) do {} while (0)
49 static int32_t option_red_mode;
50 static int32_t option_triangle_mode;
51 static int32_t option_no_triangle;
52 static int32_t option_help;
54 static 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 },
61 static enum subsurface_mode
62 int_to_mode(int32_t i)
66 return SUBSURFACE_DESYNCHRONIZED;
68 return SUBSURFACE_SYNCHRONIZED;
70 fprintf(stderr, "error: %d is not a valid commit mode.\n", i);
75 static const char help_text[] =
76 "Usage: %s [options]\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"
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"
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"
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"
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"
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"
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"
114 " space - toggle red sub-surface animation loop\n"
115 " up - step window size shorter\n"
116 " down - step window size taller\n"
125 struct triangle_gl_state {
126 GLuint rotation_uniform;
132 struct egl_state *egl;
134 struct wl_surface *wl_surface;
135 struct wl_egl_window *egl_window;
136 EGLSurface egl_surface;
140 struct triangle_gl_state gl;
142 struct widget *widget;
144 struct wl_callback *frame_cb;
147 /******** Pure EGL/GLESv2/libwayland-client component: ***************/
149 static 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"
155 " gl_Position = rotation * pos;\n"
156 " v_color = color;\n"
159 static const char *frag_shader_text =
160 "precision mediump float;\n"
161 "varying vec4 v_color;\n"
163 " gl_FragColor = v_color;\n"
167 egl_print_config_info(struct egl_state *egl)
171 printf("Chosen EGL config details:\n");
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);
180 printf(" unknown\n");
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);
187 printf(" unknown\n");
190 static struct egl_state *
191 egl_state_create(struct wl_display *display)
193 struct egl_state *egl;
195 static const EGLint context_attribs[] = {
196 EGL_CONTEXT_CLIENT_VERSION, 2,
200 EGLint config_attribs[] = {
201 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
206 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
210 EGLint major, minor, n;
213 egl = calloc(1, sizeof *egl);
216 egl->dpy = eglGetDisplay(display);
219 ret = eglInitialize(egl->dpy, &major, &minor);
220 assert(ret == EGL_TRUE);
221 ret = eglBindAPI(EGL_OPENGL_ES_API);
222 assert(ret == EGL_TRUE);
224 ret = eglChooseConfig(egl->dpy, config_attribs, &egl->conf, 1, &n);
225 assert(ret && n == 1);
227 egl->ctx = eglCreateContext(egl->dpy, egl->conf,
228 EGL_NO_CONTEXT, context_attribs);
230 egl_print_config_info(egl);
236 egl_state_destroy(struct egl_state *egl)
238 /* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
239 * on eglReleaseThread(). */
240 eglMakeCurrent(egl->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
243 eglTerminate(egl->dpy);
249 egl_make_swapbuffers_nonblock(struct egl_state *egl)
251 EGLint a = EGL_MIN_SWAP_INTERVAL;
252 EGLint b = EGL_MAX_SWAP_INTERVAL;
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");
259 fprintf(stderr, "warning: minimum swap interval is %d, "
260 "while 0 is required to not deadlock on resize.\n", a);
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.
268 if (!eglSwapInterval(egl->dpy, 0)) {
269 fprintf(stderr, "error: eglSwapInterval() failed.\n");
274 create_shader(const char *source, GLenum shader_type)
279 shader = glCreateShader(shader_type);
282 glShaderSource(shader, 1, (const char **) &source, NULL);
283 glCompileShader(shader);
285 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
289 glGetShaderInfoLog(shader, 1000, &len, log);
290 fprintf(stderr, "Error: compiling %s: %*s\n",
291 shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
300 triangle_init_gl(struct triangle_gl_state *trigl)
306 frag = create_shader(frag_shader_text, GL_FRAGMENT_SHADER);
307 vert = create_shader(vert_shader_text, GL_VERTEX_SHADER);
309 program = glCreateProgram();
310 glAttachShader(program, frag);
311 glAttachShader(program, vert);
312 glLinkProgram(program);
314 glGetProgramiv(program, GL_LINK_STATUS, &status);
318 glGetProgramInfoLog(program, 1000, &len, log);
319 fprintf(stderr, "Error: linking:\n%*s\n", len, log);
323 glUseProgram(program);
328 glBindAttribLocation(program, trigl->pos, "pos");
329 glBindAttribLocation(program, trigl->col, "color");
330 glLinkProgram(program);
332 trigl->rotation_uniform = glGetUniformLocation(program, "rotation");
336 triangle_draw(const struct triangle_gl_state *trigl, uint32_t time)
338 static const GLfloat verts[3][2] = {
343 static const GLfloat colors[3][3] = {
349 GLfloat rotation[4][4] = {
355 static const int32_t speed_div = 5;
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);
363 glUniformMatrix4fv(trigl->rotation_uniform, 1, GL_FALSE,
364 (GLfloat *) rotation);
366 glClearColor(0.0, 0.0, 0.0, 0.5);
367 glClear(GL_COLOR_BUFFER_BIT);
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);
374 glDrawArrays(GL_TRIANGLES, 0, 3);
376 glDisableVertexAttribArray(trigl->pos);
377 glDisableVertexAttribArray(trigl->col);
381 triangle_frame_callback(void *data, struct wl_callback *callback,
384 static const struct wl_callback_listener triangle_frame_listener = {
385 triangle_frame_callback
389 triangle_frame_callback(void *data, struct wl_callback *callback,
392 struct triangle *tri = data;
394 DBG("%stime %u\n", callback ? "" : "artificial ", time);
395 assert(callback == tri->frame_cb);
399 wl_callback_destroy(callback);
401 eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
402 tri->egl_surface, tri->egl->ctx);
404 glViewport(0, 0, tri->width, tri->height);
406 triangle_draw(&tri->gl, tri->time);
408 tri->frame_cb = wl_surface_frame(tri->wl_surface);
409 wl_callback_add_listener(tri->frame_cb, &triangle_frame_listener, tri);
411 eglSwapBuffers(tri->egl->dpy, tri->egl_surface);
415 triangle_create_egl_surface(struct triangle *tri, int width, int height)
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,
423 tri->egl_window, NULL);
425 ret = eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
426 tri->egl_surface, tri->egl->ctx);
427 assert(ret == EGL_TRUE);
429 egl_make_swapbuffers_nonblock(tri->egl);
430 triangle_init_gl(&tri->gl);
433 /********* The widget code interfacing the toolkit agnostic code: **********/
436 triangle_resize_handler(struct widget *widget,
437 int32_t width, int32_t height, void *data)
439 struct triangle *tri = data;
441 DBG("to %dx%d\n", width, height);
443 tri->height = height;
445 if (tri->egl_surface) {
446 wl_egl_window_resize(tri->egl_window, width, height, 0, 0);
448 triangle_create_egl_surface(tri, width, height);
449 triangle_frame_callback(tri, NULL, 0);
454 triangle_redraw_handler(struct widget *widget, void *data)
456 struct triangle *tri = data;
459 wl_egl_window_get_attached_size(tri->egl_window, &w, &h);
461 DBG("previous %dx%d, new %dx%d\n", w, h, tri->width, tri->height);
463 /* If size is not changing, do not redraw ahead of time.
464 * That would risk blocking in eglSwapbuffers().
466 if (w == tri->width && h == tri->height)
470 wl_callback_destroy(tri->frame_cb);
471 tri->frame_cb = NULL;
473 triangle_frame_callback(tri, NULL, tri->time);
477 set_empty_input_region(struct widget *widget, struct display *display)
479 struct wl_compositor *compositor;
480 struct wl_surface *surface;
481 struct wl_region *region;
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);
490 static struct triangle *
491 triangle_create(struct window *window, struct egl_state *egl)
493 struct triangle *tri;
495 tri = xmalloc(sizeof *tri);
496 memset(tri, 0, sizeof *tri);
499 tri->widget = window_add_subsurface(window, tri,
500 int_to_mode(option_triangle_mode));
501 widget_set_use_cairo(tri->widget, 0);
502 widget_set_resize_handler(tri->widget, triangle_resize_handler);
503 widget_set_redraw_handler(tri->widget, triangle_redraw_handler);
505 set_empty_input_region(tri->widget, window_get_display(window));
511 triangle_destroy(struct triangle *tri)
513 if (tri->egl_surface)
514 eglDestroySurface(tri->egl->dpy, tri->egl_surface);
517 wl_egl_window_destroy(tri->egl_window);
519 widget_destroy(tri->widget);
523 /************** The toytoolkit application code: *********************/
526 struct display *display;
527 struct window *window;
528 struct widget *widget;
529 struct widget *subsurface;
531 struct egl_state *egl;
532 struct triangle *triangle;
538 draw_spinner(cairo_t *cr, const struct rectangle *rect, uint32_t time)
540 double cx, cy, r, angle;
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;
547 angle = t * (M_PI / 500.0);
549 cairo_set_line_width(cr, 4.0);
552 cairo_arc(cr, cx, cy, r, 0.0, angle);
554 cairo_arc(cr, cx, cy, r, angle, 0.0);
560 sub_redraw_handler(struct widget *widget, void *data)
562 struct demoapp *app = data;
564 struct rectangle allocation;
567 widget_get_allocation(app->subsurface, &allocation);
569 cr = widget_cairo_create(widget);
570 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
572 /* debug: paint whole surface magenta; no magenta should show */
573 cairo_set_source_rgba(cr, 0.9, 0.0, 0.9, 1.0);
583 cairo_set_source_rgba(cr, 0.8, 0, 0, 0.8);
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);
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);
600 sub_resize_handler(struct widget *widget,
601 int32_t width, int32_t height, void *data)
603 DBG("%dx%d\n", width, height);
604 widget_input_region_add(widget, NULL);
608 redraw_handler(struct widget *widget, void *data)
610 struct demoapp *app = data;
612 struct rectangle allocation;
615 widget_get_allocation(app->widget, &allocation);
617 cr = widget_cairo_create(widget);
618 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
624 cairo_set_source_rgba(cr, 0, 0.8, 0, 0.8);
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);
633 DBG("%dx%d @ %d,%d, last time %u\n",
634 allocation.width, allocation.height,
635 allocation.x, allocation.y, time);
639 resize_handler(struct widget *widget,
640 int32_t width, int32_t height, void *data)
642 struct demoapp *app = data;
643 struct rectangle area;
646 widget_get_allocation(widget, &area);
648 side = area.width < area.height ? area.width / 2 : area.height / 2;
649 h = area.height - side;
651 widget_set_allocation(app->subsurface,
652 area.x + area.width - side,
657 widget_set_allocation(app->triangle->widget,
658 area.x + area.width - side,
663 DBG("green %dx%d, red %dx%d, GL %dx%d\n",
664 area.width, area.height, side, h, side, side);
668 keyboard_focus_handler(struct window *window,
669 struct input *device, void *data)
671 struct demoapp *app = data;
673 window_schedule_redraw(app->window);
677 key_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)
681 struct demoapp *app = data;
682 struct rectangle winrect;
684 if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
689 app->animate = !app->animate;
690 window_schedule_redraw(window);
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);
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);
707 display_exit(app->display);
712 static struct demoapp *
713 demoapp_create(struct display *display)
717 app = xmalloc(sizeof *app);
718 memset(app, 0, sizeof *app);
720 app->egl = egl_state_create(display_get_display(display));
722 app->display = display;
723 display_set_user_data(app->display, app);
725 app->window = window_create(app->display);
726 app->widget = window_frame_create(app->window, app);
727 window_set_title(app->window, "Wayland Sub-surface Demo");
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);
733 widget_set_redraw_handler(app->widget, redraw_handler);
734 widget_set_resize_handler(app->widget, resize_handler);
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);
741 if (app->egl && !option_no_triangle)
742 app->triangle = triangle_create(app->window, app->egl);
745 widget_schedule_resize(app->widget, 100, 100);
748 widget_schedule_resize(app->widget, 400, 300);
756 demoapp_destroy(struct demoapp *app)
759 triangle_destroy(app->triangle);
762 egl_state_destroy(app->egl);
764 widget_destroy(app->subsurface);
765 widget_destroy(app->widget);
766 window_destroy(app->window);
771 main(int argc, char *argv[])
773 struct display *display;
776 parse_options(options, ARRAY_LENGTH(options), &argc, argv);
778 printf(help_text, argv[0]);
782 display = display_create(&argc, argv);
783 if (display == NULL) {
784 fprintf(stderr, "failed to create display: %m\n");
788 if (!display_has_subcompositor(display)) {
789 fprintf(stderr, "compositor does not support "
790 "the subcompositor extension\n");
794 app = demoapp_create(display);
796 display_run(display);
798 demoapp_destroy(app);
799 display_destroy(display);