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 }