// SPDX-License-Identifier: GPL-3.0-only /* * Wayland buffer manager and surface. * * Copyright (c) 2024, Richard Acayan. All rights reserved. */ #include #include #include #include #include "graphics.h" #include "ufkbd.h" #include "layout.h" #include "wayland.h" #include "wayland/viewporter.h" #include "wayland/wlr-layer-shell-unstable-v1.h" extern struct ufkbd_driver ufkbd_wayland; static int ufkbd_wl_surface_resize_buffers(struct ufkbd_wl_surface *ctx, uint32_t width, uint32_t height); static void on_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t width, uint32_t height) { struct ufkbd_wl_surface *ctx = data; struct ufkbd_input_wayland *ufkbd_wl = ctx->ufkbd_wl; struct ufkbd_ctx *ufkbd = ufkbd_wl->ufkbd; zwlr_layer_surface_v1_ack_configure(surface, serial); wp_viewport_set_source(ctx->vp, 0, 0, width * 256 * ctx->scale, height * 256 * ctx->scale); wp_viewport_set_destination(ctx->vp, width, height); ufkbd_wl_surface_resize_buffers(ctx, width, height); ufkbd_layout_resize(ufkbd->layout, width, height); ufkbd_graphics_draw_layout(ufkbd, ufkbd_wl->ufkbd->layout, width, height); } static void on_surface_closed(void *data, struct zwlr_layer_surface_v1 *toplev) { struct ufkbd_wl_surface *ctx = data; struct ufkbd_input_wayland *ufkbd_wl = ctx->ufkbd_wl; struct ufkbd_ctx *ufkbd = ufkbd_wl->ufkbd; ufkbd_terminate(ufkbd); } static const struct zwlr_layer_surface_v1_listener surface_listener = { .configure = on_surface_configure, .closed = on_surface_closed, }; static int try_create_layer_surface(struct ufkbd_wl_surface *ctx) { if (ctx->wl == NULL) return -EINVAL; if (ctx->shell == NULL) return -EINVAL; if (ctx->vper == NULL) return -EINVAL; ctx->vp = wp_viewporter_get_viewport(ctx->vper, ctx->wl); if (ctx->vp == NULL) return -ENOMEM; ctx->wlr = zwlr_layer_shell_v1_get_layer_surface(ctx->shell, ctx->wl, NULL, ZWLR_LAYER_SHELL_V1_LAYER_TOP, "ufkbd-osk"); if (ctx->wlr == NULL) return -ENOMEM; zwlr_layer_surface_v1_set_anchor(ctx->wlr, ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM); zwlr_layer_surface_v1_set_size(ctx->wlr, 0, 185); zwlr_layer_surface_v1_set_exclusive_zone(ctx->wlr, 185); zwlr_layer_surface_v1_add_listener(ctx->wlr, &surface_listener, ctx); // Commit the role given to the surface wl_surface_commit(ctx->wl); return 0; } static void on_global_compositor(void *data, struct wl_registry *reg, uint32_t name, uint32_t version) { struct ufkbd_wl_surface *ctx = data; if (version < WL_COMPOSITOR_CREATE_SURFACE_SINCE_VERSION) return; ctx->compositor = wl_registry_bind(reg, name, &wl_compositor_interface, version); if (ctx->compositor == NULL) { fprintf(stderr, "warn: could not bind to compositor\n"); return; } ctx->wl = wl_compositor_create_surface(ctx->compositor); if (ctx->wl == NULL) { fprintf(stderr, "warn: no valid surface\n"); return; } try_create_layer_surface(ctx); } static void on_global_shell(void *data, struct wl_registry *reg, uint32_t name, uint32_t version) { struct ufkbd_wl_surface *ctx = data; ctx->shell = wl_registry_bind(reg, name, &zwlr_layer_shell_v1_interface, version); if (ctx->shell == NULL) { fprintf(stderr, "wl: warn: could not bind to window manager\n"); return; } try_create_layer_surface(ctx); } static void on_global_viewporter(void *data, struct wl_registry *reg, uint32_t name, uint32_t version) { struct ufkbd_wl_surface *ctx = data; ctx->vper = wl_registry_bind(reg, name, &wp_viewporter_interface, version); if (ctx->vper == NULL) { fprintf(stderr, "wl: warn: could not bind to viewporter\n"); return; } try_create_layer_surface(ctx); } int ufkbd_wl_surface_consume_buffer(struct ufkbd_wl_surface *ctx, struct wl_buffer **buf, void **ptr) { int ret, i; if (buf == NULL || ptr == NULL) return -EINVAL; for (i = 0; i < 2; i++) { ret = ufkbd_wl_buffer_consume(&ctx->bufs[i], buf, ptr); if (!ret) { wl_surface_attach(ctx->wl, *buf, 0, 0); ctx->used_buf = i; break; } if (ret != -EBUSY) break; } return ret; } static int ufkbd_wl_surface_resize_buffers(struct ufkbd_wl_surface *ctx, uint32_t width, uint32_t height) { int ret; ctx->stride = (size_t) width * 4; ret = ufkbd_wl_buffer_resize(&ctx->bufs[0], width * ctx->scale, height * ctx->scale); if (ret) return ret; return ufkbd_wl_buffer_resize(&ctx->bufs[1], width * ctx->scale, height * ctx->scale); } void ufkbd_wl_surface_release_unused(struct ufkbd_wl_surface *ctx) { int i; for (i = 0; i < 2; i++) { if (i != ctx->used_buf) ufkbd_wl_buffer_release(&ctx->bufs[i]); } } int ufkbd_wl_surface_init(struct ufkbd_input_wayland *ufkbd_wl, struct ufkbd_wl_surface **out) { struct ufkbd_wl_surface *ctx; int ret; ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) return -errno; ctx->ufkbd_wl = ufkbd_wl; ctx->scale = 1; ctx->used_buf = -1; ret = ufkbd_wl_buffer_init(&ctx->bufs[0], ufkbd_wl, 0); if (ret) goto err_free_ctx; ret = ufkbd_wl_buffer_init(&ctx->bufs[1], ufkbd_wl, 1); if (ret) goto err_uninit_buf0; ctx->listener_comp.iface = &wl_compositor_interface; ctx->listener_comp.cb = on_global_compositor; ctx->listener_comp.data = ctx; ret = ufkbd_wl_global_listener_add(ufkbd_wl, &ctx->listener_comp); if (ret) goto err_uninit_buf1; ctx->listener_shell.iface = &zwlr_layer_shell_v1_interface; ctx->listener_shell.cb = on_global_shell; ctx->listener_shell.data = ctx; ret = ufkbd_wl_global_listener_add(ufkbd_wl, &ctx->listener_shell); if (ret) goto err_uninit_buf1; ctx->listener_viewporter.iface = &wp_viewporter_interface; ctx->listener_viewporter.cb = on_global_viewporter; ctx->listener_viewporter.data = ctx; ret = ufkbd_wl_global_listener_add(ufkbd_wl, &ctx->listener_viewporter); if (ret) goto err_uninit_buf1; ufkbd_graphics_set_scale(ctx->ufkbd_wl->ufkbd->graphics, ctx->scale); *out = ctx; return 0; err_uninit_buf1: ufkbd_wl_buffer_uninit(&ctx->bufs[1]); err_uninit_buf0: ufkbd_wl_buffer_uninit(&ctx->bufs[0]); err_free_ctx: free(ctx); return ret; } void ufkbd_wl_surface_uninit(struct ufkbd_wl_surface *ctx) { if (ctx->vper != NULL) wp_viewporter_destroy(ctx->vper); if (ctx->vp != NULL) wp_viewport_destroy(ctx->vp); if (ctx->wlr != NULL) zwlr_layer_surface_v1_destroy(ctx->wlr); if (ctx->wl != NULL) wl_surface_destroy(ctx->wl); if (ctx->shell != NULL) zwlr_layer_shell_v1_destroy(ctx->shell); if (ctx->compositor != NULL) wl_compositor_destroy(ctx->compositor); ufkbd_wl_buffer_uninit(&ctx->bufs[1]); ufkbd_wl_buffer_uninit(&ctx->bufs[0]); free(ctx); }