blob: 9c019fbb4b96d217f8d275f40244e13889c793b6 [file] [log] [blame]
Ander Conselvan de Oliveira1b4312d2012-11-27 17:03:44 +02001/*
2 * Copyright © 2011 Benjamin Franzke
3 * Copyright © 2012 Intel Corporation
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no representations
12 * about the suitability of this software for any purpose. It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <stdbool.h>
28#include <math.h>
29#include <assert.h>
30#include <signal.h>
31
32#include <linux/input.h>
33
34#include <wayland-client.h>
35#include <wayland-egl.h>
36
37#include <GLES2/gl2.h>
38#include <EGL/egl.h>
39
40struct window;
41struct seat;
42
43struct display {
44 struct wl_display *display;
45 struct wl_registry *registry;
46 struct wl_compositor *compositor;
47 struct wl_shell *shell;
48 struct wl_seat *seat;
49 struct wl_pointer *pointer;
50 struct wl_keyboard *keyboard;
51 struct wl_shm *shm;
52 struct wl_list output_list;
53 struct {
54 EGLDisplay dpy;
55 EGLContext ctx;
56 EGLConfig conf;
57 } egl;
58 struct window *window;
59};
60
61struct output {
62 struct wl_output *output;
63 int transform;
64 struct wl_list link;
65};
66
67struct geometry {
68 int width, height;
69};
70
71struct window {
72 struct display *display;
73 struct geometry geometry, window_size;
74 struct {
75 GLuint fbo;
76 GLuint color_rbo;
77
78 GLuint transform_uniform;
79
80 GLuint pos;
81 GLuint col;
82 } gl;
83
84 struct output *output;
85 struct wl_egl_window *native;
86 struct wl_surface *surface;
87 struct wl_shell_surface *shell_surface;
88 EGLSurface egl_surface;
89 struct wl_callback *callback;
90 int fullscreen, configured;
91};
92
93static const char *vert_shader_text =
94 "uniform mat4 transform;\n"
95 "attribute vec4 pos;\n"
96 "attribute vec4 color;\n"
97 "varying vec4 v_color;\n"
98 "void main() {\n"
99 " gl_Position = transform * pos;\n"
100 " v_color = color;\n"
101 "}\n";
102
103static const char *frag_shader_text =
104 "precision mediump float;\n"
105 "varying vec4 v_color;\n"
106 "void main() {\n"
107 " gl_FragColor = v_color;\n"
108 "}\n";
109
110static int running = 1;
111
112static void
113init_egl(struct display *display)
114{
115 static const EGLint context_attribs[] = {
116 EGL_CONTEXT_CLIENT_VERSION, 2,
117 EGL_NONE
118 };
119
120 EGLint config_attribs[] = {
121 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
122 EGL_RED_SIZE, 1,
123 EGL_GREEN_SIZE, 1,
124 EGL_BLUE_SIZE, 1,
125 EGL_ALPHA_SIZE, 0,
126 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
127 EGL_NONE
128 };
129
130 EGLint major, minor, n;
131 EGLBoolean ret;
132
133 display->egl.dpy = eglGetDisplay(display->display);
134 assert(display->egl.dpy);
135
136 ret = eglInitialize(display->egl.dpy, &major, &minor);
137 assert(ret == EGL_TRUE);
138 ret = eglBindAPI(EGL_OPENGL_ES_API);
139 assert(ret == EGL_TRUE);
140
141 ret = eglChooseConfig(display->egl.dpy, config_attribs,
142 &display->egl.conf, 1, &n);
143 assert(ret && n == 1);
144
145 display->egl.ctx = eglCreateContext(display->egl.dpy,
146 display->egl.conf,
147 EGL_NO_CONTEXT, context_attribs);
148 assert(display->egl.ctx);
149
150}
151
152static void
153fini_egl(struct display *display)
154{
155 /* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
156 * on eglReleaseThread(). */
157 eglMakeCurrent(display->egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
158 EGL_NO_CONTEXT);
159
160 eglTerminate(display->egl.dpy);
161 eglReleaseThread();
162}
163
164static GLuint
165create_shader(struct window *window, const char *source, GLenum shader_type)
166{
167 GLuint shader;
168 GLint status;
169
170 shader = glCreateShader(shader_type);
171 assert(shader != 0);
172
173 glShaderSource(shader, 1, (const char **) &source, NULL);
174 glCompileShader(shader);
175
176 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
177 if (!status) {
178 char log[1000];
179 GLsizei len;
180 glGetShaderInfoLog(shader, 1000, &len, log);
181 fprintf(stderr, "Error: compiling %s: %*s\n",
182 shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
183 len, log);
184 exit(1);
185 }
186
187 return shader;
188}
189
190static void
191init_gl(struct window *window)
192{
193 GLuint frag, vert;
194 GLuint program;
195 GLint status;
196
197 frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER);
198 vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER);
199
200 program = glCreateProgram();
201 glAttachShader(program, frag);
202 glAttachShader(program, vert);
203 glLinkProgram(program);
204
205 glGetProgramiv(program, GL_LINK_STATUS, &status);
206 if (!status) {
207 char log[1000];
208 GLsizei len;
209 glGetProgramInfoLog(program, 1000, &len, log);
210 fprintf(stderr, "Error: linking:\n%*s\n", len, log);
211 exit(1);
212 }
213
214 glUseProgram(program);
215
216 window->gl.pos = 0;
217 window->gl.col = 1;
218
219 glBindAttribLocation(program, window->gl.pos, "pos");
220 glBindAttribLocation(program, window->gl.col, "color");
221 glLinkProgram(program);
222
223 window->gl.transform_uniform =
224 glGetUniformLocation(program, "transform");
225}
226
227static void
228handle_ping(void *data, struct wl_shell_surface *shell_surface,
229 uint32_t serial)
230{
231 wl_shell_surface_pong(shell_surface, serial);
232}
233
234static void
235handle_configure(void *data, struct wl_shell_surface *shell_surface,
236 uint32_t edges, int32_t width, int32_t height)
237{
238 struct window *window = data;
239 int transform = WL_OUTPUT_TRANSFORM_NORMAL;
240 int32_t tmp;
241
242 window->geometry.width = width;
243 window->geometry.height = height;
244
245 if (!window->fullscreen)
246 window->window_size = window->geometry;
247
248 if (window->output)
249 transform = window->output->transform;
250
251 switch (transform) {
252 case WL_OUTPUT_TRANSFORM_90:
253 case WL_OUTPUT_TRANSFORM_270:
254 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
255 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
256 tmp = width;
257 width = height;
258 height = tmp;
259 break;
260 default:
261 break;
262 }
263
264 if (window->native)
265 wl_egl_window_resize(window->native, width, height, 0, 0);
266}
267
268static void
269handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
270{
271}
272
273static const struct wl_shell_surface_listener shell_surface_listener = {
274 handle_ping,
275 handle_configure,
276 handle_popup_done
277};
278
279static void
280toggle_fullscreen(struct window *window, int fullscreen);
281
282static void
283surface_enter(void *data, struct wl_surface *wl_surface,
284 struct wl_output *wl_output)
285{
286 struct window *window = data;
287 struct output *output = NULL;
288
289 wl_list_for_each(output, &window->display->output_list, link) {
290 if (output->output == wl_output)
291 break;
292 }
293
294 if (output && output->output == wl_output) {
295 /* We use the output information only for rendering according
296 * to the output transform. Since we can't "please" two
297 * different outputs if they have a different transform, just
298 * do it according to the last output the surface entered. */
299 window->output = output;
300
301 /* Force a redraw with the new transform */
302 toggle_fullscreen(window, window->fullscreen);
303 }
304}
305
306static void
307surface_leave(void *data, struct wl_surface *wl_surface,
308 struct wl_output *wl_output)
309{
310 struct window *window = data;
311 struct output *output = NULL;
312
313 wl_list_for_each(output, &window->display->output_list, link) {
314 if (output->output == wl_output)
315 break;
316 }
317
318 if (output && output->output == wl_output && output == window->output)
319 window->output = NULL;
320}
321
322static const struct wl_surface_listener surface_listener = {
323 surface_enter,
324 surface_leave
325};
326
327static void
328redraw(struct window *window);
329
330static void
331configure_callback(void *data, struct wl_callback *callback, uint32_t time)
332{
333 struct window *window = data;
334
335 wl_callback_destroy(callback);
336
337 redraw(window);
338}
339
340static struct wl_callback_listener configure_callback_listener = {
341 configure_callback,
342};
343
344static void
345toggle_fullscreen(struct window *window, int fullscreen)
346{
347 struct wl_callback *callback;
348
349 window->fullscreen = fullscreen;
350
351 if (fullscreen) {
352 wl_shell_surface_set_fullscreen(window->shell_surface,
353 WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
354 0, NULL);
355 } else {
356 wl_shell_surface_set_toplevel(window->shell_surface);
357 handle_configure(window, window->shell_surface, 0,
358 window->window_size.width,
359 window->window_size.height);
360 }
361
362 callback = wl_display_sync(window->display->display);
363 wl_callback_add_listener(callback, &configure_callback_listener,
364 window);
365}
366
367static void
368create_surface(struct window *window)
369{
370 struct display *display = window->display;
371 EGLBoolean ret;
372
373 window->surface = wl_compositor_create_surface(display->compositor);
374 window->shell_surface = wl_shell_get_shell_surface(display->shell,
375 window->surface);
376
377 wl_surface_add_listener(window->surface, &surface_listener, window);
378 wl_shell_surface_add_listener(window->shell_surface,
379 &shell_surface_listener, window);
380
381 window->native =
382 wl_egl_window_create(window->surface,
383 window->window_size.width,
384 window->window_size.height);
385 window->egl_surface =
386 eglCreateWindowSurface(display->egl.dpy,
387 display->egl.conf,
388 window->native, NULL);
389
390 wl_shell_surface_set_title(window->shell_surface, "simple-egl");
391
392 ret = eglMakeCurrent(window->display->egl.dpy, window->egl_surface,
393 window->egl_surface, window->display->egl.ctx);
394 assert(ret == EGL_TRUE);
395
396 toggle_fullscreen(window, window->fullscreen);
397}
398
399static void
400destroy_surface(struct window *window)
401{
402 wl_egl_window_destroy(window->native);
403
404 wl_shell_surface_destroy(window->shell_surface);
405 wl_surface_destroy(window->surface);
406
407 if (window->callback)
408 wl_callback_destroy(window->callback);
409}
410
411static void
412update_transform(struct window *window)
413{
414 int transform = WL_OUTPUT_TRANSFORM_NORMAL;
415 int swap_w_h = 0;
416 int flip;
417
418 GLfloat mat[4][4] = {
419 { 1, 0, 0, 0 },
420 { 0, 1, 0, 0 },
421 { 0, 0, 1, 0 },
422 { 0, 0, 0, 1 }
423 };
424
425
426 if (window->output)
427 transform = window->output->transform;
428
429 switch(transform) {
430 case WL_OUTPUT_TRANSFORM_FLIPPED:
431 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
432 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
433 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
434 flip = -1;
435 break;
436 default:
437 flip = 1;
438 break;
439 }
440
441 switch (transform) {
442 case WL_OUTPUT_TRANSFORM_NORMAL:
443 case WL_OUTPUT_TRANSFORM_FLIPPED:
444 mat[0][0] = flip;
445 mat[0][1] = 0;
446 mat[1][0] = 0;
447 mat[1][1] = 1;
448 break;
449 case WL_OUTPUT_TRANSFORM_90:
450 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
451 mat[0][0] = 0;
452 mat[0][1] = -flip;
453 mat[1][0] = 1;
454 mat[1][1] = 0;
455 swap_w_h = 1;
456 break;
457 case WL_OUTPUT_TRANSFORM_180:
458 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
459 mat[0][0] = -flip;
460 mat[0][1] = 0;
461 mat[1][0] = 0;
462 mat[1][1] = -1;
463 break;
464 case WL_OUTPUT_TRANSFORM_270:
465 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
466 mat[0][0] = 0;
467 mat[0][1] = flip;
468 mat[1][0] = -1;
469 mat[1][1] = 0;
470 swap_w_h = 1;
471 break;
472 default:
473 break;
474 }
475
476 if (swap_w_h)
477 glViewport(0, 0, window->geometry.height,
478 window->geometry.width);
479 else
480 glViewport(0, 0, window->geometry.width,
481 window->geometry.height);
482
483 glUniformMatrix4fv(window->gl.transform_uniform, 1, GL_FALSE,
484 (GLfloat *) mat);
485 wl_surface_set_buffer_transform(window->surface, transform);
486 printf("Rendering buffer with transform == %d\n", transform);
487}
488
489static void
490redraw(struct window *window)
491{
492 static const GLfloat verts[8][2] = {
493 { 0.0, 0.0 },
494 { 0.0, 1.0 },
495 { 0.0, 0.0 },
496 { 1.0, 0.0 },
497 { 0.0, 0.0 },
498 { -1.0, 0.0 },
499 { 0.0, 0.0 },
500 { 0.0, -1.0 }
501 };
502 static const GLfloat colors[8][3] = {
503 { 1, 0, 0 },
504 { 1, 0, 0 },
505 { 0, 1, 0 },
506 { 0, 1, 0 },
507 { 1, 1, 1 },
508 { 1, 1, 1 },
509 { 1, 1, 1 },
510 { 1, 1, 1 }
511 };
512 struct wl_region *region;
513
514 update_transform(window);
515
516 glClearColor(0.0, 0.0, 0.0, 1.0);
517 glClear(GL_COLOR_BUFFER_BIT);
518
519 glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
520 glVertexAttribPointer(window->gl.col, 3, GL_FLOAT, GL_FALSE, 0, colors);
521 glEnableVertexAttribArray(window->gl.pos);
522 glEnableVertexAttribArray(window->gl.col);
523
524 glDrawArrays(GL_LINES, 0, 8);
525
526 glDisableVertexAttribArray(window->gl.pos);
527 glDisableVertexAttribArray(window->gl.col);
528
529 region = wl_compositor_create_region(window->display->compositor);
530 wl_region_add(region, 0, 0,
531 window->geometry.width,
532 window->geometry.height);
533 wl_surface_set_opaque_region(window->surface, region);
534 wl_region_destroy(region);
535
536 eglSwapBuffers(window->display->egl.dpy, window->egl_surface);
537}
538
539static void
540pointer_handle_enter(void *data, struct wl_pointer *pointer,
541 uint32_t serial, struct wl_surface *surface,
542 wl_fixed_t sx, wl_fixed_t sy)
543{
544 wl_pointer_set_cursor(pointer, serial, NULL, 0, 0);
545}
546
547static void
548pointer_handle_leave(void *data, struct wl_pointer *pointer,
549 uint32_t serial, struct wl_surface *surface)
550{
551}
552
553static void
554pointer_handle_motion(void *data, struct wl_pointer *pointer,
555 uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
556{
557}
558
559static void
560pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
561 uint32_t serial, uint32_t time, uint32_t button,
562 uint32_t state)
563{
564}
565
566static void
567pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
568 uint32_t time, uint32_t axis, wl_fixed_t value)
569{
570}
571
572static const struct wl_pointer_listener pointer_listener = {
573 pointer_handle_enter,
574 pointer_handle_leave,
575 pointer_handle_motion,
576 pointer_handle_button,
577 pointer_handle_axis,
578};
579
580static void
581keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
582 uint32_t format, int fd, uint32_t size)
583{
584}
585
586static void
587keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
588 uint32_t serial, struct wl_surface *surface,
589 struct wl_array *keys)
590{
591}
592
593static void
594keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
595 uint32_t serial, struct wl_surface *surface)
596{
597}
598
599static void
600keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
601 uint32_t serial, uint32_t time, uint32_t key,
602 uint32_t state)
603{
604 struct display *d = data;
605
606 if (key == KEY_F11 && state)
607 toggle_fullscreen(d->window, d->window->fullscreen ^ 1);
608 else if (key == KEY_ESC && state)
609 running = 0;
610}
611
612static void
613keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
614 uint32_t serial, uint32_t mods_depressed,
615 uint32_t mods_latched, uint32_t mods_locked,
616 uint32_t group)
617{
618}
619
620static const struct wl_keyboard_listener keyboard_listener = {
621 keyboard_handle_keymap,
622 keyboard_handle_enter,
623 keyboard_handle_leave,
624 keyboard_handle_key,
625 keyboard_handle_modifiers,
626};
627
628static void
629seat_handle_capabilities(void *data, struct wl_seat *seat,
630 enum wl_seat_capability caps)
631{
632 struct display *d = data;
633
634 if ((caps & WL_SEAT_CAPABILITY_POINTER) && !d->pointer) {
635 d->pointer = wl_seat_get_pointer(seat);
636 wl_pointer_add_listener(d->pointer, &pointer_listener, d);
637 } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && d->pointer) {
638 wl_pointer_destroy(d->pointer);
639 d->pointer = NULL;
640 }
641
642 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !d->keyboard) {
643 d->keyboard = wl_seat_get_keyboard(seat);
644 wl_keyboard_add_listener(d->keyboard, &keyboard_listener, d);
645 } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && d->keyboard) {
646 wl_keyboard_destroy(d->keyboard);
647 d->keyboard = NULL;
648 }
649}
650
651static const struct wl_seat_listener seat_listener = {
652 seat_handle_capabilities,
653};
654
655static void
656output_handle_geometry(void *data, struct wl_output *wl_output, int x, int y,
657 int physical_width, int physical_height, int subpixel,
658 const char *make, const char *model, int transform)
659{
660 struct output *output = data;
661
662 output->transform = transform;
663}
664
665static void
666output_handle_mode(void *data, struct wl_output *output, uint32_t flags,
667 int width, int height, int refresh)
668{
669}
670
671static const struct wl_output_listener output_listener = {
672 output_handle_geometry,
673 output_handle_mode
674};
675
676static void
677registry_handle_global(void *data, struct wl_registry *registry,
678 uint32_t name, const char *interface, uint32_t version)
679{
680 struct display *d = data;
681
682 if (strcmp(interface, "wl_compositor") == 0) {
683 d->compositor =
684 wl_registry_bind(registry, name,
685 &wl_compositor_interface, 1);
686 } else if (strcmp(interface, "wl_shell") == 0) {
687 d->shell = wl_registry_bind(registry, name,
688 &wl_shell_interface, 1);
689 } else if (strcmp(interface, "wl_seat") == 0) {
690 d->seat = wl_registry_bind(registry, name,
691 &wl_seat_interface, 1);
692 wl_seat_add_listener(d->seat, &seat_listener, d);
693 } else if (strcmp(interface, "wl_output") == 0) {
694 struct output *output = malloc(sizeof *output);
695
696 if (!output)
697 return;
698
699 output->output = wl_registry_bind(registry, name,
700 &wl_output_interface, 1);
701 wl_output_add_listener(output->output, &output_listener,
702 output);
703 wl_list_insert(&d->output_list, &output->link);
704 }
705}
706
707static const struct wl_registry_listener registry_listener = {
708 registry_handle_global
709};
710
711static void
712signal_int(int signum)
713{
714 running = 0;
715}
716
717static void
718usage(int error_code)
719{
720 fprintf(stderr, "Usage: simple-egl [OPTIONS]\n\n"
721 " -f\tRun in fullscreen mode\n"
722 " -h\tThis help text\n\n");
723
724 exit(error_code);
725}
726
727int
728main(int argc, char **argv)
729{
730 struct sigaction sigint;
731 struct display display = { 0 };
732 struct window window = { 0 };
733 int i, ret = 0;
734
735 window.display = &display;
736 display.window = &window;
737 window.window_size.width = 500;
738 window.window_size.height = 250;
739
740 wl_list_init(&display.output_list);
741
742 for (i = 1; i < argc; i++) {
743 if (strcmp("-f", argv[i]) == 0)
744 window.fullscreen = 1;
745 else if (strcmp("-h", argv[i]) == 0)
746 usage(EXIT_SUCCESS);
747 else
748 usage(EXIT_FAILURE);
749 }
750
751 display.display = wl_display_connect(NULL);
752 assert(display.display);
753
754 display.registry = wl_display_get_registry(display.display);
755 wl_registry_add_listener(display.registry,
756 &registry_listener, &display);
757
758 wl_display_dispatch(display.display);
759
760 init_egl(&display);
761 create_surface(&window);
762 init_gl(&window);
763
764 sigint.sa_handler = signal_int;
765 sigemptyset(&sigint.sa_mask);
766 sigint.sa_flags = SA_RESETHAND;
767 sigaction(SIGINT, &sigint, NULL);
768
769 while (running && ret != -1)
770 ret = wl_display_dispatch(display.display);
771
772 fprintf(stderr, "simple-egl exiting\n");
773
774 destroy_surface(&window);
775 fini_egl(&display);
776
777 if (display.shell)
778 wl_shell_destroy(display.shell);
779
780 if (display.compositor)
781 wl_compositor_destroy(display.compositor);
782
783 wl_display_flush(display.display);
784 wl_display_disconnect(display.display);
785
786 return 0;
787}