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_resize_handler(tri->widget, triangle_resize_handler);
502 widget_set_redraw_handler(tri->widget, triangle_redraw_handler);
504 set_empty_input_region(tri->widget, window_get_display(window));
510 triangle_destroy(struct triangle *tri)
512 if (tri->egl_surface)
513 eglDestroySurface(tri->egl->dpy, tri->egl_surface);
516 wl_egl_window_destroy(tri->egl_window);
518 widget_destroy(tri->widget);
522 /************** The toytoolkit application code: *********************/
525 struct display *display;
526 struct window *window;
527 struct widget *widget;
528 struct widget *subsurface;
530 struct egl_state *egl;
531 struct triangle *triangle;
537 draw_spinner(cairo_t *cr, const struct rectangle *rect, uint32_t time)
539 double cx, cy, r, angle;
542 cx = rect->x + rect->width / 2;
543 cy = rect->y + rect->height / 2;
544 r = (rect->width < rect->height ? rect->width : rect->height) * 0.3;
546 angle = t * (M_PI / 500.0);
548 cairo_set_line_width(cr, 4.0);
551 cairo_arc(cr, cx, cy, r, 0.0, angle);
553 cairo_arc(cr, cx, cy, r, angle, 0.0);
559 sub_redraw_handler(struct widget *widget, void *data)
561 struct demoapp *app = data;
563 struct rectangle allocation;
566 widget_get_allocation(app->subsurface, &allocation);
568 cr = widget_cairo_create(widget);
569 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
571 /* debug: paint whole surface magenta; no magenta should show */
572 cairo_set_source_rgba(cr, 0.9, 0.0, 0.9, 1.0);
582 cairo_set_source_rgba(cr, 0.8, 0, 0, 0.8);
585 time = widget_get_last_time(widget);
586 cairo_set_source_rgba(cr, 1.0, 0.5, 0.5, 1.0);
587 draw_spinner(cr, &allocation, time);
592 widget_schedule_redraw(app->subsurface);
593 DBG("%dx%d @ %d,%d, last time %u\n",
594 allocation.width, allocation.height,
595 allocation.x, allocation.y, time);
599 sub_resize_handler(struct widget *widget,
600 int32_t width, int32_t height, void *data)
602 DBG("%dx%d\n", width, height);
603 widget_input_region_add(widget, NULL);
607 redraw_handler(struct widget *widget, void *data)
609 struct demoapp *app = data;
611 struct rectangle allocation;
614 widget_get_allocation(app->widget, &allocation);
616 cr = widget_cairo_create(widget);
617 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
623 cairo_set_source_rgba(cr, 0, 0.8, 0, 0.8);
626 time = widget_get_last_time(widget);
627 cairo_set_source_rgba(cr, 0.5, 1.0, 0.5, 1.0);
628 draw_spinner(cr, &allocation, time);
632 DBG("%dx%d @ %d,%d, last time %u\n",
633 allocation.width, allocation.height,
634 allocation.x, allocation.y, time);
638 resize_handler(struct widget *widget,
639 int32_t width, int32_t height, void *data)
641 struct demoapp *app = data;
642 struct rectangle area;
645 widget_get_allocation(widget, &area);
647 side = area.width < area.height ? area.width / 2 : area.height / 2;
648 h = area.height - side;
650 widget_set_allocation(app->subsurface,
651 area.x + area.width - side,
656 widget_set_allocation(app->triangle->widget,
657 area.x + area.width - side,
662 DBG("green %dx%d, red %dx%d, GL %dx%d\n",
663 area.width, area.height, side, h, side, side);
667 keyboard_focus_handler(struct window *window,
668 struct input *device, void *data)
670 struct demoapp *app = data;
672 window_schedule_redraw(app->window);
676 key_handler(struct window *window, struct input *input, uint32_t time,
677 uint32_t key, uint32_t sym,
678 enum wl_keyboard_key_state state, void *data)
680 struct demoapp *app = data;
681 struct rectangle winrect;
683 if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
688 app->animate = !app->animate;
689 window_schedule_redraw(window);
692 window_get_allocation(window, &winrect);
693 winrect.height -= 100;
694 if (winrect.height < 150)
695 winrect.height = 150;
696 window_schedule_resize(window, winrect.width, winrect.height);
699 window_get_allocation(window, &winrect);
700 winrect.height += 100;
701 if (winrect.height > 600)
702 winrect.height = 600;
703 window_schedule_resize(window, winrect.width, winrect.height);
706 display_exit(app->display);
711 static struct demoapp *
712 demoapp_create(struct display *display)
716 app = xmalloc(sizeof *app);
717 memset(app, 0, sizeof *app);
719 app->egl = egl_state_create(display_get_display(display));
721 app->display = display;
722 display_set_user_data(app->display, app);
724 app->window = window_create(app->display);
725 app->widget = window_frame_create(app->window, app);
726 window_set_title(app->window, "Wayland Sub-surface Demo");
728 window_set_key_handler(app->window, key_handler);
729 window_set_user_data(app->window, app);
730 window_set_keyboard_focus_handler(app->window, keyboard_focus_handler);
732 widget_set_redraw_handler(app->widget, redraw_handler);
733 widget_set_resize_handler(app->widget, resize_handler);
735 app->subsurface = window_add_subsurface(app->window, app,
736 int_to_mode(option_red_mode));
737 widget_set_redraw_handler(app->subsurface, sub_redraw_handler);
738 widget_set_resize_handler(app->subsurface, sub_resize_handler);
740 if (app->egl && !option_no_triangle)
741 app->triangle = triangle_create(app->window, app->egl);
744 widget_schedule_resize(app->widget, 100, 100);
747 widget_schedule_resize(app->widget, 400, 300);
755 demoapp_destroy(struct demoapp *app)
758 triangle_destroy(app->triangle);
761 egl_state_destroy(app->egl);
763 widget_destroy(app->subsurface);
764 widget_destroy(app->widget);
765 window_destroy(app->window);
770 main(int argc, char *argv[])
772 struct display *display;
775 parse_options(options, ARRAY_LENGTH(options), &argc, argv);
777 printf(help_text, argv[0]);
781 display = display_create(&argc, argv);
782 if (display == NULL) {
783 fprintf(stderr, "failed to create display: %m\n");
787 if (!display_has_subcompositor(display)) {
788 fprintf(stderr, "compositor does not support "
789 "the subcompositor extension\n");
793 app = demoapp_create(display);
795 display_run(display);
797 demoapp_destroy(app);
798 display_destroy(display);