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