mirror of
				https://codeberg.org/adnano/wmenu.git
				synced 2025-10-29 05:40:20 -04:00 
			
		
		
		
	
		
			
	
	
		
			146 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			146 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | #define _POSIX_C_SOURCE 200809L
 | ||
|  | #include <assert.h>
 | ||
|  | #include <cairo.h>
 | ||
|  | #include <errno.h>
 | ||
|  | #include <fcntl.h>
 | ||
|  | #include <pango/pangocairo.h>
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <stdlib.h>
 | ||
|  | #include <string.h>
 | ||
|  | #include <sys/mman.h>
 | ||
|  | #include <time.h>
 | ||
|  | #include <unistd.h>
 | ||
|  | #include <wayland-client.h>
 | ||
|  | #include "pool-buffer.h"
 | ||
|  | 
 | ||
|  | static void randname(char *buf) { | ||
|  | 	struct timespec ts; | ||
|  | 	clock_gettime(CLOCK_REALTIME, &ts); | ||
|  | 	long r = ts.tv_nsec; | ||
|  | 	for (int i = 0; i < 6; ++i) { | ||
|  | 		buf[i] = 'A'+(r&15)+(r&16)*2; | ||
|  | 		r >>= 5; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static int anonymous_shm_open(void) { | ||
|  | 	char name[] = "/wmenu-XXXXXX"; | ||
|  | 	int retries = 100; | ||
|  | 
 | ||
|  | 	do { | ||
|  | 		randname(name + strlen(name) - 6); | ||
|  | 
 | ||
|  | 		--retries; | ||
|  | 		// shm_open guarantees that O_CLOEXEC is set
 | ||
|  | 		int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); | ||
|  | 		if (fd >= 0) { | ||
|  | 			shm_unlink(name); | ||
|  | 			return fd; | ||
|  | 		} | ||
|  | 	} while (retries > 0 && errno == EEXIST); | ||
|  | 
 | ||
|  | 	return -1; | ||
|  | } | ||
|  | 
 | ||
|  | static int create_shm_file(off_t size) { | ||
|  | 	int fd = anonymous_shm_open(); | ||
|  | 	if (fd < 0) { | ||
|  | 		return fd; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (ftruncate(fd, size) < 0) { | ||
|  | 		close(fd); | ||
|  | 		return -1; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return fd; | ||
|  | } | ||
|  | 
 | ||
|  | static void buffer_release(void *data, struct wl_buffer *wl_buffer) { | ||
|  | 	struct pool_buffer *buffer = data; | ||
|  | 	buffer->busy = false; | ||
|  | } | ||
|  | 
 | ||
|  | static const struct wl_buffer_listener buffer_listener = { | ||
|  | 	.release = buffer_release | ||
|  | }; | ||
|  | 
 | ||
|  | static struct pool_buffer *create_buffer(struct wl_shm *shm, | ||
|  | 		struct pool_buffer *buf, int32_t width, int32_t height, | ||
|  | 		int32_t scale, uint32_t format) { | ||
|  | 	uint32_t stride = width * scale * 4; | ||
|  | 	size_t size = stride * height * scale; | ||
|  | 
 | ||
|  | 	int fd = create_shm_file(size); | ||
|  | 	assert(fd != -1); | ||
|  | 	void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||
|  | 	struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); | ||
|  | 	buf->buffer = wl_shm_pool_create_buffer(pool, 0, | ||
|  | 			width * scale, height * scale, stride, format); | ||
|  | 	wl_shm_pool_destroy(pool); | ||
|  | 	close(fd); | ||
|  | 
 | ||
|  | 	buf->size = size; | ||
|  | 	buf->width = width; | ||
|  | 	buf->height = height; | ||
|  | 	buf->scale = scale; | ||
|  | 	buf->data = data; | ||
|  | 	buf->surface = cairo_image_surface_create_for_data(data, | ||
|  | 			CAIRO_FORMAT_ARGB32, width * scale, height * scale, stride); | ||
|  | 	cairo_surface_set_device_scale(buf->surface, scale, scale); | ||
|  | 	buf->cairo = cairo_create(buf->surface); | ||
|  | 	buf->pango = pango_cairo_create_context(buf->cairo); | ||
|  | 
 | ||
|  | 	wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); | ||
|  | 	return buf; | ||
|  | } | ||
|  | 
 | ||
|  | void destroy_buffer(struct pool_buffer *buffer) { | ||
|  | 	if (buffer->buffer) { | ||
|  | 		wl_buffer_destroy(buffer->buffer); | ||
|  | 	} | ||
|  | 	if (buffer->cairo) { | ||
|  | 		cairo_destroy(buffer->cairo); | ||
|  | 	} | ||
|  | 	if (buffer->surface) { | ||
|  | 		cairo_surface_destroy(buffer->surface); | ||
|  | 	} | ||
|  | 	if (buffer->pango) { | ||
|  | 		g_object_unref(buffer->pango); | ||
|  | 	} | ||
|  | 	if (buffer->data) { | ||
|  | 		munmap(buffer->data, buffer->size); | ||
|  | 	} | ||
|  | 	memset(buffer, 0, sizeof(struct pool_buffer)); | ||
|  | } | ||
|  | 
 | ||
|  | struct pool_buffer *get_next_buffer(struct wl_shm *shm, | ||
|  | 		struct pool_buffer pool[static 2], int32_t width, int32_t height, int32_t scale) { | ||
|  | 	struct pool_buffer *buffer = NULL; | ||
|  | 
 | ||
|  | 	for (size_t i = 0; i < 2; ++i) { | ||
|  | 		if (pool[i].busy) { | ||
|  | 			continue; | ||
|  | 		} | ||
|  | 		buffer = &pool[i]; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (!buffer) { | ||
|  | 		return NULL; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (buffer->width != width || buffer->height != height | ||
|  | 			|| buffer->scale != scale) { | ||
|  | 		destroy_buffer(buffer); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (!buffer->buffer) { | ||
|  | 		if (!create_buffer(shm, buffer, width, height, scale, | ||
|  | 					WL_SHM_FORMAT_ARGB8888)) { | ||
|  | 			return NULL; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	buffer->busy = true; | ||
|  | 	return buffer; | ||
|  | } |