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 }