#include #include #include #include "wayland.h" static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer) { /* Sent by the compositor when it's no longer using this buffer */ wl_buffer_destroy(wl_buffer); } static const struct wl_buffer_listener wl_buffer_listener = { .release = wl_buffer_release, }; static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { struct client_state *state = data; xdg_surface_ack_configure(xdg_surface, serial); } static const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_configure, }; static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) { xdg_wm_base_pong(xdg_wm_base, serial); } static const struct xdg_wm_base_listener xdg_wm_base_listener = { .ping = xdg_wm_base_ping, }; static void xdg_toplevel_configure ( void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states ) { struct client_state *state = data; state->width = width; state->height = height; // resize egl window wl_egl_window_resize(state->egl_window, width, height, 0, 0); } static void xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { struct client_state *state = data; state->running = 0; } void xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h) { } void xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities) { } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_configure, .close = xdg_toplevel_close, .configure_bounds = xdg_toplevel_configure_bounds, .wm_capabilities = xdg_toplevel_wm_capabilities, }; static void zwlr_layer_surface_v1_configure(void *data, struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t serial, uint32_t width, uint32_t height) { struct client_state *state = data; state->width = width; state->height = height; wl_egl_window_resize(state->egl_window, width, height, 0, 0); zwlr_layer_surface_v1_ack_configure(zwlr_layer_surface_v1, serial); } static void zwlr_layer_surface_v1_closed(void *data, struct zwlr_layer_surface_v1 * zwlr_layer_surface_v1) { struct client_state *state = data; state->running = 0; } static const struct zwlr_layer_surface_v1_listener zwlr_layer_surface_v1_listener = { .configure = zwlr_layer_surface_v1_configure, .closed = zwlr_layer_surface_v1_closed, }; static void registry_handle_global( void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version ) { printf("interface: '%s', version: %d, name: %d\n", interface, version, name); struct client_state *state = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { state->wl_compositor = wl_registry_bind( registry, name, &wl_compositor_interface, 4); // Set up wl_surface state->wl_surface = wl_compositor_create_surface(state->wl_compositor); } if ((strcmp(interface, xdg_wm_base_interface.name) == 0) && (state->output_type == OUTPUT_WINDOW)) { state->xdg_wm_base = wl_registry_bind( registry, name, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(state->xdg_wm_base, &xdg_wm_base_listener, state); state->xdg_surface = xdg_wm_base_get_xdg_surface( state->xdg_wm_base, state->wl_surface); xdg_surface_add_listener(state->xdg_surface, &xdg_surface_listener, state); state->xdg_toplevel = xdg_surface_get_toplevel(state->xdg_surface); xdg_toplevel_set_title(state->xdg_toplevel, "GLONKERS! 🕴️"); xdg_toplevel_add_listener(state->xdg_toplevel, &xdg_toplevel_listener, state); } if ((strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) && (state->output_type == OUTPUT_LAYER)) { state->zwlr_layer_shell_v1 = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 4); } if (strcmp(interface, wl_output_interface.name) == 0) { state->wl_output = wl_registry_bind( registry, name, &wl_output_interface, 4); } } static void registry_handle_global_remove( void *data, struct wl_registry *registry, uint32_t name ) { // This space deliberately left blank } static const struct wl_registry_listener wl_registry_listener = { .global = registry_handle_global, .global_remove = registry_handle_global_remove, }; static void egl_init(struct client_state *state) { EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; state->egl_display = eglGetDisplay((EGLNativeDisplayType) state->wl_display); if (state->egl_display == EGL_NO_DISPLAY) { fprintf(stderr, "Can't create egl display\n"); exit(1); } else { fprintf(stderr, "Created egl display\n"); } EGLint major, minor; if (eglInitialize(state->egl_display, &major, &minor) != EGL_TRUE) { fprintf(stderr, "Can't initialise egl display\n"); exit(1); } printf("EGL major: %d, minor %d\n", major, minor); EGLint count; eglGetConfigs(state->egl_display, NULL, 0, &count); printf("EGL has %d configs\n", count); EGLConfig *configs = calloc(count, sizeof *configs); EGLint n; eglChooseConfig(state->egl_display, config_attribs, configs, count, &n); for (int i = 0; i < n; i++) { EGLint size; eglGetConfigAttrib(state->egl_display, configs[i], EGL_BUFFER_SIZE, &size); printf("Buffer size for config %d is %d\n", i, size); eglGetConfigAttrib(state->egl_display, configs[i], EGL_RED_SIZE, &size); printf("Red size for config %d is %d\n", i, size); // just choose the first one state->egl_config = configs[i]; break; } // Desktop GL? eglBindAPI(EGL_OPENGL_API); state->egl_context = eglCreateContext(state->egl_display, state->egl_config, EGL_NO_CONTEXT, context_attribs); // EGL window state->egl_window = wl_egl_window_create(state->wl_surface, state->width, state->height); if (state->egl_window == EGL_NO_SURFACE) { fprintf(stderr, "Can't create egl window\n"); exit(1); } else { fprintf(stderr, "Created egl window\n"); } state->egl_surface = eglCreateWindowSurface(state->egl_display, state->egl_config, (unsigned long) state->egl_window, NULL); if (eglMakeCurrent(state->egl_display, state->egl_surface, state->egl_surface, state->egl_context)) { fprintf(stderr, "Made current\n"); } else { fprintf(stderr, "Made current failed\n"); } } /// Initializes wayland and creates an opengl context void wayland_init(struct client_state *state, int output_type) { int width = 700, height = 700; state->width = width; state->height = height; state->running = 1; state->output_type = output_type; state->wl_display = wl_display_connect(NULL); state->wl_registry = wl_display_get_registry(state->wl_display); state->wl_output = NULL; wl_registry_add_listener(state->wl_registry, &wl_registry_listener, state); wl_display_roundtrip(state->wl_display); if (output_type == OUTPUT_LAYER) { // wlr_layer_shell int layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; state->zwlr_layer_surface_v1 = zwlr_layer_shell_v1_get_layer_surface(state->zwlr_layer_shell_v1, state->wl_surface, state->wl_output, layer, "wallpaper"); zwlr_layer_surface_v1_set_anchor(state->zwlr_layer_surface_v1, ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT); zwlr_layer_surface_v1_set_size(state->zwlr_layer_surface_v1, 0, 0); zwlr_layer_surface_v1_add_listener(state->zwlr_layer_surface_v1, &zwlr_layer_surface_v1_listener, state); } wl_surface_commit(state->wl_surface); egl_init(state); } /// Swaps front/backbuffers and dispatches pending wayland commands. void commit(struct client_state *state) { wl_display_dispatch(state->wl_display); eglSwapBuffers(state->egl_display, state->egl_surface); }