commit cb31c30
sewn
·
2026-05-07 20:01:45 +0000 UTC
parent 18d72bf
drop usage of wp_viewporter
M
Makefile
+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, ®istry_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))