1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <cairo/cairo.h>
4#include <errno.h>
5#include <fcntl.h>
6#include <stdio.h>
7#include <stdlib.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 "pool-buffer.h"
14
15static int anonymous_shm_open(void) {
16 int retries = 100;
17
18 do {
19 // try a probably-unique name
20 struct timespec ts;
21 clock_gettime(CLOCK_MONOTONIC, &ts);
22 pid_t pid = getpid();
23 char name[50];
24 snprintf(name, sizeof(name), "/swaylock-%x-%x",
25 (unsigned int)pid, (unsigned int)ts.tv_nsec);
26
27 // shm_open guarantees that O_CLOEXEC is set
28 int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
29 if (fd >= 0) {
30 shm_unlink(name);
31 return fd;
32 }
33
34 --retries;
35 } while (retries > 0 && errno == EEXIST);
36
37 return -1;
38}
39
40static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
41 struct pool_buffer *buffer = data;
42 buffer->busy = false;
43}
44
45static const struct wl_buffer_listener buffer_listener = {
46 .release = buffer_release
47};
48
49static struct pool_buffer *create_buffer(struct wl_shm *shm,
50 struct pool_buffer *buf, int32_t width, int32_t height,
51 uint32_t format) {
52 uint32_t stride = width * 4;
53 size_t size = stride * height;
54
55 void *data = NULL;
56 if (size > 0) {
57 int fd = anonymous_shm_open();
58 if (fd == -1) {
59 return NULL;
60 }
61 if (ftruncate(fd, size) < 0) {
62 close(fd);
63 return NULL;
64 }
65 data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
66 struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
67 buf->buffer = wl_shm_pool_create_buffer(pool, 0,
68 width, height, stride, format);
69 wl_buffer_add_listener(buf->buffer, &buffer_listener, buf);
70 wl_shm_pool_destroy(pool);
71 close(fd);
72 }
73
74 buf->size = size;
75 buf->width = width;
76 buf->height = height;
77 buf->data = data;
78 buf->surface = cairo_image_surface_create_for_data(data,
79 CAIRO_FORMAT_ARGB32, width, height, stride);
80 buf->cairo = cairo_create(buf->surface);
81 return buf;
82}
83
84void destroy_buffer(struct pool_buffer *buffer) {
85 if (buffer->buffer) {
86 wl_buffer_destroy(buffer->buffer);
87 }
88 if (buffer->cairo) {
89 cairo_destroy(buffer->cairo);
90 }
91 if (buffer->surface) {
92 cairo_surface_destroy(buffer->surface);
93 }
94 if (buffer->data) {
95 munmap(buffer->data, buffer->size);
96 }
97 memset(buffer, 0, sizeof(struct pool_buffer));
98}
99
100struct pool_buffer *get_next_buffer(struct wl_shm *shm,
101 struct pool_buffer pool[static 2], uint32_t width, uint32_t height) {
102 struct pool_buffer *buffer = NULL;
103
104 for (size_t i = 0; i < 2; ++i) {
105 if (pool[i].busy) {
106 continue;
107 }
108 buffer = &pool[i];
109 }
110
111 if (!buffer) {
112 return NULL;
113 }
114
115 if (buffer->width != width || buffer->height != height) {
116 destroy_buffer(buffer);
117 }
118
119 if (!buffer->buffer) {
120 if (!create_buffer(shm, buffer, width, height,
121 WL_SHM_FORMAT_ARGB8888)) {
122 return NULL;
123 }
124 }
125 buffer->busy = true;
126 return buffer;
127}