commit d7412e0

sewn  ·  2026-05-14 14:13:41 +0000 UTC
parent c4c3286
support spreading across monitors
2 files changed,  +58, -7
M wawa.c
+6, -3
 1@@ -1,8 +1,9 @@
 2 # wawa
 3 
 4-A simple, hackable Wayland wallpaper setter utilizing `stb_image` that targets
 5-`wlr-layer-shell` supported compositors, with stretch, fit, fill, tile and a color,
 6-with less SLOC than your average wallpaper setter.
 7+A simple, hackable, and disctintive Wayland wallpaper setter utilizing
 8+`stb_image` that targets `wlr-layer-shell` supported compositors, featuring
 9+tiling, spreading across monitors, along with fill, fit and stretching the
10+wallpaper, with less SLOC than your average wallpaper setter.
11 
12 In wawa, the image is loaded only for monitors that need it, and is immediately
13 freed when all monitors have been configured; this keeps the memory usage of
14@@ -23,5 +24,7 @@ Unfortunately, wbg is so simple, while supporting modern libraries, it has only
15 two modes, fit (with `-s` flag) and fill (default), which makes it undesireable
16 for those who want to tile or etc.
17 
18+No other wallpaper setter supports tiling out of the box.
19+
20 [swaybg]: https://github.com/swaywm/swaybg
21 [wbg]: https://codeberg.org/dnkl/wbg
M wawa.c
+52, -4
  1@@ -16,6 +16,9 @@
  2 
  3 #include "wlr-layer-shell-unstable-v1-protocol.h"
  4 
  5+#define MAX(A, B) ((A) > (B) ? (A) : (B))
  6+#define MIN(A, B) ((A) < (B) ? (A) : (B))
  7+
  8 struct output {
  9 	struct wl_output *wl;
 10 	struct wl_surface *surface;
 11@@ -164,6 +167,45 @@ image_tile(unsigned char *dst, struct output *o)
 12 	}
 13 }
 14 
 15+static void
 16+image_spread(unsigned char *dst, struct output *output)
 17+{
 18+	/* i can only claim that i designated the exact ways to perform
 19+	 * the calculations. */
 20+	struct output *o;
 21+	struct rect crop;
 22+	int min_x, min_y, max_x, max_y;
 23+	double scale;
 24+
 25+	/* set initial value to account for incoming negative outputs */
 26+	o = wl_container_of(outputs.next, o, link);
 27+	max_x = (min_x = o->x) + (o->width);
 28+	max_y = (min_y = o->y) + (o->height);
 29+
 30+	wl_list_for_each(o, &outputs, link) {
 31+		/* avoids some obscure compiler optimization ?? */
 32+		int32_t ow = o->width, oh = o->height;
 33+		min_x = MIN(min_x, o->x);
 34+		min_y = MIN(min_y, o->y);
 35+		max_x = MAX(o->x + ow, max_x);
 36+		max_y = MAX(o->y + oh, max_y);
 37+	}
 38+
 39+	crop.width = max_x - min_x;
 40+	crop.height = max_y - min_y;
 41+	scale = 1.0 / fmax((double)crop.width / image.width, (double)crop.height / image.height);
 42+	/* offset total bounding to center */
 43+	crop.x = (output->x - min_x) * scale + (image.width - crop.width * scale) / 2;
 44+	crop.y = (output->y - min_y) * scale + (image.height - crop.height * scale) / 2;
 45+	crop.width = MIN(ceil(output->width * scale), image.width - crop.x);
 46+	crop.height = MIN(ceil(output->height * scale), image.height - crop.y);
 47+
 48+	stbir_resize_uint8_linear(
 49+		image.data + (crop.y * image.width + crop.x) * 4,
 50+		crop.width, crop.height, image.width * 4,
 51+		dst, output->width, output->height, output->stride, 4);
 52+}
 53+
 54 static struct wl_buffer *
 55 output_load_image(struct output *output)
 56 {
 57@@ -211,8 +253,8 @@ layer_surface_handle_configure(void *data, struct zwlr_layer_surface_v1 *layer_s
 58 
 59 	zwlr_layer_surface_v1_ack_configure(layer_surface, serial);
 60 
 61-	output->configured = width == output->width && height == output->height;
 62-	if (output->configured) return;
 63+	if (output->configured && width == output->width && height == output->height)
 64+		return;
 65 
 66 	if (!image.data && image.fp) {
 67 		if (fseek(image.fp, 0, SEEK_SET) < 0) die("fseek:");
 68@@ -265,6 +307,10 @@ output_handle_geometry(void *data, struct wl_output *wl_output,
 69 	struct output *output = data;
 70 	output->x = x;
 71 	output->y = y;
 72+
 73+	/* A output's geometry has changed, mark all outputs as misconfigured */
 74+	wl_list_for_each(output, &outputs, link)
 75+		output->configured = false;
 76 }
 77 
 78 static const struct wl_output_listener output_listener = {
 79@@ -354,6 +400,7 @@ main(int argc, char *argv[])
 80 		else if (!strcmp(argv[1], "fit")) image_modify = image_fit;
 81 		else if (!strcmp(argv[1], "fill")) image_modify = image_fill;
 82 		else if (!strcmp(argv[1], "tile")) image_modify = image_tile;
 83+		else if (!strcmp(argv[1], "spread")) image_modify = image_spread;
 84 		else goto usage;
 85 
 86 		if (!(image.fp = fopen(argv[2], "rb"))) die("fopen %s:", argv[2]);
 87@@ -370,7 +417,8 @@ main(int argc, char *argv[])
 88 	registry = wl_display_get_registry(display);
 89 	wl_registry_add_listener(registry, &registry_listener, NULL);
 90 	wl_display_roundtrip(display);
 91-		
 92+	wl_display_roundtrip(display); /* output handlers */
 93+
 94 	if (!compositor || !layer_shell || !shm)
 95 		die("bad compositor available");
 96 
 97@@ -380,7 +428,7 @@ main(int argc, char *argv[])
 98 	return EXIT_SUCCESS;
 99 
100 usage:
101-	fprintf(stderr, "usage: %s stretch|fit|fill|tile filename\n"
102+	fprintf(stderr, "usage: %s fill|fit|spread|stretch|tile filename\n"
103 	                "       %s RRGGBBAA\n",
104 	        argv[0], argv[0]);
105 	return EXIT_FAILURE;