pool-buffer.c - wmenu - [fork] efficient dynamic menu for wayland
 (HTM) git clone https://git.drkhsh.at/wmenu.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       pool-buffer.c (3337B)
       ---
            1 #define _POSIX_C_SOURCE 200809L
            2 #include <assert.h>
            3 #include <cairo.h>
            4 #include <errno.h>
            5 #include <fcntl.h>
            6 #include <pango/pangocairo.h>
            7 #include <stdio.h>
            8 #include <stdlib.h>
            9 #include <string.h>
           10 #include <sys/mman.h>
           11 #include <time.h>
           12 #include <unistd.h>
           13 #include <wayland-client.h>
           14 
           15 #include "pool-buffer.h"
           16 
           17 static void randname(char *buf) {
           18         struct timespec ts;
           19         clock_gettime(CLOCK_REALTIME, &ts);
           20         long r = ts.tv_nsec;
           21         for (int i = 0; i < 6; ++i) {
           22                 buf[i] = 'A'+(r&15)+(r&16)*2;
           23                 r >>= 5;
           24         }
           25 }
           26 
           27 static int anonymous_shm_open(void) {
           28         char name[] = "/wmenu-XXXXXX";
           29         int retries = 100;
           30 
           31         do {
           32                 randname(name + strlen(name) - 6);
           33 
           34                 --retries;
           35                 // shm_open guarantees that O_CLOEXEC is set
           36                 int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
           37                 if (fd >= 0) {
           38                         shm_unlink(name);
           39                         return fd;
           40                 }
           41         } while (retries > 0 && errno == EEXIST);
           42 
           43         return -1;
           44 }
           45 
           46 static int create_shm_file(off_t size) {
           47         int fd = anonymous_shm_open();
           48         if (fd < 0) {
           49                 return fd;
           50         }
           51 
           52         if (ftruncate(fd, size) < 0) {
           53                 close(fd);
           54                 return -1;
           55         }
           56 
           57         return fd;
           58 }
           59 
           60 static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
           61         struct pool_buffer *buffer = data;
           62         buffer->busy = false;
           63 }
           64 
           65 static const struct wl_buffer_listener buffer_listener = {
           66         .release = buffer_release
           67 };
           68 
           69 static struct pool_buffer *create_buffer(struct wl_shm *shm,
           70                 struct pool_buffer *buf, int32_t width, int32_t height,
           71                 int32_t scale, uint32_t format) {
           72         int32_t stride = width * scale * 4;
           73         int32_t size = stride * height * scale;
           74 
           75         int fd = create_shm_file(size);
           76         assert(fd != -1);
           77         void *data = mmap(NULL, (size_t)size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
           78         struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
           79         buf->buffer = wl_shm_pool_create_buffer(pool, 0,
           80                         width * scale, height * scale, stride, format);
           81         wl_shm_pool_destroy(pool);
           82         close(fd);
           83 
           84         buf->size = (size_t)size;
           85         buf->width = width;
           86         buf->height = height;
           87         buf->scale = scale;
           88         buf->data = data;
           89         buf->surface = cairo_image_surface_create_for_data(data,
           90                         CAIRO_FORMAT_ARGB32, width * scale, height * scale, stride);
           91         cairo_surface_set_device_scale(buf->surface, scale, scale);
           92         buf->cairo = cairo_create(buf->surface);
           93         buf->pango = pango_cairo_create_context(buf->cairo);
           94 
           95         wl_buffer_add_listener(buf->buffer, &buffer_listener, buf);
           96         return buf;
           97 }
           98 
           99 void destroy_buffer(struct pool_buffer *buffer) {
          100         if (buffer->buffer) {
          101                 wl_buffer_destroy(buffer->buffer);
          102         }
          103         if (buffer->cairo) {
          104                 cairo_destroy(buffer->cairo);
          105         }
          106         if (buffer->surface) {
          107                 cairo_surface_destroy(buffer->surface);
          108         }
          109         if (buffer->pango) {
          110                 g_object_unref(buffer->pango);
          111         }
          112         if (buffer->data) {
          113                 munmap(buffer->data, buffer->size);
          114         }
          115         memset(buffer, 0, sizeof(struct pool_buffer));
          116 }
          117 
          118 struct pool_buffer *get_next_buffer(struct wl_shm *shm,
          119                 struct pool_buffer pool[static 2], int32_t width, int32_t height, int32_t scale) {
          120         struct pool_buffer *buffer = NULL;
          121 
          122         for (size_t i = 0; i < 2; ++i) {
          123                 if (pool[i].busy) {
          124                         continue;
          125                 }
          126                 buffer = &pool[i];
          127         }
          128 
          129         if (!buffer) {
          130                 return NULL;
          131         }
          132 
          133         if (buffer->width != width || buffer->height != height
          134                         || buffer->scale != scale) {
          135                 destroy_buffer(buffer);
          136         }
          137 
          138         if (!buffer->buffer) {
          139                 if (!create_buffer(shm, buffer, width, height, scale,
          140                                         WL_SHM_FORMAT_ARGB8888)) {
          141                         return NULL;
          142                 }
          143         }
          144         buffer->busy = true;
          145         return buffer;
          146 }