surface.c (3795B)


      1 /*
      2  * Wig --- Widgets in Guile
      3  * Copyright © 2026 Luke Willis <lukejw@monastech.xyz>
      4  *
      5  * This file is part of Wig.
      6  *
      7  * Wig is free software: you can redistribute it and/or modify it under
      8  * the terms of the GNU General Public License as published by the Free
      9  * Software Foundation, either version 3 of the License, or (at your option)
     10  * any later version.
     11  *
     12  * Wig is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
     15  * more details.
     16  *
     17  * You should have received a copy of the GNU General Public License along
     18  * with Wig. If not, see <https://www.gnu.org/licenses/>.
     19  */
     20 
     21 #include <arpa/inet.h>
     22 #include <errno.h>
     23 #include <fcntl.h>
     24 #include <stdio.h>
     25 #include <string.h>
     26 #include <sys/mman.h>
     27 #include <time.h>
     28 #include <unistd.h>
     29 
     30 #include <pixman.h>
     31 #include <wayland-client.h>
     32 
     33 #include "surface.h"
     34 
     35 /*
     36  * SHM Utils
     37  */
     38 static void
     39 randname(char *buf)
     40 {
     41 	struct timespec ts;
     42 	clock_gettime(CLOCK_REALTIME, &ts);
     43 	long r = ts.tv_nsec;
     44 	for (int i = 0; i < 6; ++i) {
     45 		buf[i] = 'A' + (r & 15) + (r & 16) * 2;
     46 		r >>= 5;
     47 	}
     48 }
     49 
     50 static int
     51 create_shm_file(void)
     52 {
     53 	int retries = 100;
     54 	do {
     55 		char name[] = "/wl_shm-XXXXXX";
     56 		randname(name + sizeof(name) - 7);
     57 		--retries;
     58 		int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
     59 		if (fd >= 0) {
     60 			shm_unlink(name);
     61 			return fd;
     62 		}
     63 	} while (retries > 0 && errno == EEXIST);
     64 	return -1;
     65 }
     66 
     67 static int
     68 allocate_shm_file(size_t size)
     69 {
     70 	int fd = create_shm_file();
     71 	if (fd < 0) {
     72 		return -1;
     73 	}
     74 	int ret;
     75 	do {
     76 		ret = ftruncate(fd, size);
     77 	} while (ret < 0 && errno == EINTR);
     78 	if (ret < 0) {
     79 		close(fd);
     80 		return -1;
     81 	}
     82 	return fd;
     83 }
     84 
     85 /*
     86  * Surface
     87  */
     88 int
     89 wig_surface_init(struct wig_surface *surface, struct wl_shm *wl_shm,
     90                  struct wl_compositor *wl_compositor)
     91 {
     92 	surface->wl_shm = wl_shm;
     93 
     94 	surface->configured = 0;
     95 	surface->width = 0;
     96 	surface->height = 0;
     97 	surface->wl_surface = wl_compositor_create_surface(wl_compositor);
     98 
     99 	surface->free = 0;
    100 	surface->buffer = NULL;
    101 	surface->wl_buffer = NULL;
    102 
    103 	return 0;
    104 }
    105 
    106 static void
    107 wl_buffer_release(void *data, struct wl_buffer *wl_buffer)
    108 {
    109 	struct wig_surface *surface = data;
    110 	surface->free = 1;
    111 }
    112 
    113 static const struct wl_buffer_listener wl_buffer_listener = {
    114 	.release = wl_buffer_release,
    115 };
    116 
    117 int
    118 wig_surface_reconfigure(struct wig_surface *surface, uint32_t width,
    119                         uint32_t height)
    120 {
    121 	if (surface->buffer != NULL) {
    122 		wl_buffer_destroy(surface->wl_buffer);
    123 		munmap(surface->buffer, surface->width * surface->height * 4);
    124 	}
    125 
    126 	size_t stride = width * 4;
    127 	size_t size = stride * height;
    128 
    129 	int fd = allocate_shm_file(size);
    130 	if (fd < 0) {
    131 		return -1;
    132 	}
    133 
    134 	uint32_t *buffer =
    135 		mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    136 	if (buffer == MAP_FAILED) {
    137 		close(fd);
    138 		return -1;
    139 	}
    140 
    141 	memset(buffer, 0, size);
    142 
    143 	struct wl_shm_pool *pool =
    144 		wl_shm_create_pool(surface->wl_shm, fd, size);
    145 	struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer(
    146 		pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888);
    147 	wl_shm_pool_destroy(pool);
    148 	close(fd);
    149 
    150 	wl_buffer_add_listener(wl_buffer, &wl_buffer_listener, surface);
    151 
    152 	surface->configured = 1;
    153 	surface->width = width;
    154 	surface->height = height;
    155 
    156 	surface->buffer = buffer;
    157 	surface->wl_buffer = wl_buffer;
    158 	surface->free = 1;
    159 
    160 	return 0;
    161 }
    162 
    163 int
    164 wig_surface_commit(struct wig_surface *surface)
    165 {
    166 	if (surface->configured) {
    167 		if (!surface->free) {
    168 			fprintf(stderr,
    169 			        "Refusing to commit unreleased surface\n");
    170 			return -1;
    171 		}
    172 		wl_surface_attach(surface->wl_surface, surface->wl_buffer, 0,
    173 		                  0);
    174 		surface->free = 0;
    175 	}
    176 
    177 	wl_surface_commit(surface->wl_surface);
    178 
    179 	return 0;
    180 }