diff options
author | adnano <me@adnano.co> | 2022-01-16 08:32:58 -0500 |
---|---|---|
committer | adnano <me@adnano.co> | 2022-01-16 08:32:58 -0500 |
commit | 2f1c189d535c2d8dce74ec44c670305f00e4a30c (patch) | |
tree | 79811ea0ebe16d28b4bd7bfe2cff1db1977919db /pool-buffer.c | |
download | wmenu-2f1c189d535c2d8dce74ec44c670305f00e4a30c.tar.gz |
Initial commit
Diffstat (limited to 'pool-buffer.c')
-rw-r--r-- | pool-buffer.c | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/pool-buffer.c b/pool-buffer.c new file mode 100644 index 0000000..aca95a1 --- /dev/null +++ b/pool-buffer.c @@ -0,0 +1,145 @@ +#define _POSIX_C_SOURCE 200809L +#include <assert.h> +#include <cairo.h> +#include <errno.h> +#include <fcntl.h> +#include <pango/pangocairo.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <time.h> +#include <unistd.h> +#include <wayland-client.h> +#include "pool-buffer.h" + +static void randname(char *buf) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + buf[i] = 'A'+(r&15)+(r&16)*2; + r >>= 5; + } +} + +static int anonymous_shm_open(void) { + char name[] = "/wmenu-XXXXXX"; + int retries = 100; + + do { + randname(name + strlen(name) - 6); + + --retries; + // shm_open guarantees that O_CLOEXEC is set + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + + return -1; +} + +static int create_shm_file(off_t size) { + int fd = anonymous_shm_open(); + if (fd < 0) { + return fd; + } + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} + +static void buffer_release(void *data, struct wl_buffer *wl_buffer) { + struct pool_buffer *buffer = data; + buffer->busy = false; +} + +static const struct wl_buffer_listener buffer_listener = { + .release = buffer_release +}; + +static struct pool_buffer *create_buffer(struct wl_shm *shm, + struct pool_buffer *buf, int32_t width, int32_t height, + int32_t scale, uint32_t format) { + uint32_t stride = width * scale * 4; + size_t size = stride * height * scale; + + int fd = create_shm_file(size); + assert(fd != -1); + void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); + buf->buffer = wl_shm_pool_create_buffer(pool, 0, + width * scale, height * scale, stride, format); + wl_shm_pool_destroy(pool); + close(fd); + + buf->size = size; + buf->width = width; + buf->height = height; + buf->scale = scale; + buf->data = data; + buf->surface = cairo_image_surface_create_for_data(data, + CAIRO_FORMAT_ARGB32, width * scale, height * scale, stride); + cairo_surface_set_device_scale(buf->surface, scale, scale); + buf->cairo = cairo_create(buf->surface); + buf->pango = pango_cairo_create_context(buf->cairo); + + wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); + return buf; +} + +void destroy_buffer(struct pool_buffer *buffer) { + if (buffer->buffer) { + wl_buffer_destroy(buffer->buffer); + } + if (buffer->cairo) { + cairo_destroy(buffer->cairo); + } + if (buffer->surface) { + cairo_surface_destroy(buffer->surface); + } + if (buffer->pango) { + g_object_unref(buffer->pango); + } + if (buffer->data) { + munmap(buffer->data, buffer->size); + } + memset(buffer, 0, sizeof(struct pool_buffer)); +} + +struct pool_buffer *get_next_buffer(struct wl_shm *shm, + struct pool_buffer pool[static 2], int32_t width, int32_t height, int32_t scale) { + struct pool_buffer *buffer = NULL; + + for (size_t i = 0; i < 2; ++i) { + if (pool[i].busy) { + continue; + } + buffer = &pool[i]; + } + + if (!buffer) { + return NULL; + } + + if (buffer->width != width || buffer->height != height + || buffer->scale != scale) { + destroy_buffer(buffer); + } + + if (!buffer->buffer) { + if (!create_buffer(shm, buffer, width, height, scale, + WL_SHM_FORMAT_ARGB8888)) { + return NULL; + } + } + buffer->busy = true; + return buffer; +} |