├── wayland-compositor ├── README.md ├── makefile ├── backend.h ├── texture.h ├── backend-x11.c └── wayland-compositor.c ├── http-server.c ├── LICENSE ├── pulseaudio.c ├── libinput.c ├── wayland-shm.c ├── wayland-egl.c ├── drm-gbm.c └── wayland-input.c /wayland-compositor/README.md: -------------------------------------------------------------------------------- 1 | minimal wayland compositor 2 | -------------------------- 3 | 4 | compile with the following command: 5 | 6 | make 7 | -------------------------------------------------------------------------------- /wayland-compositor/makefile: -------------------------------------------------------------------------------- 1 | XDG_SHELL_PROTOCOL = $(shell pkg-config --variable=pkgdatadir wayland-protocols)/stable/xdg-shell/xdg-shell.xml 2 | 3 | wayland-compositor: wayland-compositor.c backend-x11.c xdg-shell.c backend.h texture.h xdg-shell.h 4 | gcc -o $@ wayland-compositor.c backend-x11.c xdg-shell.c -lwayland-server -lX11 -lEGL -lGL -lX11-xcb -lxkbcommon-x11 -lxkbcommon 5 | 6 | xdg-shell.c: $(XDG_SHELL_PROTOCOL) 7 | wayland-scanner private-code $< $@ 8 | 9 | xdg-shell.h: $(XDG_SHELL_PROTOCOL) 10 | wayland-scanner server-header $< $@ 11 | -------------------------------------------------------------------------------- /wayland-compositor/backend.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct modifier_state { 4 | uint32_t depressed, latched, locked, group; 5 | }; 6 | 7 | struct callbacks { 8 | void (*resize) (int width, int height); 9 | void (*draw) (void); 10 | void (*mouse_motion) (int x, int y); 11 | void (*mouse_button) (int button, int state); 12 | void (*key) (int key, int state); 13 | void (*modifiers) (struct modifier_state modifier_state); 14 | }; 15 | 16 | void backend_init (struct callbacks *callbacks); 17 | EGLDisplay backend_get_egl_display (void); 18 | void backend_swap_buffers (void); 19 | void backend_dispatch_nonblocking (void); 20 | void backend_wait_for_events (int wayland_fd); 21 | void backend_get_keymap (int *fd, int *size); 22 | long backend_get_timestamp (void); 23 | -------------------------------------------------------------------------------- /http-server.c: -------------------------------------------------------------------------------- 1 | // gcc -o http-server http-server.c 2 | 3 | // documentation: man 7 ip 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main () { 13 | int sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 14 | struct sockaddr_in addr; 15 | addr.sin_family = AF_INET; 16 | addr.sin_port = htons (8080); 17 | addr.sin_addr.s_addr = htonl (INADDR_ANY); 18 | bind (sock, (struct sockaddr*)&addr, sizeof(addr)); 19 | listen (sock, 1); 20 | while (1) { 21 | int fd = accept (sock, NULL, NULL); 22 | char buf[1024]; 23 | ssize_t size = read (fd, buf, 1024); 24 | fwrite (buf, 1, size, stdout); 25 | fputc ('\n', stdout); 26 | const char *response = "HTTP/1.1 200 OK\r\n\r\n

Hello, World!

"; 27 | write (fd, response, strlen (response)); 28 | close (fd); 29 | } 30 | close (sock); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. 4 | 5 | In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | For more information, please refer to 10 | -------------------------------------------------------------------------------- /pulseaudio.c: -------------------------------------------------------------------------------- 1 | // gcc -o pulseaudio pulseaudio.c -lpulse 2 | 3 | #include 4 | 5 | static void write_callback (pa_stream *stream, size_t nbytes, void *userdata) { 6 | void *_data = NULL; 7 | pa_stream_begin_write (stream, &_data, &nbytes); 8 | float *data = _data; 9 | for (size_t t = 0; t < nbytes/sizeof(float); ++t) { 10 | // a simple saw wave 11 | static float y = -1.f; 12 | data[t] = y * .2f; 13 | y += 220.f / 44100.f * 2.f; 14 | if (y > 1.f) y -= 2.f; 15 | } 16 | pa_stream_write (stream, data, nbytes, NULL, 0, PA_SEEK_RELATIVE); 17 | } 18 | 19 | static void state_callback (pa_context *context, void *userdata) { 20 | if (pa_context_get_state(context) == PA_CONTEXT_READY) { 21 | pa_sample_spec sample_spec = { 22 | PA_SAMPLE_FLOAT32LE, 23 | 44100, 24 | 1 25 | }; 26 | pa_stream *stream = pa_stream_new (context, "tutorial", &sample_spec, NULL); 27 | pa_stream_set_write_callback (stream, write_callback, NULL); 28 | pa_stream_connect_playback (stream, NULL, NULL, PA_STREAM_NOFLAGS, NULL, NULL); 29 | } 30 | } 31 | 32 | int main () { 33 | pa_mainloop *mainloop = pa_mainloop_new (); 34 | pa_context *context = pa_context_new (pa_mainloop_get_api(mainloop), "tutorial"); 35 | pa_context_set_state_callback (context, state_callback, NULL); 36 | pa_context_connect (context, NULL, PA_CONTEXT_NOFLAGS, NULL); 37 | pa_mainloop_run (mainloop, NULL); 38 | pa_context_unref (context); 39 | pa_mainloop_free (mainloop); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /wayland-compositor/texture.h: -------------------------------------------------------------------------------- 1 | struct texture { 2 | GLuint identifier; 3 | int width, height; 4 | }; 5 | 6 | static void texture_create (struct texture *texture, int width, int height, void *data) { 7 | texture->width = width; 8 | texture->height = height; 9 | glGenTextures (1, &texture->identifier); 10 | glBindTexture (GL_TEXTURE_2D, texture->identifier); 11 | glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 12 | glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 13 | glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, texture->width, texture->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); 14 | glBindTexture (GL_TEXTURE_2D, 0); 15 | } 16 | 17 | static void texture_create_from_egl_image (struct texture *texture, int width, int height, EGLImage image) { 18 | texture->width = width; 19 | texture->height = height; 20 | glGenTextures (1, &texture->identifier); 21 | glBindTexture (GL_TEXTURE_2D, texture->identifier); 22 | glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 23 | glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 24 | glEGLImageTargetTexture2DOES (GL_TEXTURE_2D, image); 25 | glBindTexture (GL_TEXTURE_2D, 0); 26 | } 27 | 28 | static void texture_draw (struct texture *texture, int x, int y) { 29 | if (!texture->identifier) return; 30 | 31 | GLint vertices[] = { 32 | x, y, 33 | x+texture->width, y, 34 | x+texture->width, y+texture->height, 35 | x, y+texture->height 36 | }; 37 | GLint tex_coords[] = { 38 | 0, 0, 39 | 1, 0, 40 | 1, 1, 41 | 0, 1 42 | }; 43 | 44 | glEnable (GL_TEXTURE_2D); 45 | glEnableClientState (GL_VERTEX_ARRAY); 46 | glEnableClientState (GL_TEXTURE_COORD_ARRAY); 47 | glBindTexture (GL_TEXTURE_2D, texture->identifier); 48 | glVertexPointer (2, GL_INT, 0, vertices); 49 | glTexCoordPointer (2, GL_INT, 0, tex_coords); 50 | 51 | glDrawArrays (GL_QUADS, 0, 4); 52 | 53 | glBindTexture (GL_TEXTURE_2D, 0); 54 | glDisable (GL_TEXTURE_RECTANGLE); 55 | glDisableClientState (GL_VERTEX_ARRAY); 56 | glDisableClientState (GL_TEXTURE_COORD_ARRAY); 57 | } 58 | 59 | static void texture_delete (struct texture* texture) { 60 | if (texture->identifier) 61 | glDeleteTextures (1, &texture->identifier); 62 | } 63 | -------------------------------------------------------------------------------- /libinput.c: -------------------------------------------------------------------------------- 1 | // gcc -o libinput libinput.c -linput -ludev -lxkbcommon 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define EXIT(msg) { fputs (msg, stderr); exit (EXIT_FAILURE); } 11 | 12 | static struct xkb_context *xkb_context; 13 | static struct xkb_keymap *keymap = NULL; 14 | static struct xkb_state *xkb_state = NULL; 15 | static double x = 0, y = 0; 16 | 17 | static void process_event (struct libinput_event *event) { 18 | int type = libinput_event_get_type (event); 19 | if (type == LIBINPUT_EVENT_POINTER_MOTION) { 20 | struct libinput_event_pointer *pointer_event = libinput_event_get_pointer_event (event); 21 | x += libinput_event_pointer_get_dx (pointer_event); 22 | y += libinput_event_pointer_get_dy (pointer_event); 23 | printf ("pointer motion event: %d %d\n", (int)x, (int)y); 24 | } 25 | else if (type == LIBINPUT_EVENT_POINTER_BUTTON) { 26 | struct libinput_event_pointer *pointer_event = libinput_event_get_pointer_event (event); 27 | printf ("pointer button event\n"); 28 | } 29 | else if (type == LIBINPUT_EVENT_KEYBOARD_KEY) { 30 | struct libinput_event_keyboard *keyboard_event = libinput_event_get_keyboard_event (event); 31 | uint32_t key = libinput_event_keyboard_get_key (keyboard_event); 32 | int state = libinput_event_keyboard_get_key_state (keyboard_event); 33 | xkb_state_update_key (xkb_state, key+8, state); 34 | if (state == LIBINPUT_KEY_STATE_PRESSED) { 35 | uint32_t utf32 = xkb_state_key_get_utf32 (xkb_state, key+8); 36 | if (utf32) { 37 | if (utf32 >= 0x21 && utf32 <= 0x7E) { 38 | printf ("the key %c was pressed\n", (char)utf32); 39 | } 40 | else { 41 | printf ("the key U+%04X was pressed\n", utf32); 42 | } 43 | } 44 | } 45 | } 46 | libinput_event_destroy (event); 47 | } 48 | 49 | static int open_restricted (const char *path, int flags, void *user_data) { 50 | return open (path, flags); 51 | } 52 | static void close_restricted (int fd, void *user_data) { 53 | close (fd); 54 | } 55 | static struct libinput_interface interface = {&open_restricted, &close_restricted}; 56 | 57 | int main () { 58 | struct udev *udev = udev_new (); 59 | if (!udev) EXIT ("udev error\n"); 60 | struct libinput *libinput = libinput_udev_create_context (&interface, NULL, udev); 61 | if (!libinput) EXIT ("libinput error\n"); 62 | if (libinput_udev_assign_seat (libinput, "seat0") == -1) 63 | EXIT ("cannot assign seat\n"); 64 | 65 | xkb_context = xkb_context_new (XKB_CONTEXT_NO_FLAGS); 66 | keymap = xkb_keymap_new_from_names (xkb_context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); 67 | if (!keymap) EXIT ("keymap error\n"); 68 | xkb_state = xkb_state_new (keymap); 69 | 70 | while (1) { 71 | struct pollfd fd = {libinput_get_fd(libinput), POLLIN, 0}; 72 | poll (&fd, 1, -1); 73 | libinput_dispatch (libinput); 74 | struct libinput_event *event; 75 | while (event = libinput_get_event(libinput)) { 76 | process_event (event); 77 | } 78 | } 79 | 80 | libinput_unref (libinput); 81 | udev_unref (udev); 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /wayland-shm.c: -------------------------------------------------------------------------------- 1 | // gcc -o wayland-shm wayland-shm.c -lwayland-client 2 | 3 | #define _GNU_SOURCE // for O_TMPFILE 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define WIDTH 256 12 | #define HEIGHT 256 13 | 14 | static struct wl_display *display; 15 | static struct wl_compositor *compositor = NULL; 16 | static struct wl_shell *shell = NULL; 17 | static struct wl_shm *wl_shm = NULL; 18 | static char running = 1; 19 | 20 | struct window { 21 | int32_t width, height; 22 | struct wl_surface *surface; 23 | struct wl_shell_surface *shell_surface; 24 | struct wl_buffer *front_buffer; 25 | int32_t *data; 26 | }; 27 | 28 | // listeners 29 | static void registry_add_object (void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { 30 | if (!strcmp(interface,"wl_compositor")) { 31 | compositor = wl_registry_bind (registry, name, &wl_compositor_interface, 1); 32 | } 33 | else if (!strcmp(interface,"wl_shell")) { 34 | shell = wl_registry_bind (registry, name, &wl_shell_interface, 1); 35 | } 36 | else if (!strcmp(interface,"wl_shm")) { 37 | wl_shm = wl_registry_bind (registry, name, &wl_shm_interface, 1); 38 | } 39 | } 40 | static void registry_remove_object (void *data, struct wl_registry *registry, uint32_t name) { 41 | 42 | } 43 | static struct wl_registry_listener registry_listener = {®istry_add_object, ®istry_remove_object}; 44 | 45 | static void shell_surface_ping (void *data, struct wl_shell_surface *shell_surface, uint32_t serial) { 46 | wl_shell_surface_pong (shell_surface, serial); 47 | } 48 | static void shell_surface_configure (void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height) { 49 | 50 | } 51 | static void shell_surface_popup_done (void *data, struct wl_shell_surface *shell_surface) { 52 | 53 | } 54 | static struct wl_shell_surface_listener shell_surface_listener = {&shell_surface_ping, &shell_surface_configure, &shell_surface_popup_done}; 55 | 56 | static void create_window (struct window *window, int32_t width, int32_t height) { 57 | window->width = width; 58 | window->height = height; 59 | window->surface = wl_compositor_create_surface (compositor); 60 | window->shell_surface = wl_shell_get_shell_surface (shell, window->surface); 61 | wl_shell_surface_add_listener (window->shell_surface, &shell_surface_listener, NULL); 62 | wl_shell_surface_set_toplevel (window->shell_surface); 63 | 64 | size_t size = width * height * 4; 65 | char *xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR"); 66 | int fd = open (xdg_runtime_dir, O_TMPFILE|O_RDWR|O_EXCL, 0600); 67 | ftruncate (fd, size); 68 | window->data = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 69 | struct wl_shm_pool *pool = wl_shm_create_pool (wl_shm, fd, size); 70 | window->front_buffer = wl_shm_pool_create_buffer (pool, 0, width, height, width*4, WL_SHM_FORMAT_XRGB8888); 71 | wl_shm_pool_destroy (pool); 72 | close (fd); 73 | } 74 | static void delete_window (struct window *window) { 75 | wl_shell_surface_destroy (window->shell_surface); 76 | wl_surface_destroy (window->surface); 77 | } 78 | static void draw_window (struct window *window) { 79 | int x, y; 80 | for (y = 0; y < window->height; y++) { 81 | for (x = 0; x < window->width; x++) { 82 | int index = y * window->width + x; 83 | window->data[index] = 0xFF00FF00; // XRGB format -> green 84 | } 85 | } 86 | wl_surface_attach (window->surface, window->front_buffer, 0, 0); 87 | //wl_surface_damage (window->surface, 0, 0, window->width, window->height); 88 | wl_surface_commit (window->surface); 89 | } 90 | 91 | int main () { 92 | display = wl_display_connect (NULL); 93 | struct wl_registry *registry = wl_display_get_registry (display); 94 | wl_registry_add_listener (registry, ®istry_listener, NULL); 95 | wl_display_roundtrip (display); 96 | 97 | struct window window; 98 | create_window (&window, WIDTH, HEIGHT); 99 | 100 | draw_window (&window); 101 | while (running) 102 | wl_display_dispatch (display); 103 | 104 | delete_window (&window); 105 | wl_display_disconnect (display); 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /wayland-egl.c: -------------------------------------------------------------------------------- 1 | // gcc -o wayland-egl wayland-egl.c -lwayland-client -lwayland-egl -lEGL -lGL 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define WIDTH 256 10 | #define HEIGHT 256 11 | 12 | static struct wl_display *display; 13 | static struct wl_compositor *compositor = NULL; 14 | static struct wl_shell *shell = NULL; 15 | static EGLDisplay egl_display; 16 | static char running = 1; 17 | 18 | struct window { 19 | EGLContext egl_context; 20 | struct wl_surface *surface; 21 | struct wl_shell_surface *shell_surface; 22 | struct wl_egl_window *egl_window; 23 | EGLSurface egl_surface; 24 | }; 25 | 26 | // listeners 27 | static void registry_add_object (void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { 28 | if (!strcmp(interface,"wl_compositor")) { 29 | compositor = wl_registry_bind (registry, name, &wl_compositor_interface, 1); 30 | } 31 | else if (!strcmp(interface,"wl_shell")) { 32 | shell = wl_registry_bind (registry, name, &wl_shell_interface, 1); 33 | } 34 | } 35 | static void registry_remove_object (void *data, struct wl_registry *registry, uint32_t name) { 36 | 37 | } 38 | static struct wl_registry_listener registry_listener = {®istry_add_object, ®istry_remove_object}; 39 | 40 | static void shell_surface_ping (void *data, struct wl_shell_surface *shell_surface, uint32_t serial) { 41 | wl_shell_surface_pong (shell_surface, serial); 42 | } 43 | static void shell_surface_configure (void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height) { 44 | struct window *window = data; 45 | wl_egl_window_resize (window->egl_window, width, height, 0, 0); 46 | } 47 | static void shell_surface_popup_done (void *data, struct wl_shell_surface *shell_surface) { 48 | 49 | } 50 | static struct wl_shell_surface_listener shell_surface_listener = {&shell_surface_ping, &shell_surface_configure, &shell_surface_popup_done}; 51 | 52 | static void create_window (struct window *window, int32_t width, int32_t height) { 53 | eglBindAPI (EGL_OPENGL_API); 54 | EGLint attributes[] = { 55 | EGL_RED_SIZE, 8, 56 | EGL_GREEN_SIZE, 8, 57 | EGL_BLUE_SIZE, 8, 58 | EGL_NONE}; 59 | EGLConfig config; 60 | EGLint num_config; 61 | eglChooseConfig (egl_display, attributes, &config, 1, &num_config); 62 | window->egl_context = eglCreateContext (egl_display, config, EGL_NO_CONTEXT, NULL); 63 | 64 | window->surface = wl_compositor_create_surface (compositor); 65 | window->shell_surface = wl_shell_get_shell_surface (shell, window->surface); 66 | wl_shell_surface_add_listener (window->shell_surface, &shell_surface_listener, window); 67 | wl_shell_surface_set_toplevel (window->shell_surface); 68 | window->egl_window = wl_egl_window_create (window->surface, width, height); 69 | window->egl_surface = eglCreateWindowSurface (egl_display, config, window->egl_window, NULL); 70 | eglMakeCurrent (egl_display, window->egl_surface, window->egl_surface, window->egl_context); 71 | } 72 | static void delete_window (struct window *window) { 73 | eglDestroySurface (egl_display, window->egl_surface); 74 | wl_egl_window_destroy (window->egl_window); 75 | wl_shell_surface_destroy (window->shell_surface); 76 | wl_surface_destroy (window->surface); 77 | eglDestroyContext (egl_display, window->egl_context); 78 | } 79 | static void draw_window (struct window *window) { 80 | glClearColor (0.0, 1.0, 0.0, 1.0); 81 | glClear (GL_COLOR_BUFFER_BIT); 82 | eglSwapBuffers (egl_display, window->egl_surface); 83 | } 84 | 85 | int main () { 86 | display = wl_display_connect (NULL); 87 | struct wl_registry *registry = wl_display_get_registry (display); 88 | wl_registry_add_listener (registry, ®istry_listener, NULL); 89 | wl_display_roundtrip (display); 90 | 91 | egl_display = eglGetDisplay (display); 92 | eglInitialize (egl_display, NULL, NULL); 93 | 94 | struct window window; 95 | create_window (&window, WIDTH, HEIGHT); 96 | 97 | while (running) { 98 | wl_display_dispatch_pending (display); 99 | draw_window (&window); 100 | } 101 | 102 | delete_window (&window); 103 | eglTerminate (egl_display); 104 | wl_display_disconnect (display); 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /drm-gbm.c: -------------------------------------------------------------------------------- 1 | // gcc -o drm-gbm drm-gbm.c -ldrm -lgbm -lEGL -lGL -I/usr/include/libdrm 2 | 3 | // general documentation: man drm 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define EXIT(msg) { fputs (msg, stderr); exit (EXIT_FAILURE); } 16 | 17 | static int device; 18 | 19 | static drmModeConnector *find_connector (drmModeRes *resources) { 20 | // iterate the connectors 21 | int i; 22 | for (i=0; icount_connectors; i++) { 23 | drmModeConnector *connector = drmModeGetConnector (device, resources->connectors[i]); 24 | // pick the first connected connector 25 | if (connector->connection == DRM_MODE_CONNECTED) { 26 | return connector; 27 | } 28 | drmModeFreeConnector (connector); 29 | } 30 | // no connector found 31 | return NULL; 32 | } 33 | 34 | static drmModeEncoder *find_encoder (drmModeRes *resources, drmModeConnector *connector) { 35 | if (connector->encoder_id) { 36 | return drmModeGetEncoder (device, connector->encoder_id); 37 | } 38 | // no encoder found 39 | return NULL; 40 | } 41 | 42 | static uint32_t connector_id; 43 | static drmModeModeInfo mode_info; 44 | static drmModeCrtc *crtc; 45 | 46 | static void find_display_configuration () { 47 | drmModeRes *resources = drmModeGetResources (device); 48 | // find a connector 49 | drmModeConnector *connector = find_connector (resources); 50 | if (!connector) EXIT ("no connector found\n"); 51 | // save the connector_id 52 | connector_id = connector->connector_id; 53 | // save the first mode 54 | mode_info = connector->modes[0]; 55 | printf ("resolution: %ix%i\n", mode_info.hdisplay, mode_info.vdisplay); 56 | // find an encoder 57 | drmModeEncoder *encoder = find_encoder (resources, connector); 58 | if (!encoder) EXIT ("no encoder found\n"); 59 | // find a CRTC 60 | if (encoder->crtc_id) { 61 | crtc = drmModeGetCrtc (device, encoder->crtc_id); 62 | } 63 | drmModeFreeEncoder (encoder); 64 | drmModeFreeConnector (connector); 65 | drmModeFreeResources (resources); 66 | } 67 | 68 | static struct gbm_device *gbm_device; 69 | static EGLDisplay display; 70 | static EGLContext context; 71 | static struct gbm_surface *gbm_surface; 72 | static EGLSurface egl_surface; 73 | 74 | static void setup_opengl () { 75 | gbm_device = gbm_create_device (device); 76 | display = eglGetDisplay (gbm_device); 77 | eglInitialize (display, NULL, NULL); 78 | 79 | // create an OpenGL context 80 | eglBindAPI (EGL_OPENGL_API); 81 | EGLint attributes[] = { 82 | EGL_RED_SIZE, 8, 83 | EGL_GREEN_SIZE, 8, 84 | EGL_BLUE_SIZE, 8, 85 | EGL_NONE}; 86 | EGLConfig config; 87 | EGLint num_config; 88 | eglChooseConfig (display, attributes, &config, 1, &num_config); 89 | context = eglCreateContext (display, config, EGL_NO_CONTEXT, NULL); 90 | 91 | // create the GBM and EGL surface 92 | gbm_surface = gbm_surface_create (gbm_device, mode_info.hdisplay, mode_info.vdisplay, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT|GBM_BO_USE_RENDERING); 93 | egl_surface = eglCreateWindowSurface (display, config, gbm_surface, NULL); 94 | eglMakeCurrent (display, egl_surface, egl_surface, context); 95 | } 96 | 97 | static struct gbm_bo *previous_bo = NULL; 98 | static uint32_t previous_fb; 99 | 100 | static void swap_buffers () { 101 | eglSwapBuffers (display, egl_surface); 102 | struct gbm_bo *bo = gbm_surface_lock_front_buffer (gbm_surface); 103 | uint32_t handle = gbm_bo_get_handle (bo).u32; 104 | uint32_t pitch = gbm_bo_get_stride (bo); 105 | uint32_t fb; 106 | drmModeAddFB (device, mode_info.hdisplay, mode_info.vdisplay, 24, 32, pitch, handle, &fb); 107 | drmModeSetCrtc (device, crtc->crtc_id, fb, 0, 0, &connector_id, 1, &mode_info); 108 | 109 | if (previous_bo) { 110 | drmModeRmFB (device, previous_fb); 111 | gbm_surface_release_buffer (gbm_surface, previous_bo); 112 | } 113 | previous_bo = bo; 114 | previous_fb = fb; 115 | } 116 | 117 | static void draw (float progress) { 118 | glClearColor (1.0f-progress, progress, 0.0, 1.0); 119 | glClear (GL_COLOR_BUFFER_BIT); 120 | swap_buffers (); 121 | } 122 | 123 | static void clean_up () { 124 | // set the previous crtc 125 | drmModeSetCrtc (device, crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, &connector_id, 1, &crtc->mode); 126 | drmModeFreeCrtc (crtc); 127 | 128 | if (previous_bo) { 129 | drmModeRmFB (device, previous_fb); 130 | gbm_surface_release_buffer (gbm_surface, previous_bo); 131 | } 132 | 133 | eglDestroySurface (display, egl_surface); 134 | gbm_surface_destroy (gbm_surface); 135 | eglDestroyContext (display, context); 136 | eglTerminate (display); 137 | gbm_device_destroy (gbm_device); 138 | } 139 | 140 | int main () { 141 | device = open ("/dev/dri/card0", O_RDWR|O_CLOEXEC); 142 | find_display_configuration (); 143 | setup_opengl (); 144 | 145 | int i; 146 | for (i = 0; i < 600; i++) 147 | draw (i / 600.0f); 148 | 149 | clean_up (); 150 | close (device); 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /wayland-compositor/backend-x11.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include "backend.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define WINDOW_WIDTH 800 19 | #define WINDOW_HEIGHT 600 20 | 21 | static struct { 22 | Window window; 23 | EGLContext context; 24 | EGLSurface surface; 25 | } window; 26 | static Display *x_display; 27 | static EGLDisplay egl_display; 28 | static struct callbacks callbacks; 29 | static xcb_connection_t *xcb_connection; 30 | static int32_t keyboard_device_id; 31 | static struct xkb_keymap *keymap; 32 | static struct xkb_state *state; 33 | 34 | static void create_window (void) { 35 | // setup EGL 36 | EGLint attribs[] = { 37 | EGL_RED_SIZE, 1, 38 | EGL_GREEN_SIZE, 1, 39 | EGL_BLUE_SIZE, 1, 40 | EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, 41 | EGL_NONE}; 42 | EGLConfig config; 43 | EGLint num_configs_returned; 44 | eglChooseConfig (egl_display, attribs, &config, 1, &num_configs_returned); 45 | 46 | // get the visual from the EGL config 47 | EGLint visual_id; 48 | eglGetConfigAttrib (egl_display, config, EGL_NATIVE_VISUAL_ID, &visual_id); 49 | XVisualInfo visual_template; 50 | visual_template.visualid = visual_id; 51 | int num_visuals_returned; 52 | XVisualInfo *visual = XGetVisualInfo (x_display, VisualIDMask, &visual_template, &num_visuals_returned); 53 | 54 | // create a window 55 | XSetWindowAttributes window_attributes; 56 | window_attributes.event_mask = ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask; 57 | window_attributes.colormap = XCreateColormap (x_display, RootWindow(x_display,DefaultScreen(x_display)), visual->visual, AllocNone); 58 | window.window = XCreateWindow ( 59 | x_display, 60 | RootWindow(x_display, DefaultScreen(x_display)), 61 | 0, 0, 62 | WINDOW_WIDTH, WINDOW_HEIGHT, 63 | 0, // border width 64 | visual->depth, // depth 65 | InputOutput, // class 66 | visual->visual, // visual 67 | CWEventMask|CWColormap, // attribute mask 68 | &window_attributes // attributes 69 | ); 70 | 71 | // EGL context and surface 72 | eglBindAPI (EGL_OPENGL_API); 73 | window.context = eglCreateContext (egl_display, config, EGL_NO_CONTEXT, NULL); 74 | window.surface = eglCreateWindowSurface (egl_display, config, window.window, NULL); 75 | eglMakeCurrent (egl_display, window.surface, window.surface, window.context); 76 | 77 | XFree (visual); 78 | 79 | XMapWindow (x_display, window.window); 80 | } 81 | 82 | void backend_init (struct callbacks *_callbacks) { 83 | callbacks = *_callbacks; 84 | x_display = XOpenDisplay (NULL); 85 | 86 | xcb_connection = XGetXCBConnection (x_display); 87 | struct xkb_context *context = xkb_context_new (XKB_CONTEXT_NO_FLAGS); 88 | xkb_x11_setup_xkb_extension (xcb_connection, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, 0, NULL, NULL, NULL, NULL); 89 | keyboard_device_id = xkb_x11_get_core_keyboard_device_id (xcb_connection); 90 | keymap = xkb_x11_keymap_new_from_device (context, xcb_connection, keyboard_device_id, XKB_KEYMAP_COMPILE_NO_FLAGS); 91 | state = xkb_x11_state_new_from_device (keymap, xcb_connection, keyboard_device_id); 92 | 93 | egl_display = eglGetDisplay (x_display); 94 | eglInitialize (egl_display, NULL, NULL); 95 | create_window (); 96 | } 97 | 98 | EGLDisplay backend_get_egl_display (void) { 99 | return egl_display; 100 | } 101 | 102 | void backend_swap_buffers (void) { 103 | eglSwapBuffers (egl_display, window.surface); 104 | } 105 | 106 | static void update_modifiers (void) { 107 | struct modifier_state modifier_state; 108 | modifier_state.depressed = xkb_state_serialize_mods (state, XKB_STATE_MODS_DEPRESSED); 109 | modifier_state.latched = xkb_state_serialize_mods (state, XKB_STATE_MODS_LATCHED); 110 | modifier_state.locked = xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED); 111 | modifier_state.group = xkb_state_serialize_layout (state, XKB_STATE_LAYOUT_EFFECTIVE); 112 | callbacks.modifiers (modifier_state); 113 | } 114 | 115 | void backend_dispatch_nonblocking (void) { 116 | XEvent event; 117 | while (XPending(x_display)) { 118 | XNextEvent (x_display, &event); 119 | if (event.type == ConfigureNotify) { 120 | callbacks.resize (event.xconfigure.width, event.xconfigure.height); 121 | } 122 | else if (event.type == Expose) { 123 | callbacks.draw (); 124 | } 125 | else if (event.type == MotionNotify) { 126 | callbacks.mouse_motion (event.xbutton.x, event.xbutton.y); 127 | } 128 | else if (event.type == ButtonPress) { 129 | if (event.xbutton.button == Button1) 130 | callbacks.mouse_button (BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); 131 | else if (event.xbutton.button == Button2) 132 | callbacks.mouse_button (BTN_MIDDLE, WL_POINTER_BUTTON_STATE_PRESSED); 133 | else if (event.xbutton.button == Button3) 134 | callbacks.mouse_button (BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED); 135 | } 136 | else if (event.type == ButtonRelease) { 137 | if (event.xbutton.button == Button1) 138 | callbacks.mouse_button (BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); 139 | else if (event.xbutton.button == Button2) 140 | callbacks.mouse_button (BTN_MIDDLE, WL_POINTER_BUTTON_STATE_RELEASED); 141 | else if (event.xbutton.button == Button3) 142 | callbacks.mouse_button (BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED); 143 | } 144 | else if (event.type == KeyPress) { 145 | callbacks.key (event.xkey.keycode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); 146 | xkb_state_update_key (state, event.xkey.keycode, XKB_KEY_DOWN); 147 | update_modifiers (); 148 | } 149 | else if (event.type == KeyRelease) { 150 | callbacks.key (event.xkey.keycode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); 151 | xkb_state_update_key (state, event.xkey.keycode, XKB_KEY_UP); 152 | update_modifiers (); 153 | } 154 | else if (event.type == FocusIn) { 155 | xkb_state_unref (state); 156 | state = xkb_x11_state_new_from_device (keymap, xcb_connection, keyboard_device_id); 157 | update_modifiers (); 158 | } 159 | } 160 | } 161 | 162 | void backend_wait_for_events (int wayland_fd) { 163 | struct pollfd fds[2] = {{ConnectionNumber(x_display), POLLIN}, {wayland_fd, POLLIN}}; 164 | poll (fds, 2, -1); 165 | } 166 | 167 | void backend_get_keymap (int *fd, int *size) { 168 | char *string = xkb_keymap_get_as_string (keymap, XKB_KEYMAP_FORMAT_TEXT_V1); 169 | *size = strlen (string) + 1; 170 | char *xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR"); 171 | *fd = open (xdg_runtime_dir, O_TMPFILE|O_RDWR|O_EXCL, 0600); 172 | ftruncate (*fd, *size); 173 | char *map = mmap (NULL, *size, PROT_READ|PROT_WRITE, MAP_SHARED, *fd, 0); 174 | strcpy (map, string); 175 | munmap (map, *size); 176 | free (string); 177 | } 178 | 179 | long backend_get_timestamp (void) { 180 | struct timespec t; 181 | clock_gettime (CLOCK_MONOTONIC, &t); 182 | return t.tv_sec * 1000 + t.tv_nsec / 1000000; 183 | } 184 | -------------------------------------------------------------------------------- /wayland-input.c: -------------------------------------------------------------------------------- 1 | // gcc -o wayland-input wayland-input.c -lwayland-client -lwayland-egl -lEGL -lGL -lxkbcommon 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define WIDTH 256 14 | #define HEIGHT 256 15 | 16 | static struct wl_display *display; 17 | static struct wl_compositor *compositor = NULL; 18 | static struct wl_shell *shell = NULL; 19 | static struct wl_seat *seat = NULL; 20 | static EGLDisplay egl_display; 21 | static struct xkb_context *xkb_context; 22 | static struct xkb_keymap *keymap = NULL; 23 | static struct xkb_state *xkb_state = NULL; 24 | static char running = 1; 25 | 26 | struct window { 27 | EGLContext egl_context; 28 | struct wl_surface *surface; 29 | struct wl_shell_surface *shell_surface; 30 | struct wl_egl_window *egl_window; 31 | EGLSurface egl_surface; 32 | }; 33 | 34 | // listeners 35 | static void pointer_enter (void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { 36 | printf ("pointer enter\n"); 37 | } 38 | static void pointer_leave (void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { 39 | printf ("pointer leave\n"); 40 | } 41 | static void pointer_motion (void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { 42 | printf ("pointer motion %f %f\n", wl_fixed_to_double(x), wl_fixed_to_double(y)); 43 | } 44 | static void pointer_button (void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { 45 | printf ("pointer button (button %d, state %d)\n", button, state); 46 | } 47 | static void pointer_axis (void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { 48 | printf ("pointer axis\n"); 49 | } 50 | static struct wl_pointer_listener pointer_listener = {&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis}; 51 | 52 | static void keyboard_keymap (void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) { 53 | char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0); 54 | xkb_keymap_unref (keymap); 55 | keymap = xkb_keymap_new_from_string (xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); 56 | munmap (keymap_string, size); 57 | close (fd); 58 | xkb_state_unref (xkb_state); 59 | xkb_state = xkb_state_new (keymap); 60 | } 61 | static void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { 62 | 63 | } 64 | static void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { 65 | 66 | } 67 | static void keyboard_key (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { 68 | if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { 69 | xkb_keysym_t keysym = xkb_state_key_get_one_sym (xkb_state, key+8); 70 | uint32_t utf32 = xkb_keysym_to_utf32 (keysym); 71 | if (utf32) { 72 | if (utf32 >= 0x21 && utf32 <= 0x7E) { 73 | printf ("the key %c was pressed\n", (char)utf32); 74 | if (utf32 == 'q') running = 0; 75 | } 76 | else { 77 | printf ("the key U+%04X was pressed\n", utf32); 78 | } 79 | } 80 | else { 81 | char name[64]; 82 | xkb_keysym_get_name (keysym, name, 64); 83 | printf ("the key %s was pressed\n", name); 84 | } 85 | } 86 | } 87 | static void keyboard_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) { 88 | xkb_state_update_mask (xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); 89 | } 90 | static struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers}; 91 | 92 | static void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) { 93 | if (capabilities & WL_SEAT_CAPABILITY_POINTER) { 94 | struct wl_pointer *pointer = wl_seat_get_pointer (seat); 95 | wl_pointer_add_listener (pointer, &pointer_listener, NULL); 96 | } 97 | if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { 98 | struct wl_keyboard *keyboard = wl_seat_get_keyboard (seat); 99 | wl_keyboard_add_listener (keyboard, &keyboard_listener, NULL); 100 | } 101 | } 102 | static struct wl_seat_listener seat_listener = {&seat_capabilities}; 103 | 104 | static void registry_add_object (void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { 105 | if (!strcmp(interface,"wl_compositor")) { 106 | compositor = wl_registry_bind (registry, name, &wl_compositor_interface, 1); 107 | } 108 | else if (!strcmp(interface,"wl_shell")) { 109 | shell = wl_registry_bind (registry, name, &wl_shell_interface, 1); 110 | } 111 | else if (!strcmp(interface,"wl_seat")) { 112 | seat = wl_registry_bind (registry, name, &wl_seat_interface, 1); 113 | wl_seat_add_listener (seat, &seat_listener, NULL); 114 | } 115 | } 116 | static void registry_remove_object (void *data, struct wl_registry *registry, uint32_t name) { 117 | 118 | } 119 | static struct wl_registry_listener registry_listener = {®istry_add_object, ®istry_remove_object}; 120 | 121 | static void shell_surface_ping (void *data, struct wl_shell_surface *shell_surface, uint32_t serial) { 122 | wl_shell_surface_pong (shell_surface, serial); 123 | } 124 | static void shell_surface_configure (void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height) { 125 | struct window *window = data; 126 | wl_egl_window_resize (window->egl_window, width, height, 0, 0); 127 | } 128 | static void shell_surface_popup_done (void *data, struct wl_shell_surface *shell_surface) { 129 | 130 | } 131 | static struct wl_shell_surface_listener shell_surface_listener = {&shell_surface_ping, &shell_surface_configure, &shell_surface_popup_done}; 132 | 133 | static void create_window (struct window *window, int32_t width, int32_t height) { 134 | eglBindAPI (EGL_OPENGL_API); 135 | EGLint attributes[] = { 136 | EGL_RED_SIZE, 8, 137 | EGL_GREEN_SIZE, 8, 138 | EGL_BLUE_SIZE, 8, 139 | EGL_NONE}; 140 | EGLConfig config; 141 | EGLint num_config; 142 | eglChooseConfig (egl_display, attributes, &config, 1, &num_config); 143 | window->egl_context = eglCreateContext (egl_display, config, EGL_NO_CONTEXT, NULL); 144 | 145 | window->surface = wl_compositor_create_surface (compositor); 146 | window->shell_surface = wl_shell_get_shell_surface (shell, window->surface); 147 | wl_shell_surface_add_listener (window->shell_surface, &shell_surface_listener, window); 148 | wl_shell_surface_set_toplevel (window->shell_surface); 149 | window->egl_window = wl_egl_window_create (window->surface, width, height); 150 | window->egl_surface = eglCreateWindowSurface (egl_display, config, window->egl_window, NULL); 151 | eglMakeCurrent (egl_display, window->egl_surface, window->egl_surface, window->egl_context); 152 | } 153 | static void delete_window (struct window *window) { 154 | eglDestroySurface (egl_display, window->egl_surface); 155 | wl_egl_window_destroy (window->egl_window); 156 | wl_shell_surface_destroy (window->shell_surface); 157 | wl_surface_destroy (window->surface); 158 | eglDestroyContext (egl_display, window->egl_context); 159 | } 160 | static void draw_window (struct window *window) { 161 | glClearColor (0.0, 1.0, 0.0, 1.0); 162 | glClear (GL_COLOR_BUFFER_BIT); 163 | eglSwapBuffers (egl_display, window->egl_surface); 164 | } 165 | 166 | int main () { 167 | display = wl_display_connect (NULL); 168 | struct wl_registry *registry = wl_display_get_registry (display); 169 | wl_registry_add_listener (registry, ®istry_listener, NULL); 170 | wl_display_roundtrip (display); 171 | 172 | egl_display = eglGetDisplay (display); 173 | eglInitialize (egl_display, NULL, NULL); 174 | 175 | xkb_context = xkb_context_new (XKB_CONTEXT_NO_FLAGS); 176 | 177 | struct window window; 178 | create_window (&window, WIDTH, HEIGHT); 179 | 180 | while (running) { 181 | wl_display_dispatch_pending (display); 182 | draw_window (&window); 183 | } 184 | 185 | delete_window (&window); 186 | eglTerminate (egl_display); 187 | wl_display_disconnect (display); 188 | return 0; 189 | } 190 | -------------------------------------------------------------------------------- /wayland-compositor/wayland-compositor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "xdg-shell.h" 3 | #include 4 | #include "backend.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static PFNEGLBINDWAYLANDDISPLAYWL eglBindWaylandDisplayWL = NULL; 12 | static PFNEGLQUERYWAYLANDBUFFERWL eglQueryWaylandBufferWL = NULL; 13 | static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = NULL; 14 | 15 | #include "texture.h" 16 | 17 | static struct wl_display *display; 18 | static int pointer_x, pointer_y; 19 | static struct modifier_state modifier_state; 20 | static char redraw_needed = 0; 21 | 22 | struct client { 23 | struct wl_client *client; 24 | struct wl_resource *pointer; 25 | struct wl_resource *keyboard; 26 | struct wl_list link; 27 | }; 28 | static struct wl_list clients; 29 | 30 | static struct client *get_client (struct wl_client *_client) { 31 | struct client *client; 32 | wl_list_for_each (client, &clients, link) { 33 | if (client->client == _client) return client; 34 | } 35 | client = calloc (1, sizeof(struct client)); 36 | client->client = _client; 37 | wl_list_insert (&clients, &client->link); 38 | return client; 39 | } 40 | 41 | struct surface { 42 | struct wl_resource *surface; 43 | struct wl_resource *xdg_surface; 44 | struct wl_resource *xdg_toplevel; 45 | struct wl_resource *buffer; 46 | struct wl_resource *frame_callback; 47 | int x, y; 48 | struct texture texture; 49 | struct client *client; 50 | struct wl_list link; 51 | }; 52 | static struct wl_list surfaces; 53 | static struct surface *cursor = NULL; 54 | static struct surface *moving_surface = NULL; 55 | static struct surface *active_surface = NULL; 56 | static struct surface *pointer_surface = NULL; // surface under the pointer 57 | 58 | static void deactivate_surface (struct surface *surface) { 59 | if (surface->client->keyboard) wl_keyboard_send_leave (surface->client->keyboard, 0, surface->surface); 60 | struct wl_array state_array; 61 | wl_array_init (&state_array); 62 | xdg_toplevel_send_configure (surface->xdg_toplevel, 0, 0, &state_array); 63 | } 64 | static void activate_surface (struct surface *surface) { 65 | wl_list_remove (&surface->link); 66 | wl_list_insert (&surfaces, &surface->link); 67 | struct wl_array array; 68 | wl_array_init (&array); 69 | if (surface->client->keyboard) { 70 | wl_keyboard_send_enter (surface->client->keyboard, 0, surface->surface, &array); 71 | wl_keyboard_send_modifiers (surface->client->keyboard, 0, modifier_state.depressed, modifier_state.latched, modifier_state.locked, modifier_state.group); 72 | } 73 | int32_t *states = wl_array_add (&array, sizeof(int32_t)); 74 | states[0] = XDG_TOPLEVEL_STATE_ACTIVATED; 75 | xdg_toplevel_send_configure (surface->xdg_toplevel, 0, 0, &array); 76 | } 77 | static void delete_surface (struct wl_resource *resource) { 78 | struct surface *surface = wl_resource_get_user_data (resource); 79 | wl_list_remove (&surface->link); 80 | if (surface == active_surface) active_surface = NULL; 81 | if (surface == pointer_surface) pointer_surface = NULL; 82 | free (surface); 83 | redraw_needed = 1; 84 | } 85 | 86 | // surface 87 | static void surface_destroy (struct wl_client *client, struct wl_resource *resource) { 88 | 89 | } 90 | static void surface_attach (struct wl_client *client, struct wl_resource *resource, struct wl_resource *buffer, int32_t x, int32_t y) { 91 | struct surface *surface = wl_resource_get_user_data (resource); 92 | surface->buffer = buffer; 93 | } 94 | static void surface_damage (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { 95 | 96 | } 97 | static void surface_frame (struct wl_client *client, struct wl_resource *resource, uint32_t callback) { 98 | struct surface *surface = wl_resource_get_user_data (resource); 99 | surface->frame_callback = wl_resource_create (client, &wl_callback_interface, 1, callback); 100 | } 101 | static void surface_set_opaque_region (struct wl_client *client, struct wl_resource *resource, struct wl_resource *region) { 102 | 103 | } 104 | static void surface_set_input_region (struct wl_client *client, struct wl_resource *resource, struct wl_resource *region) { 105 | 106 | } 107 | static void surface_commit (struct wl_client *client, struct wl_resource *resource) { 108 | struct surface *surface = wl_resource_get_user_data (resource); 109 | if (!surface->buffer) { 110 | xdg_surface_send_configure(surface->xdg_surface, 0); 111 | return; 112 | } 113 | EGLint texture_format; 114 | if (eglQueryWaylandBufferWL (backend_get_egl_display(), surface->buffer, EGL_TEXTURE_FORMAT, &texture_format)) { 115 | EGLint width, height; 116 | eglQueryWaylandBufferWL (backend_get_egl_display(), surface->buffer, EGL_WIDTH, &width); 117 | eglQueryWaylandBufferWL (backend_get_egl_display(), surface->buffer, EGL_WIDTH, &height); 118 | EGLAttrib attribs = EGL_NONE; 119 | EGLImage image = eglCreateImage (backend_get_egl_display(), EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, surface->buffer, &attribs); 120 | texture_delete (&surface->texture); 121 | texture_create_from_egl_image (&surface->texture, width, height, image); 122 | eglDestroyImage (backend_get_egl_display(), image); 123 | } 124 | else { 125 | struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get (surface->buffer); 126 | uint32_t width = wl_shm_buffer_get_width (shm_buffer); 127 | uint32_t height = wl_shm_buffer_get_height (shm_buffer); 128 | void *data = wl_shm_buffer_get_data (shm_buffer); 129 | texture_delete (&surface->texture); 130 | texture_create (&surface->texture, width, height, data); 131 | } 132 | wl_buffer_send_release (surface->buffer); 133 | redraw_needed = 1; 134 | } 135 | static void surface_set_buffer_transform (struct wl_client *client, struct wl_resource *resource, int32_t transform) { 136 | 137 | } 138 | static void surface_set_buffer_scale (struct wl_client *client, struct wl_resource *resource, int32_t scale) { 139 | 140 | } 141 | static struct wl_surface_interface surface_implementation = {&surface_destroy, &surface_attach, &surface_damage, &surface_frame, &surface_set_opaque_region, &surface_set_input_region, &surface_commit, &surface_set_buffer_transform, &surface_set_buffer_scale}; 142 | 143 | // region 144 | static void region_destroy (struct wl_client *client, struct wl_resource *resource) { 145 | 146 | } 147 | static void region_add (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { 148 | 149 | } 150 | static void region_subtract (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { 151 | 152 | } 153 | static struct wl_region_interface region_implementation = {®ion_destroy, ®ion_add, ®ion_subtract}; 154 | 155 | // compositor 156 | static void compositor_create_surface (struct wl_client *client, struct wl_resource *resource, uint32_t id) { 157 | struct surface *surface = calloc (1, sizeof(struct surface)); 158 | surface->surface = wl_resource_create (client, &wl_surface_interface, 3, id); 159 | wl_resource_set_implementation (surface->surface, &surface_implementation, surface, &delete_surface); 160 | surface->client = get_client (client); 161 | wl_list_insert (&surfaces, &surface->link); 162 | } 163 | static void compositor_create_region (struct wl_client *client, struct wl_resource *resource, uint32_t id) { 164 | struct wl_resource *region = wl_resource_create (client, &wl_region_interface, 1, id); 165 | wl_resource_set_implementation (region, ®ion_implementation, NULL, NULL); 166 | } 167 | static struct wl_compositor_interface compositor_implementation = {&compositor_create_surface, &compositor_create_region}; 168 | static void compositor_bind (struct wl_client *client, void *data, uint32_t version, uint32_t id) { 169 | printf ("bind: compositor\n"); 170 | struct wl_resource *resource = wl_resource_create (client, &wl_compositor_interface, 1, id); 171 | wl_resource_set_implementation (resource, &compositor_implementation, NULL, NULL); 172 | } 173 | 174 | // shell surface 175 | static void shell_surface_pong (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { 176 | 177 | } 178 | static void shell_surface_move (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial) { 179 | 180 | } 181 | static void shell_surface_resize (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial, uint32_t edges) { 182 | 183 | } 184 | static void shell_surface_set_toplevel (struct wl_client *client, struct wl_resource *resource) { 185 | 186 | } 187 | static void shell_surface_set_transient (struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent, int32_t x, int32_t y, uint32_t flags) { 188 | 189 | } 190 | static void shell_surface_set_fullscreen (struct wl_client *client, struct wl_resource *resource, uint32_t method, uint32_t framerate, struct wl_resource *output) { 191 | 192 | } 193 | static void shell_surface_set_popup (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial, struct wl_resource *parent, int32_t x, int32_t y, uint32_t flags) { 194 | 195 | } 196 | static void shell_surface_set_maximized (struct wl_client *client, struct wl_resource *resource, struct wl_resource *output) { 197 | 198 | } 199 | static void shell_surface_set_title (struct wl_client *client, struct wl_resource *resource, const char *title) { 200 | 201 | } 202 | static void shell_surface_set_class (struct wl_client *client, struct wl_resource *resource, const char *class_) { 203 | 204 | } 205 | static struct wl_shell_surface_interface shell_surface_implementation = {&shell_surface_pong, &shell_surface_move, &shell_surface_resize, &shell_surface_set_toplevel, &shell_surface_set_transient, &shell_surface_set_fullscreen, &shell_surface_set_popup, &shell_surface_set_maximized, &shell_surface_set_title, &shell_surface_set_class}; 206 | 207 | // wl shell 208 | static void shell_get_shell_surface (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface) { 209 | struct wl_resource *shell_surface = wl_resource_create (client, &wl_shell_surface_interface, 1, id); 210 | wl_resource_set_implementation (shell_surface, &shell_surface_implementation, NULL, NULL); 211 | } 212 | static struct wl_shell_interface shell_implementation = {&shell_get_shell_surface}; 213 | static void shell_bind (struct wl_client *client, void *data, uint32_t version, uint32_t id) { 214 | printf ("bind: wl_shell\n"); 215 | struct wl_resource *resource = wl_resource_create (client, &wl_shell_interface, 1, id); 216 | wl_resource_set_implementation (resource, &shell_implementation, NULL, NULL); 217 | } 218 | 219 | // xdg toplevel 220 | static void xdg_toplevel_destroy (struct wl_client *client, struct wl_resource *resource) { 221 | 222 | } 223 | static void xdg_toplevel_set_parent (struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent) { 224 | 225 | } 226 | static void xdg_toplevel_set_title (struct wl_client *client, struct wl_resource *resource, const char *title) { 227 | 228 | } 229 | static void xdg_toplevel_set_app_id (struct wl_client *client, struct wl_resource *resource, const char *app_id) { 230 | 231 | } 232 | static void xdg_toplevel_show_window_menu (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial, int32_t x, int32_t y) { 233 | 234 | } 235 | static void xdg_toplevel_move (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial) { 236 | struct surface *surface = wl_resource_get_user_data (resource); 237 | // during the move the surface coordinates are relative to the pointer 238 | surface->x = surface->x - pointer_x; 239 | surface->y = surface->y - pointer_y; 240 | moving_surface = surface; 241 | } 242 | static void xdg_toplevel_resize (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial, uint32_t edges) { 243 | 244 | } 245 | static void xdg_toplevel_set_max_size (struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { 246 | 247 | } 248 | static void xdg_toplevel_set_min_size (struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { 249 | 250 | } 251 | static void xdg_toplevel_set_maximized (struct wl_client *client, struct wl_resource *resource) { 252 | printf ("surface requested maximize\n"); 253 | } 254 | static void xdg_toplevel_unset_maximized (struct wl_client *client, struct wl_resource *resource) { 255 | 256 | } 257 | static void xdg_toplevel_set_fullscreen (struct wl_client *client, struct wl_resource *resource, struct wl_resource *output) { 258 | 259 | } 260 | static void xdg_toplevel_unset_fullscreen (struct wl_client *client, struct wl_resource *resource) { 261 | 262 | } 263 | static void xdg_toplevel_set_minimized (struct wl_client *client, struct wl_resource *resource) { 264 | 265 | } 266 | static struct xdg_toplevel_interface xdg_toplevel_implementation = {&xdg_toplevel_destroy, &xdg_toplevel_set_parent, &xdg_toplevel_set_title, &xdg_toplevel_set_app_id, &xdg_toplevel_show_window_menu, &xdg_toplevel_move, &xdg_toplevel_resize, &xdg_toplevel_set_max_size, &xdg_toplevel_set_min_size, &xdg_toplevel_set_maximized, &xdg_toplevel_unset_maximized, &xdg_toplevel_set_fullscreen, &xdg_toplevel_unset_fullscreen, &xdg_toplevel_set_minimized}; 267 | 268 | // xdg surface 269 | static void xdg_surface_destroy (struct wl_client *client, struct wl_resource *resource) { 270 | 271 | } 272 | static void xdg_surface_get_toplevel (struct wl_client *client, struct wl_resource *resource, uint32_t id) { 273 | struct surface *surface = wl_resource_get_user_data (resource); 274 | surface->xdg_toplevel = wl_resource_create (client, &xdg_toplevel_interface, 1, id); 275 | wl_resource_set_implementation (surface->xdg_toplevel, &xdg_toplevel_implementation, surface, NULL); 276 | } 277 | static void xdg_surface_get_popup (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *parent, struct wl_resource *positioner) { 278 | 279 | } 280 | static void xdg_surface_set_window_geometry (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { 281 | 282 | } 283 | static void xdg_surface_ack_configure (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { 284 | 285 | } 286 | static struct xdg_surface_interface xdg_surface_implementation = {&xdg_surface_destroy, &xdg_surface_get_toplevel, &xdg_surface_get_popup, &xdg_surface_set_window_geometry, &xdg_surface_ack_configure}; 287 | 288 | // xdg wm base 289 | static void xdg_wm_base_destroy (struct wl_client *client, struct wl_resource *resource) { 290 | 291 | } 292 | static void xdg_wm_base_create_positioner (struct wl_client *client, struct wl_resource *resource, uint32_t id) { 293 | 294 | } 295 | static void xdg_wm_base_get_xdg_surface (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *_surface) { 296 | struct surface *surface = wl_resource_get_user_data (_surface); 297 | surface->xdg_surface = wl_resource_create (client, &xdg_surface_interface, 1, id); 298 | wl_resource_set_implementation (surface->xdg_surface, &xdg_surface_implementation, surface, NULL); 299 | } 300 | static void xdg_wm_base_pong (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { 301 | 302 | } 303 | static struct xdg_wm_base_interface xdg_wm_base_implementation = {&xdg_wm_base_destroy, &xdg_wm_base_create_positioner, &xdg_wm_base_get_xdg_surface, &xdg_wm_base_pong}; 304 | static void xdg_wm_base_bind (struct wl_client *client, void *data, uint32_t version, uint32_t id) { 305 | printf ("bind: xdg_wm_base\n"); 306 | struct wl_resource *resource = wl_resource_create (client, &xdg_wm_base_interface, 1, id); 307 | wl_resource_set_implementation (resource, &xdg_wm_base_implementation, NULL, NULL); 308 | } 309 | 310 | // pointer 311 | static void pointer_set_cursor (struct wl_client *client, struct wl_resource *resource, uint32_t serial, struct wl_resource *_surface, int32_t hotspot_x, 312 | int32_t hotspot_y) { 313 | struct surface *surface = wl_resource_get_user_data (_surface); 314 | cursor = surface; 315 | } 316 | static void pointer_release (struct wl_client *client, struct wl_resource *resource) { 317 | 318 | } 319 | static struct wl_pointer_interface pointer_implementation = {&pointer_set_cursor, &pointer_release}; 320 | 321 | // keyboard 322 | static void keyboard_release (struct wl_client *client, struct wl_resource *resource) { 323 | 324 | } 325 | static struct wl_keyboard_interface keyboard_implementation = {&keyboard_release}; 326 | 327 | // seat 328 | static void seat_get_pointer (struct wl_client *client, struct wl_resource *resource, uint32_t id) { 329 | struct wl_resource *pointer = wl_resource_create (client, &wl_pointer_interface, 1, id); 330 | wl_resource_set_implementation (pointer, &pointer_implementation, NULL, NULL); 331 | get_client(client)->pointer = pointer; 332 | } 333 | static void seat_get_keyboard (struct wl_client *client, struct wl_resource *resource, uint32_t id) { 334 | struct wl_resource *keyboard = wl_resource_create (client, &wl_keyboard_interface, 1, id); 335 | wl_resource_set_implementation (keyboard, &keyboard_implementation, NULL, NULL); 336 | get_client(client)->keyboard = keyboard; 337 | int fd, size; 338 | backend_get_keymap (&fd, &size); 339 | wl_keyboard_send_keymap (keyboard, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, fd, size); 340 | //close (fd); 341 | } 342 | static void seat_get_touch (struct wl_client *client, struct wl_resource *resource, uint32_t id) { 343 | 344 | } 345 | static struct wl_seat_interface seat_implementation = {&seat_get_pointer, &seat_get_keyboard, &seat_get_touch}; 346 | static void seat_bind (struct wl_client *client, void *data, uint32_t version, uint32_t id) { 347 | printf ("bind: seat\n"); 348 | struct wl_resource *seat = wl_resource_create (client, &wl_seat_interface, 1, id); 349 | wl_resource_set_implementation (seat, &seat_implementation, NULL, NULL); 350 | wl_seat_send_capabilities (seat, WL_SEAT_CAPABILITY_POINTER|WL_SEAT_CAPABILITY_KEYBOARD); 351 | } 352 | 353 | // backend callbacks 354 | static void handle_resize_event (int width, int height) { 355 | glViewport (0, 0, width, height); 356 | glMatrixMode (GL_PROJECTION); 357 | glLoadIdentity (); 358 | glOrtho (0, width, height, 0, 1, -1); 359 | glMatrixMode (GL_MODELVIEW); 360 | 361 | glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 362 | glEnable (GL_BLEND); 363 | } 364 | static void handle_draw_event (void) { 365 | redraw_needed = 1; 366 | } 367 | static void handle_mouse_motion_event (int x, int y) { 368 | pointer_x = x; 369 | pointer_y = y; 370 | if (cursor) redraw_needed = 1; 371 | if (moving_surface) { 372 | redraw_needed = 1; 373 | return; 374 | } 375 | // get surface under the pointer 376 | struct surface *next_pointer_surface = NULL; 377 | struct surface *s; 378 | wl_list_for_each_reverse (s, &surfaces, link) { 379 | if (!s->xdg_surface) continue; 380 | if (x > s->x && y > s->y && x < s->x + s->texture.width && y < s->y + s->texture.height) 381 | next_pointer_surface = s; 382 | } 383 | // pointer enter and leave 384 | if (next_pointer_surface != pointer_surface) { 385 | if (pointer_surface && pointer_surface->client->pointer) 386 | wl_pointer_send_leave (pointer_surface->client->pointer, 0, pointer_surface->surface); 387 | pointer_surface = next_pointer_surface; 388 | if (pointer_surface && pointer_surface->client->pointer) 389 | wl_pointer_send_enter (pointer_surface->client->pointer, 0, pointer_surface->surface, x, y); 390 | } 391 | if (!pointer_surface || !pointer_surface->client->pointer) return; 392 | wl_fixed_t surface_x = wl_fixed_from_double (x - pointer_surface->x); 393 | wl_fixed_t surface_y = wl_fixed_from_double (y - pointer_surface->y); 394 | wl_pointer_send_motion (pointer_surface->client->pointer, backend_get_timestamp(), surface_x, surface_y); 395 | } 396 | static void handle_mouse_button_event (int button, int state) { 397 | if (moving_surface && state == WL_POINTER_BUTTON_STATE_RELEASED) { 398 | moving_surface->x = pointer_x + moving_surface->x; 399 | moving_surface->y = pointer_y + moving_surface->y; 400 | moving_surface = NULL; 401 | } 402 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { 403 | if (pointer_surface != active_surface) { 404 | if (active_surface) deactivate_surface (active_surface); 405 | active_surface = pointer_surface; 406 | if (active_surface) activate_surface (active_surface); 407 | } 408 | } 409 | if (!pointer_surface || !pointer_surface->client->pointer) return; 410 | wl_pointer_send_button (pointer_surface->client->pointer, 0, backend_get_timestamp(), button, state); 411 | } 412 | static void handle_key_event (int key, int state) { 413 | if (!active_surface || !active_surface->client->keyboard) return; 414 | wl_keyboard_send_key (active_surface->client->keyboard, 0, backend_get_timestamp(), key, state); 415 | } 416 | static void handle_modifiers_changed (struct modifier_state new_state) { 417 | if (new_state.depressed == modifier_state.depressed && new_state.latched == modifier_state.latched && new_state.locked == modifier_state.locked && new_state.group == modifier_state.group) return; 418 | modifier_state = new_state; 419 | if (active_surface && active_surface->client->keyboard) 420 | wl_keyboard_send_modifiers (active_surface->client->keyboard, 0, modifier_state.depressed, modifier_state.latched, modifier_state.locked, modifier_state.group); 421 | } 422 | static struct callbacks callbacks = {&handle_resize_event, &handle_draw_event, &handle_mouse_motion_event, &handle_mouse_button_event, &handle_key_event, &handle_modifiers_changed}; 423 | 424 | static void draw (void) { 425 | glClearColor (0, 1, 0, 1); 426 | glClear (GL_COLOR_BUFFER_BIT); 427 | glLoadIdentity(); 428 | 429 | struct surface *surface; 430 | wl_list_for_each_reverse (surface, &surfaces, link) { 431 | if (!surface->xdg_surface) continue; 432 | if (surface == moving_surface) 433 | texture_draw (&surface->texture, pointer_x + surface->x, pointer_y + surface->y); 434 | else 435 | texture_draw (&surface->texture, surface->x, surface->y); 436 | if (surface->frame_callback) { 437 | wl_callback_send_done (surface->frame_callback, backend_get_timestamp()); 438 | wl_resource_destroy (surface->frame_callback); 439 | surface->frame_callback = NULL; 440 | } 441 | } 442 | // draw the cursor last 443 | if (cursor) texture_draw (&cursor->texture, pointer_x, pointer_y); 444 | 445 | glFlush (); 446 | backend_swap_buffers (); 447 | } 448 | 449 | static void main_loop (void) { 450 | struct wl_event_loop *event_loop = wl_display_get_event_loop (display); 451 | int wayland_fd = wl_event_loop_get_fd (event_loop); 452 | while (1) { 453 | wl_event_loop_dispatch (event_loop, 0); 454 | backend_dispatch_nonblocking (); 455 | wl_display_flush_clients (display); 456 | if (redraw_needed) { 457 | draw (); 458 | redraw_needed = 0; 459 | } 460 | else { 461 | backend_wait_for_events (wayland_fd); 462 | } 463 | } 464 | } 465 | 466 | int main () { 467 | backend_init (&callbacks); 468 | eglBindWaylandDisplayWL = (PFNEGLBINDWAYLANDDISPLAYWL) eglGetProcAddress ("eglBindWaylandDisplayWL"); 469 | eglQueryWaylandBufferWL = (PFNEGLQUERYWAYLANDBUFFERWL) eglGetProcAddress ("eglQueryWaylandBufferWL"); 470 | glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) eglGetProcAddress ("glEGLImageTargetTexture2DOES"); 471 | wl_list_init (&clients); 472 | wl_list_init (&surfaces); 473 | display = wl_display_create (); 474 | wl_display_add_socket_auto (display); 475 | wl_global_create (display, &wl_compositor_interface, 3, NULL, &compositor_bind); 476 | wl_global_create (display, &wl_shell_interface, 1, NULL, &shell_bind); 477 | wl_global_create (display, &xdg_wm_base_interface, 1, NULL, &xdg_wm_base_bind); 478 | wl_global_create (display, &wl_seat_interface, 1, NULL, &seat_bind); 479 | eglBindWaylandDisplayWL (backend_get_egl_display(), display); 480 | wl_display_init_shm (display); 481 | 482 | main_loop (); 483 | 484 | wl_display_destroy (display); 485 | return 0; 486 | } 487 | --------------------------------------------------------------------------------