1 /*
2  * Copyright © 2011 Benjamin Franzke
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "config.h"
25 
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdbool.h>
31 #include <math.h>
32 #include <assert.h>
33 #include <signal.h>
34 
35 #include <linux/input.h>
36 
37 #include <wayland-client.h>
38 #include <wayland-egl.h>
39 #include <wayland-cursor.h>
40 
41 #include <GLES2/gl2.h>
42 #include <EGL/egl.h>
43 #include <EGL/eglext.h>
44 
45 #include "xdg-shell-client-protocol.h"
46 #include <sys/types.h>
47 #include <unistd.h>
48 
49 #include "shared/helpers.h"
50 #include "shared/platform.h"
51 #include "shared/weston-egl-ext.h"
52 
53 struct window;
54 struct seat;
55 
56 struct display {
57 	struct wl_display *display;
58 	struct wl_registry *registry;
59 	struct wl_compositor *compositor;
60 	struct xdg_wm_base *wm_base;
61 	struct wl_seat *seat;
62 	struct wl_pointer *pointer;
63 	struct wl_touch *touch;
64 	struct wl_keyboard *keyboard;
65 	struct wl_shm *shm;
66 	struct wl_cursor_theme *cursor_theme;
67 	struct wl_cursor *default_cursor;
68 	struct wl_surface *cursor_surface;
69 	struct {
70 		EGLDisplay dpy;
71 		EGLContext ctx;
72 		EGLConfig conf;
73 	} egl;
74 	struct window *window;
75 
76 	PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
77 };
78 
79 struct geometry {
80 	int width, height;
81 };
82 
83 struct window {
84 	struct display *display;
85 	struct geometry geometry, window_size;
86 	struct {
87 		GLuint rotation_uniform;
88 		GLuint pos;
89 		GLuint col;
90 	} gl;
91 
92 	uint32_t benchmark_time, frames;
93 	struct wl_egl_window *native;
94 	struct wl_surface *surface;
95 	struct xdg_surface *xdg_surface;
96 	struct xdg_toplevel *xdg_toplevel;
97 	EGLSurface egl_surface;
98 	struct wl_callback *callback;
99 	int fullscreen, maximized, opaque, buffer_size, frame_sync, delay;
100 	bool wait_for_configure;
101 };
102 
103 static const char *vert_shader_text =
104 	"uniform mat4 rotation;\n"
105 	"attribute vec4 pos;\n"
106 	"attribute vec4 color;\n"
107 	"varying vec4 v_color;\n"
108 	"void main() {\n"
109 	"  gl_Position = rotation * pos;\n"
110 	"  v_color = color;\n"
111 	"}\n";
112 
113 static const char *frag_shader_text =
114 	"precision mediump float;\n"
115 	"varying vec4 v_color;\n"
116 	"void main() {\n"
117 	"  gl_FragColor = v_color;\n"
118 	"}\n";
119 
120 static int running = 1;
121 
122 static void
init_egl(struct display * display,struct window * window)123 init_egl(struct display *display, struct window *window)
124 {
125 	static const struct {
126 		char *extension, *entrypoint;
127 	} swap_damage_ext_to_entrypoint[] = {
128 		{
129 			.extension = "EGL_EXT_swap_buffers_with_damage",
130 			.entrypoint = "eglSwapBuffersWithDamageEXT",
131 		},
132 		{
133 			.extension = "EGL_KHR_swap_buffers_with_damage",
134 			.entrypoint = "eglSwapBuffersWithDamageKHR",
135 		},
136 	};
137 
138 	static const EGLint context_attribs[] = {
139 		EGL_CONTEXT_CLIENT_VERSION, 2,
140 		EGL_NONE
141 	};
142 	const char *extensions;
143 
144 	EGLint config_attribs[] = {
145 		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
146 		EGL_RED_SIZE, 1,
147 		EGL_GREEN_SIZE, 1,
148 		EGL_BLUE_SIZE, 1,
149 		EGL_ALPHA_SIZE, 1,
150 		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
151 		EGL_NONE
152 	};
153 
154 	EGLint major, minor, n, count, i, size;
155 	EGLConfig *configs;
156 	EGLBoolean ret;
157 
158 	if (window->opaque || window->buffer_size == 16)
159 		config_attribs[9] = 0;
160 
161 	display->egl.dpy =
162 		weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
163 						display->display, NULL);
164 	assert(display->egl.dpy);
165 
166 	ret = eglInitialize(display->egl.dpy, &major, &minor);
167 	assert(ret == EGL_TRUE);
168 	ret = eglBindAPI(EGL_OPENGL_ES_API);
169 	assert(ret == EGL_TRUE);
170 
171 	if (!eglGetConfigs(display->egl.dpy, NULL, 0, &count) || count < 1)
172 		assert(0);
173 
174 	configs = calloc(count, sizeof *configs);
175 	assert(configs);
176 
177 	ret = eglChooseConfig(display->egl.dpy, config_attribs,
178 			      configs, count, &n);
179 	assert(ret && n >= 1);
180 
181 	for (i = 0; i < n; i++) {
182 		eglGetConfigAttrib(display->egl.dpy,
183 				   configs[i], EGL_BUFFER_SIZE, &size);
184 		if (window->buffer_size == size) {
185 			display->egl.conf = configs[i];
186 			break;
187 		}
188 	}
189 	free(configs);
190 	if (display->egl.conf == NULL) {
191 		fprintf(stderr, "did not find config with buffer size %d\n",
192 			window->buffer_size);
193 		exit(EXIT_FAILURE);
194 	}
195 
196 	display->egl.ctx = eglCreateContext(display->egl.dpy,
197 					    display->egl.conf,
198 					    EGL_NO_CONTEXT, context_attribs);
199 	assert(display->egl.ctx);
200 
201 	display->swap_buffers_with_damage = NULL;
202 	extensions = eglQueryString(display->egl.dpy, EGL_EXTENSIONS);
203 	if (extensions &&
204 	    weston_check_egl_extension(extensions, "EGL_EXT_buffer_age")) {
205 		for (i = 0; i < (int) ARRAY_LENGTH(swap_damage_ext_to_entrypoint); i++) {
206 			if (weston_check_egl_extension(extensions,
207 						       swap_damage_ext_to_entrypoint[i].extension)) {
208 				/* The EXTPROC is identical to the KHR one */
209 				display->swap_buffers_with_damage =
210 					(PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)
211 					eglGetProcAddress(swap_damage_ext_to_entrypoint[i].entrypoint);
212 				break;
213 			}
214 		}
215 	}
216 
217 	if (display->swap_buffers_with_damage)
218 		printf("has EGL_EXT_buffer_age and %s\n", swap_damage_ext_to_entrypoint[i].extension);
219 
220 }
221 
222 static void
fini_egl(struct display * display)223 fini_egl(struct display *display)
224 {
225 	eglTerminate(display->egl.dpy);
226 	eglReleaseThread();
227 }
228 
229 static GLuint
create_shader(struct window * window,const char * source,GLenum shader_type)230 create_shader(struct window *window, const char *source, GLenum shader_type)
231 {
232 	GLuint shader;
233 	GLint status;
234 
235 	shader = glCreateShader(shader_type);
236 	assert(shader != 0);
237 
238 	glShaderSource(shader, 1, (const char **) &source, NULL);
239 	glCompileShader(shader);
240 
241 	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
242 	if (!status) {
243 		char log[1000];
244 		GLsizei len;
245 		glGetShaderInfoLog(shader, 1000, &len, log);
246 		fprintf(stderr, "Error: compiling %s: %*s\n",
247 			shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
248 			len, log);
249 		exit(1);
250 	}
251 
252 	return shader;
253 }
254 
255 static void
init_gl(struct window * window)256 init_gl(struct window *window)
257 {
258 	GLuint frag, vert;
259 	GLuint program;
260 	GLint status;
261 
262 	frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER);
263 	vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER);
264 
265 	program = glCreateProgram();
266 	glAttachShader(program, frag);
267 	glAttachShader(program, vert);
268 	glLinkProgram(program);
269 
270 	glGetProgramiv(program, GL_LINK_STATUS, &status);
271 	if (!status) {
272 		char log[1000];
273 		GLsizei len;
274 		glGetProgramInfoLog(program, 1000, &len, log);
275 		fprintf(stderr, "Error: linking:\n%*s\n", len, log);
276 		exit(1);
277 	}
278 
279 	glUseProgram(program);
280 
281 	window->gl.pos = 0;
282 	window->gl.col = 1;
283 
284 	glBindAttribLocation(program, window->gl.pos, "pos");
285 	glBindAttribLocation(program, window->gl.col, "color");
286 	glLinkProgram(program);
287 
288 	window->gl.rotation_uniform =
289 		glGetUniformLocation(program, "rotation");
290 }
291 
292 static void
handle_surface_configure(void * data,struct xdg_surface * surface,uint32_t serial)293 handle_surface_configure(void *data, struct xdg_surface *surface,
294 			 uint32_t serial)
295 {
296 	struct window *window = data;
297 
298 	xdg_surface_ack_configure(surface, serial);
299 
300 	window->wait_for_configure = false;
301 }
302 
303 static const struct xdg_surface_listener xdg_surface_listener = {
304 	handle_surface_configure
305 };
306 
307 static void
handle_toplevel_configure(void * data,struct xdg_toplevel * toplevel,int32_t width,int32_t height,struct wl_array * states)308 handle_toplevel_configure(void *data, struct xdg_toplevel *toplevel,
309 			  int32_t width, int32_t height,
310 			  struct wl_array *states)
311 {
312 	struct window *window = data;
313 	uint32_t *p;
314 
315 	window->fullscreen = 0;
316 	window->maximized = 0;
317 	wl_array_for_each(p, states) {
318 		uint32_t state = *p;
319 		switch (state) {
320 		case XDG_TOPLEVEL_STATE_FULLSCREEN:
321 			window->fullscreen = 1;
322 			break;
323 		case XDG_TOPLEVEL_STATE_MAXIMIZED:
324 			window->maximized = 1;
325 			break;
326 		}
327 	}
328 
329 	if (width > 0 && height > 0) {
330 		if (!window->fullscreen && !window->maximized) {
331 			window->window_size.width = width;
332 			window->window_size.height = height;
333 		}
334 		window->geometry.width = width;
335 		window->geometry.height = height;
336 	} else if (!window->fullscreen && !window->maximized) {
337 		window->geometry = window->window_size;
338 	}
339 
340 	if (window->native)
341 		wl_egl_window_resize(window->native,
342 				     window->geometry.width,
343 				     window->geometry.height, 0, 0);
344 }
345 
346 static void
handle_toplevel_close(void * data,struct xdg_toplevel * xdg_toplevel)347 handle_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
348 {
349 	running = 0;
350 }
351 
352 static const struct xdg_toplevel_listener xdg_toplevel_listener = {
353 	handle_toplevel_configure,
354 	handle_toplevel_close,
355 };
356 
357 static void
create_surface(struct window * window)358 create_surface(struct window *window)
359 {
360 	struct display *display = window->display;
361 	EGLBoolean ret;
362 
363 	window->surface = wl_compositor_create_surface(display->compositor);
364 
365 	window->native =
366 		wl_egl_window_create(window->surface,
367 				     window->geometry.width,
368 				     window->geometry.height);
369 	window->egl_surface =
370 		weston_platform_create_egl_surface(display->egl.dpy,
371 						   display->egl.conf,
372 						   window->native, NULL);
373 
374 	window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base,
375 							  window->surface);
376 	xdg_surface_add_listener(window->xdg_surface,
377 				 &xdg_surface_listener, window);
378 
379 	window->xdg_toplevel =
380 		xdg_surface_get_toplevel(window->xdg_surface);
381 	xdg_toplevel_add_listener(window->xdg_toplevel,
382 				  &xdg_toplevel_listener, window);
383 
384 	xdg_toplevel_set_title(window->xdg_toplevel, "simple-egl");
385 
386 	window->wait_for_configure = true;
387 	wl_surface_commit(window->surface);
388 
389 	ret = eglMakeCurrent(window->display->egl.dpy, window->egl_surface,
390 			     window->egl_surface, window->display->egl.ctx);
391 	assert(ret == EGL_TRUE);
392 
393 	if (!window->frame_sync)
394 		eglSwapInterval(display->egl.dpy, 0);
395 
396 	if (!display->wm_base)
397 		return;
398 
399 	if (window->fullscreen)
400 		xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL);
401 }
402 
403 static void
destroy_surface(struct window * window)404 destroy_surface(struct window *window)
405 {
406 	/* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
407 	 * on eglReleaseThread(). */
408 	eglMakeCurrent(window->display->egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
409 		       EGL_NO_CONTEXT);
410 
411 	weston_platform_destroy_egl_surface(window->display->egl.dpy,
412 					    window->egl_surface);
413 	wl_egl_window_destroy(window->native);
414 
415 	if (window->xdg_toplevel)
416 		xdg_toplevel_destroy(window->xdg_toplevel);
417 	if (window->xdg_surface)
418 		xdg_surface_destroy(window->xdg_surface);
419 	wl_surface_destroy(window->surface);
420 
421 	if (window->callback)
422 		wl_callback_destroy(window->callback);
423 }
424 
425 static void
redraw(void * data,struct wl_callback * callback,uint32_t time)426 redraw(void *data, struct wl_callback *callback, uint32_t time)
427 {
428 	struct window *window = data;
429 	struct display *display = window->display;
430 	static const GLfloat verts[3][2] = {
431 		{ -0.5, -0.5 },
432 		{  0.5, -0.5 },
433 		{  0,    0.5 }
434 	};
435 	static const GLfloat colors[3][3] = {
436 		{ 1, 0, 0 },
437 		{ 0, 1, 0 },
438 		{ 0, 0, 1 }
439 	};
440 	GLfloat angle;
441 	GLfloat rotation[4][4] = {
442 		{ 1, 0, 0, 0 },
443 		{ 0, 1, 0, 0 },
444 		{ 0, 0, 1, 0 },
445 		{ 0, 0, 0, 1 }
446 	};
447 	static const uint32_t speed_div = 5, benchmark_interval = 5;
448 	struct wl_region *region;
449 	EGLint rect[4];
450 	EGLint buffer_age = 0;
451 	struct timeval tv;
452 
453 	assert(window->callback == callback);
454 	window->callback = NULL;
455 
456 	if (callback)
457 		wl_callback_destroy(callback);
458 
459 	gettimeofday(&tv, NULL);
460 	time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
461 	if (window->frames == 0)
462 		window->benchmark_time = time;
463 	if (time - window->benchmark_time > (benchmark_interval * 1000)) {
464 		printf("%d frames in %d seconds: %f fps\n",
465 		       window->frames,
466 		       benchmark_interval,
467 		       (float) window->frames / benchmark_interval);
468 		window->benchmark_time = time;
469 		window->frames = 0;
470 	}
471 
472 	angle = (time / speed_div) % 360 * M_PI / 180.0;
473 	rotation[0][0] =  cos(angle);
474 	rotation[0][2] =  sin(angle);
475 	rotation[2][0] = -sin(angle);
476 	rotation[2][2] =  cos(angle);
477 
478 	if (display->swap_buffers_with_damage)
479 		eglQuerySurface(display->egl.dpy, window->egl_surface,
480 				EGL_BUFFER_AGE_EXT, &buffer_age);
481 
482 	glViewport(0, 0, window->geometry.width, window->geometry.height);
483 
484 	glUniformMatrix4fv(window->gl.rotation_uniform, 1, GL_FALSE,
485 			   (GLfloat *) rotation);
486 
487 	glClearColor(0.0, 0.0, 0.0, 0.5);
488 	glClear(GL_COLOR_BUFFER_BIT);
489 
490 	glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
491 	glVertexAttribPointer(window->gl.col, 3, GL_FLOAT, GL_FALSE, 0, colors);
492 	glEnableVertexAttribArray(window->gl.pos);
493 	glEnableVertexAttribArray(window->gl.col);
494 
495 	glDrawArrays(GL_TRIANGLES, 0, 3);
496 
497 	glDisableVertexAttribArray(window->gl.pos);
498 	glDisableVertexAttribArray(window->gl.col);
499 
500 	usleep(window->delay);
501 
502 	if (window->opaque || window->fullscreen) {
503 		region = wl_compositor_create_region(window->display->compositor);
504 		wl_region_add(region, 0, 0,
505 			      window->geometry.width,
506 			      window->geometry.height);
507 		wl_surface_set_opaque_region(window->surface, region);
508 		wl_region_destroy(region);
509 	} else {
510 		wl_surface_set_opaque_region(window->surface, NULL);
511 	}
512 
513 	if (display->swap_buffers_with_damage && buffer_age > 0) {
514 		rect[0] = window->geometry.width / 4 - 1;
515 		rect[1] = window->geometry.height / 4 - 1;
516 		rect[2] = window->geometry.width / 2 + 2;
517 		rect[3] = window->geometry.height / 2 + 2;
518 		display->swap_buffers_with_damage(display->egl.dpy,
519 						  window->egl_surface,
520 						  rect, 1);
521 	} else {
522 		eglSwapBuffers(display->egl.dpy, window->egl_surface);
523 	}
524 	window->frames++;
525 }
526 
527 static void
pointer_handle_enter(void * data,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t sx,wl_fixed_t sy)528 pointer_handle_enter(void *data, struct wl_pointer *pointer,
529 		     uint32_t serial, struct wl_surface *surface,
530 		     wl_fixed_t sx, wl_fixed_t sy)
531 {
532 	struct display *display = data;
533 	struct wl_buffer *buffer;
534 	struct wl_cursor *cursor = display->default_cursor;
535 	struct wl_cursor_image *image;
536 
537 	if (display->window->fullscreen)
538 		wl_pointer_set_cursor(pointer, serial, NULL, 0, 0);
539 	else if (cursor) {
540 		image = display->default_cursor->images[0];
541 		buffer = wl_cursor_image_get_buffer(image);
542 		if (!buffer)
543 			return;
544 		wl_pointer_set_cursor(pointer, serial,
545 				      display->cursor_surface,
546 				      image->hotspot_x,
547 				      image->hotspot_y);
548 		wl_surface_attach(display->cursor_surface, buffer, 0, 0);
549 		wl_surface_damage(display->cursor_surface, 0, 0,
550 				  image->width, image->height);
551 		wl_surface_commit(display->cursor_surface);
552 	}
553 }
554 
555 static void
pointer_handle_leave(void * data,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface)556 pointer_handle_leave(void *data, struct wl_pointer *pointer,
557 		     uint32_t serial, struct wl_surface *surface)
558 {
559 }
560 
561 static void
pointer_handle_motion(void * data,struct wl_pointer * pointer,uint32_t time,wl_fixed_t sx,wl_fixed_t sy)562 pointer_handle_motion(void *data, struct wl_pointer *pointer,
563 		      uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
564 {
565 }
566 
567 static void
pointer_handle_button(void * data,struct wl_pointer * wl_pointer,uint32_t serial,uint32_t time,uint32_t button,uint32_t state)568 pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
569 		      uint32_t serial, uint32_t time, uint32_t button,
570 		      uint32_t state)
571 {
572 	struct display *display = data;
573 
574 	if (!display->window->xdg_toplevel)
575 		return;
576 
577 	if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
578 		xdg_toplevel_move(display->window->xdg_toplevel,
579 				  display->seat, serial);
580 }
581 
582 static void
pointer_handle_axis(void * data,struct wl_pointer * wl_pointer,uint32_t time,uint32_t axis,wl_fixed_t value)583 pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
584 		    uint32_t time, uint32_t axis, wl_fixed_t value)
585 {
586 }
587 
588 static const struct wl_pointer_listener pointer_listener = {
589 	pointer_handle_enter,
590 	pointer_handle_leave,
591 	pointer_handle_motion,
592 	pointer_handle_button,
593 	pointer_handle_axis,
594 };
595 
596 static void
touch_handle_down(void * data,struct wl_touch * wl_touch,uint32_t serial,uint32_t time,struct wl_surface * surface,int32_t id,wl_fixed_t x_w,wl_fixed_t y_w)597 touch_handle_down(void *data, struct wl_touch *wl_touch,
598 		  uint32_t serial, uint32_t time, struct wl_surface *surface,
599 		  int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
600 {
601 	struct display *d = (struct display *)data;
602 
603 	if (!d->wm_base)
604 		return;
605 
606 	xdg_toplevel_move(d->window->xdg_toplevel, d->seat, serial);
607 }
608 
609 static void
touch_handle_up(void * data,struct wl_touch * wl_touch,uint32_t serial,uint32_t time,int32_t id)610 touch_handle_up(void *data, struct wl_touch *wl_touch,
611 		uint32_t serial, uint32_t time, int32_t id)
612 {
613 }
614 
615 static void
touch_handle_motion(void * data,struct wl_touch * wl_touch,uint32_t time,int32_t id,wl_fixed_t x_w,wl_fixed_t y_w)616 touch_handle_motion(void *data, struct wl_touch *wl_touch,
617 		    uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
618 {
619 }
620 
621 static void
touch_handle_frame(void * data,struct wl_touch * wl_touch)622 touch_handle_frame(void *data, struct wl_touch *wl_touch)
623 {
624 }
625 
626 static void
touch_handle_cancel(void * data,struct wl_touch * wl_touch)627 touch_handle_cancel(void *data, struct wl_touch *wl_touch)
628 {
629 }
630 
631 static const struct wl_touch_listener touch_listener = {
632 	touch_handle_down,
633 	touch_handle_up,
634 	touch_handle_motion,
635 	touch_handle_frame,
636 	touch_handle_cancel,
637 };
638 
639 static void
keyboard_handle_keymap(void * data,struct wl_keyboard * keyboard,uint32_t format,int fd,uint32_t size)640 keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
641 		       uint32_t format, int fd, uint32_t size)
642 {
643 	/* Just so we don’t leak the keymap fd */
644 	close(fd);
645 }
646 
647 static void
keyboard_handle_enter(void * data,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface,struct wl_array * keys)648 keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
649 		      uint32_t serial, struct wl_surface *surface,
650 		      struct wl_array *keys)
651 {
652 }
653 
654 static void
keyboard_handle_leave(void * data,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface)655 keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
656 		      uint32_t serial, struct wl_surface *surface)
657 {
658 }
659 
660 static void
keyboard_handle_key(void * data,struct wl_keyboard * keyboard,uint32_t serial,uint32_t time,uint32_t key,uint32_t state)661 keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
662 		    uint32_t serial, uint32_t time, uint32_t key,
663 		    uint32_t state)
664 {
665 	struct display *d = data;
666 
667 	if (!d->wm_base)
668 		return;
669 
670 	if (key == KEY_F11 && state) {
671 		if (d->window->fullscreen)
672 			xdg_toplevel_unset_fullscreen(d->window->xdg_toplevel);
673 		else
674 			xdg_toplevel_set_fullscreen(d->window->xdg_toplevel, NULL);
675 	} else if (key == KEY_ESC && state)
676 		running = 0;
677 }
678 
679 static void
keyboard_handle_modifiers(void * data,struct wl_keyboard * keyboard,uint32_t serial,uint32_t mods_depressed,uint32_t mods_latched,uint32_t mods_locked,uint32_t group)680 keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
681 			  uint32_t serial, uint32_t mods_depressed,
682 			  uint32_t mods_latched, uint32_t mods_locked,
683 			  uint32_t group)
684 {
685 }
686 
687 static const struct wl_keyboard_listener keyboard_listener = {
688 	keyboard_handle_keymap,
689 	keyboard_handle_enter,
690 	keyboard_handle_leave,
691 	keyboard_handle_key,
692 	keyboard_handle_modifiers,
693 };
694 
695 static void
seat_handle_capabilities(void * data,struct wl_seat * seat,enum wl_seat_capability caps)696 seat_handle_capabilities(void *data, struct wl_seat *seat,
697 			 enum wl_seat_capability caps)
698 {
699 	struct display *d = data;
700 
701 	if ((caps & WL_SEAT_CAPABILITY_POINTER) && !d->pointer) {
702 		d->pointer = wl_seat_get_pointer(seat);
703 		wl_pointer_add_listener(d->pointer, &pointer_listener, d);
704 	} else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && d->pointer) {
705 		wl_pointer_destroy(d->pointer);
706 		d->pointer = NULL;
707 	}
708 
709 	if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !d->keyboard) {
710 		d->keyboard = wl_seat_get_keyboard(seat);
711 		wl_keyboard_add_listener(d->keyboard, &keyboard_listener, d);
712 	} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && d->keyboard) {
713 		wl_keyboard_destroy(d->keyboard);
714 		d->keyboard = NULL;
715 	}
716 
717 	if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !d->touch) {
718 		d->touch = wl_seat_get_touch(seat);
719 		wl_touch_set_user_data(d->touch, d);
720 		wl_touch_add_listener(d->touch, &touch_listener, d);
721 	} else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && d->touch) {
722 		wl_touch_destroy(d->touch);
723 		d->touch = NULL;
724 	}
725 }
726 
727 static const struct wl_seat_listener seat_listener = {
728 	seat_handle_capabilities,
729 };
730 
731 static void
xdg_wm_base_ping(void * data,struct xdg_wm_base * shell,uint32_t serial)732 xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
733 {
734 	xdg_wm_base_pong(shell, serial);
735 }
736 
737 static const struct xdg_wm_base_listener wm_base_listener = {
738 	xdg_wm_base_ping,
739 };
740 
741 static void
registry_handle_global(void * data,struct wl_registry * registry,uint32_t name,const char * interface,uint32_t version)742 registry_handle_global(void *data, struct wl_registry *registry,
743 		       uint32_t name, const char *interface, uint32_t version)
744 {
745 	struct display *d = data;
746 
747 	if (strcmp(interface, "wl_compositor") == 0) {
748 		d->compositor =
749 			wl_registry_bind(registry, name,
750 					 &wl_compositor_interface,
751 					 MIN(version, 4));
752 	} else if (strcmp(interface, "xdg_wm_base") == 0) {
753 		d->wm_base = wl_registry_bind(registry, name,
754 					      &xdg_wm_base_interface, 1);
755 		xdg_wm_base_add_listener(d->wm_base, &wm_base_listener, d);
756 	} else if (strcmp(interface, "wl_seat") == 0) {
757 		d->seat = wl_registry_bind(registry, name,
758 					   &wl_seat_interface, 1);
759 		wl_seat_add_listener(d->seat, &seat_listener, d);
760 	} else if (strcmp(interface, "wl_shm") == 0) {
761 		d->shm = wl_registry_bind(registry, name,
762 					  &wl_shm_interface, 1);
763 		d->cursor_theme = wl_cursor_theme_load(NULL, 32, d->shm);
764 		if (!d->cursor_theme) {
765 			fprintf(stderr, "unable to load default theme\n");
766 			return;
767 		}
768 		d->default_cursor =
769 			wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
770 		if (!d->default_cursor) {
771 			fprintf(stderr, "unable to load default left pointer\n");
772 			// TODO: abort ?
773 		}
774 	}
775 }
776 
777 static void
registry_handle_global_remove(void * data,struct wl_registry * registry,uint32_t name)778 registry_handle_global_remove(void *data, struct wl_registry *registry,
779 			      uint32_t name)
780 {
781 }
782 
783 static const struct wl_registry_listener registry_listener = {
784 	registry_handle_global,
785 	registry_handle_global_remove
786 };
787 
788 static void
signal_int(int signum)789 signal_int(int signum)
790 {
791 	running = 0;
792 }
793 
794 static void
usage(int error_code)795 usage(int error_code)
796 {
797 	fprintf(stderr, "Usage: simple-egl [OPTIONS]\n\n"
798 		"  -d <us>\tBuffer swap delay in microseconds\n"
799 		"  -f\tRun in fullscreen mode\n"
800 		"  -o\tCreate an opaque surface\n"
801 		"  -s\tUse a 16 bpp EGL config\n"
802 		"  -b\tDon't sync to compositor redraw (eglSwapInterval 0)\n"
803 		"  -h\tThis help text\n\n");
804 
805 	exit(error_code);
806 }
807 
808 int
main(int argc,char ** argv)809 main(int argc, char **argv)
810 {
811 	struct sigaction sigint;
812 	struct display display = { 0 };
813 	struct window  window  = { 0 };
814 	int i, ret = 0;
815 
816 	window.display = &display;
817 	display.window = &window;
818 	window.geometry.width  = 250;
819 	window.geometry.height = 250;
820 	window.window_size = window.geometry;
821 	window.buffer_size = 32;
822 	window.frame_sync = 1;
823 	window.delay = 0;
824 
825 	for (i = 1; i < argc; i++) {
826 		if (strcmp("-d", argv[i]) == 0 && i+1 < argc)
827 			window.delay = atoi(argv[++i]);
828 		else if (strcmp("-f", argv[i]) == 0)
829 			window.fullscreen = 1;
830 		else if (strcmp("-o", argv[i]) == 0)
831 			window.opaque = 1;
832 		else if (strcmp("-s", argv[i]) == 0)
833 			window.buffer_size = 16;
834 		else if (strcmp("-b", argv[i]) == 0)
835 			window.frame_sync = 0;
836 		else if (strcmp("-h", argv[i]) == 0)
837 			usage(EXIT_SUCCESS);
838 		else
839 			usage(EXIT_FAILURE);
840 	}
841 
842 	display.display = wl_display_connect(NULL);
843 	assert(display.display);
844 
845 	display.registry = wl_display_get_registry(display.display);
846 	wl_registry_add_listener(display.registry,
847 				 &registry_listener, &display);
848 
849 	wl_display_roundtrip(display.display);
850 
851 	init_egl(&display, &window);
852 	create_surface(&window);
853 	init_gl(&window);
854 
855 	display.cursor_surface =
856 		wl_compositor_create_surface(display.compositor);
857 
858 	sigint.sa_handler = signal_int;
859 	sigemptyset(&sigint.sa_mask);
860 	sigint.sa_flags = SA_RESETHAND;
861 	sigaction(SIGINT, &sigint, NULL);
862 
863 	/* The mainloop here is a little subtle.  Redrawing will cause
864 	 * EGL to read events so we can just call
865 	 * wl_display_dispatch_pending() to handle any events that got
866 	 * queued up as a side effect. */
867 	while (running && ret != -1) {
868 		if (window.wait_for_configure) {
869 			wl_display_dispatch(display.display);
870 		} else {
871 			wl_display_dispatch_pending(display.display);
872 			redraw(&window, NULL, 0);
873 		}
874 	}
875 
876 	fprintf(stderr, "simple-egl exiting\n");
877 
878 	destroy_surface(&window);
879 	fini_egl(&display);
880 
881 	wl_surface_destroy(display.cursor_surface);
882 	if (display.cursor_theme)
883 		wl_cursor_theme_destroy(display.cursor_theme);
884 
885 	if (display.wm_base)
886 		xdg_wm_base_destroy(display.wm_base);
887 
888 	if (display.compositor)
889 		wl_compositor_destroy(display.compositor);
890 
891 	wl_registry_destroy(display.registry);
892 	wl_display_flush(display.display);
893 	wl_display_disconnect(display.display);
894 
895 	return 0;
896 }
897