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