unfettered-keyboard/wayland/buffer.c
2024-04-15 22:11:20 -04:00

215 lines
4.2 KiB
C

// SPDX-License-Identifier: GPL-3.0-only
/*
* Wayland buffer management.
*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client-protocol.h>
#include "wayland.h"
static int commit_buffer_resize(struct ufkbd_wl_buffer *ctx);
static void on_buffer_release(void *data, struct wl_buffer *buf)
{
struct ufkbd_wl_buffer *ctx = data;
if (ctx->resize)
commit_buffer_resize(ctx);
ctx->avail = true;
}
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;
}
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);
}