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 }