main
44ceea7 ยท 1 month ago 7 commits
  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}