// SPDX-License-Identifier: GPL-3.0-only /* * Wayland buffer management. * * Copyright (c) 2024, Richard Acayan. All rights reserved. */ #include #include #include #include #include #include #include "wayland.h" static void on_buffer_release(void *data, struct wl_buffer *buf) { struct ufkbd_wl_buffer *ctx = data; ufkbd_wl_buffer_release(ctx); } static const struct wl_buffer_listener buf_listener = { .release = on_buffer_release, }; static int commit_buffer_resize(struct ufkbd_wl_buffer *ctx) { int ret; wl_buffer_destroy(ctx->buf); ret = ftruncate(ctx->fd, ctx->size); if (ret == -1) { perror("wl: failed to resize shared memory"); return -errno; } ctx->ptr = mmap(NULL, ctx->size, PROT_READ | PROT_WRITE, MAP_SHARED, ctx->fd, 0); if (ctx->ptr == MAP_FAILED) { perror("wl: failed to map buffer"); return -errno; } wl_shm_pool_resize(ctx->pool, ctx->size); ctx->buf = wl_shm_pool_create_buffer(ctx->pool, 0, ctx->width, ctx->height, ctx->width * 4, WL_SHM_FORMAT_XRGB8888); if (ctx->buf == NULL) { fprintf(stderr, "wl: lost a buffer to failed resize\n"); ctx->avail = false; } wl_buffer_add_listener(ctx->buf, &buf_listener, ctx); ctx->resize = false; return 0; } static void on_wl_shm(void *data, struct wl_registry *reg, uint32_t name, uint32_t ver) { struct ufkbd_wl_buffer *ctx = data; ctx->wl = wl_registry_bind(reg, name, &wl_shm_interface, ver); if (ctx->wl == NULL) { fprintf(stderr, "wl: failed to bind to shared memory\n"); return; } ctx->pool = wl_shm_create_pool(ctx->wl, ctx->fd, 4); if (ctx->pool == NULL) { fprintf(stderr, "wl: failed to create memory pool\n"); return; } ctx->buf = wl_shm_pool_create_buffer(ctx->pool, 0, 1, 1, 4, WL_SHM_FORMAT_XRGB8888); if (ctx->buf == NULL) { fprintf(stderr, "wl: failed to create buffer\n"); return; } wl_buffer_add_listener(ctx->buf, &buf_listener, ctx); } int ufkbd_wl_buffer_consume(struct ufkbd_wl_buffer *ctx, struct wl_buffer **buf, void **ptr) { if (buf == NULL || ptr == NULL) return -EINVAL; if (!ctx->avail) return -EBUSY; ctx->avail = false; *buf = ctx->buf; *ptr = ctx->ptr; return 0; } int ufkbd_wl_buffer_resize(struct ufkbd_wl_buffer *ctx, uint32_t width, uint32_t height) { int ret; if (width == 0 && height == 0) return -EINVAL; ctx->width = width; ctx->height = height; if (ctx->size < ctx->width * ctx->height * 4) { munmap(ctx->ptr, ctx->size); ctx->size = ctx->width * ctx->height * 4; } if (ctx->avail) { ret = commit_buffer_resize(ctx); if (ret) return ret; } else { ctx->resize = true; } return 0; } void ufkbd_wl_buffer_release(struct ufkbd_wl_buffer *ctx) { if (ctx->resize) commit_buffer_resize(ctx); ctx->avail = true; } int ufkbd_wl_buffer_init(struct ufkbd_wl_buffer *ctx, struct ufkbd_input_wayland *ufkbd_wl, unsigned int id) { intmax_t pid; int ret; ctx->ufkbd = ufkbd_wl->ufkbd; ctx->id = id; ctx->avail = true; ctx->dirty = false; ctx->resize = false; ctx->listener.iface = &wl_shm_interface; ctx->listener.cb = on_wl_shm; ctx->listener.data = ctx; pid = getpid(); snprintf(ctx->path, SHM_PATH_SIZE, "/wl_shm_ufkbd_pid%jd_%u", pid, id); ctx->path[SHM_PATH_SIZE - 1] = '\0'; ctx->fd = shm_open(ctx->path, O_RDWR | O_CREAT | O_EXCL, 0600); if (ctx->fd == -1) { perror("wl: failed to create shared memory"); return -errno; } ret = ftruncate(ctx->fd, 4); if (ret) { perror("wl: failed to allocate 4 bytes on shm"); ret = -errno; goto err; } ctx->ptr = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, ctx->fd, 0); if (ctx->ptr == MAP_FAILED) { perror("wl: failed to map buffer"); ret = -errno; goto err; } ret = ufkbd_wl_global_listener_add(ufkbd_wl, &ctx->listener); if (ret) goto err; ctx->width = 1; ctx->height = 1; ctx->size = 4; return 0; err: close(ctx->fd); shm_unlink(ctx->path); return ret; } void ufkbd_wl_buffer_uninit(struct ufkbd_wl_buffer *ctx) { if (ctx->buf != NULL) wl_buffer_destroy(ctx->buf); if (ctx->pool != NULL) wl_shm_pool_destroy(ctx->pool); if (ctx->wl != NULL) wl_shm_destroy(ctx->wl); close(ctx->fd); shm_unlink(ctx->path); }