commit cb31c30

sewn  ·  2026-05-07 20:01:45 +0000 UTC
parent 18d72bf
drop usage of wp_viewporter
3 files changed,  +92, -122
M wawa.c
+1, -5
 1@@ -17,7 +17,7 @@ LDLIBS   = $(LIBS)
 2 
 3 all: wawa
 4 
 5-PROTO = viewporter-protocol.h wlr-layer-shell-unstable-v1-protocol.h xdg-shell-protocol.h
 6+PROTO = wlr-layer-shell-unstable-v1-protocol.h xdg-shell-protocol.h
 7 OBJ = wawa.o $(PROTO:.h=.o)
 8 
 9 wawa.c: $(PROTO)
10@@ -32,10 +32,6 @@ xdg-shell-protocol.h:
11 	$(WAYLAND_SCANNER) client-header $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
12 xdg-shell-protocol.c:
13 	$(WAYLAND_SCANNER) private-code $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
14-viewporter-protocol.h:
15-	$(WAYLAND_SCANNER) client-header $(WAYLAND_PROTOCOLS)/stable/viewporter/viewporter.xml $@
16-viewporter-protocol.c:
17-	$(WAYLAND_SCANNER) private-code $(WAYLAND_PROTOCOLS)/stable/viewporter/viewporter.xml $@
18 wlr-layer-shell-unstable-v1-protocol.h:
19 	$(WAYLAND_SCANNER) client-header wlr-layer-shell-unstable-v1.xml $@
20 wlr-layer-shell-unstable-v1-protocol.c:
+13, -8
 1@@ -1,18 +1,23 @@
 2 # wawa
 3 
 4-A even simpler Wayland wallpaper setter that targets `wlr-layer-shell` supported
 5-compositors. It takes in a path and zooms it to each screen.
 6+A even simpler Wayland wallpaper setter utilizing `stb_image` that targets `wlr-layer-shell`
 7+supported compositors. It takes in a path and zooms it to each screen.
 8 
 9 ## Comparison
10 
11 [swaybg] uses cairo, and has a significant amount of customization, with
12-a slightly larger codebase. [wbg] is more sophisticated, but is simpler,
13-and relies on speed using minimal libraries. Both seem to create a buffer
14-for each screen, and resize the image from its side before pushing to the
15-compositor. wawa retains a single buffer until all monitors have been set
16-up, and uses `wp_viewporter` to scale or crop the image to the monitor.
17+a slightly larger codebase. [wbg] is more sophisticated, but is simpler than
18+swaybg, and relies on speed using minimal third-party libraries for loading
19+images.
20 
21-It is reccomended you stick to [swaybg].
22+Unfortunately, wbg is not simple enough. It forces the average user if they
23+want more than just fitting the wallpaper to the screen - which is completely
24+undesirable for 4:3 wallpapers or other monitors, which wbg doesn't even have
25+support for setting for each monitor, wawa zooms the image by default, and is
26+significantly simpler.
27+
28+It is reccomended you stick to [swaybg] for all intents and purposes. wawa aims
29+to be a simpler replacement for [swaybg] eventually.
30 
31 [swaybg]: https://github.com/swaywm/swaybg
32 [wbg]: https://codeberg.org/dnkl/wbg
M wawa.c
+78, -109
  1@@ -12,11 +12,14 @@
  2 #include <linux/memfd.h>
  3 #endif
  4 
  5+#include "stbi_alloc.h"
  6+
  7 #define STB_IMAGE_IMPLEMENTATION
  8 #include "stb_image.h"
  9+#define STB_IMAGE_RESIZE_IMPLEMENTATION
 10+#include "stb_image_resize2.h"
 11 
 12 #include "wlr-layer-shell-unstable-v1-protocol.h"
 13-#include "viewporter-protocol.h"
 14 
 15 struct output {
 16 	uint32_t registry_name;
 17@@ -24,32 +27,32 @@ struct output {
 18 	struct wl_output *wl;
 19 	struct wl_surface *surface;
 20 	struct zwlr_layer_surface_v1 *layer_surface;
 21-	struct wp_viewport *viewport;	
 22+
 23+	uint32_t width, height;
 24+	uint32_t size, stride;
 25 
 26 	bool configured;
 27 
 28 	struct wl_list link;
 29 };
 30 
 31+/*
 32+ * Must be valid when a single monitor is misconfigured
 33+ * or during startup, after which the image is freed.
 34+ */
 35+struct {
 36+	int width, height;
 37+	unsigned char *data;
 38+} image;
 39+
 40 static struct wl_display *display;
 41 static struct wl_registry *registry;
 42 static struct wl_compositor *compositor;
 43-static struct wp_viewporter *viewporter;
 44 static struct wl_shm *shm;
 45 static struct zwlr_layer_shell_v1 *layer_shell;
 46 static struct wl_list outputs;
 47 
 48-/*
 49- * Rather than create, resize, and maintain a image buffer for each
 50- * output available, use wp_viewport to scale and crop the image to the
 51- * output contents by having a full representation of the image.
 52- * This is created every time a set of monitors need to be drawn.
 53- */
 54-struct image {
 55-	struct wl_buffer *buffer;
 56-	const char *filename;
 57-	int32_t width, height;
 58-} image;
 59+static const char *filename;
 60 
 61 static void
 62 die(const char *fmt, ...)
 63@@ -71,129 +74,96 @@ die(const char *fmt, ...)
 64 }
 65 
 66 static void
 67-image_cleanup_callback(void *data, struct wl_callback *callback, uint32_t id)
 68+image_crop(unsigned char *dst, struct output *output)
 69 {
 70-	struct output *output;
 71-	wl_callback_destroy(callback);
 72-
 73-	/* Already destroyed */
 74-	if (!image.buffer) return;
 75-
 76-	wl_list_for_each(output, &outputs, link)
 77-		if (!output->configured) return;
 78-
 79-	wl_buffer_destroy(image.buffer);
 80-	image = (struct image){0};
 81+	struct {
 82+		int width, height;
 83+		int x, y;
 84+	} crop;
 85+	double factor;
 86+
 87+	factor = fmin((double)image.width/output->width, (double)image.height/output->height);
 88+	crop.x = (image.width - (output->width * factor)) / 2;
 89+	crop.y = (image.height - (output->height * factor)) / 2;
 90+	crop.width = output->width * factor;
 91+	crop.height = output->height * factor;
 92+	stbir_resize_uint8_linear(
 93+	  image.data + (crop.y * image.width + crop.x) * 4, crop.width, crop.height, image.width * 4,
 94+		dst, output->width, output->height, output->stride,
 95+		4);
 96 }
 97 
 98-static struct wl_callback_listener image_cleanup_callback_listener = {
 99-	.done = image_cleanup_callback,
100-};
101-
102-static void
103-load_image(void)
104+static struct wl_buffer *
105+output_load_image(struct output *output)
106 {
107-	int fd;
108+	int fd = -1;
109 	struct wl_shm_pool *shm_pool;
110-	uint32_t *data;
111-	unsigned char *image_data;
112-	int32_t size;
113+	struct wl_buffer *buffer;
114+	unsigned char *data;
115+	
116+	fd = memfd_create("drwbuf-shm-buffer-pool",
117+		MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL);
118+	if (fd < 0) die("memfd_create:");
119 
120-	if (!(image_data = stbi_load(image.filename,
121-	                       &image.width, &image.height, NULL, 4)))
122-		die("failed to load image: %s", stbi_failure_reason());
123+	if ((ftruncate(fd, output->size)) < 0) die("ftruncate:");
124 
125-	size = image.width * image.height * 4;
126-	
127-#if defined(__linux__) || \
128-	((defined(__FreeBSD__) && (__FreeBSD_version >= 1300048)))
129-	fd = memfd_create("wawa-shm-buffer",
130-		MFD_CLOEXEC | MFD_ALLOW_SEALING |
131-#if defined(MFD_NOEXEC_SEAL)
132-		MFD_NOEXEC_SEAL
133-#else
134-		0
135-#endif
136-	);
137-#else
138-	char template[] = "/tmp/wawa-XXXXXX";
139-#if defined(__OpenBSD__)
140-	fd = shm_mkstemp(template);
141-#else
142-	fd = mkostemp(template, O_CLOEXEC);
143-#endif
144-	if (fd < 0) die("mktemp:")
145-#if defined(__OpenBSD__)
146-	shm_unlink(template);
147-#else
148-	unlink(template);
149-#endif
150-#endif
151+	data = mmap(NULL, output->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
152+	if (data == MAP_FAILED) die("mmap:");
153 
154-	if ((ftruncate(fd, size)) < 0) {
155-		close(fd);
156-		die("ftruncate:");
157-	}
158+	image_crop(data, output);
159 
160-	data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
161-	if (data == MAP_FAILED) {
162-		close(fd);
163-		die("mmap:");
164+	/* BGR->RGB */ 
165+	for (int i = 0; i < output->size; i += 4) {
166+		data[i+2] ^= (data[i] ^= data[i+2]);
167+		data[i] ^= data[i+2];
168 	}
169 
170-  /* [R, G, B, A] -> ARGB8888 */    
171-	for (int i = 0; i < image.width * image.height; i++)
172-			data[i] =
173-				image_data[4 * i + 3] << 24 |
174-			  image_data[4 * i + 0] << 16 |
175-			  image_data[4 * i + 1] << 8 |
176-			  image_data[4 * i + 2]; 
177-
178-	stbi_image_free(image_data);
179-	munmap(data, size);
180+	munmap(data, output->size);
181 
182 #if defined(__linux__) || \
183 	((defined(__FreeBSD__) && (__FreeBSD_version >= 1300048)))
184 	fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL);
185 #endif
186 
187-	shm_pool = wl_shm_create_pool(shm, fd, size);
188-	image.buffer = wl_shm_pool_create_buffer(shm_pool, 0,
189-		image.width, image.height, image.width * 4, WL_SHM_FORMAT_ARGB8888);
190+	shm_pool = wl_shm_create_pool(shm, fd, output->size);
191+	buffer = wl_shm_pool_create_buffer(shm_pool, 0,
192+		output->width, output->height, output->stride, WL_SHM_FORMAT_ARGB8888);
193 	wl_shm_pool_destroy(shm_pool);
194 	close(fd);
195+
196+	return buffer;
197 }
198 
199 static void
200 layer_surface_handle_configure(void *data, struct zwlr_layer_surface_v1 *layer_surface,
201 		uint32_t serial, uint32_t width, uint32_t height)
202 {
203-	struct wl_callback *callback;
204+	struct wl_buffer *buffer;
205 	struct output *output = data;
206 	zwlr_layer_surface_v1_ack_configure(layer_surface, serial);
207 
208-	/* Load the image only when necessary, and free when all monitors have
209-	 * been configured. */
210-	if (!image.buffer) load_image();
211+	output->configured = width == output->width && height == output->height;
212+	if (output->configured) return;
213 
214-	/* Fit to screen */
215-	double factor = fmin((double)(image.width)/width, (double)(image.height)/height);
216-	wp_viewport_set_source(output->viewport,
217-	    wl_fixed_from_double((image.width - (width * factor)) / 2),
218-	    wl_fixed_from_double((image.height - (height * factor)) / 2),
219-	    wl_fixed_from_double(width * factor), wl_fixed_from_double(height * factor));
220+	if (!image.data) {
221+		fputs("loaded image", stderr);
222+		if (!(image.data = stbi_load(filename,
223+		                       &image.width, &image.height, NULL, 4)))
224+			die("failed to load image: %s", stbi_failure_reason());	
225+	}
226+
227+	output->width = width;
228+	output->height = height;
229+	output->stride = width * 4;
230+	output->size = width * height * 4;
231 
232-	wl_surface_attach(output->surface, image.buffer, 0, 0);
233+	buffer = output_load_image(output);
234+	wl_surface_attach(output->surface, buffer, 0, 0);
235 	wl_surface_damage_buffer(output->surface, 0, 0, INT32_MAX, INT32_MAX);
236-	wp_viewport_set_destination(output->viewport, width, height);
237 	wl_surface_commit(output->surface);
238+	wl_buffer_destroy(buffer);
239 
240 	output->configured = true;
241-
242-	/* Niri doesn't actually tell the buffer that it is released or not.
243-	 * Use a callback.. */
244-	callback = wl_display_sync(display);
245-	wl_callback_add_listener(callback, &image_cleanup_callback_listener, NULL);
246 }
247 
248 static void
249@@ -201,7 +171,6 @@ layer_surface_handle_closed(void *data, struct zwlr_layer_surface_v1 *surface)
250 {
251 	struct output *output = data;
252 	zwlr_layer_surface_v1_destroy(output->layer_surface);
253-	wp_viewport_destroy(output->viewport);
254 	wl_surface_destroy(output->surface);
255 }
256 
257@@ -217,7 +186,6 @@ output_setup_callback(void *data, struct wl_callback *callback,
258 	struct output *output = data;
259 
260 	output->surface = wl_compositor_create_surface(compositor);
261-	output->viewport = wp_viewporter_get_viewport(viewporter, output->surface);
262 	
263 	output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell,
264 				output->surface, output->wl, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper");
265@@ -246,13 +214,12 @@ registry_handle_global(void *data, struct wl_registry *registry,
266 		compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 6);
267  	else if (!strcmp(interface, wl_shm_interface.name))
268 		shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
269-	else if (!strcmp(interface, wp_viewporter_interface.name))
270-		viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1);
271 	else if (!strcmp(interface, zwlr_layer_shell_v1_interface.name))
272 		layer_shell = wl_registry_bind(registry, name,
273 			&zwlr_layer_shell_v1_interface, 1);
274 	else if (!strcmp(interface, wl_output_interface.name)) {
275 		struct output *output = calloc(1, sizeof(struct output));
276+		if (!output) die("calloc:");
277 		output->registry_name = name;
278 		output->wl = wl_registry_bind(registry, name,
279 			&wl_output_interface, 4);
280@@ -277,6 +244,7 @@ registry_handle_remove(void *data, struct wl_registry *registry, uint32_t name)
281 			continue;	
282 		wl_output_destroy(output->wl);
283 		wl_list_remove(&output->link);
284+		free(output);
285 		break;
286 	}
287 }
288@@ -289,12 +257,13 @@ static const struct wl_registry_listener registry_listener = {
289 int
290 main(int argc, char *argv[])
291 {
292+	struct output *output, *tmp;
293 	if (argc != 2) {
294 		fprintf(stderr, "usage: %s filename\n", argv[0]);
295 		return EXIT_FAILURE;
296 	}
297 
298-	image.filename = argv[1];
299+	filename = argv[1];
300 
301 	if (!(display = wl_display_connect(NULL)))
302 		die("failed to connect to wayland");
303@@ -305,7 +274,7 @@ main(int argc, char *argv[])
304 	wl_registry_add_listener(registry, &registry_listener, NULL);
305 	wl_display_roundtrip(display);
306 		
307-	if (!compositor || !layer_shell || !viewporter || !shm)
308+	if (!compositor || !layer_shell || !shm)
309 		die("bad compositor available");
310 
311 	while (wl_display_dispatch(display))