commit 6b0d26b

hovercats  ·  2026-07-31 04:46:12 +0000 UTC
parent ce3fd53
swc: drop the patches from neuswc, as we have ported neuswc instead for those packages that need it
21 files changed,  +1, -5069
+0, -16
 1@@ -30,16 +30,6 @@ waylandproto('protocol/server-decoration.xml', {
 2 	code='server-decoration-protocol.c',
 3 })
 4 
 5-waylandproto('protocol/swc_snap.xml', {
 6-       server='include/swc_snap-server-protocol.h',
 7-       code='swc_snap-protocol.c',
 8-})
 9-
10-waylandproto('protocol/swc_wallpaper.xml', {
11-       server='include/swc_wallpaper-server-protocol.h',
12-       code='swc_wallpaper-protocol.c',
13-})
14-
15 pkg.hdrs = {
16 	copy('$outdir/include', '$srcdir/libswc', {'swc.h'}),
17 	'$outdir/include/server-decoration-server-protocol.h',
18@@ -60,8 +50,6 @@ build('convert_font', '$outdir/cursor/cursor_data.h', {'$srcdir/cursor/cursor.pc
19 
20 pkg.deps = {
21 	'$gendir/headers',
22-	'$outdir/include/swc_snap-server-protocol.h',
23-	'$outdir/include/swc_wallpaper-server-protocol.h',
24 	'$outdir/wayland-drm-server-protocol.h',
25 	'$outdir/cursor/cursor_data.h',
26 	'pkg/fontconfig/headers',
27@@ -101,14 +89,12 @@ lib('libswc.a', [[
28 		shell_surface.c
29 		seat.c
30 		shm.c
31-		snap.c
32 		subcompositor.c
33 		subsurface.c
34 		surface.c
35 		swc.c
36 		util.c
37 		view.c
38-		wallpaper.c
39 		wayland_buffer.c
40 		window.c
41 		xdg_decoration.c
42@@ -117,8 +103,6 @@ lib('libswc.a', [[
43 	launch/protocol.c.o
44 	server-decoration-protocol.c.o
45 	swc-protocol.c.o
46-	swc_snap-protocol.c.o
47-	swc_wallpaper-protocol.c.o
48 	$builddir/(
49 		pkg/libinput/libinput.a.d
50 		pkg/libxkbcommon/libxkbcommon.a
+0, -283
  1@@ -1,283 +0,0 @@
  2-From ca2cd556f5832eb1d10785a77c564dc4ef65e931 Mon Sep 17 00:00:00 2001
  3-From: shrub <maybeshrub@gmail.com>
  4-Date: Mon, 15 Dec 2025 19:06:24 +0000
  5-Subject: [PATCH] add helper functions
  6-
  7-- a overlay helper so compositor's can draw arbitrary boxes
  8-- swc_pointer_send_button helper, for compositor's that intercept button
  9-  events
 10-- swc_cursor_position helper, for tracking cursor position
 11----
 12- libswc/compositor.c | 89 +++++++++++++++++++++++++++++++++++++++++++++
 13- libswc/pointer.c    | 21 +++++++++++
 14- libswc/swc.c        | 19 ++++++++++
 15- libswc/swc.h        | 32 ++++++++++++++++
 16- libswc/xdg_shell.c  |  9 ++++-
 17- 5 files changed, 168 insertions(+), 2 deletions(-)
 18-
 19-diff --git a/libswc/compositor.c b/libswc/compositor.c
 20-index 543615c..66f962a 100644
 21---- a/libswc/compositor.c
 22-+++ b/libswc/compositor.c
 23-@@ -84,6 +84,14 @@ static struct {
 24- 	struct wl_global *global;
 25- } compositor;
 26- 
 27-+static struct {
 28-+	bool active;
 29-+	int32_t x, y;
 30-+	uint32_t width, height;
 31-+	uint32_t color;
 32-+	uint32_t border_width;
 33-+} overlay;
 34-+
 35- struct swc_compositor swc_compositor = {
 36- 	.pointer_handler = &pointer_handler,
 37- };
 38-@@ -216,6 +224,7 @@ static void
 39- renderer_repaint(struct target *target, pixman_region32_t *damage, pixman_region32_t *base_damage, struct wl_list *views)
 40- {
 41- 	struct compositor_view *view;
 42-+	const struct swc_rectangle *target_geom = &target->view->geometry;
 43- 
 44- 	DEBUG("Rendering to target { x: %d, y: %d, w: %u, h: %u }\n",
 45- 	      target->view->geometry.x, target->view->geometry.y,
 46-@@ -234,6 +243,42 @@ renderer_repaint(struct target *target, pixman_region32_t *damage, pixman_region
 47- 			repaint_view(target, view, damage);
 48- 	}
 49- 
 50-+	if (overlay.active && overlay.border_width > 0) {
 51-+		int32_t x = overlay.x - target_geom->x;
 52-+		int32_t y = overlay.y - target_geom->y;
 53-+		uint32_t w = overlay.width, h = overlay.height, bw = overlay.border_width;
 54-+		int32_t tx = (int32_t)target_geom->width;
 55-+		int32_t ty = (int32_t)target_geom->height;
 56-+
 57-+		/* draw box as 4 rectangles with wld */
 58-+		#define CLAMP_LOW(v, lo) ((v) < (lo) ? (lo) : (v))
 59-+		#define CLAMP_HIGH(v, hi) ((v) > (hi) ? (hi) : (v))
 60-+		#define DRAW_CLIPPED(rx, ry, rw, rh) do { \
 61-+			int32_t _x1 = CLAMP_LOW((rx), 0); \
 62-+			int32_t _y1 = CLAMP_LOW((ry), 0); \
 63-+			int32_t _x2 = CLAMP_HIGH((rx) + (int32_t)(rw), tx); \
 64-+			int32_t _y2 = CLAMP_HIGH((ry) + (int32_t)(rh), ty); \
 65-+			if (_x2 > _x1 && _y2 > _y1) \
 66-+				wld_fill_rectangle(swc.drm->renderer, overlay.color, _x1, _y1, (uint32_t)(_x2 - _x1), (uint32_t)(_y2 - _y1)); \
 67-+		} while (0)
 68-+
 69-+		if (w > 0 && h > 0) {
 70-+			if (bw > w)
 71-+				bw = w;
 72-+			if (bw > h)
 73-+				bw = h;
 74-+
 75-+			DRAW_CLIPPED(x, y, (int32_t)w, (int32_t)bw);                              /* top */
 76-+			DRAW_CLIPPED(x, y + (int32_t)h - (int32_t)bw, (int32_t)w, (int32_t)bw);   /* bottom */
 77-+			DRAW_CLIPPED(x, y, (int32_t)bw, (int32_t)h);                              /* left */
 78-+			DRAW_CLIPPED(x + (int32_t)w - (int32_t)bw, y, (int32_t)bw, (int32_t)h);   /* right */
 79-+		}
 80-+
 81-+		#undef DRAW_CLIPPED
 82-+		#undef CLAMP_HIGH
 83-+		#undef CLAMP_LOW
 84-+	}
 85-+
 86- 	wld_flush(swc.drm->renderer);
 87- }
 88- 
 89-@@ -345,6 +390,50 @@ schedule_updates(uint32_t screens)
 90- 	compositor.scheduled_updates |= screens;
 91- }
 92- 
 93-+static void
 94-+overlay_damage_region(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t border_width)
 95-+{
 96-+	(void)border_width;
 97-+	pixman_region32_union_rect(&compositor.damage, &compositor.damage, x, y, width, height);
 98-+}
 99-+
100-+EXPORT void
101-+swc_overlay_set_box(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color, uint32_t border_width)
102-+{
103-+	int32_t x = x1 < x2 ? x1 : x2;
104-+	int32_t y = y1 < y2 ? y1 : y2;
105-+	uint32_t width = (uint32_t)abs(x2 - x1);
106-+	uint32_t height = (uint32_t)abs(y2 - y1);
107-+
108-+	if (border_width == 0)
109-+		border_width = 1;
110-+
111-+	if (overlay.active)
112-+		overlay_damage_region(overlay.x, overlay.y, overlay.width, overlay.height, overlay.border_width);
113-+
114-+	overlay.active = true;
115-+	overlay.x = x;
116-+	overlay.y = y;
117-+	overlay.width = width;
118-+	overlay.height = height;
119-+	overlay.color = color;
120-+	overlay.border_width = border_width;
121-+
122-+	overlay_damage_region(overlay.x, overlay.y, overlay.width, overlay.height, overlay.border_width);
123-+	schedule_updates(-1);
124-+}
125-+
126-+EXPORT void
127-+swc_overlay_clear(void)
128-+{
129-+	if (!overlay.active)
130-+		return;
131-+
132-+	overlay_damage_region(overlay.x, overlay.y, overlay.width, overlay.height, overlay.border_width);
133-+	overlay.active = false;
134-+	schedule_updates(-1);
135-+}
136-+
137- static bool
138- update(struct view *base)
139- {
140-diff --git a/libswc/pointer.c b/libswc/pointer.c
141-index 7e4d24d..184186f 100644
142---- a/libswc/pointer.c
143-+++ b/libswc/pointer.c
144-@@ -27,6 +27,7 @@
145- #include "internal.h"
146- #include "plane.h"
147- #include "screen.h"
148-+#include "seat.h"
149- #include "shm.h"
150- #include "surface.h"
151- #include "util.h"
152-@@ -36,6 +37,26 @@
153- #include <stdio.h>
154- #include <wld/wld.h>
155- 
156-+EXPORT void
157-+swc_pointer_send_button(uint32_t time, uint32_t button, uint32_t state)
158-+{
159-+	struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
160-+	struct wl_resource *resource;
161-+	uint32_t serial;
162-+
163-+	if (!pointer || wl_list_empty(&pointer->focus.active))
164-+		return;
165-+
166-+	serial = wl_display_next_serial(swc.display);
167-+	wl_resource_for_each (resource, &pointer->focus.active)
168-+		wl_pointer_send_button(resource, serial, time, button, state);
169-+	wl_resource_for_each (resource, &pointer->focus.active) {
170-+		if (wl_resource_get_version(resource) >= WL_POINTER_FRAME_SINCE_VERSION)
171-+			wl_pointer_send_frame(resource);
172-+	}
173-+	pointer->client_axis_source = -1;
174-+}
175-+
176- static void
177- enter(struct input_focus_handler *handler, struct wl_list *resources, struct compositor_view *view)
178- {
179-diff --git a/libswc/swc.c b/libswc/swc.c
180-index 0fca9f9..8814c13 100644
181---- a/libswc/swc.c
182-+++ b/libswc/swc.c
183-@@ -108,6 +108,25 @@ swc_deactivate(void)
184- 		swc.manager->deactivate();
185- }
186- 
187-+EXPORT bool
188-+swc_cursor_position(int32_t *x, int32_t *y)
189-+{
190-+	if (x)
191-+		*x = 0;
192-+	if (y)
193-+		*y = 0;
194-+
195-+	if (!swc.seat || !swc.seat->pointer)
196-+		return false;
197-+
198-+	if (x)
199-+		*x = swc.seat->pointer->x;
200-+	if (y)
201-+		*y = swc.seat->pointer->y;
202-+
203-+	return true;
204-+}
205-+
206- EXPORT bool
207- swc_initialize(struct wl_display *display, struct wl_event_loop *event_loop, const struct swc_manager *manager)
208- {
209-diff --git a/libswc/swc.h b/libswc/swc.h
210-index cb0f7eb..d199e3c 100644
211---- a/libswc/swc.h
212-+++ b/libswc/swc.h
213-@@ -35,6 +35,38 @@ struct libinput_device;
214- struct wl_display;
215- struct wl_event_loop;
216- 
217-+/**
218-+ * gett the current cursor position
219-+ *
220-+ * The returned coordinates are in compositor-global space, in wl_fixed_t
221-+ * (24.8) fixed-point units, but exposed as raw int32_t to avoid needing
222-+ * wayland headers
223-+ *
224-+ */
225-+bool swc_cursor_position(int32_t *x, int32_t *y);
226-+
227-+/**
228-+ * Send a pointer button event to the currently focused client.
229-+ *
230-+ * This is intended for window managers which intercept button events (for
231-+ * example for mouse chords) but want normal clicks to still reach clients.
232-+ */
233-+void swc_pointer_send_button(uint32_t time, uint32_t button, uint32_t state);
234-+
235-+/**
236-+ * draw [or update] a simple box overlay
237-+ *
238-+ * box is defined by two diagonally opposite corners in compositor-global
239-+ * coordinates. this draws only the border. Call swc_overlay_clear() to remove
240-+ * it
241-+ */
242-+void swc_overlay_set_box(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color, uint32_t border_width);
243-+
244-+/**
245-+ * Clear the current overlay, if any.
246-+ */
247-+void swc_overlay_clear(void);
248-+
249- /* Rectangles {{{ */
250- 
251- struct swc_rectangle {
252-diff --git a/libswc/xdg_shell.c b/libswc/xdg_shell.c
253-index 97ad5fe..33a7f9c 100644
254---- a/libswc/xdg_shell.c
255-+++ b/libswc/xdg_shell.c
256-@@ -286,18 +286,23 @@ static void
257- focus(struct window *window)
258- {
259- 	struct xdg_toplevel *toplevel = wl_container_of(window, toplevel, window);
260-+	uint32_t width = window->view->base.geometry.width;
261-+	uint32_t height = window->view->base.geometry.height;
262- 
263- 	add_state(toplevel, XDG_TOPLEVEL_STATE_ACTIVATED);
264--	send_configure(toplevel, -1, -1);
265-+	/* dont send  0x0 on focus change */
266-+	send_configure(toplevel, width ? (int32_t)width : -1, height ? (int32_t)height : -1);
267- }
268- 
269- static void
270- unfocus(struct window *window)
271- {
272- 	struct xdg_toplevel *toplevel = wl_container_of(window, toplevel, window);
273-+	uint32_t width = window->view->base.geometry.width;
274-+	uint32_t height = window->view->base.geometry.height;
275- 
276- 	remove_state(toplevel, XDG_TOPLEVEL_STATE_ACTIVATED);
277--	send_configure(toplevel, -1, -1);
278-+	send_configure(toplevel, width ? (int32_t)width : -1, height ? (int32_t)height : -1);
279- }
280- 
281- static void
282--- 
283-2.49.0
284-
+0, -170
  1@@ -1,170 +0,0 @@
  2-From ab5eb634b9e10346b5c184ae3422eeda1c67b91a Mon Sep 17 00:00:00 2001
  3-From: shrub <maybeshrub@gmail.com>
  4-Date: Mon, 15 Dec 2025 21:55:21 +0000
  5-Subject: [PATCH] z-ordering compositor.c : click to raise window to top. uses
  6- internal helpers to find the window under the pointer and raise it on button
  7- press.
  8-
  9----
 10- libswc/compositor.c | 113 +++++++++++++++++++++++++++++++++++++-------
 11- 1 file changed, 97 insertions(+), 16 deletions(-)
 12-
 13-diff --git a/libswc/compositor.c b/libswc/compositor.c
 14-index 66f962a..2808426 100644
 15---- a/libswc/compositor.c
 16-+++ b/libswc/compositor.c
 17-@@ -63,10 +63,12 @@ struct target {
 18- };
 19- 
 20- static bool handle_motion(struct pointer_handler *handler, uint32_t time, wl_fixed_t x, wl_fixed_t y);
 21-+static bool handle_button(struct pointer_handler *handler, uint32_t time, struct button *button, uint32_t state);
 22- static void perform_update(void *data);
 23- 
 24- static struct pointer_handler pointer_handler = {
 25- 	.motion = handle_motion,
 26-+	.button = handle_button,
 27- };
 28- 
 29- static struct {
 30-@@ -523,6 +525,78 @@ static const struct view_impl view_impl = {
 31- 	.move = move,
 32- };
 33- 
 34-+static struct compositor_view *
 35-+view_at(int32_t x, int32_t y)
 36-+{
 37-+	struct compositor_view *view;
 38-+	struct swc_rectangle *geom;
 39-+
 40-+	wl_list_for_each (view, &compositor.views, link) {
 41-+		if (!view->visible)
 42-+			continue;
 43-+
 44-+		geom = &view->base.geometry;
 45-+		if (!rectangle_contains_point(geom, x, y))
 46-+			continue;
 47-+
 48-+		if (pixman_region32_contains_point(&view->surface->state.input,
 49-+		                                   x - geom->x, y - geom->y, NULL))
 50-+		{
 51-+			return view;
 52-+		}
 53-+	}
 54-+
 55-+	return NULL;
 56-+}
 57-+
 58-+static struct compositor_view *
 59-+window_view(struct compositor_view *view)
 60-+{
 61-+	while (view && !view->window && view->parent && view->parent != view)
 62-+		view = view->parent;
 63-+	return (view && view->window) ? view : NULL;
 64-+}
 65-+
 66-+static void
 67-+raise_window(struct compositor_view *view)
 68-+{
 69-+	struct compositor_view *other, *top_window;
 70-+	struct wl_list *insert_after;
 71-+	uint32_t screens;
 72-+
 73-+	view = window_view(view);
 74-+	if (!view || !view->visible)
 75-+		return;
 76-+
 77-+	top_window = NULL;
 78-+	insert_after = &compositor.views;
 79-+	wl_list_for_each (other, &compositor.views, link) {
 80-+		if (!other->visible)
 81-+			continue;
 82-+
 83-+		if (other->window) {
 84-+			top_window = other;
 85-+			break;
 86-+		}
 87-+		insert_after = &other->link;
 88-+	}
 89-+
 90-+	if (view == top_window)
 91-+		return;
 92-+
 93-+	screens = view->base.screens;
 94-+
 95-+	wl_list_remove(&view->link);
 96-+	wl_list_insert(insert_after, &view->link);
 97-+
 98-+	view->border.damaged = true;
 99-+	pixman_region32_union_rect(&compositor.damage, &compositor.damage,
100-+	                           view->extents.x1, view->extents.y1,
101-+	                           (uint32_t)(view->extents.x2 - view->extents.x1),
102-+	                           (uint32_t)(view->extents.y2 - view->extents.y1));
103-+	schedule_updates(screens);
104-+}
105-+
106- struct compositor_view *
107- compositor_create_view(struct surface *surface)
108- {
109-@@ -575,7 +649,7 @@ compositor_view(struct view *view)
110- void
111- compositor_view_set_parent(struct compositor_view *view, struct compositor_view *parent)
112- {
113--	view->parent = view;
114-+	view->parent = parent;
115- 
116- 	if (parent->visible)
117- 		compositor_view_show(view);
118-@@ -789,28 +863,35 @@ perform_update(void *data)
119- bool
120- handle_motion(struct pointer_handler *handler, uint32_t time, wl_fixed_t fx, wl_fixed_t fy)
121- {
122--	struct compositor_view *view;
123--	bool found = false;
124- 	int32_t x = wl_fixed_to_int(fx), y = wl_fixed_to_int(fy);
125--	struct swc_rectangle *geom;
126- 
127- 	/* If buttons are pressed, don't change pointer focus. */
128- 	if (swc.seat->pointer->buttons.size > 0)
129- 		return false;
130- 
131--	wl_list_for_each (view, &compositor.views, link) {
132--		if (!view->visible)
133--			continue;
134--		geom = &view->base.geometry;
135--		if (rectangle_contains_point(geom, x, y)) {
136--			if (pixman_region32_contains_point(&view->surface->state.input, x - geom->x, y - geom->y, NULL)) {
137--				found = true;
138--				break;
139--			}
140--		}
141--	}
142-+	struct compositor_view *view = view_at(x, y);
143-+
144-+	pointer_set_focus(swc.seat->pointer, view);
145-+
146-+	return false;
147-+}
148-+
149-+static bool
150-+handle_button(struct pointer_handler *handler, uint32_t time, struct button *button, uint32_t state)
151-+{
152-+	(void)handler;
153-+	(void)time;
154-+	(void)button;
155-+
156-+	if (state != WL_POINTER_BUTTON_STATE_PRESSED)
157-+		return false;
158-+
159-+	int32_t x = wl_fixed_to_int(swc.seat->pointer->x);
160-+	int32_t y = wl_fixed_to_int(swc.seat->pointer->y);
161-+	struct compositor_view *view = view_at(x, y);
162- 
163--	pointer_set_focus(swc.seat->pointer, found ? view : NULL);
164-+	pointer_set_focus(swc.seat->pointer, view);
165-+	raise_window(view);
166- 
167- 	return false;
168- }
169--- 
170-2.49.0
171-
+0, -59
 1@@ -1,59 +0,0 @@
 2-From b6c18cfa63ea8cef39e1bfb728cfdac4fd858c68 Mon Sep 17 00:00:00 2001
 3-From: shrub <maybeshrub@gmail.com>
 4-Date: Mon, 15 Dec 2025 22:06:23 +0000
 5-Subject: [PATCH] swc_window_at new public api, returns the topmost window at
 6- some compositor global pair of coordinates
 7-
 8----
 9- libswc/compositor.c | 9 +++++++++
10- libswc/swc.h        | 7 +++++++
11- 2 files changed, 16 insertions(+)
12-
13-diff --git a/libswc/compositor.c b/libswc/compositor.c
14-index 2808426..24554ee 100644
15---- a/libswc/compositor.c
16-+++ b/libswc/compositor.c
17-@@ -43,6 +43,7 @@
18- #include "surface.h"
19- #include "util.h"
20- #include "view.h"
21-+#include "window.h"
22- 
23- #include <errno.h>
24- #include <stdlib.h>
25-@@ -597,6 +598,14 @@ raise_window(struct compositor_view *view)
26- 	schedule_updates(screens);
27- }
28- 
29-+EXPORT struct swc_window *
30-+swc_window_at(int32_t x, int32_t y)
31-+{
32-+	struct compositor_view *view = window_view(view_at(x, y));
33-+
34-+	return view ? &view->window->base : NULL;
35-+}
36-+
37- struct compositor_view *
38- compositor_create_view(struct surface *surface)
39- {
40-diff --git a/libswc/swc.h b/libswc/swc.h
41-index d199e3c..caf966c 100644
42---- a/libswc/swc.h
43-+++ b/libswc/swc.h
44-@@ -298,6 +298,13 @@ void swc_window_begin_resize(struct swc_window *window, uint32_t edges);
45-  */
46- void swc_window_end_resize(struct swc_window *window);
47- 
48-+/**
49-+ * returns the topmost window at any given compositor global coordinates
50-+ *
51-+ * returns null if there is no window at that point
52-+ */
53-+struct swc_window *swc_window_at(int32_t x, int32_t y);
54-+
55- /* }}} */
56- 
57- /* Bindings {{{ */
58--- 
59-2.49.0
60-
+0, -264
  1@@ -1,264 +0,0 @@
  2-From 54a1db20744eab817a392cccff8ea79fe4b34de3 Mon Sep 17 00:00:00 2001
  3-From: dalem <lain@dalem.foo>
  4-Date: Tue, 16 Dec 2025 02:01:10 +0100
  5-Subject: [PATCH] add support for inner and outer window borders, lovingly
  6- known as 'double borders'
  7-
  8----
  9- libswc/compositor.c | 90 +++++++++++++++++++++++++++++++--------------
 10- libswc/compositor.h | 17 ++++++---
 11- libswc/swc.h        |  4 +-
 12- libswc/window.c     |  8 ++--
 13- 4 files changed, 83 insertions(+), 36 deletions(-)
 14-
 15-diff --git a/libswc/compositor.c b/libswc/compositor.c
 16-index 24554ee..5f45a16 100644
 17---- a/libswc/compositor.c
 18-+++ b/libswc/compositor.c
 19-@@ -212,15 +212,38 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
 20- 		wld_copy_region(swc.drm->renderer, view->buffer, geom->x - target_geom->x, geom->y - target_geom->y, &view_damage);
 21- 	}
 22- 
 23--	pixman_region32_fini(&view_damage);
 24-+	pixman_region32_t in_rect;
 25-+	pixman_region32_init_rect(&in_rect, 
 26-+			geom->x - view->border.inwidth, 
 27-+			geom->y - view->border.inwidth, 
 28-+			geom->width + (2 * view->border.inwidth), 
 29-+			geom->height + (2 * view->border.inwidth));
 30-+
 31-+	pixman_region32_t out_border;
 32-+	pixman_region32_init(&out_border);
 33-+	pixman_region32_subtract(&out_border, &border_damage, &in_rect);
 34-+
 35-+	pixman_region32_t in_border;
 36-+	pixman_region32_init(&in_border);
 37-+	pixman_region32_subtract(&in_border, &in_rect, &view_region);
 38-+		
 39- 
 40- 	/* Draw border */
 41--	if (pixman_region32_not_empty(&border_damage)) {
 42--		pixman_region32_translate(&border_damage, -target_geom->x, -target_geom->y);
 43--		wld_fill_region(swc.drm->renderer, view->border.color, &border_damage);
 44-+	if (view->border.outwidth > 0 && pixman_region32_not_empty(&out_border)) {
 45-+		pixman_region32_translate(&out_border, -target_geom->x, -target_geom->y);
 46-+		wld_fill_region(swc.drm->renderer, view->border.outcolor, &out_border);
 47-+	}
 48-+
 49-+	if (view->border.inwidth > 0 && pixman_region32_not_empty(&in_border)) {
 50-+		pixman_region32_translate(&in_border, -target_geom->x, -target_geom->y);
 51-+		wld_fill_region(swc.drm->renderer, view->border.incolor, &in_border);
 52- 	}
 53- 
 54- 	pixman_region32_fini(&border_damage);
 55-+	pixman_region32_fini(&in_rect);
 56-+	pixman_region32_fini(&out_border);
 57-+	pixman_region32_fini(&in_border);
 58-+
 59- }
 60- 
 61- static void
 62-@@ -361,21 +384,26 @@ static void
 63- damage_view(struct compositor_view *view)
 64- {
 65- 	damage_below_view(view);
 66--	view->border.damaged = true;
 67--}
 68-+	view->border.damaged_border1 = true;
 69-+	view->border.damaged_border2 = true;
 70- 
 71-+}
 72- static void
 73- update_extents(struct compositor_view *view)
 74- {
 75--	view->extents.x1 = view->base.geometry.x - view->border.width;
 76--	view->extents.y1 = view->base.geometry.y - view->border.width;
 77--	view->extents.x2 = view->base.geometry.x + view->base.geometry.width + view->border.width;
 78--	view->extents.y2 = view->base.geometry.y + view->base.geometry.height + view->border.width;
 79-+	uint32_t total_border = view->border.outwidth + view->border.inwidth;
 80-+
 81-+	view->extents.x1 = view->base.geometry.x - total_border;
 82-+	view->extents.y1 = view->base.geometry.y - total_border;
 83-+	view->extents.x2 = view->base.geometry.x + view->base.geometry.width + total_border;
 84-+	view->extents.y2 = view->base.geometry.y + view->base.geometry.height + total_border;
 85- 
 86- 	/* Damage border. */
 87--	view->border.damaged = true;
 88-+	view->border.damaged_border1 = true;
 89-+	view->border.damaged_border2 = true;
 90- }
 91- 
 92-+
 93- static void
 94- schedule_updates(uint32_t screens)
 95- {
 96-@@ -590,7 +618,7 @@ raise_window(struct compositor_view *view)
 97- 	wl_list_remove(&view->link);
 98- 	wl_list_insert(insert_after, &view->link);
 99- 
100--	view->border.damaged = true;
101-+	view->border.damaged_border1 = true;
102- 	pixman_region32_union_rect(&compositor.damage, &compositor.damage,
103- 	                           view->extents.x1, view->extents.y1,
104- 	                           (uint32_t)(view->extents.x2 - view->extents.x1),
105-@@ -626,9 +654,12 @@ compositor_create_view(struct surface *surface)
106- 	view->extents.y1 = 0;
107- 	view->extents.x2 = 0;
108- 	view->extents.y2 = 0;
109--	view->border.width = 0;
110--	view->border.color = 0x000000;
111--	view->border.damaged = false;
112-+	view->border.outwidth = 0;
113-+	view->border.outcolor = 0x000000;
114-+	view->border.damaged_border1 = false;
115-+	view->border.inwidth = 0;
116-+	view->border.incolor = 0x000000;
117-+	view->border.damaged_border2 = false;
118- 	pixman_region32_init(&view->clip);
119- 	wl_signal_init(&view->destroy_signal);
120- 	surface_set_view(surface, &view->base);
121-@@ -711,13 +742,16 @@ compositor_view_hide(struct compositor_view *view)
122- }
123- 
124- void
125--compositor_view_set_border_width(struct compositor_view *view, uint32_t width)
126-+compositor_view_set_border_width(struct compositor_view *view, uint32_t outwidth, uint32_t inwidth)
127- {
128--	if (view->border.width == width)
129-+	if (view->border.outwidth == outwidth && view->border.inwidth == inwidth)
130- 		return;
131- 
132--	view->border.width = width;
133--	view->border.damaged = true;
134-+	view->border.outwidth = outwidth;
135-+	view->border.damaged_border1 = true;
136-+
137-+	view->border.inwidth = inwidth;
138-+	view->border.damaged_border2 = true;
139- 
140- 	/* XXX: Damage above surface for transparent surfaces? */
141- 
142-@@ -726,19 +760,22 @@ compositor_view_set_border_width(struct compositor_view *view, uint32_t width)
143- }
144- 
145- void
146--compositor_view_set_border_color(struct compositor_view *view, uint32_t color)
147-+compositor_view_set_border_color(struct compositor_view *view, uint32_t outcolor, uint32_t incolor)
148- {
149--	if (view->border.color == color)
150-+	if (view->border.outcolor == outcolor && view->border.incolor == incolor)
151- 		return;
152- 
153--	view->border.color = color;
154--	view->border.damaged = true;
155-+	view->border.outcolor = outcolor;
156-+	view->border.damaged_border1 = true;
157-+
158-+	view->border.incolor = incolor;
159-+	view->border.damaged_border2 = true;
160-+	
161- 
162- 	/* XXX: Damage above surface for transparent surfaces? */
163- 
164- 	update(&view->base);
165- }
166--
167- /* }}} */
168- 
169- static void
170-@@ -781,7 +818,8 @@ calculate_damage(void)
171- 			pixman_region32_clear(surface_damage);
172- 		}
173- 
174--		if (view->border.damaged) {
175-+                /* redraw entire thingy if either */
176-+		if (view->border.damaged_border1 || view->border.damaged_border2) {
177- 			pixman_region32_t border_region, view_region;
178- 
179- 			pixman_region32_init_with_extents(&border_region, &view->extents);
180-@@ -793,8 +831,6 @@ calculate_damage(void)
181- 
182- 			pixman_region32_fini(&border_region);
183- 			pixman_region32_fini(&view_region);
184--
185--			view->border.damaged = false;
186- 		}
187- 	}
188- 
189-diff --git a/libswc/compositor.h b/libswc/compositor.h
190-index 9844bd9..9046f0b 100644
191---- a/libswc/compositor.h
192-+++ b/libswc/compositor.h
193-@@ -64,9 +64,16 @@ struct compositor_view {
194- 	pixman_region32_t clip;
195- 
196- 	struct {
197--		uint32_t width;
198--		uint32_t color;
199--		bool damaged;
200-+		uint32_t outwidth;
201-+		uint32_t outcolor;
202-+
203-+		bool damaged_border1;
204-+		
205-+		/* sir, a second border has hit the compositor! */
206-+		uint32_t inwidth;
207-+		uint32_t incolor;
208-+	
209-+		bool damaged_border2;
210- 	} border;
211- 
212- 	struct wl_list link;
213-@@ -87,7 +94,7 @@ void compositor_view_set_parent(struct compositor_view *view, struct compositor_
214- void compositor_view_show(struct compositor_view *view);
215- void compositor_view_hide(struct compositor_view *view);
216- 
217--void compositor_view_set_border_color(struct compositor_view *view, uint32_t color);
218--void compositor_view_set_border_width(struct compositor_view *view, uint32_t width);
219-+void compositor_view_set_border_color(struct compositor_view *view, uint32_t outcolor, uint32_t incolor);
220-+void compositor_view_set_border_width(struct compositor_view *view, uint32_t outwidth, uint32_t inwidth);
221- 
222- #endif
223-diff --git a/libswc/swc.h b/libswc/swc.h
224-index caf966c..24c8c3b 100644
225---- a/libswc/swc.h
226-+++ b/libswc/swc.h
227-@@ -267,8 +267,10 @@ void swc_window_set_geometry(struct swc_window *window, const struct swc_rectang
228-  *
229-  * NOTE: The window's geometry remains unchanged, and should be updated if a
230-  *       fixed top-left corner of the border is desired.
231-+ * 
232-+ * info from dalem: unsure how much double borders break!
233-  */
234--void swc_window_set_border(struct swc_window *window, uint32_t color, uint32_t width);
235-+void swc_window_set_border(struct swc_window *window, uint32_t inner_border_color, uint32_t inner_border_width, uint32_t outer_border_color, uint32_t outer_border_width);
236- 
237- /**
238-  * Begin an interactive move of the specified window.
239-diff --git a/libswc/window.c b/libswc/window.c
240-index 7f7255e..6e5a235 100644
241---- a/libswc/window.c
242-+++ b/libswc/window.c
243-@@ -249,14 +249,16 @@ swc_window_set_geometry(struct swc_window *window, const struct swc_rectangle *g
244- }
245- 
246- EXPORT void
247--swc_window_set_border(struct swc_window *window, uint32_t border_color, uint32_t border_width)
248-+swc_window_set_border(struct swc_window *window, uint32_t inner_border_color, uint32_t inner_border_width, 
249-+		uint32_t outer_border_color, uint32_t outer_border_width)
250- {
251- 	struct compositor_view *view = INTERNAL(window)->view;
252- 
253--	compositor_view_set_border_color(view, border_color);
254--	compositor_view_set_border_width(view, border_width);
255-+	compositor_view_set_border_color(view, outer_border_color, inner_border_color);
256-+	compositor_view_set_border_width(view, outer_border_width, inner_border_width);
257- }
258- 
259-+
260- EXPORT void
261- swc_window_begin_move(struct swc_window *window)
262- {
263--- 
264-2.49.0
265-
+0, -383
  1@@ -1,383 +0,0 @@
  2-From c527d1aeb3199c92dccd973b97c388f44035e929 Mon Sep 17 00:00:00 2001
  3-From: shrub <maybeshrub@gmail.com>
  4-Date: Tue, 16 Dec 2025 01:03:48 +0000
  5-Subject: [PATCH] scroll/axis bindings this adds bindings for the pointer axis.
  6- it also adds a swc_pointer_send-axis helper for compositors that want to
  7- sometimes intercept axis events. various window stacking helpers were also
  8- added
  9-
 10----
 11- libswc/bindings.c   | 66 +++++++++++++++++++++++++++++++-
 12- libswc/compositor.c | 92 +++++++++++++++++++++++++++++++++++++++++++++
 13- libswc/pointer.c    | 37 ++++++++++++++++++
 14- libswc/swc.h        | 32 ++++++++++++++++
 15- libswc/window.c     | 12 ++++++
 16- 5 files changed, 238 insertions(+), 1 deletion(-)
 17-
 18-diff --git a/libswc/bindings.c b/libswc/bindings.c
 19-index 32c2aa1..2894632 100644
 20---- a/libswc/bindings.c
 21-+++ b/libswc/bindings.c
 22-@@ -40,6 +40,13 @@ struct binding {
 23- 	void *data;
 24- };
 25- 
 26-+struct axis_binding {
 27-+	uint32_t axis;
 28-+	uint32_t modifiers;
 29-+	swc_axis_binding_handler handler;
 30-+	void *data;
 31-+};
 32-+
 33- static bool handle_key(struct keyboard *keyboard, uint32_t time, struct key *key, uint32_t state);
 34- 
 35- static struct keyboard_handler key_binding_handler = {
 36-@@ -47,12 +54,15 @@ static struct keyboard_handler key_binding_handler = {
 37- };
 38- 
 39- static bool handle_button(struct pointer_handler *handler, uint32_t time, struct button *button, uint32_t state);
 40-+static bool handle_axis(struct pointer_handler *handler, uint32_t time, enum wl_pointer_axis axis,
 41-+                        enum wl_pointer_axis_source source, wl_fixed_t value, int value120);
 42- 
 43- static struct pointer_handler button_binding_handler = {
 44- 	.button = handle_button,
 45-+	.axis = handle_axis,
 46- };
 47- 
 48--static struct wl_array key_bindings, button_bindings;
 49-+static struct wl_array key_bindings, button_bindings, axis_bindings;
 50- 
 51- const struct swc_bindings swc_bindings = {
 52- 	.keyboard_handler = &key_binding_handler,
 53-@@ -107,6 +117,19 @@ find_button_binding(uint32_t modifiers, uint32_t value)
 54- 	return find_binding(&button_bindings, modifiers, value);
 55- }
 56- 
 57-+static struct axis_binding *
 58-+find_axis_binding(uint32_t modifiers, uint32_t axis)
 59-+{
 60-+	struct axis_binding *binding;
 61-+
 62-+	wl_array_for_each (binding, &axis_bindings) {
 63-+		if (binding->axis == axis && (binding->modifiers == modifiers || binding->modifiers == SWC_MOD_ANY))
 64-+			return binding;
 65-+	}
 66-+
 67-+	return NULL;
 68-+}
 69-+
 70- static bool
 71- handle_binding(uint32_t time, struct press *press, uint32_t state, struct binding *(*find_binding)(uint32_t, uint32_t))
 72- {
 73-@@ -140,11 +163,35 @@ handle_button(struct pointer_handler *handler, uint32_t time, struct button *but
 74- 	return handle_binding(time, &button->press, state, &find_button_binding);
 75- }
 76- 
 77-+bool
 78-+handle_axis(struct pointer_handler *handler, uint32_t time, enum wl_pointer_axis axis,
 79-+            enum wl_pointer_axis_source source, wl_fixed_t value, int value120)
 80-+{
 81-+	(void)handler;
 82-+	(void)source;
 83-+
 84-+	struct axis_binding *binding = find_axis_binding(swc.seat->keyboard->modifiers, axis);
 85-+	int32_t delta120 = value120;
 86-+
 87-+	if (!binding)
 88-+		return false;
 89-+
 90-+	if (!delta120 && value) {
 91-+		delta120 = (int32_t)(wl_fixed_to_double(value) * 120.0);
 92-+		if (!delta120)
 93-+			delta120 = value > 0 ? 1 : -1;
 94-+	}
 95-+
 96-+	binding->handler(binding->data, time, axis, delta120);
 97-+	return true;
 98-+}
 99-+
100- bool
101- bindings_initialize(void)
102- {
103- 	wl_array_init(&key_bindings);
104- 	wl_array_init(&button_bindings);
105-+	wl_array_init(&axis_bindings);
106- 
107- 	return true;
108- }
109-@@ -154,6 +201,7 @@ bindings_finalize(void)
110- {
111- 	wl_array_release(&key_bindings);
112- 	wl_array_release(&button_bindings);
113-+	wl_array_release(&axis_bindings);
114- }
115- 
116- EXPORT int
117-@@ -183,3 +231,19 @@ swc_add_binding(enum swc_binding_type type, uint32_t modifiers, uint32_t value,
118- 
119- 	return 0;
120- }
121-+
122-+EXPORT int
123-+swc_add_axis_binding(uint32_t modifiers, uint32_t axis, swc_axis_binding_handler handler, void *data)
124-+{
125-+	struct axis_binding *binding;
126-+
127-+	if (!(binding = wl_array_add(&axis_bindings, sizeof(*binding))))
128-+		return -ENOMEM;
129-+
130-+	binding->axis = axis;
131-+	binding->modifiers = modifiers;
132-+	binding->handler = handler;
133-+	binding->data = data;
134-+
135-+	return 0;
136-+}
137-diff --git a/libswc/compositor.c b/libswc/compositor.c
138-index 5f45a16..ed56b7f 100644
139---- a/libswc/compositor.c
140-+++ b/libswc/compositor.c
141-@@ -634,6 +634,98 @@ swc_window_at(int32_t x, int32_t y)
142- 	return view ? &view->window->base : NULL;
143- }
144- 
145-+static struct compositor_view *
146-+view_for_window(struct swc_window *base)
147-+{
148-+	struct window *window;
149-+
150-+	if (!base)
151-+		return NULL;
152-+
153-+	window = (struct window *)base;
154-+	return window->view;
155-+}
156-+
157-+static struct compositor_view *
158-+prev_window_view(struct compositor_view *view)
159-+{
160-+	struct wl_list *link;
161-+	struct compositor_view *other;
162-+
163-+	for (link = view->link.prev; link != &compositor.views; link = link->prev) {
164-+		other = wl_container_of(link, other, link);
165-+
166-+		if (other->visible && other->window)
167-+			return other;
168-+	}
169-+
170-+	return NULL;
171-+}
172-+
173-+static struct compositor_view *
174-+next_window_view(struct compositor_view *view)
175-+{
176-+	struct wl_list *link;
177-+	struct compositor_view *other;
178-+
179-+	for (link = view->link.next; link != &compositor.views; link = link->next) {
180-+		other = wl_container_of(link, other, link);
181-+
182-+		if (other->visible && other->window)
183-+			return other;
184-+	}
185-+
186-+	return NULL;
187-+}
188-+
189-+static void
190-+damage_views(struct compositor_view *a, struct compositor_view *b)
191-+{
192-+	uint32_t screens = a->base.screens | (b ? b->base.screens : 0);
193-+
194-+	a->border.damaged = true;
195-+	pixman_region32_union_rect(&compositor.damage, &compositor.damage,
196-+	                           a->extents.x1, a->extents.y1,
197-+	                           (uint32_t)(a->extents.x2 - a->extents.x1),
198-+	                           (uint32_t)(a->extents.y2 - a->extents.y1));
199-+
200-+	if (b) {
201-+		b->border.damaged = true;
202-+		pixman_region32_union_rect(&compositor.damage, &compositor.damage,
203-+		                           b->extents.x1, b->extents.y1,
204-+		                           (uint32_t)(b->extents.x2 - b->extents.x1),
205-+		                           (uint32_t)(b->extents.y2 - b->extents.y1));
206-+	}
207-+
208-+	schedule_updates(screens);
209-+}
210-+
211-+EXPORT void
212-+swc_window_stack(struct swc_window *window, int32_t direction)
213-+{
214-+	struct compositor_view *view = view_for_window(window);
215-+	struct compositor_view *other = NULL;
216-+
217-+	if (!view || !view->visible || direction == 0)
218-+		return;
219-+
220-+	if (direction < 0) {
221-+		other = prev_window_view(view);
222-+		if (!other)
223-+			return;
224-+		wl_list_remove(&view->link);
225-+		wl_list_insert(other->link.prev, &view->link);
226-+	} else {
227-+		other = next_window_view(view);
228-+		if (!other)
229-+			return;
230-+		wl_list_remove(&view->link);
231-+		wl_list_insert(&other->link, &view->link);
232-+	}
233-+
234-+	damage_views(view, other);
235-+}
236-+
237- struct compositor_view *
238- compositor_create_view(struct surface *surface)
239- {
240-diff --git a/libswc/pointer.c b/libswc/pointer.c
241-index 184186f..cb2582c 100644
242---- a/libswc/pointer.c
243-+++ b/libswc/pointer.c
244-@@ -57,6 +57,43 @@ swc_pointer_send_button(uint32_t time, uint32_t button, uint32_t state)
245- 	pointer->client_axis_source = -1;
246- }
247- 
248-+EXPORT void
249-+swc_pointer_send_axis(uint32_t time, uint32_t axis, int32_t value120)
250-+{
251-+	struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
252-+	struct wl_resource *resource;
253-+	wl_fixed_t value;
254-+
255-+	if (!pointer || wl_list_empty(&pointer->focus.active))
256-+		return;
257-+
258-+	value = wl_fixed_from_double((double)value120 / 120.0);
259-+
260-+	wl_resource_for_each (resource, &pointer->focus.active) {
261-+		int ver = wl_resource_get_version(resource);
262-+
263-+		if (ver >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION)
264-+			wl_pointer_send_axis_source(resource, WL_POINTER_AXIS_SOURCE_WHEEL);
265-+		if (value120) {
266-+			if (ver >= WL_POINTER_AXIS_VALUE120_SINCE_VERSION)
267-+				wl_pointer_send_axis_value120(resource, axis, value120);
268-+			else if (ver >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION)
269-+				wl_pointer_send_axis_discrete(resource, axis, value120 / 120);
270-+		}
271-+
272-+		if (value)
273-+			wl_pointer_send_axis(resource, time, axis, value);
274-+		else if (ver >= WL_POINTER_AXIS_STOP_SINCE_VERSION)
275-+			wl_pointer_send_axis_stop(resource, time, axis);
276-+	}
277-+
278-+	wl_resource_for_each (resource, &pointer->focus.active) {
279-+		if (wl_resource_get_version(resource) >= WL_POINTER_FRAME_SINCE_VERSION)
280-+			wl_pointer_send_frame(resource);
281-+	}
282-+	pointer->client_axis_source = -1;
283-+}
284-+
285- static void
286- enter(struct input_focus_handler *handler, struct wl_list *resources, struct compositor_view *view)
287- {
288-diff --git a/libswc/swc.h b/libswc/swc.h
289-index 24c8c3b..2aa06c8 100644
290---- a/libswc/swc.h
291-+++ b/libswc/swc.h
292-@@ -53,6 +53,16 @@ bool swc_cursor_position(int32_t *x, int32_t *y);
293-  */
294- void swc_pointer_send_button(uint32_t time, uint32_t button, uint32_t state);
295- 
296-+/**
297-+ * Send a pointer axis event to the currently focused client.
298-+ *
299-+ * This is intended for window managers which intercept axis events (for
300-+ * example for mouse chords) but want normal scrolling to still reach clients.
301-+ *
302-+ * value120 uses the wl_pointer "120 units" convention.
303-+ */
304-+void swc_pointer_send_axis(uint32_t time, uint32_t axis, int32_t value120);
305-+
306- /**
307-  * draw [or update] a simple box overlay
308-  *
309-@@ -262,6 +272,11 @@ void swc_window_set_size(struct swc_window *window, uint32_t width, uint32_t hei
310-  */
311- void swc_window_set_geometry(struct swc_window *window, const struct swc_rectangle *geometry);
312- 
313-+/**
314-+ * Get the window's current geometry in compositor-global coordinates.
315-+ */
316-+bool swc_window_get_geometry(const struct swc_window *window, struct swc_rectangle *geometry);
317-+
318- /**
319-  * Set the window's border color and width.
320-  *
321-@@ -307,6 +322,14 @@ void swc_window_end_resize(struct swc_window *window);
322-  */
323- struct swc_window *swc_window_at(int32_t x, int32_t y);
324- 
325-+/**
326-+ * move a window in the stacking order by one step
327-+ *
328-+ * direction < 0 moves the window towards the front (higher)
329-+ * direction > 0 moves the window towards the back (lower)
330-+ */
331-+void swc_window_stack(struct swc_window *window, int32_t direction);
332-+
333- /* }}} */
334- 
335- /* Bindings {{{ */
336-@@ -325,6 +348,7 @@ enum swc_binding_type {
337- };
338- 
339- typedef void (*swc_binding_handler)(void *data, uint32_t time, uint32_t value, uint32_t state);
340-+typedef void (*swc_axis_binding_handler)(void *data, uint32_t time, uint32_t axis, int32_t value120);
341- 
342- /**
343-  * Register a new input binding.
344-@@ -333,6 +357,14 @@ typedef void (*swc_binding_handler)(void *data, uint32_t time, uint32_t value, u
345-  */
346- int swc_add_binding(enum swc_binding_type type, uint32_t modifiers, uint32_t value, swc_binding_handler handler, void *data);
347- 
348-+/**
349-+ * register a new pointer axis binding
350-+ *
351-+ * this will intercept axis events from clients; use swc_pointer_send_axis()
352-+ * from the handler to forward events when appropriate
353-+ */
354-+int swc_add_axis_binding(uint32_t modifiers, uint32_t axis, swc_axis_binding_handler handler, void *data);
355-+
356- /* }}} */
357- 
358- /**
359-diff --git a/libswc/window.c b/libswc/window.c
360-index 6e5a235..b1fdeb9 100644
361---- a/libswc/window.c
362-+++ b/libswc/window.c
363-@@ -248,6 +248,18 @@ swc_window_set_geometry(struct swc_window *window, const struct swc_rectangle *g
364- 	swc_window_set_position(window, geometry->x, geometry->y);
365- }
366- 
367-+EXPORT bool
368-+swc_window_get_geometry(const struct swc_window *base, struct swc_rectangle *geometry)
369-+{
370-+	struct window *window = INTERNAL((struct swc_window *)base);
371-+
372-+	if (!window || !geometry)
373-+		return false;
374-+
375-+	*geometry = window->view->base.geometry;
376-+	return true;
377-+}
378-+
379- EXPORT void
380- swc_window_set_border(struct swc_window *window, uint32_t inner_border_color, uint32_t inner_border_width, 
381- 		uint32_t outer_border_color, uint32_t outer_border_width)
382--- 
383-2.49.0
384-
+0, -215
  1@@ -1,215 +0,0 @@
  2-From e2aa9440d9db7422d3e671a2dec839936c424a0b Mon Sep 17 00:00:00 2001
  3-From: shrub <maybeshrub@gmail.com>
  4-Date: Tue, 16 Dec 2025 02:28:16 +0000
  5-Subject: [PATCH] fix how border damage is calculated | ignore null axis
  6- handlers
  7-
  8----
  9- libswc/bindings.c   |  8 ++--
 10- libswc/compositor.c | 93 ++++++++++++++++++++++++++++++++-------------
 11- 2 files changed, 71 insertions(+), 30 deletions(-)
 12-
 13-diff --git a/libswc/bindings.c b/libswc/bindings.c
 14-index 2894632..21e2cf7 100644
 15---- a/libswc/bindings.c
 16-+++ b/libswc/bindings.c
 17-@@ -134,9 +134,10 @@ static bool
 18- handle_binding(uint32_t time, struct press *press, uint32_t state, struct binding *(*find_binding)(uint32_t, uint32_t))
 19- {
 20- 	struct binding *binding;
 21-+	uint32_t modifiers = swc.seat && swc.seat->keyboard ? swc.seat->keyboard->modifiers : 0;
 22- 
 23- 	if (state) {
 24--		binding = find_binding(swc.seat->keyboard->modifiers, press->value);
 25-+		binding = find_binding(modifiers, press->value);
 26- 
 27- 		if (!binding)
 28- 			return false;
 29-@@ -170,10 +171,11 @@ handle_axis(struct pointer_handler *handler, uint32_t time, enum wl_pointer_axis
 30- 	(void)handler;
 31- 	(void)source;
 32- 
 33--	struct axis_binding *binding = find_axis_binding(swc.seat->keyboard->modifiers, axis);
 34-+	uint32_t modifiers = swc.seat && swc.seat->keyboard ? swc.seat->keyboard->modifiers : 0;
 35-+	struct axis_binding *binding = find_axis_binding(modifiers, axis);
 36- 	int32_t delta120 = value120;
 37- 
 38--	if (!binding)
 39-+	if (!binding || !binding->handler)
 40- 		return false;
 41- 
 42- 	if (!delta120 && value) {
 43-diff --git a/libswc/compositor.c b/libswc/compositor.c
 44-index ed56b7f..263da39 100644
 45---- a/libswc/compositor.c
 46-+++ b/libswc/compositor.c
 47-@@ -49,10 +49,33 @@
 48- #include <stdlib.h>
 49- #include <stdio.h>
 50- #include <assert.h>
 51-+#include <limits.h>
 52- #include <wld/wld.h>
 53- #include <wld/drm.h>
 54- #include <xkbcommon/xkbcommon-keysyms.h>
 55- 
 56-+static inline int32_t
 57-+clamp_i32(int64_t v)
 58-+{
 59-+	if (v > INT32_MAX)
 60-+		return INT32_MAX;
 61-+	if (v < INT32_MIN)
 62-+		return INT32_MIN;
 63-+	return (int32_t)v;
 64-+}
 65-+
 66-+static inline uint32_t
 67-+span_u32(int32_t a, int32_t b)
 68-+{
 69-+	int64_t d = (int64_t)b - (int64_t)a;
 70-+
 71-+	if (d <= 0)
 72-+		return 0;
 73-+	if (d > UINT32_MAX)
 74-+		return UINT32_MAX;
 75-+	return (uint32_t)d;
 76-+}
 77-+
 78- struct target {
 79- 	struct wld_surface *surface;
 80- 	struct wld_buffer *next_buffer, *current_buffer;
 81-@@ -205,13 +228,13 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
 82- 	pixman_region32_subtract(&border_damage, &view_damage, &view_region);
 83- 	pixman_region32_intersect(&view_damage, &view_damage, &view_region);
 84- 
 85--	pixman_region32_fini(&view_region);
 86--
 87- 	if (pixman_region32_not_empty(&view_damage)) {
 88- 		pixman_region32_translate(&view_damage, -geom->x, -geom->y);
 89- 		wld_copy_region(swc.drm->renderer, view->buffer, geom->x - target_geom->x, geom->y - target_geom->y, &view_damage);
 90- 	}
 91- 
 92-+	pixman_region32_fini(&view_damage);
 93-+
 94- 	pixman_region32_t in_rect;
 95- 	pixman_region32_init_rect(&in_rect, 
 96- 			geom->x - view->border.inwidth, 
 97-@@ -226,7 +249,9 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
 98- 	pixman_region32_t in_border;
 99- 	pixman_region32_init(&in_border);
100- 	pixman_region32_subtract(&in_border, &in_rect, &view_region);
101-+	pixman_region32_intersect(&in_border, &in_border, &border_damage);
102- 		
103-+	pixman_region32_fini(&view_region);
104- 
105- 	/* Draw border */
106- 	if (view->border.outwidth > 0 && pixman_region32_not_empty(&out_border)) {
107-@@ -386,17 +411,26 @@ damage_view(struct compositor_view *view)
108- 	damage_below_view(view);
109- 	view->border.damaged_border1 = true;
110- 	view->border.damaged_border2 = true;
111--
112- }
113-+
114- static void
115- update_extents(struct compositor_view *view)
116- {
117--	uint32_t total_border = view->border.outwidth + view->border.inwidth;
118--
119--	view->extents.x1 = view->base.geometry.x - total_border;
120--	view->extents.y1 = view->base.geometry.y - total_border;
121--	view->extents.x2 = view->base.geometry.x + view->base.geometry.width + total_border;
122--	view->extents.y2 = view->base.geometry.y + view->base.geometry.height + total_border;
123-+	int64_t total_border = (int64_t)view->border.outwidth + (int64_t)view->border.inwidth;
124-+	int64_t x = view->base.geometry.x;
125-+	int64_t y = view->base.geometry.y;
126-+	int64_t w = view->base.geometry.width;
127-+	int64_t h = view->base.geometry.height;
128-+
129-+	view->extents.x1 = clamp_i32(x - total_border);
130-+	view->extents.y1 = clamp_i32(y - total_border);
131-+	view->extents.x2 = clamp_i32(x + w + total_border);
132-+	view->extents.y2 = clamp_i32(y + h + total_border);
133-+
134-+	if (view->extents.x2 < view->extents.x1)
135-+		view->extents.x2 = view->extents.x1;
136-+	if (view->extents.y2 < view->extents.y1)
137-+		view->extents.y2 = view->extents.y1;
138- 
139- 	/* Damage border. */
140- 	view->border.damaged_border1 = true;
141-@@ -621,8 +655,8 @@ raise_window(struct compositor_view *view)
142- 	view->border.damaged_border1 = true;
143- 	pixman_region32_union_rect(&compositor.damage, &compositor.damage,
144- 	                           view->extents.x1, view->extents.y1,
145--	                           (uint32_t)(view->extents.x2 - view->extents.x1),
146--	                           (uint32_t)(view->extents.y2 - view->extents.y1));
147-+	                           span_u32(view->extents.x1, view->extents.x2),
148-+	                           span_u32(view->extents.y1, view->extents.y2));
149- 	schedule_updates(screens);
150- }
151- 
152-@@ -683,18 +717,20 @@ damage_views(struct compositor_view *a, struct compositor_view *b)
153- {
154- 	uint32_t screens = a->base.screens | (b ? b->base.screens : 0);
155- 
156--	a->border.damaged = true;
157-+	a->border.damaged_border1 = true;
158-+	a->border.damaged_border2 = true;
159- 	pixman_region32_union_rect(&compositor.damage, &compositor.damage,
160- 	                           a->extents.x1, a->extents.y1,
161--	                           (uint32_t)(a->extents.x2 - a->extents.x1),
162--	                           (uint32_t)(a->extents.y2 - a->extents.y1));
163-+	                           span_u32(a->extents.x1, a->extents.x2),
164-+	                           span_u32(a->extents.y1, a->extents.y2));
165- 
166- 	if (b) {
167--		b->border.damaged = true;
168-+		b->border.damaged_border1 = true;
169-+		b->border.damaged_border2 = true;
170- 		pixman_region32_union_rect(&compositor.damage, &compositor.damage,
171- 		                           b->extents.x1, b->extents.y1,
172--		                           (uint32_t)(b->extents.x2 - b->extents.x1),
173--		                           (uint32_t)(b->extents.y2 - b->extents.y1));
174-+		                           span_u32(b->extents.x1, b->extents.x2),
175-+		                           span_u32(b->extents.y1, b->extents.y2));
176- 	}
177- 
178- 	schedule_updates(screens);
179-@@ -910,21 +946,24 @@ calculate_damage(void)
180- 			pixman_region32_clear(surface_damage);
181- 		}
182- 
183--                /* redraw entire thingy if either */
184--		if (view->border.damaged_border1 || view->border.damaged_border2) {
185--			pixman_region32_t border_region, view_region;
186-+	                /* redraw entire thingy if either */
187-+			if (view->border.damaged_border1 || view->border.damaged_border2) {
188-+				pixman_region32_t border_region, view_region;
189-+
190-+				pixman_region32_init_with_extents(&border_region, &view->extents);
191-+				pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
192- 
193--			pixman_region32_init_with_extents(&border_region, &view->extents);
194--			pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
195-+				pixman_region32_subtract(&border_region, &border_region, &view_region);
196- 
197--			pixman_region32_subtract(&border_region, &border_region, &view_region);
198-+				pixman_region32_union(&compositor.damage, &compositor.damage, &border_region);
199- 
200--			pixman_region32_union(&compositor.damage, &compositor.damage, &border_region);
201-+				pixman_region32_fini(&border_region);
202-+				pixman_region32_fini(&view_region);
203- 
204--			pixman_region32_fini(&border_region);
205--			pixman_region32_fini(&view_region);
206-+				view->border.damaged_border1 = false;
207-+				view->border.damaged_border2 = false;
208-+			}
209- 		}
210--	}
211- 
212- 	pixman_region32_fini(&surface_opaque);
213- }
214--- 
215-2.49.0
216-
+0, -232
  1@@ -1,232 +0,0 @@
  2-From ac353c5601b7e774ae4465e432ce1d6de1a755de Mon Sep 17 00:00:00 2001
  3-From: shrub <maybeshrub@gmail.com>
  4-Date: Wed, 17 Dec 2025 02:04:55 +0000
  5-Subject: [PATCH] custom cursors add support for custom cursor images from
  6- ARGB. there are two modes, client-managed cursor and compositor cursor
  7- override. api has been added for switcing modes and changing cursor.
  8-
  9----
 10- libswc/pointer.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++-
 11- libswc/swc.h     |  43 +++++++++++++++++
 12- 2 files changed, 161 insertions(+), 1 deletion(-)
 13-
 14-diff --git a/libswc/pointer.c b/libswc/pointer.c
 15-index cb2582c..cff6bb6 100644
 16---- a/libswc/pointer.c
 17-+++ b/libswc/pointer.c
 18-@@ -35,8 +35,19 @@
 19- 
 20- #include <assert.h>
 21- #include <stdio.h>
 22-+#include <string.h>
 23- #include <wld/wld.h>
 24- 
 25-+static enum swc_cursor_kind cursor_override = SWC_CURSOR_DEFAULT;
 26-+static enum swc_cursor_mode cursor_mode = SWC_CURSOR_MODE_CLIENT;
 27-+
 28-+static struct {
 29-+	const uint32_t *data;
 30-+	uint32_t width, height;
 31-+	int32_t hotspot_x, hotspot_y;
 32-+	bool active;
 33-+} cursor_images[6];
 34-+
 35- EXPORT void
 36- swc_pointer_send_button(uint32_t time, uint32_t button, uint32_t state)
 37- {
 38-@@ -205,13 +216,114 @@ update_cursor(struct pointer *pointer)
 39- 	view_move(&pointer->cursor.view, x, y);
 40- }
 41- 
 42-+static void
 43-+drop_client_cursor_surface(struct pointer *pointer)
 44-+{
 45-+	if (!pointer || !pointer->cursor.surface)
 46-+		return;
 47-+	surface_set_view(pointer->cursor.surface, NULL);
 48-+	wl_list_remove(&pointer->cursor.destroy_listener.link);
 49-+	pointer->cursor.surface = NULL;
 50-+}
 51-+
 52-+static void
 53-+apply_cursor_override(struct pointer *pointer)
 54-+{
 55-+	if (!pointer || pointer->cursor.surface)
 56-+		return;
 57-+
 58-+	pointer_set_cursor(pointer, cursor_left_ptr);
 59-+}
 60-+
 61-+EXPORT void
 62-+swc_set_cursor(enum swc_cursor_kind kind)
 63-+{
 64-+	struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
 65-+
 66-+	cursor_override = kind;
 67-+
 68-+	drop_client_cursor_surface(pointer);
 69-+
 70-+	apply_cursor_override(pointer);
 71-+}
 72-+
 73-+EXPORT void
 74-+swc_set_cursor_mode(enum swc_cursor_mode mode)
 75-+{
 76-+	struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
 77-+
 78-+	cursor_mode = mode;
 79-+	if (cursor_mode == SWC_CURSOR_MODE_COMPOSITOR)
 80-+		drop_client_cursor_surface(pointer);
 81-+	apply_cursor_override(pointer);
 82-+}
 83-+
 84-+EXPORT void
 85-+swc_set_cursor_image(enum swc_cursor_kind kind,
 86-+                     const uint32_t *argb8888,
 87-+                     uint32_t width, uint32_t height,
 88-+                     int32_t hotspot_x, int32_t hotspot_y)
 89-+{
 90-+	struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
 91-+
 92-+	if (kind < 0 || kind >= (int)ARRAY_LENGTH(cursor_images))
 93-+		return;
 94-+	if (!argb8888 || width == 0 || height == 0)
 95-+		return;
 96-+
 97-+	cursor_images[kind].data = argb8888;
 98-+	cursor_images[kind].width = width;
 99-+	cursor_images[kind].height = height;
100-+	cursor_images[kind].hotspot_x = hotspot_x;
101-+	cursor_images[kind].hotspot_y = hotspot_y;
102-+	cursor_images[kind].active = true;
103-+
104-+	if (cursor_mode == SWC_CURSOR_MODE_COMPOSITOR)
105-+		drop_client_cursor_surface(pointer);
106-+	apply_cursor_override(pointer);
107-+}
108-+
109-+EXPORT void
110-+swc_clear_cursor_image(enum swc_cursor_kind kind)
111-+{
112-+	struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
113-+
114-+	if (kind < 0 || kind >= (int)ARRAY_LENGTH(cursor_images))
115-+		return;
116-+
117-+	cursor_images[kind].active = false;
118-+	cursor_images[kind].data = NULL;
119-+
120-+	apply_cursor_override(pointer);
121-+}
122-+
123- void
124- pointer_set_cursor(struct pointer *pointer, uint32_t id)
125- {
126- 	struct cursor *cursor = &cursor_metadata[id];
127-+	const uint32_t *data = cursor_data;
128- 	union wld_object object = { .ptr = &cursor_data[cursor->offset] };
129- 	struct wld_buffer *buffer;
130- 
131-+	if (id == cursor_left_ptr) {
132-+		enum swc_cursor_kind kind = cursor_override;
133-+		if (kind < 0 || kind >= (int)ARRAY_LENGTH(cursor_images))
134-+			kind = SWC_CURSOR_DEFAULT;
135-+
136-+		if (cursor_images[kind].active) {
137-+			static struct cursor custom_cursor;
138-+			custom_cursor.width = (int)cursor_images[kind].width;
139-+			custom_cursor.height = (int)cursor_images[kind].height;
140-+			custom_cursor.hotspot_x = (int)cursor_images[kind].hotspot_x;
141-+			custom_cursor.hotspot_y = (int)cursor_images[kind].hotspot_y;
142-+			custom_cursor.offset = 0;
143-+
144-+			cursor = &custom_cursor;
145-+			data = cursor_images[kind].data;
146-+			object.ptr = (void *)data;
147-+		}
148-+	}
149-+
150- 	if (pointer->cursor.internal_buffer)
151- 		wld_buffer_unreference(pointer->cursor.internal_buffer);
152- 	if (pointer->cursor.surface) {
153-@@ -315,7 +427,6 @@ pointer_initialize(struct pointer *pointer)
154- {
155- 	struct screen *screen = wl_container_of(swc.screens.next, screen, link);
156- 	struct swc_rectangle *geom = &screen->base.geometry;
157--
158- 	/* Center cursor in the geometry of the first screen. */
159- 	screen = wl_container_of(swc.screens.next, screen, link);
160- 	pointer->x = wl_fixed_from_int(geom->x + geom->width / 2);
161-@@ -407,9 +518,15 @@ set_cursor(struct wl_client *client, struct wl_resource *resource,
162- 	struct pointer *pointer = wl_resource_get_user_data(resource);
163- 	struct surface *surface;
164- 
165-+	(void)serial;
166-+
167- 	if (client != pointer->focus.client)
168- 		return;
169- 
170-+	/* If forcing compositor cursor, ignore client cursor surfaces. */
171-+	if (cursor_mode == SWC_CURSOR_MODE_COMPOSITOR || cursor_override != SWC_CURSOR_DEFAULT)
172-+		return;
173-+
174- 	if (pointer->cursor.surface) {
175- 		surface_set_view(pointer->cursor.surface, NULL);
176- 		wl_list_remove(&pointer->cursor.destroy_listener.link);
177-diff --git a/libswc/swc.h b/libswc/swc.h
178-index 2aa06c8..03c3cbc 100644
179---- a/libswc/swc.h
180-+++ b/libswc/swc.h
181-@@ -63,6 +63,49 @@ void swc_pointer_send_button(uint32_t time, uint32_t button, uint32_t state);
182-  */
183- void swc_pointer_send_axis(uint32_t time, uint32_t axis, int32_t value120);
184- 
185-+/* Cursor control (compositor-internal cursor) */
186-+enum swc_cursor_kind {
187-+	SWC_CURSOR_DEFAULT = 0,
188-+	SWC_CURSOR_BOX = 1,
189-+	SWC_CURSOR_CROSS = 2,
190-+	SWC_CURSOR_SIGHT = 3,
191-+	SWC_CURSOR_UP = 4,
192-+	SWC_CURSOR_DOWN = 5,
193-+};
194-+
195-+enum swc_cursor_mode {
196-+	/* Allow clients to set their own cursors (I-beam, resize, etc). */
197-+	SWC_CURSOR_MODE_CLIENT = 0,
198-+	/* Force compositor cursor; ignore client wl_pointer.set_cursor. */
199-+	SWC_CURSOR_MODE_COMPOSITOR = 1,
200-+};
201-+
202-+/**
203-+ * override the compositor's internal cursor
204-+ *
205-+ * this is intended for window managers to show mode cursors (move/resize/select) like the ones in hevel
206-+ * If a client has set its own cursor surface, swc may ignore the override.
207-+ */
208-+void swc_set_cursor(enum swc_cursor_kind kind);
209-+
210-+/**
211-+ * control whether client cursor surfaces are honored
212-+ */
213-+void swc_set_cursor_mode(enum swc_cursor_mode mode);
214-+
215-+/**
216-+ * set a custom argb8888 cursor image for a given kind
217-+ *
218-+ * `argb8888` is a pointer to `width*height` pixels in ARGB8888 order.
219-+ * the caller has to keep the pixel memory alive for as long as it may be used
220-+ */
221-+void swc_set_cursor_image(enum swc_cursor_kind kind,
222-+                          const uint32_t *argb8888,
223-+                          uint32_t width, uint32_t height,
224-+                          int32_t hotspot_x, int32_t hotspot_y);
225-+
226-+void swc_clear_cursor_image(enum swc_cursor_kind kind);
227-+
228- /**
229-  * draw [or update] a simple box overlay
230-  *
231--- 
232-2.49.0
233-
+0, -126
  1@@ -1,126 +0,0 @@
  2-From 42b049150c64e99da7e5ccaea0bba87dfbf7e102 Mon Sep 17 00:00:00 2001
  3-From: dalem <lain@dalem.foo>
  4-Date: Sat, 20 Dec 2025 21:09:41 +0100
  5-Subject: [PATCH] add wallpaper support this was the initial wallpaper
  6- implementation using stb image header. it has since been changed and mostly
  7- moved to the client.
  8-
  9----
 10- libswc/compositor.c |  8 ++++++--
 11- libswc/swc.h        | 23 ++++++++++++++++++++++
 12- libswc/wallpaper.c  | 48 +++++++++++++++++++++++++++++++++++++++++++++
 13- 3 files changed, 77 insertions(+), 2 deletions(-)
 14- create mode 100644 libswc/wallpaper.c
 15-
 16-diff --git a/libswc/compositor.c b/libswc/compositor.c
 17-index 263da39..fae7991 100644
 18---- a/libswc/compositor.c
 19-+++ b/libswc/compositor.c
 20-@@ -283,10 +283,14 @@ renderer_repaint(struct target *target, pixman_region32_t *damage, pixman_region
 21- 
 22- 	wld_set_target_surface(swc.drm->renderer, target->surface);
 23- 
 24--	/* Paint base damage black. */
 25- 	if (pixman_region32_not_empty(base_damage)) {
 26- 		pixman_region32_translate(base_damage, -target->view->geometry.x, -target->view->geometry.y);
 27--		wld_fill_region(swc.drm->renderer, 0xff000000, base_damage);
 28-+		
 29-+		if(wallbuf)
 30-+			wld_copy_region(swc.drm->renderer, wallbuf, 0, 0, base_damage);
 31-+
 32-+		else
 33-+			wld_fill_region(swc.drm->renderer, bgcolor, base_damage);
 34- 	}
 35- 
 36- 	wl_list_for_each_reverse (view, views, link) {
 37-diff --git a/libswc/swc.h b/libswc/swc.h
 38-index 03c3cbc..bbade8f 100644
 39---- a/libswc/swc.h
 40-+++ b/libswc/swc.h
 41-@@ -410,6 +410,29 @@ int swc_add_axis_binding(uint32_t modifiers, uint32_t axis, swc_axis_binding_han
 42- 
 43- /* }}} */
 44- 
 45-+/* Wallpaper {{{ */
 46-+
 47-+extern unsigned char *wallpaper;
 48-+extern struct wld_buffer *wallbuf;
 49-+
 50-+/**
 51-+ * Set wallpaper to image from fs path.
 52-+ * TODO: scaling, tiling, maybe diff image for each screen
 53-+ */
 54-+
 55-+void swc_wallpaper_init(char* path);
 56-+
 57-+/**
 58-+ * Set wallpaper to a single color
 59-+ * pretty much ignored if wallpaper is set to image
 60-+ * defaults to black
 61-+ */
 62-+
 63-+extern uint32_t bgcolor;
 64-+void swc_wallpaper_color_set(uint32_t color);
 65-+
 66-+/* }}} */
 67-+
 68- /**
 69-  * This is a user-provided structure that swc will use to notify the display
 70-  * server of new windows, screens and input devices.
 71-diff --git a/libswc/wallpaper.c b/libswc/wallpaper.c
 72-new file mode 100644
 73-index 0000000..53f8a0c
 74---- /dev/null
 75-+++ b/libswc/wallpaper.c
 76-@@ -0,0 +1,48 @@
 77-+#include <pixman.h>
 78-+#include <wld/wld.h>
 79-+
 80-+#define STB_IMAGE_IMPLEMENTATION
 81-+#define STBI_NO_HDR
 82-+#include "../stb/stb_image.h"
 83-+
 84-+#include "swc.h"
 85-+#include "internal.h"
 86-+#include "drm.h"
 87-+#include "util.h"
 88-+#include "shm.h"
 89-+
 90-+unsigned char *wallpaper = NULL;
 91-+struct wld_buffer *wallbuf = NULL;
 92-+
 93-+uint32_t bgcolor = 0xff000000;
 94-+
 95-+EXPORT void
 96-+swc_wallpaper_init(char* path)
 97-+{
 98-+	int width, height, chan;
 99-+
100-+	wallpaper = stbi_load(path, &width, &height, &chan, 4);
101-+
102-+	/* swap color channels to be compatible */
103-+	for(int i = 0; i < width * height; i++) {
104-+		unsigned char r = wallpaper[i*4];
105-+		wallpaper[i*4] = wallpaper[(i*4)+2];
106-+		wallpaper[(i*4)+2] = r;
107-+	}
108-+
109-+	union wld_object obj;
110-+	obj.ptr = (uint32_t*)wallpaper;
111-+
112-+	wallbuf = wld_import_buffer(swc.shm->context,
113-+			WLD_OBJECT_DATA,
114-+			obj,
115-+			width, height,
116-+			WLD_FORMAT_ARGB8888,
117-+			width * 4);
118-+}
119-+
120-+EXPORT void
121-+swc_wallpaper_color_set(uint32_t color)
122-+{
123-+	bgcolor = color;
124-+}
125--- 
126-2.49.0
127-
+0, -68
 1@@ -1,68 +0,0 @@
 2-From 8e7afcd98a761cdf53006de0db703412dac56310 Mon Sep 17 00:00:00 2001
 3-From: shrub <maybeshrub@gmail.com>
 4-Date: Sat, 20 Dec 2025 20:40:26 +0000
 5-Subject: [PATCH] add wallpaper scaling to the initial wallpaper
 6- implementation, so the wallpaper scales to screen size.
 7-
 8----
 9- libswc/wallpaper.c | 31 ++++++++++++++++++++++++++++++-
10- 1 file changed, 30 insertions(+), 1 deletion(-)
11-
12-diff --git a/libswc/wallpaper.c b/libswc/wallpaper.c
13-index 53f8a0c..ad6e21f 100644
14---- a/libswc/wallpaper.c
15-+++ b/libswc/wallpaper.c
16-@@ -5,11 +5,15 @@
17- #define STBI_NO_HDR
18- #include "../stb/stb_image.h"
19- 
20-+#define STB_IMAGE_RESIZE_IMPLEMENTATION
21-+#include "../stb/stb_image_resize2.h"
22-+
23- #include "swc.h"
24- #include "internal.h"
25- #include "drm.h"
26- #include "util.h"
27- #include "shm.h"
28-+#include "screen.h"
29- 
30- unsigned char *wallpaper = NULL;
31- struct wld_buffer *wallbuf = NULL;
32-@@ -20,8 +24,33 @@ EXPORT void
33- swc_wallpaper_init(char* path)
34- {
35- 	int width, height, chan;
36-+	unsigned char *loaded;
37-+	struct screen *screen;
38-+	int target_width = 0, target_height = 0;
39-+
40-+	loaded = stbi_load(path, &width, &height, &chan, 4);
41-+	if (!loaded)
42-+		return;
43- 
44--	wallpaper = stbi_load(path, &width, &height, &chan, 4);
45-+	/* get screen dimensions */
46-+	wl_list_for_each(screen, &swc.screens, link) {
47-+		target_width = screen->base.geometry.width;
48-+		target_height = screen->base.geometry.height;
49-+		break;
50-+	}
51-+
52-+	/* If we have a screen and dimensions wrong  scale  */
53-+	if (target_width > 0 && target_height > 0 &&
54-+	    (width != target_width || height != target_height)) {
55-+		wallpaper = stbir_resize_uint8_srgb(loaded, width, height, 0,
56-+		                                     NULL, target_width, target_height, 0,
57-+		                                     STBIR_RGBA);
58-+		stbi_image_free(loaded);
59-+		width = target_width;
60-+		height = target_height;
61-+	} else {
62-+		wallpaper = loaded;
63-+	}
64- 
65- 	/* swap color channels to be compatible */
66- 	for(int i = 0; i < width * height; i++) {
67--- 
68-2.49.0
69-
+0, -475
  1@@ -1,475 +0,0 @@
  2-From a6b60a7c94dd00a9900b05e932e2ffb8612f05bd Mon Sep 17 00:00:00 2001
  3-From: shrub <maybeshrub@gmail.com>
  4-Date: Mon, 29 Dec 2025 20:58:33 +0000
  5-Subject: [PATCH] screenshots initial implementation of swc_snap screenshot
  6- protocol
  7-
  8----
  9- libswc/compositor.c   | 116 +++++++++++++++++++++++++
 10- libswc/compositor.h   |  15 ++++
 11- libswc/internal.h     |   1 +
 12- libswc/snap.c         | 193 ++++++++++++++++++++++++++++++++++++++++++
 13- libswc/snap.h         |   9 ++
 14- libswc/swc.c          |  14 ++-
 15- protocol/swc_snap.xml |  15 ++++
 16- 7 files changed, 361 insertions(+), 2 deletions(-)
 17- create mode 100644 libswc/snap.c
 18- create mode 100644 libswc/snap.h
 19- create mode 100644 protocol/swc_snap.xml
 20-
 21-diff --git a/libswc/compositor.c b/libswc/compositor.c
 22-index fae7991..0bf3210 100644
 23---- a/libswc/compositor.c
 24-+++ b/libswc/compositor.c
 25-@@ -1189,3 +1189,119 @@ compositor_finalize(void)
 26- 	pixman_region32_fini(&compositor.opaque);
 27- 	wl_global_destroy(compositor.global);
 28- }
 29-+
 30-+struct wld_buffer *
 31-+compositor_get_buffer(struct screen *screen)
 32-+{
 33-+	struct target *target = target_get(screen);
 34-+	if (!target)
 35-+		return NULL;
 36-+	return target->current_buffer;
 37-+}
 38-+
 39-+struct wld_buffer *
 40-+compositor_render_to_shm(struct screen *screen)
 41-+{
 42-+	uint32_t width = screen->base.geometry.width;
 43-+	uint32_t height = screen->base.geometry.height;
 44-+	struct wld_buffer *buffer;
 45-+	struct compositor_view *view;
 46-+	pixman_region32_t region;
 47-+	pixman_region32_t damage;
 48-+	uint32_t caps;
 49-+
 50-+	/* create shm buf */
 51-+	buffer = wld_create_buffer(swc.shm->context, width, height,
 52-+	                           WLD_FORMAT_ARGB8888, WLD_FLAG_MAP);
 53-+	if (!buffer)
 54-+		return NULL;
 55-+
 56-+	caps = wld_capabilities(swc.shm->renderer, buffer);
 57-+	if (!(caps & WLD_CAPABILITY_WRITE) ||
 58-+	    !wld_set_target_buffer(swc.shm->renderer, buffer)) {
 59-+		wld_buffer_unreference(buffer);
 60-+		return NULL;
 61-+	}
 62-+
 63-+	/* set reigon */
 64-+	pixman_region32_init_rect(&region, 0, 0, width, height);
 65-+	pixman_region32_init_rect(&damage, screen->base.geometry.x, screen->base.geometry.y, width, height);
 66-+
 67-+	/* background */
 68-+	if (wallbuf)
 69-+		wld_copy_region(swc.shm->renderer, wallbuf, 0, 0, &region);
 70-+	else
 71-+		wld_fill_region(swc.shm->renderer, bgcolor, &region);
 72-+
 73-+	wl_list_for_each_reverse(view, &compositor.views, link) {
 74-+		struct wld_buffer *src = view->buffer;
 75-+
 76-+		if (!view->visible)
 77-+			continue;
 78-+
 79-+		if (src && !(wld_capabilities(swc.shm->renderer, src) & WLD_CAPABILITY_READ))
 80-+			src = view->base.buffer;
 81-+
 82-+		if (src && (wld_capabilities(swc.shm->renderer, src) & WLD_CAPABILITY_READ)) {
 83-+			int32_t x = view->base.geometry.x - screen->base.geometry.x;
 84-+			int32_t y = view->base.geometry.y - screen->base.geometry.y;
 85-+
 86-+			wld_copy_rectangle(swc.shm->renderer, src,
 87-+			                   x, y, 0, 0,
 88-+			                   view->base.geometry.width, view->base.geometry.height);
 89-+		}
 90-+
 91-+		if ((view->border.outwidth > 0 || view->border.inwidth > 0) && view->base.buffer) {
 92-+			pixman_region32_t view_region, view_damage, border_damage;
 93-+			const struct swc_rectangle *geom = &view->base.geometry;
 94-+			const struct swc_rectangle *target_geom = &screen->base.geometry;
 95-+
 96-+			pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
 97-+			pixman_region32_init_with_extents(&view_damage, &view->extents);
 98-+			pixman_region32_init(&border_damage);
 99-+
100-+			pixman_region32_intersect(&view_damage, &view_damage, &damage);
101-+			pixman_region32_subtract(&view_damage, &view_damage, &view->clip);
102-+			pixman_region32_subtract(&border_damage, &view_damage, &view_region);
103-+
104-+			pixman_region32_t in_rect;
105-+			pixman_region32_init_rect(&in_rect,
106-+			                          geom->x - view->border.inwidth,
107-+			                          geom->y - view->border.inwidth,
108-+			                          geom->width + (2 * view->border.inwidth),
109-+			                          geom->height + (2 * view->border.inwidth));
110-+
111-+			pixman_region32_t out_border;
112-+			pixman_region32_init(&out_border);
113-+			pixman_region32_subtract(&out_border, &border_damage, &in_rect);
114-+
115-+			pixman_region32_t in_border;
116-+			pixman_region32_init(&in_border);
117-+			pixman_region32_subtract(&in_border, &in_rect, &view_region);
118-+			pixman_region32_intersect(&in_border, &in_border, &border_damage);
119-+
120-+			if (view->border.outwidth > 0 && pixman_region32_not_empty(&out_border)) {
121-+				pixman_region32_translate(&out_border, -target_geom->x, -target_geom->y);
122-+				wld_fill_region(swc.shm->renderer, view->border.outcolor, &out_border);
123-+			}
124-+
125-+			if (view->border.inwidth > 0 && pixman_region32_not_empty(&in_border)) {
126-+				pixman_region32_translate(&in_border, -target_geom->x, -target_geom->y);
127-+				wld_fill_region(swc.shm->renderer, view->border.incolor, &in_border);
128-+			}
129-+
130-+			pixman_region32_fini(&border_damage);
131-+			pixman_region32_fini(&view_region);
132-+			pixman_region32_fini(&view_damage);
133-+			pixman_region32_fini(&in_rect);
134-+			pixman_region32_fini(&out_border);
135-+			pixman_region32_fini(&in_border);
136-+		}
137-+	}
138-+
139-+	wld_flush(swc.shm->renderer);
140-+	pixman_region32_fini(&region);
141-+	pixman_region32_fini(&damage);
142-+
143-+	return buffer;
144-+}
145-diff --git a/libswc/compositor.h b/libswc/compositor.h
146-index 9046f0b..2e70b38 100644
147---- a/libswc/compositor.h
148-+++ b/libswc/compositor.h
149-@@ -30,6 +30,9 @@
150- #include <pixman.h>
151- #include <wayland-server.h>
152- 
153-+struct screen;
154-+struct wld_buffer;
155-+
156- struct swc_compositor {
157- 	struct pointer_handler *const pointer_handler;
158- 	struct {
159-@@ -97,4 +100,16 @@ void compositor_view_hide(struct compositor_view *view);
160- void compositor_view_set_border_color(struct compositor_view *view, uint32_t outcolor, uint32_t incolor);
161- void compositor_view_set_border_width(struct compositor_view *view, uint32_t outwidth, uint32_t inwidth);
162- 
163-+/**
164-+ * get the current composited buffer for a screen for screenshotss.
165-+ * returns null if no buffer
166-+ */
167-+struct wld_buffer *compositor_get_buffer(struct screen *screen);
168-+
169-+/**
170-+ * render the compositor scene into a shm buffer 
171-+ * caller must free with wld_buffer_unreference()
172-+ */
173-+struct wld_buffer *compositor_render_to_shm(struct screen *screen);
174-+
175- #endif
176-diff --git a/libswc/internal.h b/libswc/internal.h
177-index 8efba6d..243b75f 100644
178---- a/libswc/internal.h
179-+++ b/libswc/internal.h
180-@@ -49,6 +49,7 @@ struct swc {
181- 	struct wl_global *kde_decoration_manager;
182- 	struct wl_global *panel_manager;
183- 	struct wl_global *shell;
184-+	struct wl_global *snap_manager;
185- 	struct wl_global *subcompositor;
186- 	struct wl_global *xdg_decoration_manager;
187- 	struct wl_global *xdg_shell;
188-diff --git a/libswc/snap.c b/libswc/snap.c
189-new file mode 100644
190-index 0000000..f977649
191---- /dev/null
192-+++ b/libswc/snap.c
193-@@ -0,0 +1,193 @@
194-+#include "snap.h"
195-+#include "internal.h"
196-+#include "screen.h"
197-+#include "compositor.h"
198-+#include "shm.h"
199-+#include "seat.h"
200-+#include "pointer.h"
201-+
202-+#include <stdio.h>
203-+#include <stdlib.h>
204-+#include <stdint.h>
205-+#include <string.h>
206-+#include <unistd.h>
207-+#include <wayland-server.h>
208-+#include <wld/wld.h>
209-+#include "swc_snap-server-protocol.h"
210-+
211-+static void
212-+ppm(int fd, const uint8_t *pixels, uint32_t width, uint32_t height,
213-+    uint32_t pitch)
214-+{
215-+	FILE *f = fdopen(fd, "wb");
216-+	if (!f) {
217-+		close(fd);
218-+		return;
219-+	}
220-+
221-+	/* ppm  header */
222-+	fprintf(f, "P6\n%u %u\n255\n", width, height);
223-+
224-+	/* pixel data convert argb8888 to rgb) */
225-+	for (uint32_t y = 0; y < height; y++) {
226-+		const uint32_t *row = (const uint32_t *)(pixels + ((size_t)y * pitch));
227-+
228-+		for (uint32_t x = 0; x < width; x++) {
229-+			uint32_t pixel = row[x];
230-+			unsigned char rgb[3] = {
231-+				(pixel >> 16) & 0xFF,  
232-+				(pixel >> 8) & 0xFF,   
233-+				pixel & 0xFF           
234-+			};
235-+			fwrite(rgb, 1, 3, f);
236-+		}
237-+	}
238-+
239-+	fclose(f); 
240-+}
241-+
242-+
243-+/* get cursor */
244-+static void
245-+cursor(uint8_t *dst, uint32_t dst_width, uint32_t dst_height,
246-+       uint32_t dst_pitch, struct screen *screen)
247-+{
248-+	struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
249-+	struct wld_buffer *cursor_buf;
250-+	const uint8_t *src;
251-+	int32_t dst_x, dst_y;
252-+	int32_t src_x = 0, src_y = 0;
253-+	uint32_t copy_w, copy_h;
254-+
255-+	if (!pointer || !pointer->cursor.buffer || !pointer->cursor.view.buffer)
256-+		return;
257-+
258-+	if (!(pointer->cursor.view.screens & screen_mask(screen)))
259-+		return;
260-+
261-+	cursor_buf = pointer->cursor.buffer;
262-+	if (!wld_map(cursor_buf) || !cursor_buf->map)
263-+		return;
264-+
265-+	dst_x = pointer->cursor.view.geometry.x - screen->base.geometry.x;
266-+	dst_y = pointer->cursor.view.geometry.y - screen->base.geometry.y;
267-+
268-+	if (dst_x >= (int32_t)dst_width || dst_y >= (int32_t)dst_height ||
269-+	    dst_x + (int32_t)cursor_buf->width <= 0 ||
270-+	    dst_y + (int32_t)cursor_buf->height <= 0) {
271-+		wld_unmap(cursor_buf);
272-+		return;
273-+	}
274-+
275-+	if (dst_x < 0) {
276-+		src_x = -dst_x;
277-+		dst_x = 0;
278-+	}
279-+	if (dst_y < 0) {
280-+		src_y = -dst_y;
281-+		dst_y = 0;
282-+	}
283-+
284-+	copy_w = cursor_buf->width - (uint32_t)src_x;
285-+	if (copy_w > dst_width - (uint32_t)dst_x)
286-+		copy_w = dst_width - (uint32_t)dst_x;
287-+	copy_h = cursor_buf->height - (uint32_t)src_y;
288-+	if (copy_h > dst_height - (uint32_t)dst_y)
289-+		copy_h = dst_height - (uint32_t)dst_y;
290-+
291-+	src = cursor_buf->map;
292-+
293-+	for (uint32_t y = 0; y < copy_h; y++) {
294-+		const uint32_t *src_row = (const uint32_t *)(src + ((size_t)(src_y + (int32_t)y) * cursor_buf->pitch)) + src_x;
295-+		uint32_t *dst_row = (uint32_t *)(dst + ((size_t)(dst_y + (int32_t)y) * dst_pitch)) + dst_x;
296-+
297-+		for (uint32_t x = 0; x < copy_w; x++) {
298-+			uint32_t src_px = src_row[x];
299-+			uint32_t a = src_px >> 24;
300-+
301-+			if (a == 0)
302-+				continue;
303-+			if (a == 255) {
304-+				dst_row[x] = 0xFF000000 | (src_px & 0x00FFFFFF);
305-+				continue;
306-+			}
307-+
308-+			uint32_t dst_px = dst_row[x];
309-+			uint32_t inv = 255 - a;
310-+			uint32_t r = ((src_px >> 16) & 0xFF) + ((((dst_px >> 16) & 0xFF) * inv + 127) / 255);
311-+			uint32_t g = ((src_px >> 8) & 0xFF) + ((((dst_px >> 8) & 0xFF) * inv + 127) / 255);
312-+			uint32_t b = (src_px & 0xFF) + (((dst_px & 0xFF) * inv + 127) / 255);
313-+
314-+			dst_row[x] = 0xFF000000 | (r << 16) | (g << 8) | b;
315-+		}
316-+	}
317-+
318-+	wld_unmap(cursor_buf);
319-+}
320-+
321-+static void
322-+capture(struct wl_client *client, struct wl_resource *resource, int32_t fd)
323-+{
324-+	struct screen *screen;
325-+	struct wld_buffer *shm_buffer;
326-+	uint8_t *pixels;
327-+	uint32_t width, height;
328-+
329-+	if (wl_list_empty(&swc.screens)) {
330-+		fprintf(stderr, "snap: no screens available\n");
331-+		close(fd);
332-+		return;
333-+	}
334-+
335-+	screen = wl_container_of(swc.screens.next, screen, link);
336-+	width = screen->base.geometry.width;
337-+	height = screen->base.geometry.height;
338-+
339-+	/* put compositor in shm*/
340-+	shm_buffer = compositor_render_to_shm(screen);
341-+	if (!shm_buffer) {
342-+		fprintf(stderr, "snap: failed to render to SHM\n");
343-+		close(fd);
344-+		return;
345-+	}
346-+
347-+	/* get pixel data from shm */
348-+	if (!wld_map(shm_buffer) || !shm_buffer->map) {
349-+		fprintf(stderr, "snap: failed to map buffer data\n");
350-+		wld_buffer_unreference(shm_buffer);
351-+		close(fd);
352-+		return;
353-+	}
354-+
355-+	pixels = shm_buffer->map;
356-+
357-+	cursor(pixels, width, height, shm_buffer->pitch, screen);
358-+
359-+	ppm(fd, pixels, width, height, shm_buffer->pitch);
360-+
361-+	wld_unmap(shm_buffer);
362-+	wld_buffer_unreference(shm_buffer);
363-+}
364-+
365-+static const struct swc_snap_interface snap_impl = {
366-+	.capture = capture,
367-+};
368-+
369-+static void
370-+bind_snap(struct wl_client *client, void *data, uint32_t version, uint32_t id)
371-+{
372-+	struct wl_resource *resource;
373-+
374-+	resource = wl_resource_create(client, &swc_snap_interface, version, id);
375-+	if (!resource) {
376-+		wl_client_post_no_memory(client);
377-+		return;
378-+	}
379-+	wl_resource_set_implementation(resource, &snap_impl, NULL, NULL);
380-+}
381-+
382-+struct wl_global *
383-+snap_manager_create(struct wl_display *display)
384-+{
385-+	return wl_global_create(display, &swc_snap_interface, 1, NULL, &bind_snap);
386-+}
387-diff --git a/libswc/snap.h b/libswc/snap.h
388-new file mode 100644
389-index 0000000..62c6fe5
390---- /dev/null
391-+++ b/libswc/snap.h
392-@@ -0,0 +1,9 @@
393-+#ifndef SWC_SNAP_H
394-+#define SWC_SNAP_H
395-+
396-+struct wl_display;
397-+struct wl_global;
398-+
399-+struct wl_global *snap_manager_create(struct wl_display *display);
400-+
401-+#endif
402-diff --git a/libswc/swc.c b/libswc/swc.c
403-index cf82913..6d3f871 100644
404---- a/libswc/swc.c
405-+++ b/libswc/swc.c
406-@@ -37,6 +37,7 @@
407- #include "seat.h"
408- #include "shell.h"
409- #include "shm.h"
410-+#include "snap.h"
411- #include "subcompositor.h"
412- #include "util.h"
413- #include "window.h"
414-@@ -215,10 +216,16 @@ swc_initialize(struct wl_display *display, struct wl_event_loop *event_loop, con
415- 		goto error13;
416- 	}
417- 
418-+	swc.snap_manager = snap_manager_create(display);
419-+	if (!swc.snap_manager) {
420-+		ERROR("Could not initialize snap manager\n");
421-+		goto error14;
422-+	}
423-+
424- #ifdef ENABLE_XWAYLAND
425- 	if (!xserver_initialize()) {
426- 		ERROR("Could not initialize xwayland\n");
427--		goto error14;
428-+		goto error15;
429- 	}
430- #endif
431- 
432-@@ -227,9 +234,11 @@ swc_initialize(struct wl_display *display, struct wl_event_loop *event_loop, con
433- 	return true;
434- 
435- #ifdef ENABLE_XWAYLAND
436-+error15:
437-+	wl_global_destroy(swc.snap_manager);
438-+#endif
439- error14:
440- 	wl_global_destroy(swc.panel_manager);
441--#endif
442- error13:
443- 	wl_global_destroy(swc.kde_decoration_manager);
444- error12:
445-@@ -266,6 +275,7 @@ swc_finalize(void)
446- #ifdef ENABLE_XWAYLAND
447- 	xserver_finalize();
448- #endif
449-+	wl_global_destroy(swc.snap_manager);
450- 	wl_global_destroy(swc.panel_manager);
451- 	wl_global_destroy(swc.xdg_decoration_manager);
452- 	wl_global_destroy(swc.xdg_shell);
453-diff --git a/protocol/swc_snap.xml b/protocol/swc_snap.xml
454-new file mode 100644
455-index 0000000..e8d8622
456---- /dev/null
457-+++ b/protocol/swc_snap.xml
458-@@ -0,0 +1,15 @@
459-+<?xml version="1.0" encoding="UTF-8"?>
460-+<protocol name="swc_snap">
461-+    <interface name="swc_snap" version="1">
462-+        <description summary="simple screenshot to file descriptor">
463-+            provides a minimal screenshot capability.
464-+        </description>
465-+
466-+        <request name="capture">
467-+            <description summary="capture screenshot to fd">
468-+                captures the current screen and writes it as ppm formay
469-+            </description>
470-+            <arg name="fd" type="fd" summary="file descriptor to write ppm data"/>
471-+        </request>
472-+    </interface>
473-+</protocol>
474--- 
475-2.49.0
476-
+0, -85
 1@@ -1,85 +0,0 @@
 2-From ebae7966352bca2ad16f0ca6a1df988b61b1a198 Mon Sep 17 00:00:00 2001
 3-From: shrub <maybeshrub@gmail.com>
 4-Date: Sat, 10 Jan 2026 00:20:19 +0000
 5-Subject: [PATCH] swc_window_get_pid add api to query a window's client pid
 6-
 7----
 8- libswc/swc.h    |  8 ++++++++
 9- libswc/window.c | 25 +++++++++++++++++++++++++
10- 2 files changed, 33 insertions(+)
11-
12-diff --git a/libswc/swc.h b/libswc/swc.h
13-index bbade8f..5d0d9a0 100644
14---- a/libswc/swc.h
15-+++ b/libswc/swc.h
16-@@ -26,6 +26,7 @@
17- 
18- #include <stdbool.h>
19- #include <stdint.h>
20-+#include <sys/types.h>
21- 
22- #ifdef __cplusplus
23- extern "C" {
24-@@ -320,6 +321,13 @@ void swc_window_set_geometry(struct swc_window *window, const struct swc_rectang
25-  */
26- bool swc_window_get_geometry(const struct swc_window *window, struct swc_rectangle *geometry);
27- 
28-+/**
29-+ * Get the pid of the client that owns this window
30-+ *
31-+ * returns pid, or 0 if unavailable
32-+ */
33-+pid_t swc_window_get_pid(struct swc_window *window);
34-+
35- /**
36-  * Set the window's border color and width.
37-  *
38-diff --git a/libswc/window.c b/libswc/window.c
39-index b1fdeb9..f46793d 100644
40---- a/libswc/window.c
41-+++ b/libswc/window.c
42-@@ -27,12 +27,14 @@
43- #include "internal.h"
44- #include "keyboard.h"
45- #include "seat.h"
46-+#include "surface.h"
47- #include "swc.h"
48- #include "util.h"
49- #include "view.h"
50- 
51- #include <stdlib.h>
52- #include <string.h>
53-+#include <sys/types.h>
54- 
55- #define INTERNAL(w) ((struct window *)(w))
56- 
57-@@ -519,3 +521,26 @@ window_begin_resize(struct window *window, uint32_t edges, struct button *button
58- 	window->resize.offset.y = geometry->y - py + ((edges & SWC_WINDOW_EDGE_BOTTOM) ? geometry->height : 0);
59- 	window->resize.edges = edges;
60- }
61-+
62-+EXPORT pid_t
63-+swc_window_get_pid(struct swc_window *base)
64-+{
65-+	struct window *window = INTERNAL(base);
66-+	struct surface *surface;
67-+	struct wl_client *client;
68-+	pid_t pid;
69-+	uid_t uid;
70-+	gid_t gid;
71-+
72-+	if (!window || !window->view || !window->view->surface)
73-+		return 0;
74-+
75-+	surface = window->view->surface;
76-+	if (!surface->resource)
77-+		return 0;
78-+
79-+	client = wl_resource_get_client(surface->resource);
80-+	wl_client_get_credentials(client, &pid, &uid, &gid);
81-+
82-+	return pid;
83-+}
84--- 
85-2.49.0
86-
+0, -333
  1@@ -1,333 +0,0 @@
  2-From 840a07ecb240c2944f359cbde277c10ab65aa135 Mon Sep 17 00:00:00 2001
  3-From: shrub <maybeshrub@gmail.com>
  4-Date: Tue, 20 Jan 2026 22:31:55 +0000
  5-Subject: [PATCH] zoom! swc_set_zoom and get_zoom api added zoom does a
  6- fullscreen damage and renders a zoomed scene into a shm buffer, with pixman
  7-
  8----
  9- libswc/compositor.c | 249 ++++++++++++++++++++++++++++++++++++++++++--
 10- libswc/swc.h        |  13 +++
 11- 2 files changed, 254 insertions(+), 8 deletions(-)
 12-
 13-diff --git a/libswc/compositor.c b/libswc/compositor.c
 14-index 0bf3210..c8786b5 100644
 15---- a/libswc/compositor.c
 16-+++ b/libswc/compositor.c
 17-@@ -108,6 +108,10 @@ static struct {
 18- 
 19- 	bool updating;
 20- 	struct wl_global *global;
 21-+
 22-+	/* zoom level (1.0 = normal, >1 = zoomed in, <1 = zoomed out) */
 23-+	float zoom;
 24-+	struct wld_buffer *zoom_buffer;
 25- } compositor;
 26- 
 27- static struct {
 28-@@ -456,6 +460,17 @@ schedule_updates(uint32_t screens)
 29- 			screens |= screen_mask(screen);
 30- 	}
 31- 
 32-+	/* when zoomed, force full screen damage since actual area differs from world coords */
 33-+	if (compositor.zoom != 1.0f) {
 34-+		struct screen *screen;
 35-+		wl_list_for_each (screen, &swc.screens, link) {
 36-+			pixman_region32_union_rect(&compositor.damage, &compositor.damage,
 37-+				screen->base.geometry.x, screen->base.geometry.y,
 38-+				screen->base.geometry.width, screen->base.geometry.height);
 39-+			screens |= screen_mask(screen);
 40-+		}
 41-+	}
 42-+
 43- 	compositor.scheduled_updates |= screens;
 44- }
 45- 
 46-@@ -503,6 +518,202 @@ swc_overlay_clear(void)
 47- 	schedule_updates(-1);
 48- }
 49- 
 50-+EXPORT void
 51-+swc_set_zoom(float level)
 52-+{
 53-+	if (level < 0.1f)
 54-+		level = 0.1f;
 55-+	if (level > 10.0f)
 56-+		level = 10.0f;
 57-+
 58-+	if (compositor.zoom != level) {
 59-+		compositor.zoom = level;
 60-+		/* damage entire screen to force full repaint */
 61-+		schedule_updates(-1);
 62-+	}
 63-+}
 64-+
 65-+EXPORT float
 66-+swc_get_zoom(void)
 67-+{
 68-+	return compositor.zoom;
 69-+}
 70-+
 71-+static pixman_format_code_t
 72-+wld_to_pixman_format(enum wld_format format)
 73-+{
 74-+	switch (format) {
 75-+	case WLD_FORMAT_XRGB8888:
 76-+		return PIXMAN_x8r8g8b8;
 77-+	case WLD_FORMAT_ARGB8888:
 78-+		return PIXMAN_a8r8g8b8;
 79-+	default:
 80-+		return PIXMAN_x8r8g8b8;
 81-+	}
 82-+}
 83-+
 84-+/* render zoomed view to shm      wallpaper unscaled, windows scaled */
 85-+static struct wld_buffer *
 86-+render_zoomed_to_shm(struct screen *screen, float zoom)
 87-+{
 88-+	uint32_t width = screen->base.geometry.width;
 89-+	uint32_t height = screen->base.geometry.height;
 90-+	int32_t screen_x = screen->base.geometry.x;
 91-+	int32_t screen_y = screen->base.geometry.y;
 92-+	int32_t cx = screen_x + width / 2;
 93-+	int32_t cy = screen_y + height / 2;
 94-+	struct compositor_view *view;
 95-+
 96-+	struct wld_buffer *buffer = wld_create_buffer(swc.shm->context,
 97-+		width, height, WLD_FORMAT_XRGB8888, WLD_FLAG_MAP);
 98-+	if (!buffer)
 99-+		return NULL;
100-+
101-+	if (!wld_set_target_buffer(swc.shm->renderer, buffer)) {
102-+		wld_buffer_unreference(buffer);
103-+		return NULL;
104-+	}
105-+
106-+	pixman_region32_t full;
107-+	pixman_region32_init_rect(&full, 0, 0, width, height);
108-+	if (wallbuf)
109-+		wld_copy_region(swc.shm->renderer, wallbuf, 0, 0, &full);
110-+	else
111-+		wld_fill_region(swc.shm->renderer, bgcolor, &full);
112-+	pixman_region32_fini(&full);
113-+	wld_flush(swc.shm->renderer);
114-+
115-+	if (!wld_map(buffer)) {
116-+		wld_buffer_unreference(buffer);
117-+		return NULL;
118-+	}
119-+
120-+	pixman_image_t *dst_img = pixman_image_create_bits(
121-+		wld_to_pixman_format(buffer->format),
122-+		buffer->width, buffer->height,
123-+		buffer->map, buffer->pitch);
124-+
125-+	if (!dst_img) {
126-+		wld_unmap(buffer);
127-+		wld_buffer_unreference(buffer);
128-+		return NULL;
129-+	}
130-+
131-+	/* render each view with scaling */
132-+	wl_list_for_each_reverse(view, &compositor.views, link) {
133-+		struct wld_buffer *src = view->buffer;
134-+		const struct swc_rectangle *geom = &view->base.geometry;
135-+
136-+		if (!src)
137-+			continue;
138-+
139-+		if (!(wld_capabilities(swc.shm->renderer, src) & WLD_CAPABILITY_READ))
140-+			src = view->base.buffer;
141-+		if (!src)
142-+			continue;
143-+
144-+		/* maths     zoom position and size */
145-+		float zoomed_x = (geom->x - cx) * zoom + width / 2.0f;
146-+		float zoomed_y = (geom->y - cy) * zoom + height / 2.0f;
147-+		float zoomed_w = geom->width * zoom;
148-+		float zoomed_h = geom->height * zoom;
149-+
150-+		float border_out = view->border.outwidth * zoom;
151-+		float border_in = view->border.inwidth * zoom;
152-+		float total_border = border_out + border_in;
153-+
154-+		if (zoomed_x + zoomed_w + total_border < 0 || zoomed_x - total_border >= (int32_t)width ||
155-+		    zoomed_y + zoomed_h + total_border < 0 || zoomed_y - total_border >= (int32_t)height)
156-+			continue;
157-+
158-+		if (view->border.outwidth > 0 && border_out >= 1) {
159-+			int32_t bx = (int32_t)(zoomed_x - total_border);
160-+			int32_t by = (int32_t)(zoomed_y - total_border);
161-+			int32_t bw = (int32_t)(zoomed_w + 2 * total_border);
162-+			int32_t bh = (int32_t)(zoomed_h + 2 * total_border);
163-+			int32_t bo = (int32_t)border_out;
164-+
165-+			pixman_color_t color = {
166-+				.red = ((view->border.outcolor >> 16) & 0xff) * 257,
167-+				.green = ((view->border.outcolor >> 8) & 0xff) * 257,
168-+				.blue = (view->border.outcolor & 0xff) * 257,
169-+				.alpha = 0xffff
170-+			};
171-+			pixman_image_t *fill = pixman_image_create_solid_fill(&color);
172-+			if (fill) {
173-+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
174-+					0, 0, 0, 0, bx, by, bw, bo);
175-+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
176-+					0, 0, 0, 0, bx, by + bh - bo, bw, bo);
177-+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
178-+					0, 0, 0, 0, bx, by + bo, bo, bh - 2 * bo);
179-+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
180-+					0, 0, 0, 0, bx + bw - bo, by + bo, bo, bh - 2 * bo);
181-+				pixman_image_unref(fill);
182-+			}
183-+		}
184-+
185-+		if (view->border.inwidth > 0 && border_in >= 1) {
186-+			int32_t bx = (int32_t)(zoomed_x - border_in);
187-+			int32_t by = (int32_t)(zoomed_y - border_in);
188-+			int32_t bw = (int32_t)(zoomed_w + 2 * border_in);
189-+			int32_t bh = (int32_t)(zoomed_h + 2 * border_in);
190-+			int32_t bi = (int32_t)border_in;
191-+
192-+			pixman_color_t color = {
193-+				.red = ((view->border.incolor >> 16) & 0xff) * 257,
194-+				.green = ((view->border.incolor >> 8) & 0xff) * 257,
195-+				.blue = (view->border.incolor & 0xff) * 257,
196-+				.alpha = 0xffff
197-+			};
198-+			pixman_image_t *fill = pixman_image_create_solid_fill(&color);
199-+			if (fill) {
200-+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
201-+					0, 0, 0, 0, bx, by, bw, bi);
202-+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
203-+					0, 0, 0, 0, bx, by + bh - bi, bw, bi);
204-+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
205-+					0, 0, 0, 0, bx, by + bi, bi, bh - 2 * bi);
206-+				pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst_img,
207-+					0, 0, 0, 0, bx + bw - bi, by + bi, bi, bh - 2 * bi);
208-+				pixman_image_unref(fill);
209-+			}
210-+		}
211-+
212-+		if (!wld_map(src))
213-+			continue;
214-+
215-+		pixman_image_t *src_img = pixman_image_create_bits(
216-+			wld_to_pixman_format(src->format),
217-+			src->width, src->height,
218-+			src->map, src->pitch);
219-+
220-+		if (src_img) {
221-+			pixman_transform_t transform;
222-+			pixman_transform_init_identity(&transform);
223-+			pixman_fixed_t scale = pixman_double_to_fixed(1.0 / zoom);
224-+			pixman_transform_scale(&transform, NULL, scale, scale);
225-+			pixman_image_set_transform(src_img, &transform);
226-+			pixman_image_set_filter(src_img, PIXMAN_FILTER_BILINEAR, NULL, 0);
227-+
228-+			pixman_image_composite32(PIXMAN_OP_OVER,
229-+				src_img, NULL, dst_img,
230-+				0, 0, 0, 0,
231-+				(int32_t)zoomed_x, (int32_t)zoomed_y,
232-+				(int32_t)(zoomed_w + 1), (int32_t)(zoomed_h + 1));
233-+
234-+			pixman_image_unref(src_img);
235-+		}
236-+
237-+		wld_unmap(src);
238-+	}
239-+
240-+	pixman_image_unref(dst_img);
241-+	wld_unmap(buffer);
242-+
243-+	return buffer;
244-+}
245-+
246- static bool
247- update(struct view *base)
248- {
249-@@ -996,14 +1207,32 @@ update_screen(struct screen *screen)
250- 		return;
251- 	}
252- 
253--	pixman_region32_t base_damage;
254--	pixman_region32_copy(&damage, total_damage);
255--	pixman_region32_translate(&damage, geom->x, geom->y);
256--	pixman_region32_init(&base_damage);
257--	pixman_region32_subtract(&base_damage, &damage, &compositor.opaque);
258--	renderer_repaint(target, &damage, &base_damage, &compositor.views);
259--	pixman_region32_fini(&damage);
260--	pixman_region32_fini(&base_damage);
261-+	/* check if zoom */
262-+	if (compositor.zoom != 1.0f) {
263-+		pixman_region32_fini(&damage);
264-+
265-+		struct wld_buffer *zoomed = render_zoomed_to_shm(screen, compositor.zoom);
266-+		if (!zoomed)
267-+			return;
268-+
269-+		pixman_region32_t full;
270-+		pixman_region32_init_rect(&full, 0, 0, geom->width, geom->height);
271-+		wld_set_target_surface(swc.drm->renderer, target->surface);
272-+		wld_copy_region(swc.drm->renderer, zoomed, 0, 0, &full);
273-+		wld_flush(swc.drm->renderer);
274-+		pixman_region32_fini(&full);
275-+
276-+		wld_buffer_unreference(zoomed);
277-+	} else {
278-+		pixman_region32_t base_damage;
279-+		pixman_region32_copy(&damage, total_damage);
280-+		pixman_region32_translate(&damage, geom->x, geom->y);
281-+		pixman_region32_init(&base_damage);
282-+		pixman_region32_subtract(&base_damage, &damage, &compositor.opaque);
283-+		renderer_repaint(target, &damage, &base_damage, &compositor.views);
284-+		pixman_region32_fini(&damage);
285-+		pixman_region32_fini(&base_damage);
286-+	}
287- 
288- 	switch (target_swap_buffers(target)) {
289- 	case -EACCES:
290-@@ -1162,6 +1391,8 @@ compositor_initialize(void)
291- 	compositor.scheduled_updates = 0;
292- 	compositor.pending_flips = 0;
293- 	compositor.updating = false;
294-+	compositor.zoom = 1.0f;
295-+	compositor.zoom_buffer = NULL;
296- 	pixman_region32_init(&compositor.damage);
297- 	pixman_region32_init(&compositor.opaque);
298- 	wl_list_init(&compositor.views);
299-@@ -1185,6 +1416,8 @@ compositor_initialize(void)
300- void
301- compositor_finalize(void)
302- {
303-+	if (compositor.zoom_buffer)
304-+		wld_buffer_unreference(compositor.zoom_buffer);
305- 	pixman_region32_fini(&compositor.damage);
306- 	pixman_region32_fini(&compositor.opaque);
307- 	wl_global_destroy(compositor.global);
308-diff --git a/libswc/swc.h b/libswc/swc.h
309-index 5d0d9a0..b95fc4c 100644
310---- a/libswc/swc.h
311-+++ b/libswc/swc.h
312-@@ -121,6 +121,19 @@ void swc_overlay_set_box(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_
313-  */
314- void swc_overlay_clear(void);
315- 
316-+/**
317-+ * Set the compositor zoom level.
318-+ *
319-+ * 1.0 = normal, >1.0 = zoomed in, <1.0 = zoomed out
320-+ * Uses software (pixman) scaling.
321-+ */
322-+void swc_set_zoom(float level);
323-+
324-+/**
325-+ * Get the current zoom level.
326-+ */
327-+float swc_get_zoom(void);
328-+
329- /* Rectangles {{{ */
330- 
331- struct swc_rectangle {
332--- 
333-2.49.0
334-
+0, -62
 1@@ -1,62 +0,0 @@
 2-From 52e195daf74388d29d3b20d1ffd9ca3ffbfe929f Mon Sep 17 00:00:00 2001
 3-From: dalem <lain@dalem.foo>
 4-Date: Tue, 27 Jan 2026 17:36:27 +0100
 5-Subject: [PATCH] add fullscreen support
 6-
 7----
 8- libswc/window.c | 21 ++++++++++++++++-----
 9- libswc/window.h |  5 +++++
10- 2 files changed, 21 insertions(+), 5 deletions(-)
11-
12-diff --git a/libswc/window.c b/libswc/window.c
13-index f46793d..84e97b0 100644
14---- a/libswc/window.c
15-+++ b/libswc/window.c
16-@@ -194,12 +194,23 @@ EXPORT void
17- swc_window_set_fullscreen(struct swc_window *base, struct swc_screen *screen)
18- {
19- 	struct window *window = INTERNAL(base);
20-+	struct swc_rectangle geom;
21-+	swc_window_get_geometry(base, &geom);
22-+	
23-+	if (window->mode != WINDOW_MODE_FULLSCREEN) {
24-+		window->prev.geom = geom;
25-+		window->prev.mode = window->mode;
26-+		swc_window_set_geometry(base, &screen->usable_geometry);
27-+
28-+		if (window->impl->set_mode)
29-+			window->impl->set_mode(window, WINDOW_MODE_FULLSCREEN);
30-+		window->mode = WINDOW_MODE_FULLSCREEN;
31-+	}
32- 
33--	/* TODO: Implement fullscreen windows. */
34--
35--	if (window->impl->set_mode)
36--		window->impl->set_mode(window, WINDOW_MODE_FULLSCREEN);
37--	window->mode = WINDOW_MODE_FULLSCREEN;
38-+	else {
39-+		swc_window_set_geometry(base, &window->prev.geom);
40-+		window->mode = window->prev.mode;
41-+	}
42- }
43- 
44- EXPORT void
45-diff --git a/libswc/window.h b/libswc/window.h
46-index 08c9d36..d8f6bfa 100644
47---- a/libswc/window.h
48-+++ b/libswc/window.h
49-@@ -52,6 +52,11 @@ struct window {
50- 	struct view_handler view_handler;
51- 	bool managed;
52- 	unsigned mode;
53-+	
54-+	struct {
55-+		struct swc_rectangle geom;
56-+		unsigned mode;
57-+	} prev;
58- 
59- 	struct {
60- 		struct window_pointer_interaction interaction;
61--- 
62-2.49.0
63-
+0, -678
  1@@ -1,678 +0,0 @@
  2-From 53714281024d80814f9884a01ba232cf0342e15b Mon Sep 17 00:00:00 2001
  3-From: shrub <maybeshrub@gmail.com>
  4-Date: Sat, 31 Jan 2026 21:34:22 +0000
  5-Subject: [PATCH] initial subsurface implementation add basic wl_subsurface
  6- support with naive patrent child tracking, subsurface positioning, parent
  7- clipping, synchronized subsurface commits, stacking, and so on. TLDR, it gets
  8- firefox rendering, but it's not very good and crashed often.
  9-
 10----
 11- libswc/compositor.c    |  69 +++++++++++++--
 12- libswc/compositor.h    |   2 +
 13- libswc/subcompositor.c |  13 ++-
 14- libswc/subsurface.c    | 190 +++++++++++++++++++++++++++++++++++++++--
 15- libswc/subsurface.h    |  17 +++-
 16- libswc/surface.c       |  38 ++++++++-
 17- libswc/surface.h       |   9 ++
 18- libswc/window.c        |  10 ++-
 19- libswc/xdg_shell.c     |  19 +++++
 20- 9 files changed, 346 insertions(+), 21 deletions(-)
 21-
 22-diff --git a/libswc/compositor.c b/libswc/compositor.c
 23-index c8786b5..477af8b 100644
 24---- a/libswc/compositor.c
 25-+++ b/libswc/compositor.c
 26-@@ -219,11 +219,18 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
 27- {
 28- 	pixman_region32_t view_region, view_damage, border_damage;
 29- 	const struct swc_rectangle *geom = &view->base.geometry, *target_geom = &target->view->geometry;
 30-+	bool has_parent = view->parent && view->parent != view;
 31-+	pixman_region32_t parent_region;
 32- 
 33- 	if (!view->base.buffer)
 34- 		return;
 35- 
 36- 	pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
 37-+	if (has_parent) {
 38-+		const struct swc_rectangle *parent_geom = &view->parent->base.geometry;
 39-+		pixman_region32_init_rect(&parent_region, parent_geom->x, parent_geom->y,
 40-+					  parent_geom->width, parent_geom->height);
 41-+	}
 42- 	pixman_region32_init_with_extents(&view_damage, &view->extents);
 43- 	pixman_region32_init(&border_damage);
 44- 
 45-@@ -231,9 +238,13 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
 46- 	pixman_region32_subtract(&view_damage, &view_damage, &view->clip);
 47- 	pixman_region32_subtract(&border_damage, &view_damage, &view_region);
 48- 	pixman_region32_intersect(&view_damage, &view_damage, &view_region);
 49-+	if (has_parent) {
 50-+		pixman_region32_intersect(&view_damage, &view_damage, &parent_region);
 51-+		pixman_region32_intersect(&border_damage, &border_damage, &parent_region);
 52-+	}
 53- 
 54- 	if (pixman_region32_not_empty(&view_damage)) {
 55--		pixman_region32_translate(&view_damage, -geom->x, -geom->y);
 56-+		pixman_region32_translate(&view_damage, -geom->x + view->buffer_offset_x, -geom->y + view->buffer_offset_y);
 57- 		wld_copy_region(swc.drm->renderer, view->buffer, geom->x - target_geom->x, geom->y - target_geom->y, &view_damage);
 58- 	}
 59- 
 60-@@ -272,6 +283,8 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
 61- 	pixman_region32_fini(&in_rect);
 62- 	pixman_region32_fini(&out_border);
 63- 	pixman_region32_fini(&in_border);
 64-+	if (has_parent)
 65-+		pixman_region32_fini(&parent_region);
 66- 
 67- }
 68- 
 69-@@ -731,8 +744,11 @@ static int
 70- attach(struct view *base, struct wld_buffer *buffer)
 71- {
 72- 	struct compositor_view *view = (void *)base;
 73-+	struct surface *surface = view->surface;
 74- 	pixman_box32_t old_extents;
 75- 	pixman_region32_t old, new, both;
 76-+	uint32_t new_width = buffer ? buffer->width : 0;
 77-+	uint32_t new_height = buffer ? buffer->height : 0;
 78- 	int ret;
 79- 
 80- 	if ((ret = renderer_attach(view, buffer)) < 0)
 81-@@ -742,7 +758,18 @@ attach(struct view *base, struct wld_buffer *buffer)
 82- 	 * visible on. */
 83- 	update(&view->base);
 84- 
 85--	if (view_set_size_from_buffer(&view->base, buffer)) {
 86-+	view->buffer_offset_x = 0;
 87-+	view->buffer_offset_y = 0;
 88-+	if (surface && surface->has_window_geometry && buffer) {
 89-+		if (surface->window_width > 0 && surface->window_height > 0) {
 90-+			new_width = (uint32_t)surface->window_width;
 91-+			new_height = (uint32_t)surface->window_height;
 92-+			view->buffer_offset_x = surface->window_x;
 93-+			view->buffer_offset_y = surface->window_y;
 94-+		}
 95-+	}
 96-+
 97-+	if (view_set_size(&view->base, new_width, new_height)) {
 98- 		/* The view was resized. */
 99- 		old_extents = view->extents;
100- 		update_extents(view);
101-@@ -818,7 +845,8 @@ view_at(int32_t x, int32_t y)
102- 			continue;
103- 
104- 		if (pixman_region32_contains_point(&view->surface->state.input,
105--		                                   x - geom->x, y - geom->y, NULL))
106-+		                                   x - geom->x + view->buffer_offset_x,
107-+		                                   y - geom->y + view->buffer_offset_y, NULL))
108- 		{
109- 			return view;
110- 		}
111-@@ -992,6 +1020,8 @@ compositor_create_view(struct surface *surface)
112- 	view->buffer = NULL;
113- 	view->window = NULL;
114- 	view->parent = NULL;
115-+	view->buffer_offset_x = 0;
116-+	view->buffer_offset_y = 0;
117- 	view->visible = false;
118- 	view->extents.x1 = 0;
119- 	view->extents.y1 = 0;
120-@@ -1137,13 +1167,28 @@ calculate_damage(void)
121- 			continue;
122- 
123- 		geom = &view->base.geometry;
124-+		bool has_parent = view->parent && view->parent != view;
125-+		pixman_region32_t view_region;
126-+		pixman_region32_t parent_region;
127-+
128-+		pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
129-+		if (has_parent) {
130-+			const struct swc_rectangle *parent_geom = &view->parent->base.geometry;
131-+			pixman_region32_init_rect(&parent_region, parent_geom->x, parent_geom->y,
132-+			                          parent_geom->width, parent_geom->height);
133-+		}
134- 
135- 		/* Clip the surface by the opaque region covering it. */
136- 		pixman_region32_copy(&view->clip, &compositor.opaque);
137- 
138- 		/* Translate the opaque region to global coordinates. */
139- 		pixman_region32_copy(&surface_opaque, &view->surface->state.opaque);
140--		pixman_region32_translate(&surface_opaque, geom->x, geom->y);
141-+		pixman_region32_translate(&surface_opaque,
142-+		                          geom->x - view->buffer_offset_x,
143-+		                          geom->y - view->buffer_offset_y);
144-+		pixman_region32_intersect(&surface_opaque, &surface_opaque, &view_region);
145-+		if (has_parent)
146-+			pixman_region32_intersect(&surface_opaque, &surface_opaque, &parent_region);
147- 
148- 		/* Add the surface's opaque region to the accumulated opaque region. */
149- 		pixman_region32_union(&compositor.opaque, &compositor.opaque, &surface_opaque);
150-@@ -1154,7 +1199,11 @@ calculate_damage(void)
151- 			renderer_flush_view(view);
152- 
153- 			/* Translate surface damage to global coordinates. */
154--			pixman_region32_translate(surface_damage, geom->x, geom->y);
155-+			pixman_region32_translate(surface_damage,
156-+			                          geom->x - view->buffer_offset_x,
157-+			                          geom->y - view->buffer_offset_y);
158-+			if (has_parent)
159-+				pixman_region32_intersect(surface_damage, surface_damage, &parent_region);
160- 
161- 			/* Add the surface damage to the compositor damage. */
162- 			pixman_region32_union(&compositor.damage, &compositor.damage, surface_damage);
163-@@ -1163,22 +1212,24 @@ calculate_damage(void)
164- 
165- 	                /* redraw entire thingy if either */
166- 			if (view->border.damaged_border1 || view->border.damaged_border2) {
167--				pixman_region32_t border_region, view_region;
168-+				pixman_region32_t border_region;
169- 
170- 				pixman_region32_init_with_extents(&border_region, &view->extents);
171--				pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
172- 
173- 				pixman_region32_subtract(&border_region, &border_region, &view_region);
174- 
175- 				pixman_region32_union(&compositor.damage, &compositor.damage, &border_region);
176- 
177- 				pixman_region32_fini(&border_region);
178--				pixman_region32_fini(&view_region);
179- 
180- 				view->border.damaged_border1 = false;
181- 				view->border.damaged_border2 = false;
182- 			}
183--		}
184-+
185-+		pixman_region32_fini(&view_region);
186-+		if (has_parent)
187-+			pixman_region32_fini(&parent_region);
188-+	}
189- 
190- 	pixman_region32_fini(&surface_opaque);
191- }
192-diff --git a/libswc/compositor.h b/libswc/compositor.h
193-index 2e70b38..1d6b9e2 100644
194---- a/libswc/compositor.h
195-+++ b/libswc/compositor.h
196-@@ -55,6 +55,8 @@ struct compositor_view {
197- 	struct wld_buffer *buffer;
198- 	struct window *window;
199- 	struct compositor_view *parent;
200-+	int32_t buffer_offset_x;
201-+	int32_t buffer_offset_y;
202- 
203- 	/* Whether or not the view is visible (mapped). */
204- 	bool visible;
205-diff --git a/libswc/subcompositor.c b/libswc/subcompositor.c
206-index 5d1da45..1211f83 100644
207---- a/libswc/subcompositor.c
208-+++ b/libswc/subcompositor.c
209-@@ -25,6 +25,7 @@
210- #include "internal.h"
211- #include "subcompositor.h"
212- #include "subsurface.h"
213-+#include "surface.h"
214- #include "util.h"
215- 
216- static void
217-@@ -32,13 +33,23 @@ get_subsurface(struct wl_client *client, struct wl_resource *resource,
218-                uint32_t id, struct wl_resource *surface_resource, struct wl_resource *parent_resource)
219- {
220- 	struct subsurface *subsurface;
221-+	struct surface *surface = wl_resource_get_user_data(surface_resource);
222-+	struct surface *parent = wl_resource_get_user_data(parent_resource);
223- 
224--	subsurface = subsurface_new(client, wl_resource_get_version(resource), id);
225-+	if (!surface || !parent || surface == parent)
226-+		return;
227-+
228-+	if (surface->subsurface)
229-+		return;
230-+
231-+	subsurface = subsurface_new(client, wl_resource_get_version(resource), id, surface, parent);
232- 
233- 	if (!subsurface) {
234- 		wl_resource_post_no_memory(resource);
235- 		return;
236- 	}
237-+
238-+	surface->subsurface = subsurface;
239- }
240- 
241- static const struct wl_subcompositor_interface subcompositor_impl = {
242-diff --git a/libswc/subsurface.c b/libswc/subsurface.c
243-index 6f68b2c..e9b7d7c 100644
244---- a/libswc/subsurface.c
245-+++ b/libswc/subsurface.c
246-@@ -22,39 +22,144 @@
247-  */
248- 
249- #include "subsurface.h"
250-+#include "compositor.h"
251-+#include "surface.h"
252- #include "util.h"
253-+#include "view.h"
254- 
255- #include <stdlib.h>
256- #include <wayland-server.h>
257- 
258-+static void
259-+subsurface_update_position(struct subsurface *subsurface)
260-+{
261-+	struct compositor_view *parent_view;
262-+	struct compositor_view *view;
263-+
264-+	if (!subsurface->surface || !subsurface->parent)
265-+		return;
266-+
267-+	view = compositor_view(subsurface->surface->view);
268-+	parent_view = compositor_view(subsurface->parent->view);
269-+	if (!view || !parent_view)
270-+		return;
271-+
272-+	view_move(&view->base,
273-+	          parent_view->base.geometry.x + subsurface->x - parent_view->buffer_offset_x,
274-+	          parent_view->base.geometry.y + subsurface->y - parent_view->buffer_offset_y);
275-+}
276-+
277-+static void
278-+handle_parent_view_move(struct view_handler *handler)
279-+{
280-+	struct subsurface *subsurface = wl_container_of(handler, subsurface, parent_view_handler);
281-+	subsurface_update_position(subsurface);
282-+}
283-+
284-+static const struct view_handler_impl parent_view_handler_impl = {
285-+	.move = handle_parent_view_move,
286-+};
287-+
288-+static void
289-+handle_surface_destroy(struct wl_listener *listener, void *data)
290-+{
291-+	(void)data;
292-+	struct subsurface *subsurface = wl_container_of(listener, subsurface, surface_destroy_listener);
293-+	if (subsurface->resource)
294-+		wl_resource_destroy(subsurface->resource);
295-+}
296-+
297-+static void
298-+handle_parent_destroy(struct wl_listener *listener, void *data)
299-+{
300-+	(void)data;
301-+	struct subsurface *subsurface = wl_container_of(listener, subsurface, parent_destroy_listener);
302-+	struct compositor_view *view = NULL;
303-+
304-+	if (subsurface->surface && subsurface->surface->view)
305-+		view = compositor_view(subsurface->surface->view);
306-+
307-+	if (view) {
308-+		view->parent = NULL;
309-+		compositor_view_hide(view);
310-+	}
311-+
312-+	if (!wl_list_empty(&subsurface->parent_view_handler.link)) {
313-+		wl_list_remove(&subsurface->parent_view_handler.link);
314-+		wl_list_init(&subsurface->parent_view_handler.link);
315-+	}
316-+
317-+	if (!wl_list_empty(&subsurface->link)) {
318-+		wl_list_remove(&subsurface->link);
319-+		wl_list_init(&subsurface->link);
320-+	}
321-+
322-+	subsurface->parent = NULL;
323-+}
324-+
325- static void
326- set_position(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y)
327- {
328--	/* TODO: Implement. */
329-+	(void)client;
330-+	struct subsurface *subsurface = wl_resource_get_user_data(resource);
331-+
332-+	subsurface->x = x;
333-+	subsurface->y = y;
334-+	subsurface_update_position(subsurface);
335- }
336- 
337- static void
338- place_above(struct wl_client *client, struct wl_resource *resource, struct wl_resource *sibling_resource)
339- {
340--	/* TODO: Implement. */
341-+	(void)client;
342-+	struct subsurface *subsurface = wl_resource_get_user_data(resource);
343-+	struct surface *sibling_surface = wl_resource_get_user_data(sibling_resource);
344-+	struct compositor_view *view = compositor_view(subsurface->surface->view);
345-+	struct compositor_view *sibling_view = compositor_view(sibling_surface->view);
346-+
347-+	if (!view || !sibling_view || view == sibling_view)
348-+		return;
349-+
350-+	if (view->parent != sibling_view->parent)
351-+		return;
352-+
353-+	wl_list_remove(&view->link);
354-+	wl_list_insert(sibling_view->link.prev, &view->link);
355- }
356- 
357- static void
358- place_below(struct wl_client *client, struct wl_resource *resource, struct wl_resource *sibling_resource)
359- {
360--	/* TODO: Implement. */
361-+	(void)client;
362-+	struct subsurface *subsurface = wl_resource_get_user_data(resource);
363-+	struct surface *sibling_surface = wl_resource_get_user_data(sibling_resource);
364-+	struct compositor_view *view = compositor_view(subsurface->surface->view);
365-+	struct compositor_view *sibling_view = compositor_view(sibling_surface->view);
366-+
367-+	if (!view || !sibling_view || view == sibling_view)
368-+		return;
369-+
370-+	if (view->parent != sibling_view->parent)
371-+		return;
372-+
373-+	wl_list_remove(&view->link);
374-+	wl_list_insert(&sibling_view->link, &view->link);
375- }
376- 
377- static void
378- set_sync(struct wl_client *client, struct wl_resource *resource)
379- {
380--	/* TODO: Implement. */
381-+	(void)client;
382-+	struct subsurface *subsurface = wl_resource_get_user_data(resource);
383-+	subsurface->sync = true;
384- }
385- 
386- static void
387- set_desync(struct wl_client *client, struct wl_resource *resource)
388- {
389--	/* TODO: Implement. */
390-+	(void)client;
391-+	struct subsurface *subsurface = wl_resource_get_user_data(resource);
392-+	subsurface->sync = false;
393- }
394- 
395- static const struct wl_subsurface_interface subsurface_impl = {
396-@@ -70,13 +175,47 @@ static void
397- subsurface_destroy(struct wl_resource *resource)
398- {
399- 	struct subsurface *subsurface = wl_resource_get_user_data(resource);
400-+
401-+	if (subsurface->surface) {
402-+		if (subsurface->surface->subsurface == subsurface)
403-+			subsurface->surface->subsurface = NULL;
404-+	}
405-+
406-+	if (!wl_list_empty(&subsurface->parent_destroy_listener.link)) {
407-+		wl_list_remove(&subsurface->parent_destroy_listener.link);
408-+		wl_list_init(&subsurface->parent_destroy_listener.link);
409-+	}
410-+	if (!wl_list_empty(&subsurface->surface_destroy_listener.link)) {
411-+		wl_list_remove(&subsurface->surface_destroy_listener.link);
412-+		wl_list_init(&subsurface->surface_destroy_listener.link);
413-+	}
414-+
415-+	if (!wl_list_empty(&subsurface->parent_view_handler.link)) {
416-+		wl_list_remove(&subsurface->parent_view_handler.link);
417-+		wl_list_init(&subsurface->parent_view_handler.link);
418-+	}
419-+
420-+	if (!wl_list_empty(&subsurface->link)) {
421-+		wl_list_remove(&subsurface->link);
422-+		wl_list_init(&subsurface->link);
423-+	}
424-+
425-+	if (subsurface->surface && subsurface->surface->view) {
426-+		struct compositor_view *view = compositor_view(subsurface->surface->view);
427-+		if (view && !view->window)
428-+			compositor_view_destroy(view);
429-+	}
430-+
431- 	free(subsurface);
432- }
433- 
434- struct subsurface *
435--subsurface_new(struct wl_client *client, uint32_t version, uint32_t id)
436-+subsurface_new(struct wl_client *client, uint32_t version, uint32_t id,
437-+               struct surface *surface, struct surface *parent)
438- {
439- 	struct subsurface *subsurface;
440-+	struct compositor_view *parent_view;
441-+	struct compositor_view *view;
442- 
443- 	if (!(subsurface = malloc(sizeof(*subsurface))))
444- 		goto error0;
445-@@ -88,8 +227,47 @@ subsurface_new(struct wl_client *client, uint32_t version, uint32_t id)
446- 
447- 	wl_resource_set_implementation(subsurface->resource, &subsurface_impl, subsurface, &subsurface_destroy);
448- 
449-+	subsurface->surface = surface;
450-+	subsurface->parent = parent;
451-+	subsurface->x = 0;
452-+	subsurface->y = 0;
453-+	subsurface->sync = true;
454-+	subsurface->pending = false;
455-+
456-+	subsurface->parent_view_handler.impl = &parent_view_handler_impl;
457-+	wl_list_init(&subsurface->parent_view_handler.link);
458-+	wl_list_init(&subsurface->surface_destroy_listener.link);
459-+	wl_list_init(&subsurface->parent_destroy_listener.link);
460-+	wl_list_init(&subsurface->link);
461-+
462-+	if (!surface->view)
463-+		compositor_create_view(surface);
464-+	if (!parent->view)
465-+		compositor_create_view(parent);
466-+
467-+	parent_view = compositor_view(parent->view);
468-+	view = compositor_view(surface->view);
469-+	if (!parent_view || !view)
470-+		goto error2;
471-+
472-+	compositor_view_set_parent(view, parent_view);
473-+	wl_list_remove(&view->link);
474-+	wl_list_insert(parent_view->link.prev, &view->link);
475-+
476-+	wl_list_insert(&parent_view->base.handlers, &subsurface->parent_view_handler.link);
477-+	subsurface_update_position(subsurface);
478-+	wl_list_insert(&parent->subsurfaces, &subsurface->link);
479-+
480-+	subsurface->surface_destroy_listener.notify = handle_surface_destroy;
481-+	wl_resource_add_destroy_listener(surface->resource, &subsurface->surface_destroy_listener);
482-+	subsurface->parent_destroy_listener.notify = handle_parent_destroy;
483-+	wl_resource_add_destroy_listener(parent->resource, &subsurface->parent_destroy_listener);
484-+
485- 	return subsurface;
486- 
487-+error2:
488-+	wl_resource_destroy(subsurface->resource);
489-+	return NULL;
490- error1:
491- 	free(subsurface);
492- error0:
493-diff --git a/libswc/subsurface.h b/libswc/subsurface.h
494-index a78ed69..c747530 100644
495---- a/libswc/subsurface.h
496-+++ b/libswc/subsurface.h
497-@@ -24,14 +24,29 @@
498- #ifndef SWC_SUBSURFACE_H
499- #define SWC_SUBSURFACE_H
500- 
501-+#include "view.h"
502-+
503-+#include <stdbool.h>
504- #include <stdint.h>
505-+#include <wayland-server.h>
506- 
507- struct wl_client;
508-+struct surface;
509- 
510- struct subsurface {
511- 	struct wl_resource *resource;
512-+	struct surface *surface;
513-+	struct surface *parent;
514-+	struct view_handler parent_view_handler;
515-+	struct wl_listener surface_destroy_listener;
516-+	struct wl_listener parent_destroy_listener;
517-+	struct wl_list link;
518-+	int32_t x, y;
519-+	bool sync;
520-+	bool pending;
521- };
522- 
523--struct subsurface *subsurface_new(struct wl_client *client, uint32_t version, uint32_t id);
524-+struct subsurface *subsurface_new(struct wl_client *client, uint32_t version, uint32_t id,
525-+                                  struct surface *surface, struct surface *parent);
526- 
527- #endif
528-diff --git a/libswc/surface.c b/libswc/surface.c
529-index 19a36ec..9332054 100644
530---- a/libswc/surface.c
531-+++ b/libswc/surface.c
532-@@ -27,6 +27,7 @@
533- #include "output.h"
534- #include "region.h"
535- #include "screen.h"
536-+#include "subsurface.h"
537- #include "util.h"
538- #include "view.h"
539- #include "wayland_buffer.h"
540-@@ -218,9 +219,8 @@ trim_region(pixman_region32_t *region, struct wld_buffer *buffer)
541- }
542- 
543- static void
544--commit(struct wl_client *client, struct wl_resource *resource)
545-+surface_apply_pending(struct surface *surface, bool flush_children)
546- {
547--	struct surface *surface = wl_resource_get_user_data(resource);
548- 	struct wld_buffer *buffer;
549- 
550- 	/* Attach */
551-@@ -263,6 +263,32 @@ commit(struct wl_client *client, struct wl_resource *resource)
552- 	}
553- 
554- 	surface->pending.commit = 0;
555-+
556-+	if (surface->subsurface)
557-+		surface->subsurface->pending = false;
558-+
559-+	if (flush_children) {
560-+		struct subsurface *child;
561-+		wl_list_for_each (child, &surface->subsurfaces, link) {
562-+			if (!child->sync || !child->pending)
563-+				continue;
564-+			if (child->surface)
565-+				surface_apply_pending(child->surface, true);
566-+		}
567-+	}
568-+}
569-+
570-+static void
571-+commit(struct wl_client *client, struct wl_resource *resource)
572-+{
573-+	struct surface *surface = wl_resource_get_user_data(resource);
574-+
575-+	if (surface->subsurface && surface->subsurface->sync) {
576-+		surface->subsurface->pending = true;
577-+		return;
578-+	}
579-+
580-+	surface_apply_pending(surface, true);
581- }
582- 
583- static void
584-@@ -339,6 +365,14 @@ surface_new(struct wl_client *client, uint32_t version, uint32_t id)
585- 	surface->pending.commit = 0;
586- 	surface->view = NULL;
587- 	surface->view_handler.impl = &view_handler_impl;
588-+	surface->subsurface = NULL;
589-+	wl_list_init(&surface->subsurfaces);
590-+	surface->has_window_geometry = false;
591-+	surface->window_x = 0;
592-+	surface->window_y = 0;
593-+	surface->window_width = 0;
594-+	surface->window_height = 0;
595-+	surface->window_geometry_applied = false;
596- 
597- 	state_initialize(&surface->state);
598- 	state_initialize(&surface->pending.state);
599-diff --git a/libswc/surface.h b/libswc/surface.h
600-index 039ed4c..5eb751c 100644
601---- a/libswc/surface.h
602-+++ b/libswc/surface.h
603-@@ -29,6 +29,8 @@
604- #include <pixman.h>
605- #include <wayland-server.h>
606- 
607-+struct subsurface;
608-+
609- enum {
610- 	SURFACE_COMMIT_ATTACH = (1 << 0),
611- 	SURFACE_COMMIT_DAMAGE = (1 << 1),
612-@@ -67,6 +69,13 @@ struct surface {
613- 
614- 	struct view *view;
615- 	struct view_handler view_handler;
616-+
617-+	struct subsurface *subsurface;
618-+	struct wl_list subsurfaces;
619-+	bool has_window_geometry;
620-+	int32_t window_x, window_y;
621-+	int32_t window_width, window_height;
622-+	bool window_geometry_applied;
623- };
624- 
625- struct surface *surface_new(struct wl_client *client, uint32_t version, uint32_t id);
626-diff --git a/libswc/window.c b/libswc/window.c
627-index 84e97b0..d539d3c 100644
628---- a/libswc/window.c
629-+++ b/libswc/window.c
630-@@ -395,8 +395,14 @@ window_initialize(struct window *window, const struct window_impl *impl, struct
631- 	window->base.app_id = NULL;
632- 	window->base.parent = NULL;
633- 
634--	if (!(window->view = compositor_create_view(surface)))
635--		return false;
636-+	if (surface->view) {
637-+		window->view = compositor_view(surface->view);
638-+		if (!window->view || window->view->window)
639-+			return false;
640-+	} else {
641-+		if (!(window->view = compositor_create_view(surface)))
642-+			return false;
643-+	}
644- 
645- 	window->impl = impl;
646- 	window->handler = &null_handler;
647-diff --git a/libswc/xdg_shell.c b/libswc/xdg_shell.c
648-index 33a7f9c..d1cf081 100644
649---- a/libswc/xdg_shell.c
650-+++ b/libswc/xdg_shell.c
651-@@ -591,6 +591,25 @@ ack_configure(struct wl_client *client, struct wl_resource *resource, uint32_t s
652- static void
653- set_window_geometry(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
654- {
655-+	(void)client;
656-+	struct xdg_surface *xdg_surface = wl_resource_get_user_data(resource);
657-+	struct surface *surface = xdg_surface->surface;
658-+
659-+	if (width <= 0 || height <= 0) {
660-+		surface->has_window_geometry = false;
661-+		return;
662-+	}
663-+
664-+	surface->has_window_geometry = true;
665-+	surface->window_x = x;
666-+	surface->window_y = y;
667-+	surface->window_width = width;
668-+	surface->window_height = height;
669-+	if (!surface->window_geometry_applied && surface->view && (x != 0 || y != 0)) {
670-+		struct swc_rectangle *geom = &surface->view->geometry;
671-+		view_move(surface->view, geom->x - x, geom->y - y);
672-+		surface->window_geometry_applied = true;
673-+	}
674- }
675- 
676- static const struct xdg_surface_interface xdg_surface_impl = {
677--- 
678-2.49.0
679-
+0, -44
 1@@ -1,44 +0,0 @@
 2-From d783f4c858eb415946abc02fbacf5632d2eb2aab Mon Sep 17 00:00:00 2001
 3-From: dalem <lain@dalem.foo>
 4-Date: Sun, 8 Feb 2026 03:33:43 +0100
 5-Subject: [PATCH] Rename swc_wallpaper_init to swc_wallpaper_set
 6-
 7-this change makes it appear as more consistent
 8----
 9- libswc/swc.h       | 4 ++--
10- libswc/wallpaper.c | 2 +-
11- 2 files changed, 3 insertions(+), 3 deletions(-)
12-
13-diff --git a/libswc/swc.h b/libswc/swc.h
14-index b95fc4c..1f4ada4 100644
15---- a/libswc/swc.h
16-+++ b/libswc/swc.h
17-@@ -438,10 +438,10 @@ extern struct wld_buffer *wallbuf;
18- 
19- /**
20-  * Set wallpaper to image from fs path.
21-- * TODO: scaling, tiling, maybe diff image for each screen
22-+ * TODO: tiling, maybe diff image for each screen
23-  */
24- 
25--void swc_wallpaper_init(char* path);
26-+void swc_wallpaper_set(char* path);
27- 
28- /**
29-  * Set wallpaper to a single color
30-diff --git a/libswc/wallpaper.c b/libswc/wallpaper.c
31-index ad6e21f..7618bf0 100644
32---- a/libswc/wallpaper.c
33-+++ b/libswc/wallpaper.c
34-@@ -21,7 +21,7 @@ struct wld_buffer *wallbuf = NULL;
35- uint32_t bgcolor = 0xff000000;
36- 
37- EXPORT void
38--swc_wallpaper_init(char* path)
39-+swc_wallpaper_set(char* path)
40- {
41- 	int width, height, chan;
42- 	unsigned char *loaded;
43--- 
44-2.49.0
45-
+0, -850
  1@@ -1,850 +0,0 @@
  2-From 78df047f81cf877eab899fe44b3f350f13015703 Mon Sep 17 00:00:00 2001
  3-From: shrub <maybeshrub@gmail.com>
  4-Date: Tue, 10 Feb 2026 21:34:58 +0000
  5-Subject: [PATCH] fix subsurfaces, so they can actually work fix subsurface
  6- buffer rendering and do damage calculations properly, calculate pointer
  7- position and translate it properly, and do better subusrfcae comittis and
  8- restacking.
  9-
 10----
 11- libswc/compositor.c    | 148 ++++++++++++++++---------
 12- libswc/compositor.h    |   1 +
 13- libswc/pointer.c       |  15 ++-
 14- libswc/subcompositor.c |  25 ++++-
 15- libswc/subsurface.c    | 243 +++++++++++++++++++++++++++++++++++++----
 16- libswc/subsurface.h    |   9 ++
 17- libswc/surface.c       |  17 ++-
 18- libswc/surface.h       |   5 +
 19- 8 files changed, 383 insertions(+), 80 deletions(-)
 20-
 21-diff --git a/libswc/compositor.c b/libswc/compositor.c
 22-index 477af8b..4333d99 100644
 23---- a/libswc/compositor.c
 24-+++ b/libswc/compositor.c
 25-@@ -41,6 +41,7 @@
 26- #include "seat.h"
 27- #include "shm.h"
 28- #include "surface.h"
 29-+#include "subsurface.h"
 30- #include "util.h"
 31- #include "view.h"
 32- #include "window.h"
 33-@@ -217,38 +218,50 @@ error0:
 34- static void
 35- repaint_view(struct target *target, struct compositor_view *view, pixman_region32_t *damage)
 36- {
 37--	pixman_region32_t view_region, view_damage, border_damage;
 38-+	pixman_region32_t geom_region, buffer_region, border_region, view_damage, buffer_damage, border_damage;
 39- 	const struct swc_rectangle *geom = &view->base.geometry, *target_geom = &target->view->geometry;
 40--	bool has_parent = view->parent && view->parent != view;
 41--	pixman_region32_t parent_region;
 42-+	int32_t buf_x, buf_y;
 43-+	uint32_t buf_w, buf_h;
 44-+	int64_t total_border;
 45- 
 46- 	if (!view->base.buffer)
 47- 		return;
 48- 
 49--	pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
 50--	if (has_parent) {
 51--		const struct swc_rectangle *parent_geom = &view->parent->base.geometry;
 52--		pixman_region32_init_rect(&parent_region, parent_geom->x, parent_geom->y,
 53--					  parent_geom->width, parent_geom->height);
 54-+	buf_w = view->base.buffer->width;
 55-+	buf_h = view->base.buffer->height;
 56-+	buf_x = geom->x - view->buffer_offset_x;
 57-+	buf_y = geom->y - view->buffer_offset_y;
 58-+
 59-+	total_border = (int64_t)view->border.outwidth + (int64_t)view->border.inwidth;
 60-+	pixman_region32_init_rect(&geom_region, geom->x, geom->y, geom->width, geom->height);
 61-+	if (view->window) {
 62-+		pixman_region32_init_rect(&buffer_region, geom->x, geom->y, geom->width, geom->height);
 63-+	} else {
 64-+		pixman_region32_init_rect(&buffer_region, buf_x, buf_y, buf_w, buf_h);
 65- 	}
 66-+	pixman_region32_init_rect(&border_region,
 67-+		geom->x - (int32_t)total_border,
 68-+		geom->y - (int32_t)total_border,
 69-+		geom->width + (uint32_t)(2 * total_border),
 70-+		geom->height + (uint32_t)(2 * total_border));
 71-+	pixman_region32_subtract(&border_region, &border_region, &geom_region);
 72- 	pixman_region32_init_with_extents(&view_damage, &view->extents);
 73-+	pixman_region32_init(&buffer_damage);
 74- 	pixman_region32_init(&border_damage);
 75- 
 76- 	pixman_region32_intersect(&view_damage, &view_damage, damage);
 77- 	pixman_region32_subtract(&view_damage, &view_damage, &view->clip);
 78--	pixman_region32_subtract(&border_damage, &view_damage, &view_region);
 79--	pixman_region32_intersect(&view_damage, &view_damage, &view_region);
 80--	if (has_parent) {
 81--		pixman_region32_intersect(&view_damage, &view_damage, &parent_region);
 82--		pixman_region32_intersect(&border_damage, &border_damage, &parent_region);
 83--	}
 84-+	pixman_region32_intersect(&border_damage, &view_damage, &border_region);
 85-+	pixman_region32_intersect(&buffer_damage, &view_damage, &buffer_region);
 86- 
 87--	if (pixman_region32_not_empty(&view_damage)) {
 88--		pixman_region32_translate(&view_damage, -geom->x + view->buffer_offset_x, -geom->y + view->buffer_offset_y);
 89--		wld_copy_region(swc.drm->renderer, view->buffer, geom->x - target_geom->x, geom->y - target_geom->y, &view_damage);
 90-+	if (pixman_region32_not_empty(&buffer_damage)) {
 91-+		pixman_region32_translate(&buffer_damage, -geom->x + view->buffer_offset_x, -geom->y + view->buffer_offset_y);
 92-+		wld_copy_region(swc.drm->renderer, view->buffer,
 93-+			buf_x - target_geom->x, buf_y - target_geom->y, &buffer_damage);
 94- 	}
 95- 
 96- 	pixman_region32_fini(&view_damage);
 97-+	pixman_region32_fini(&buffer_damage);
 98- 
 99- 	pixman_region32_t in_rect;
100- 	pixman_region32_init_rect(&in_rect, 
101-@@ -263,10 +276,12 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
102- 
103- 	pixman_region32_t in_border;
104- 	pixman_region32_init(&in_border);
105--	pixman_region32_subtract(&in_border, &in_rect, &view_region);
106-+	pixman_region32_subtract(&in_border, &in_rect, &geom_region);
107- 	pixman_region32_intersect(&in_border, &in_border, &border_damage);
108- 		
109--	pixman_region32_fini(&view_region);
110-+	pixman_region32_fini(&geom_region);
111-+	pixman_region32_fini(&buffer_region);
112-+	pixman_region32_fini(&border_region);
113- 
114- 	/* Draw border */
115- 	if (view->border.outwidth > 0 && pixman_region32_not_empty(&out_border)) {
116-@@ -283,8 +298,6 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
117- 	pixman_region32_fini(&in_rect);
118- 	pixman_region32_fini(&out_border);
119- 	pixman_region32_fini(&in_border);
120--	if (has_parent)
121--		pixman_region32_fini(&parent_region);
122- 
123- }
124- 
125-@@ -409,8 +422,7 @@ renderer_flush_view(struct compositor_view *view)
126- /* Surface Views {{{ */
127- 
128- /**
129-- * Adds the region below a view to the compositor's damaged region,
130-- * taking into account its clip region.
131-+ * Adds the region below a view to the compositor's damaged region.
132-  */
133- static void
134- damage_below_view(struct compositor_view *view)
135-@@ -418,7 +430,6 @@ damage_below_view(struct compositor_view *view)
136- 	pixman_region32_t damage_below;
137- 
138- 	pixman_region32_init_with_extents(&damage_below, &view->extents);
139--	pixman_region32_subtract(&damage_below, &damage_below, &view->clip);
140- 	pixman_region32_union(&compositor.damage, &compositor.damage, &damage_below);
141- 	pixman_region32_fini(&damage_below);
142- }
143-@@ -438,15 +449,25 @@ static void
144- update_extents(struct compositor_view *view)
145- {
146- 	int64_t total_border = (int64_t)view->border.outwidth + (int64_t)view->border.inwidth;
147--	int64_t x = view->base.geometry.x;
148--	int64_t y = view->base.geometry.y;
149--	int64_t w = view->base.geometry.width;
150--	int64_t h = view->base.geometry.height;
151--
152--	view->extents.x1 = clamp_i32(x - total_border);
153--	view->extents.y1 = clamp_i32(y - total_border);
154--	view->extents.x2 = clamp_i32(x + w + total_border);
155--	view->extents.y2 = clamp_i32(y + h + total_border);
156-+	int64_t geom_x = view->base.geometry.x;
157-+	int64_t geom_y = view->base.geometry.y;
158-+	int64_t geom_w = view->base.geometry.width;
159-+	int64_t geom_h = view->base.geometry.height;
160-+
161-+	int64_t border_x1 = geom_x - total_border;
162-+	int64_t border_y1 = geom_y - total_border;
163-+	int64_t border_x2 = geom_x + geom_w + total_border;
164-+	int64_t border_y2 = geom_y + geom_h + total_border;
165-+
166-+	int64_t buffer_x1 = geom_x - view->buffer_offset_x;
167-+	int64_t buffer_y1 = geom_y - view->buffer_offset_y;
168-+	int64_t buffer_x2 = buffer_x1 + (view->base.buffer ? view->base.buffer->width : (uint32_t)geom_w);
169-+	int64_t buffer_y2 = buffer_y1 + (view->base.buffer ? view->base.buffer->height : (uint32_t)geom_h);
170-+
171-+	view->extents.x1 = clamp_i32(MIN(border_x1, buffer_x1));
172-+	view->extents.y1 = clamp_i32(MIN(border_y1, buffer_y1));
173-+	view->extents.x2 = clamp_i32(MAX(border_x2, buffer_x2));
174-+	view->extents.y2 = clamp_i32(MAX(border_y2, buffer_y2));
175- 
176- 	if (view->extents.x2 < view->extents.x1)
177- 		view->extents.x2 = view->extents.x1;
178-@@ -775,15 +796,13 @@ attach(struct view *base, struct wld_buffer *buffer)
179- 		update_extents(view);
180- 
181- 		if (view->visible) {
182--			/* Damage the region that was newly uncovered
183--			 * or covered, minus the clip region. */
184-+			/* Damage the region that was newly uncovered or covered. */
185- 			pixman_region32_init_with_extents(&old, &old_extents);
186- 			pixman_region32_init_with_extents(&new, &view->extents);
187- 			pixman_region32_init(&both);
188- 			pixman_region32_intersect(&both, &old, &new);
189- 			pixman_region32_union(&new, &old, &new);
190- 			pixman_region32_subtract(&new, &new, &both);
191--			pixman_region32_subtract(&new, &new, &view->clip);
192- 			pixman_region32_union(&compositor.damage, &compositor.damage, &new);
193- 			pixman_region32_fini(&old);
194- 			pixman_region32_fini(&new);
195-@@ -835,14 +854,26 @@ view_at(int32_t x, int32_t y)
196- {
197- 	struct compositor_view *view;
198- 	struct swc_rectangle *geom;
199-+	struct swc_rectangle buffer_geom;
200- 
201- 	wl_list_for_each (view, &compositor.views, link) {
202- 		if (!view->visible)
203- 			continue;
204- 
205- 		geom = &view->base.geometry;
206--		if (!rectangle_contains_point(geom, x, y))
207-+		if (view->window) {
208-+			if (!rectangle_contains_point(geom, x, y))
209-+				continue;
210-+		} else if (view->base.buffer) {
211-+			buffer_geom.x = geom->x - view->buffer_offset_x;
212-+			buffer_geom.y = geom->y - view->buffer_offset_y;
213-+			buffer_geom.width = view->base.buffer->width;
214-+			buffer_geom.height = view->base.buffer->height;
215-+			if (!rectangle_contains_point(&buffer_geom, x, y))
216-+				continue;
217-+		} else if (!rectangle_contains_point(geom, x, y)) {
218- 			continue;
219-+		}
220- 
221- 		if (pixman_region32_contains_point(&view->surface->state.input,
222- 		                                   x - geom->x + view->buffer_offset_x,
223-@@ -1070,14 +1101,42 @@ compositor_view_set_parent(struct compositor_view *view, struct compositor_view
224- 		compositor_view_hide(view);
225- }
226- 
227-+void
228-+compositor_view_restack(struct compositor_view *view, struct compositor_view *sibling, bool above)
229-+{
230-+	if (!view || !sibling || view == sibling)
231-+		return;
232-+
233-+	if (above) {
234-+		if (view->link.next == &sibling->link)
235-+			return;
236-+		wl_list_remove(&view->link);
237-+		wl_list_insert(sibling->link.prev, &view->link);
238-+	} else {
239-+		if (view->link.prev == &sibling->link)
240-+			return;
241-+		wl_list_remove(&view->link);
242-+		wl_list_insert(&sibling->link, &view->link);
243-+	}
244-+
245-+	damage_views(view, sibling);
246-+}
247-+
248- void
249- compositor_view_show(struct compositor_view *view)
250- {
251- 	struct compositor_view *other;
252-+	struct subsurface *subsurface;
253- 
254- 	if (view->visible)
255- 		return;
256- 
257-+	subsurface = view->surface ? view->surface->subsurface : NULL;
258-+	if (subsurface) {
259-+		if (!subsurface->added || !view->surface->state.buffer)
260-+			return;
261-+	}
262-+
263- 	view->visible = true;
264- 	view_update_screens(&view->base);
265- 
266-@@ -1167,16 +1226,9 @@ calculate_damage(void)
267- 			continue;
268- 
269- 		geom = &view->base.geometry;
270--		bool has_parent = view->parent && view->parent != view;
271- 		pixman_region32_t view_region;
272--		pixman_region32_t parent_region;
273- 
274- 		pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
275--		if (has_parent) {
276--			const struct swc_rectangle *parent_geom = &view->parent->base.geometry;
277--			pixman_region32_init_rect(&parent_region, parent_geom->x, parent_geom->y,
278--			                          parent_geom->width, parent_geom->height);
279--		}
280- 
281- 		/* Clip the surface by the opaque region covering it. */
282- 		pixman_region32_copy(&view->clip, &compositor.opaque);
283-@@ -1187,8 +1239,6 @@ calculate_damage(void)
284- 		                          geom->x - view->buffer_offset_x,
285- 		                          geom->y - view->buffer_offset_y);
286- 		pixman_region32_intersect(&surface_opaque, &surface_opaque, &view_region);
287--		if (has_parent)
288--			pixman_region32_intersect(&surface_opaque, &surface_opaque, &parent_region);
289- 
290- 		/* Add the surface's opaque region to the accumulated opaque region. */
291- 		pixman_region32_union(&compositor.opaque, &compositor.opaque, &surface_opaque);
292-@@ -1202,8 +1252,6 @@ calculate_damage(void)
293- 			pixman_region32_translate(surface_damage,
294- 			                          geom->x - view->buffer_offset_x,
295- 			                          geom->y - view->buffer_offset_y);
296--			if (has_parent)
297--				pixman_region32_intersect(surface_damage, surface_damage, &parent_region);
298- 
299- 			/* Add the surface damage to the compositor damage. */
300- 			pixman_region32_union(&compositor.damage, &compositor.damage, surface_damage);
301-@@ -1226,9 +1274,7 @@ calculate_damage(void)
302- 				view->border.damaged_border2 = false;
303- 			}
304- 
305--		pixman_region32_fini(&view_region);
306--		if (has_parent)
307--			pixman_region32_fini(&parent_region);
308-+			pixman_region32_fini(&view_region);
309- 	}
310- 
311- 	pixman_region32_fini(&surface_opaque);
312-diff --git a/libswc/compositor.h b/libswc/compositor.h
313-index 1d6b9e2..94de9e3 100644
314---- a/libswc/compositor.h
315-+++ b/libswc/compositor.h
316-@@ -95,6 +95,7 @@ void compositor_view_destroy(struct compositor_view *view);
317- struct compositor_view *compositor_view(struct view *view);
318- 
319- void compositor_view_set_parent(struct compositor_view *view, struct compositor_view *parent);
320-+void compositor_view_restack(struct compositor_view *view, struct compositor_view *sibling, bool above);
321- 
322- void compositor_view_show(struct compositor_view *view);
323- void compositor_view_hide(struct compositor_view *view);
324-diff --git a/libswc/pointer.c b/libswc/pointer.c
325-index cff6bb6..8b8896f 100644
326---- a/libswc/pointer.c
327-+++ b/libswc/pointer.c
328-@@ -112,14 +112,18 @@ enter(struct input_focus_handler *handler, struct wl_list *resources, struct com
329- 	struct wl_resource *resource;
330- 	uint32_t serial;
331- 	wl_fixed_t surface_x, surface_y;
332-+	int32_t origin_x, origin_y;
333- 
334- 	if (wl_list_empty(resources)) {
335- 		pointer_set_cursor(pointer, cursor_left_ptr);
336- 		return;
337- 	}
338- 	serial = wl_display_next_serial(swc.display);
339--	surface_x = pointer->x - wl_fixed_from_int(view->base.geometry.x);
340--	surface_y = pointer->y - wl_fixed_from_int(view->base.geometry.y);
341-+	/* do based on buffer origin, holy fuck */
342-+	origin_x = view->base.geometry.x - view->buffer_offset_x;
343-+	origin_y = view->base.geometry.y - view->buffer_offset_y;
344-+	surface_x = pointer->x - wl_fixed_from_int(origin_x);
345-+	surface_y = pointer->y - wl_fixed_from_int(origin_y);
346- 	wl_resource_for_each (resource, resources)
347- 		wl_pointer_send_enter(resource, serial, view->surface->resource, surface_x, surface_y);
348- }
349-@@ -349,12 +353,15 @@ client_handle_motion(struct pointer_handler *handler, uint32_t time, wl_fixed_t
350- 	struct pointer *pointer = wl_container_of(handler, pointer, client_handler);
351- 	struct wl_resource *resource;
352- 	wl_fixed_t sx, sy;
353-+	int32_t origin_x, origin_y;
354- 
355- 	if (wl_list_empty(&pointer->focus.active))
356- 		return false;
357- 
358--	sx = x - wl_fixed_from_int(pointer->focus.view->base.geometry.x);
359--	sy = y - wl_fixed_from_int(pointer->focus.view->base.geometry.y);
360-+	origin_x = pointer->focus.view->base.geometry.x - pointer->focus.view->buffer_offset_x;
361-+	origin_y = pointer->focus.view->base.geometry.y - pointer->focus.view->buffer_offset_y;
362-+	sx = x - wl_fixed_from_int(origin_x);
363-+	sy = y - wl_fixed_from_int(origin_y);
364- 	wl_resource_for_each (resource, &pointer->focus.active)
365- 		wl_pointer_send_motion(resource, time, sx, sy);
366- 	return true;
367-diff --git a/libswc/subcompositor.c b/libswc/subcompositor.c
368-index 1211f83..2dad2bc 100644
369---- a/libswc/subcompositor.c
370-+++ b/libswc/subcompositor.c
371-@@ -28,6 +28,18 @@
372- #include "surface.h"
373- #include "util.h"
374- 
375-+static bool
376-+is_descendant_of(struct surface *ancestor, struct surface *surface)
377-+{
378-+	while (surface && surface->subsurface) {
379-+		surface = surface->subsurface->parent;
380-+		if (surface == ancestor)
381-+			return true;
382-+	}
383-+
384-+	return false;
385-+}
386-+
387- static void
388- get_subsurface(struct wl_client *client, struct wl_resource *resource,
389-                uint32_t id, struct wl_resource *surface_resource, struct wl_resource *parent_resource)
390-@@ -36,11 +48,20 @@ get_subsurface(struct wl_client *client, struct wl_resource *resource,
391- 	struct surface *surface = wl_resource_get_user_data(surface_resource);
392- 	struct surface *parent = wl_resource_get_user_data(parent_resource);
393- 
394--	if (!surface || !parent || surface == parent)
395-+	if (!surface || !parent) {
396-+		wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "invalid surface");
397- 		return;
398-+	}
399- 
400--	if (surface->subsurface)
401-+	if (surface == parent || is_descendant_of(surface, parent)) {
402-+		wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_PARENT, "invalid parent surface");
403- 		return;
404-+	}
405-+
406-+	if (surface->subsurface) {
407-+		wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "surface already has a subsurface role");
408-+		return;
409-+	}
410- 
411- 	subsurface = subsurface_new(client, wl_resource_get_version(resource), id, surface, parent);
412- 
413-diff --git a/libswc/subsurface.c b/libswc/subsurface.c
414-index e9b7d7c..f8aa66c 100644
415---- a/libswc/subsurface.c
416-+++ b/libswc/subsurface.c
417-@@ -30,6 +30,20 @@
418- #include <stdlib.h>
419- #include <wayland-server.h>
420- 
421-+bool
422-+subsurface_is_synchronized(const struct subsurface *subsurface)
423-+{
424-+	while (subsurface) {
425-+		if (subsurface->sync)
426-+			return true;
427-+		if (!subsurface->parent)
428-+			return false;
429-+		subsurface = subsurface->parent->subsurface;
430-+	}
431-+
432-+	return false;
433-+}
434-+
435- static void
436- subsurface_update_position(struct subsurface *subsurface)
437- {
438-@@ -50,16 +64,99 @@ subsurface_update_position(struct subsurface *subsurface)
439- }
440- 
441- static void
442--handle_parent_view_move(struct view_handler *handler)
443-+list_remove_if_linked(struct wl_list *link)
444-+{
445-+	if (!wl_list_empty(link)) {
446-+		wl_list_remove(link);
447-+		wl_list_init(link);
448-+	}
449-+}
450-+
451-+void
452-+subsurface_update_visibility(struct subsurface *subsurface)
453-+{
454-+	struct compositor_view *view;
455-+	struct compositor_view *parent_view;
456-+
457-+	if (!subsurface || !subsurface->surface || !subsurface->parent)
458-+		return;
459-+
460-+	view = compositor_view(subsurface->surface->view);
461-+	parent_view = compositor_view(subsurface->parent->view);
462-+	if (!view || !parent_view)
463-+		return;
464-+
465-+	if (subsurface->added && parent_view->visible && subsurface->surface->state.buffer)
466-+		compositor_view_show(view);
467-+	else
468-+		compositor_view_hide(view);
469-+}
470-+
471-+static void
472-+handle_parent_view_change(struct view_handler *handler)
473- {
474- 	struct subsurface *subsurface = wl_container_of(handler, subsurface, parent_view_handler);
475- 	subsurface_update_position(subsurface);
476- }
477- 
478-+static void
479-+handle_parent_view_resize(struct view_handler *handler, uint32_t old_width, uint32_t old_height)
480-+{
481-+	(void)old_width;
482-+	(void)old_height;
483-+	handle_parent_view_change(handler);
484-+}
485-+
486- static const struct view_handler_impl parent_view_handler_impl = {
487--	.move = handle_parent_view_move,
488-+	.attach = handle_parent_view_change,
489-+	.move = handle_parent_view_change,
490-+	.resize = handle_parent_view_resize,
491- };
492- 
493-+static struct subsurface *
494-+subsurface_find_sibling(struct subsurface *subsurface, struct surface *surface)
495-+{
496-+	struct surface *parent = subsurface->parent;
497-+	struct subsurface *sibling;
498-+
499-+	if (!parent)
500-+		return NULL;
501-+
502-+	wl_list_for_each (sibling, &parent->pending.state.subsurfaces_below, pending_link) {
503-+		if (sibling->surface == surface && sibling != subsurface)
504-+			return sibling;
505-+	}
506-+
507-+	wl_list_for_each (sibling, &parent->pending.state.subsurfaces_above, pending_link) {
508-+		if (sibling->surface == surface && sibling != subsurface)
509-+			return sibling;
510-+	}
511-+
512-+	return NULL;
513-+}
514-+
515-+static bool
516-+is_valid_sibling(struct subsurface *subsurface, struct surface *sibling_surface,
517-+                 struct subsurface **sibling_subsurface)
518-+{
519-+	struct subsurface *sibling;
520-+
521-+	if (!subsurface->parent || !sibling_surface || sibling_surface == subsurface->surface)
522-+		return false;
523-+
524-+	if (sibling_surface == subsurface->parent) {
525-+		*sibling_subsurface = NULL;
526-+		return true;
527-+	}
528-+
529-+	sibling = subsurface_find_sibling(subsurface, sibling_surface);
530-+	if (!sibling)
531-+		return false;
532-+
533-+	*sibling_subsurface = sibling;
534-+	return true;
535-+}
536-+
537- static void
538- handle_surface_destroy(struct wl_listener *listener, void *data)
539- {
540-@@ -94,6 +191,9 @@ handle_parent_destroy(struct wl_listener *listener, void *data)
541- 		wl_list_init(&subsurface->link);
542- 	}
543- 
544-+	list_remove_if_linked(&subsurface->pending_link);
545-+	list_remove_if_linked(&subsurface->current_link);
546-+
547- 	subsurface->parent = NULL;
548- }
549- 
550-@@ -103,9 +203,9 @@ set_position(struct wl_client *client, struct wl_resource *resource, int32_t x,
551- 	(void)client;
552- 	struct subsurface *subsurface = wl_resource_get_user_data(resource);
553- 
554--	subsurface->x = x;
555--	subsurface->y = y;
556--	subsurface_update_position(subsurface);
557-+	subsurface->pending_x = x;
558-+	subsurface->pending_y = y;
559-+	subsurface->pending_position = true;
560- }
561- 
562- static void
563-@@ -114,17 +214,20 @@ place_above(struct wl_client *client, struct wl_resource *resource, struct wl_re
564- 	(void)client;
565- 	struct subsurface *subsurface = wl_resource_get_user_data(resource);
566- 	struct surface *sibling_surface = wl_resource_get_user_data(sibling_resource);
567--	struct compositor_view *view = compositor_view(subsurface->surface->view);
568--	struct compositor_view *sibling_view = compositor_view(sibling_surface->view);
569-+	struct subsurface *sibling_subsurface;
570- 
571--	if (!view || !sibling_view || view == sibling_view)
572--		return;
573--
574--	if (view->parent != sibling_view->parent)
575-+	if (!is_valid_sibling(subsurface, sibling_surface, &sibling_subsurface)) {
576-+		wl_resource_post_error(resource, WL_SUBSURFACE_ERROR_BAD_SURFACE, "invalid sibling surface");
577- 		return;
578-+	}
579- 
580--	wl_list_remove(&view->link);
581--	wl_list_insert(sibling_view->link.prev, &view->link);
582-+	if (!sibling_subsurface) {
583-+		wl_list_remove(&subsurface->pending_link);
584-+		wl_list_insert(&subsurface->parent->pending.state.subsurfaces_above, &subsurface->pending_link);
585-+	} else {
586-+		wl_list_remove(&subsurface->pending_link);
587-+		wl_list_insert(&sibling_subsurface->pending_link, &subsurface->pending_link);
588-+	}
589- }
590- 
591- static void
592-@@ -133,17 +236,20 @@ place_below(struct wl_client *client, struct wl_resource *resource, struct wl_re
593- 	(void)client;
594- 	struct subsurface *subsurface = wl_resource_get_user_data(resource);
595- 	struct surface *sibling_surface = wl_resource_get_user_data(sibling_resource);
596--	struct compositor_view *view = compositor_view(subsurface->surface->view);
597--	struct compositor_view *sibling_view = compositor_view(sibling_surface->view);
598-+	struct subsurface *sibling_subsurface;
599- 
600--	if (!view || !sibling_view || view == sibling_view)
601--		return;
602--
603--	if (view->parent != sibling_view->parent)
604-+	if (!is_valid_sibling(subsurface, sibling_surface, &sibling_subsurface)) {
605-+		wl_resource_post_error(resource, WL_SUBSURFACE_ERROR_BAD_SURFACE, "invalid sibling surface");
606- 		return;
607-+	}
608- 
609--	wl_list_remove(&view->link);
610--	wl_list_insert(&sibling_view->link, &view->link);
611-+	if (!sibling_subsurface) {
612-+		wl_list_remove(&subsurface->pending_link);
613-+		wl_list_insert(subsurface->parent->pending.state.subsurfaces_below.prev, &subsurface->pending_link);
614-+	} else {
615-+		wl_list_remove(&subsurface->pending_link);
616-+		wl_list_insert(sibling_subsurface->pending_link.prev, &subsurface->pending_link);
617-+	}
618- }
619- 
620- static void
621-@@ -159,7 +265,91 @@ set_desync(struct wl_client *client, struct wl_resource *resource)
622- {
623- 	(void)client;
624- 	struct subsurface *subsurface = wl_resource_get_user_data(resource);
625-+	bool synchronized = subsurface_is_synchronized(subsurface);
626-+
627- 	subsurface->sync = false;
628-+
629-+	if (synchronized
630-+	 && !subsurface_is_synchronized(subsurface)
631-+	 && subsurface->pending
632-+	 && subsurface->surface)
633-+	{
634-+		surface_commit_pending(subsurface->surface);
635-+	}
636-+}
637-+
638-+void
639-+subsurface_parent_commit(struct surface *parent)
640-+{
641-+	struct subsurface *child;
642-+	struct compositor_view *parent_view;
643-+	struct compositor_view *reference;
644-+	struct compositor_view *child_view;
645-+
646-+	if (!parent)
647-+		return;
648-+
649-+	wl_list_for_each (child, &parent->subsurfaces, link)
650-+		list_remove_if_linked(&child->current_link);
651-+
652-+	wl_list_init(&parent->state.subsurfaces_below);
653-+	wl_list_init(&parent->state.subsurfaces_above);
654-+
655-+	wl_list_for_each (child, &parent->pending.state.subsurfaces_below, pending_link)
656-+		wl_list_insert(parent->state.subsurfaces_below.prev, &child->current_link);
657-+
658-+	wl_list_for_each (child, &parent->pending.state.subsurfaces_above, pending_link)
659-+		wl_list_insert(parent->state.subsurfaces_above.prev, &child->current_link);
660-+
661-+	parent_view = parent->view ? compositor_view(parent->view) : NULL;
662-+	if (parent_view) {
663-+		reference = parent_view;
664-+		wl_list_for_each_reverse (child, &parent->state.subsurfaces_below, current_link) {
665-+			if (!child->surface || !child->surface->view)
666-+				continue;
667-+
668-+			child_view = compositor_view(child->surface->view);
669-+			if (!child_view)
670-+				continue;
671-+
672-+			compositor_view_restack(child_view, reference, false);
673-+			reference = child_view;
674-+		}
675-+
676-+		reference = parent_view;
677-+		wl_list_for_each (child, &parent->state.subsurfaces_above, current_link) {
678-+			if (!child->surface || !child->surface->view)
679-+				continue;
680-+
681-+			child_view = compositor_view(child->surface->view);
682-+			if (!child_view)
683-+				continue;
684-+
685-+			compositor_view_restack(child_view, reference, true);
686-+			reference = child_view;
687-+		}
688-+	}
689-+
690-+	wl_list_for_each (child, &parent->subsurfaces, link) {
691-+		if (!child->pending_position)
692-+			continue;
693-+
694-+		child->x = child->pending_x;
695-+		child->y = child->pending_y;
696-+		child->pending_position = false;
697-+		subsurface_update_position(child);
698-+	}
699-+
700-+	wl_list_for_each (child, &parent->state.subsurfaces_below, current_link) {
701-+		if (!child->added)
702-+			child->added = true;
703-+		subsurface_update_visibility(child);
704-+	}
705-+	wl_list_for_each (child, &parent->state.subsurfaces_above, current_link) {
706-+		if (!child->added)
707-+			child->added = true;
708-+		subsurface_update_visibility(child);
709-+	}
710- }
711- 
712- static const struct wl_subsurface_interface subsurface_impl = {
713-@@ -200,6 +390,9 @@ subsurface_destroy(struct wl_resource *resource)
714- 		wl_list_init(&subsurface->link);
715- 	}
716- 
717-+	list_remove_if_linked(&subsurface->pending_link);
718-+	list_remove_if_linked(&subsurface->current_link);
719-+
720- 	if (subsurface->surface && subsurface->surface->view) {
721- 		struct compositor_view *view = compositor_view(subsurface->surface->view);
722- 		if (view && !view->window)
723-@@ -231,14 +424,20 @@ subsurface_new(struct wl_client *client, uint32_t version, uint32_t id,
724- 	subsurface->parent = parent;
725- 	subsurface->x = 0;
726- 	subsurface->y = 0;
727-+	subsurface->pending_x = 0;
728-+	subsurface->pending_y = 0;
729-+	subsurface->pending_position = false;
730- 	subsurface->sync = true;
731- 	subsurface->pending = false;
732-+	subsurface->added = false;
733- 
734- 	subsurface->parent_view_handler.impl = &parent_view_handler_impl;
735- 	wl_list_init(&subsurface->parent_view_handler.link);
736- 	wl_list_init(&subsurface->surface_destroy_listener.link);
737- 	wl_list_init(&subsurface->parent_destroy_listener.link);
738- 	wl_list_init(&subsurface->link);
739-+	wl_list_init(&subsurface->pending_link);
740-+	wl_list_init(&subsurface->current_link);
741- 
742- 	if (!surface->view)
743- 		compositor_create_view(surface);
744-@@ -257,6 +456,8 @@ subsurface_new(struct wl_client *client, uint32_t version, uint32_t id,
745- 	wl_list_insert(&parent_view->base.handlers, &subsurface->parent_view_handler.link);
746- 	subsurface_update_position(subsurface);
747- 	wl_list_insert(&parent->subsurfaces, &subsurface->link);
748-+	wl_list_insert(parent->pending.state.subsurfaces_above.prev, &subsurface->pending_link);
749-+	subsurface_update_visibility(subsurface);
750- 
751- 	subsurface->surface_destroy_listener.notify = handle_surface_destroy;
752- 	wl_resource_add_destroy_listener(surface->resource, &subsurface->surface_destroy_listener);
753-diff --git a/libswc/subsurface.h b/libswc/subsurface.h
754-index c747530..a4eafdf 100644
755---- a/libswc/subsurface.h
756-+++ b/libswc/subsurface.h
757-@@ -41,11 +41,20 @@ struct subsurface {
758- 	struct wl_listener surface_destroy_listener;
759- 	struct wl_listener parent_destroy_listener;
760- 	struct wl_list link;
761-+	struct wl_list pending_link;
762-+	struct wl_list current_link;
763- 	int32_t x, y;
764-+	int32_t pending_x, pending_y;
765-+	bool pending_position;
766- 	bool sync;
767- 	bool pending;
768-+	bool added;
769- };
770- 
771-+bool subsurface_is_synchronized(const struct subsurface *subsurface);
772-+void subsurface_update_visibility(struct subsurface *subsurface);
773-+void subsurface_parent_commit(struct surface *parent);
774-+
775- struct subsurface *subsurface_new(struct wl_client *client, uint32_t version, uint32_t id,
776-                                   struct surface *surface, struct surface *parent);
777- 
778-diff --git a/libswc/surface.c b/libswc/surface.c
779-index 9332054..0e84f00 100644
780---- a/libswc/surface.c
781-+++ b/libswc/surface.c
782-@@ -59,6 +59,8 @@ state_initialize(struct surface_state *state)
783- 	pixman_region32_init_with_extents(&state->input, &infinite_extents);
784- 
785- 	wl_list_init(&state->frame_callbacks);
786-+	wl_list_init(&state->subsurfaces_below);
787-+	wl_list_init(&state->subsurfaces_above);
788- }
789- 
790- static void
791-@@ -267,10 +269,15 @@ surface_apply_pending(struct surface *surface, bool flush_children)
792- 	if (surface->subsurface)
793- 		surface->subsurface->pending = false;
794- 
795-+	if (surface->subsurface)
796-+		subsurface_update_visibility(surface->subsurface);
797-+
798-+	subsurface_parent_commit(surface);
799-+
800- 	if (flush_children) {
801- 		struct subsurface *child;
802- 		wl_list_for_each (child, &surface->subsurfaces, link) {
803--			if (!child->sync || !child->pending)
804-+			if (!child->pending || !subsurface_is_synchronized(child))
805- 				continue;
806- 			if (child->surface)
807- 				surface_apply_pending(child->surface, true);
808-@@ -283,7 +290,7 @@ commit(struct wl_client *client, struct wl_resource *resource)
809- {
810- 	struct surface *surface = wl_resource_get_user_data(resource);
811- 
812--	if (surface->subsurface && surface->subsurface->sync) {
813-+	if (surface->subsurface && subsurface_is_synchronized(surface->subsurface)) {
814- 		surface->subsurface->pending = true;
815- 		return;
816- 	}
817-@@ -402,3 +409,9 @@ surface_set_view(struct surface *surface, struct view *view)
818- 		view_update(view);
819- 	}
820- }
821-+
822-+void
823-+surface_commit_pending(struct surface *surface)
824-+{
825-+	surface_apply_pending(surface, true);
826-+}
827-diff --git a/libswc/surface.h b/libswc/surface.h
828-index 5eb751c..0e8c4e5 100644
829---- a/libswc/surface.h
830-+++ b/libswc/surface.h
831-@@ -54,6 +54,10 @@ struct surface_state {
832- 	pixman_region32_t input;
833- 
834- 	struct wl_list frame_callbacks;
835-+
836-+	/* subsurface order; double-buffered with surface state. */
837-+	struct wl_list subsurfaces_below;
838-+	struct wl_list subsurfaces_above;
839- };
840- 
841- struct surface {
842-@@ -80,5 +84,6 @@ struct surface {
843- 
844- struct surface *surface_new(struct wl_client *client, uint32_t version, uint32_t id);
845- void surface_set_view(struct surface *surface, struct view *view);
846-+void surface_commit_pending(struct surface *surface);
847- 
848- #endif
849--- 
850-2.49.0
851-
+0, -134
  1@@ -1,134 +0,0 @@
  2-From ed94892107d6a6a84c4c68910c702708f18508a6 Mon Sep 17 00:00:00 2001
  3-From: uint <abhinav.prsai@gmail.com>
  4-Date: Sun, 15 Feb 2026 12:40:13 +0000
  5-Subject: [PATCH] add motion throttle
  6-
  7-fixes random crashes from overloading the server with
  8-events. window managers are unaffected as there is a
  9-def_motion_throttle_ms=60 and should even fix some
 10----
 11- libswc/swc.h    |  1 +
 12- libswc/window.c | 27 +++++++++++++++++++++++++++
 13- libswc/window.h |  2 ++
 14- 3 files changed, 30 insertions(+)
 15-
 16-diff --git a/libswc/swc.h b/libswc/swc.h
 17-index 1f4ada4..058ff2c 100644
 18---- a/libswc/swc.h
 19-+++ b/libswc/swc.h
 20-@@ -249,6 +249,7 @@ struct swc_window {
 21- 	char *app_id;
 22- 
 23- 	struct swc_window *parent;
 24-+	uint32_t motion_throttle_ms;
 25- };
 26- 
 27- /**
 28-diff --git a/libswc/window.c b/libswc/window.c
 29-index d539d3c..1e9ee2b 100644
 30---- a/libswc/window.c
 31-+++ b/libswc/window.c
 32-@@ -38,8 +38,23 @@
 33- 
 34- #define INTERNAL(w) ((struct window *)(w))
 35- 
 36-+static const uint32_t def_motion_throttle_ms = 16;
 37-+
 38- static const struct swc_window_handler null_handler;
 39- 
 40-+static bool
 41-+should_throttle_motion(uint32_t throttle_ms, uint32_t *last_time, uint32_t time)
 42-+{
 43-+	if (!throttle_ms)
 44-+		return false;
 45-+
 46-+	if (*last_time && time - *last_time < throttle_ms)
 47-+		return true;
 48-+
 49-+	*last_time = time;
 50-+	return false;
 51-+}
 52-+
 53- static void
 54- handle_window_enter(struct wl_listener *listener, void *data)
 55- {
 56-@@ -312,6 +327,10 @@ static bool
 57- move_motion(struct pointer_handler *handler, uint32_t time, wl_fixed_t fx, wl_fixed_t fy)
 58- {
 59- 	struct window *window = wl_container_of(handler, window, move.interaction.handler);
 60-+
 61-+	if (should_throttle_motion(window->base.motion_throttle_ms, &window->move.last_time, time))
 62-+		return true;
 63-+
 64- 	int32_t x = wl_fixed_to_int(fx) + window->move.offset.x,
 65- 	        y = wl_fixed_to_int(fy) + window->move.offset.y;
 66- 
 67-@@ -326,6 +345,9 @@ resize_motion(struct pointer_handler *handler, uint32_t time, wl_fixed_t fx, wl_
 68- 	const struct swc_rectangle *geometry = &window->view->base.geometry;
 69- 	uint32_t width = geometry->width, height = geometry->height;
 70- 
 71-+	if (should_throttle_motion(window->base.motion_throttle_ms, &window->resize.last_time, time))
 72-+		return true;
 73-+
 74- 	if (window->resize.edges & SWC_WINDOW_EDGE_LEFT)
 75- 		width -= wl_fixed_to_int(fx) + window->resize.offset.x - geometry->x;
 76- 	else if (window->resize.edges & SWC_WINDOW_EDGE_RIGHT)
 77-@@ -408,9 +430,11 @@ window_initialize(struct window *window, const struct window_impl *impl, struct
 78- 	window->handler = &null_handler;
 79- 	window->view_handler.impl = &view_handler_impl;
 80- 	window->view->window = window;
 81-+	window->base.motion_throttle_ms = def_motion_throttle_ms;
 82- 	window->managed = false;
 83- 	window->mode = WINDOW_MODE_STACKED;
 84- 	window->move.pending = false;
 85-+	window->move.last_time = 0;
 86- 	window->move.interaction.active = false;
 87- 	window->move.interaction.handler = (struct pointer_handler){
 88- 		.motion = move_motion,
 89-@@ -424,6 +448,7 @@ window_initialize(struct window *window, const struct window_impl *impl, struct
 90- 		.motion = resize_motion,
 91- 		.button = handle_button,
 92- 	};
 93-+	window->resize.last_time = 0;
 94- 
 95- 	wl_list_insert(&window->view->base.handlers, &window->view_handler.link);
 96- 
 97-@@ -510,6 +535,7 @@ window_begin_move(struct window *window, struct button *button)
 98- 	        py = wl_fixed_to_int(swc.seat->pointer->y);
 99- 
100- 	begin_interaction(&window->move.interaction, button);
101-+	window->move.last_time = 0;
102- 	window->move.offset.x = geometry->x - px;
103- 	window->move.offset.y = geometry->y - py;
104- }
105-@@ -528,6 +554,7 @@ window_begin_resize(struct window *window, uint32_t edges, struct button *button
106- 	        py = wl_fixed_to_int(swc.seat->pointer->y);
107- 
108- 	begin_interaction(&window->resize.interaction, button);
109-+	window->resize.last_time = 0;
110- 
111- 	if (!edges) {
112- 		edges |= (px < geometry->x + geometry->width / 2) ? SWC_WINDOW_EDGE_LEFT : SWC_WINDOW_EDGE_RIGHT;
113-diff --git a/libswc/window.h b/libswc/window.h
114-index d8f6bfa..71a597f 100644
115---- a/libswc/window.h
116-+++ b/libswc/window.h
117-@@ -66,6 +66,7 @@ struct window {
118- 
119- 		bool pending;
120- 		int32_t x, y;
121-+		uint32_t last_time;
122- 	} move;
123- 
124- 	struct {
125-@@ -74,6 +75,7 @@ struct window {
126- 			int32_t x, y;
127- 		} offset;
128- 		uint32_t edges;
129-+		uint32_t last_time;
130- 	} resize;
131- 
132- 	struct {
133--- 
134-2.49.0
135-
+0, -99
  1@@ -1,99 +0,0 @@
  2-From a86a27f5fac950e085a5069f72d4737f26ded3ba Mon Sep 17 00:00:00 2001
  3-From: uint <abhinav.prsai@gmail.com>
  4-Date: Sun, 15 Feb 2026 14:24:18 +0000
  5-Subject: [PATCH] add min/max window width/height
  6-
  7-fixes swc wm dying if resize value is too large
  8-(unsigned wrapping back)
  9----
 10- libswc/swc.h    |  4 ++++
 11- libswc/window.c | 37 +++++++++++++++++++++++++++++++++++++
 12- 2 files changed, 41 insertions(+)
 13-
 14-diff --git a/libswc/swc.h b/libswc/swc.h
 15-index 058ff2c..033bdd4 100644
 16---- a/libswc/swc.h
 17-+++ b/libswc/swc.h
 18-@@ -250,6 +250,10 @@ struct swc_window {
 19- 
 20- 	struct swc_window *parent;
 21- 	uint32_t motion_throttle_ms;
 22-+	uint32_t min_width;
 23-+	uint32_t min_height;
 24-+	uint32_t max_width;
 25-+	uint32_t max_height;
 26- };
 27- 
 28- /**
 29-diff --git a/libswc/window.c b/libswc/window.c
 30-index 1e9ee2b..728243d 100644
 31---- a/libswc/window.c
 32-+++ b/libswc/window.c
 33-@@ -55,6 +55,36 @@ should_throttle_motion(uint32_t throttle_ms, uint32_t *last_time, uint32_t time)
 34- 	return false;
 35- }
 36- 
 37-+static uint32_t
 38-+clamp_dimension(int32_t value, uint32_t min, uint32_t max)
 39-+{
 40-+	if (value < 0)
 41-+		value = 0;
 42-+
 43-+	if (min && value < min)
 44-+		value = min;
 45-+
 46-+	if (max) {
 47-+		if (min && max < min)
 48-+			max = min;
 49-+
 50-+		if (value > max)
 51-+			value = max;
 52-+	}
 53-+
 54-+	if (value > UINT32_MAX)
 55-+		value = UINT32_MAX;
 56-+
 57-+	return value;
 58-+}
 59-+
 60-+static void
 61-+clamp_window_size(const struct window *window, uint32_t *width, uint32_t *height)
 62-+{
 63-+	*width = clamp_dimension(*width, window->base.min_width, window->base.max_width);
 64-+	*height = clamp_dimension(*height, window->base.min_height, window->base.max_height);
 65-+}
 66-+
 67- static void
 68- handle_window_enter(struct wl_listener *listener, void *data)
 69- {
 70-@@ -254,6 +284,8 @@ swc_window_set_size(struct swc_window *base, uint32_t width, uint32_t height)
 71- 	struct window *window = INTERNAL(base);
 72- 	struct swc_rectangle *geom = &window->view->base.geometry;
 73- 
 74-+	clamp_window_size(window, &width, &height);
 75-+
 76- 	if ((window->configure.pending && width == window->configure.width && height == window->configure.height)
 77- 	 || (!window->configure.pending && width == geom->width && height == geom->height))
 78- 	{
 79-@@ -358,6 +390,7 @@ resize_motion(struct pointer_handler *handler, uint32_t time, wl_fixed_t fx, wl_
 80- 	else if (window->resize.edges & SWC_WINDOW_EDGE_BOTTOM)
 81- 		height = wl_fixed_to_int(fy) + window->resize.offset.y - geometry->y;
 82- 
 83-+	clamp_window_size(window, &width, &height);
 84- 	window->impl->configure(window, width, height);
 85- 
 86- 	return true;
 87-@@ -431,6 +464,10 @@ window_initialize(struct window *window, const struct window_impl *impl, struct
 88- 	window->view_handler.impl = &view_handler_impl;
 89- 	window->view->window = window;
 90- 	window->base.motion_throttle_ms = def_motion_throttle_ms;
 91-+	window->base.min_width = 0;
 92-+	window->base.min_height = 0;
 93-+	window->base.max_width = 0;
 94-+	window->base.max_height = 0;
 95- 	window->managed = false;
 96- 	window->mode = WINDOW_MODE_STACKED;
 97- 	window->move.pending = false;
 98--- 
 99-2.49.0
100-
+0, -492
  1@@ -1,492 +0,0 @@
  2-From f9d37ac047966356dbe1bde645e6729c7d8c3d48 Mon Sep 17 00:00:00 2001
  3-From: uint <abhinav.prsai@gmail.com>
  4-Date: Wed, 18 Feb 2026 06:07:03 +0000
  5-Subject: [PATCH] add wallpaper protocol with separate screen buffers support
  6-
  7-add wallpaper protocol
  8-> swc just uses a buffer, processing is done on client
  9-removed STB
 10----
 11- libswc/compositor.c        |  45 ++++++++++--
 12- libswc/compositor.h        |   1 +
 13- libswc/internal.h          |   1 +
 14- libswc/swc.c               |  14 +++-
 15- libswc/swc.h               |  17 +++--
 16- libswc/wallpaper.c         | 147 +++++++++++++++++++++++--------------
 17- libswc/wallpaper.h         |  12 +++
 18- protocol/swc_wallpaper.xml |  19 +++++
 19- 8 files changed, 185 insertions(+), 71 deletions(-)
 20- create mode 100644 libswc/wallpaper.h
 21- create mode 100644 protocol/swc_wallpaper.xml
 22-
 23-diff --git a/libswc/compositor.c b/libswc/compositor.c
 24-index 4333d99..fd52973 100644
 25---- a/libswc/compositor.c
 26-+++ b/libswc/compositor.c
 27-@@ -44,6 +44,7 @@
 28- #include "subsurface.h"
 29- #include "util.h"
 30- #include "view.h"
 31-+#include "wallpaper.h"
 32- #include "window.h"
 33- 
 34- #include <errno.h>
 35-@@ -109,6 +110,7 @@ static struct {
 36- 
 37- 	bool updating;
 38- 	struct wl_global *global;
 39-+	bool initialized;
 40- 
 41- 	/* zoom level (1.0 = normal, >1 = zoomed in, <1 = zoomed out) */
 42- 	float zoom;
 43-@@ -302,7 +304,7 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
 44- }
 45- 
 46- static void
 47--renderer_repaint(struct target *target, pixman_region32_t *damage, pixman_region32_t *base_damage, struct wl_list *views)
 48-+renderer_repaint(struct target *target, pixman_region32_t *damage, pixman_region32_t *base_damage, struct wl_list *views, struct screen *screen)
 49- {
 50- 	struct compositor_view *view;
 51- 	const struct swc_rectangle *target_geom = &target->view->geometry;
 52-@@ -314,10 +316,12 @@ renderer_repaint(struct target *target, pixman_region32_t *damage, pixman_region
 53- 	wld_set_target_surface(swc.drm->renderer, target->surface);
 54- 
 55- 	if (pixman_region32_not_empty(base_damage)) {
 56-+		struct wld_buffer *background = swc_wallpaper_buffer_for_screen(screen);
 57-+
 58- 		pixman_region32_translate(base_damage, -target->view->geometry.x, -target->view->geometry.y);
 59- 		
 60--		if(wallbuf)
 61--			wld_copy_region(swc.drm->renderer, wallbuf, 0, 0, base_damage);
 62-+		if (background)
 63-+			wld_copy_region(swc.drm->renderer, background, 0, 0, base_damage);
 64- 
 65- 		else
 66- 			wld_fill_region(swc.drm->renderer, bgcolor, base_damage);
 67-@@ -508,6 +512,23 @@ schedule_updates(uint32_t screens)
 68- 	compositor.scheduled_updates |= screens;
 69- }
 70- 
 71-+void
 72-+compositor_damage_all(void)
 73-+{
 74-+	struct screen *screen;
 75-+
 76-+	if (!compositor.initialized)
 77-+		return;
 78-+
 79-+	wl_list_for_each (screen, &swc.screens, link) {
 80-+		pixman_region32_union_rect(&compositor.damage, &compositor.damage,
 81-+			screen->base.geometry.x, screen->base.geometry.y,
 82-+			screen->base.geometry.width, screen->base.geometry.height);
 83-+	}
 84-+
 85-+	schedule_updates(-1);
 86-+}
 87-+
 88- static void
 89- overlay_damage_region(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t border_width)
 90- {
 91-@@ -597,6 +618,7 @@ render_zoomed_to_shm(struct screen *screen, float zoom)
 92- 	int32_t cx = screen_x + width / 2;
 93- 	int32_t cy = screen_y + height / 2;
 94- 	struct compositor_view *view;
 95-+	struct wld_buffer *background;
 96- 
 97- 	struct wld_buffer *buffer = wld_create_buffer(swc.shm->context,
 98- 		width, height, WLD_FORMAT_XRGB8888, WLD_FLAG_MAP);
 99-@@ -610,8 +632,9 @@ render_zoomed_to_shm(struct screen *screen, float zoom)
100- 
101- 	pixman_region32_t full;
102- 	pixman_region32_init_rect(&full, 0, 0, width, height);
103--	if (wallbuf)
104--		wld_copy_region(swc.shm->renderer, wallbuf, 0, 0, &full);
105-+	background = swc_wallpaper_buffer_for_screen(screen);
106-+	if (background)
107-+		wld_copy_region(swc.shm->renderer, background, 0, 0, &full);
108- 	else
109- 		wld_fill_region(swc.shm->renderer, bgcolor, &full);
110- 	pixman_region32_fini(&full);
111-@@ -1326,7 +1349,7 @@ update_screen(struct screen *screen)
112- 		pixman_region32_translate(&damage, geom->x, geom->y);
113- 		pixman_region32_init(&base_damage);
114- 		pixman_region32_subtract(&base_damage, &damage, &compositor.opaque);
115--		renderer_repaint(target, &damage, &base_damage, &compositor.views);
116-+		renderer_repaint(target, &damage, &base_damage, &compositor.views, screen);
117- 		pixman_region32_fini(&damage);
118- 		pixman_region32_fini(&base_damage);
119- 	}
120-@@ -1507,12 +1530,16 @@ compositor_initialize(void)
121- 	for (keysym = XKB_KEY_XF86Switch_VT_1; keysym <= XKB_KEY_XF86Switch_VT_12; ++keysym)
122- 		swc_add_binding(SWC_BINDING_KEY, SWC_MOD_ANY, keysym, &handle_switch_vt, NULL);
123- 
124-+	compositor.initialized = true;
125-+
126- 	return true;
127- }
128- 
129- void
130- compositor_finalize(void)
131- {
132-+	compositor.initialized = false;
133-+
134- 	if (compositor.zoom_buffer)
135- 		wld_buffer_unreference(compositor.zoom_buffer);
136- 	pixman_region32_fini(&compositor.damage);
137-@@ -1539,6 +1566,7 @@ compositor_render_to_shm(struct screen *screen)
138- 	pixman_region32_t region;
139- 	pixman_region32_t damage;
140- 	uint32_t caps;
141-+	struct wld_buffer *background;
142- 
143- 	/* create shm buf */
144- 	buffer = wld_create_buffer(swc.shm->context, width, height,
145-@@ -1558,8 +1586,9 @@ compositor_render_to_shm(struct screen *screen)
146- 	pixman_region32_init_rect(&damage, screen->base.geometry.x, screen->base.geometry.y, width, height);
147- 
148- 	/* background */
149--	if (wallbuf)
150--		wld_copy_region(swc.shm->renderer, wallbuf, 0, 0, &region);
151-+	background = swc_wallpaper_buffer_for_screen(screen);
152-+	if (background)
153-+		wld_copy_region(swc.shm->renderer, background, 0, 0, &region);
154- 	else
155- 		wld_fill_region(swc.shm->renderer, bgcolor, &region);
156- 
157-diff --git a/libswc/compositor.h b/libswc/compositor.h
158-index 94de9e3..a404534 100644
159---- a/libswc/compositor.h
160-+++ b/libswc/compositor.h
161-@@ -48,6 +48,7 @@ struct swc_compositor {
162- 
163- bool compositor_initialize(void);
164- void compositor_finalize(void);
165-+void compositor_damage_all(void);
166- 
167- struct compositor_view {
168- 	struct view base;
169-diff --git a/libswc/internal.h b/libswc/internal.h
170-index 243b75f..a238a3d 100644
171---- a/libswc/internal.h
172-+++ b/libswc/internal.h
173-@@ -51,6 +51,7 @@ struct swc {
174- 	struct wl_global *shell;
175- 	struct wl_global *snap_manager;
176- 	struct wl_global *subcompositor;
177-+	struct wl_global *wallpaper_manager;
178- 	struct wl_global *xdg_decoration_manager;
179- 	struct wl_global *xdg_shell;
180- 
181-diff --git a/libswc/swc.c b/libswc/swc.c
182-index 6d3f871..6647c65 100644
183---- a/libswc/swc.c
184-+++ b/libswc/swc.c
185-@@ -40,6 +40,7 @@
186- #include "snap.h"
187- #include "subcompositor.h"
188- #include "util.h"
189-+#include "wallpaper.h"
190- #include "window.h"
191- #include "xdg_decoration.h"
192- #include "xdg_shell.h"
193-@@ -222,10 +223,16 @@ swc_initialize(struct wl_display *display, struct wl_event_loop *event_loop, con
194- 		goto error14;
195- 	}
196- 
197-+	swc.wallpaper_manager = swc_wallpaper_manager_create(display);
198-+	if (!swc.wallpaper_manager) {
199-+		ERROR("Could not initialize wallpaper manager\n");
200-+		goto error15;
201-+	}
202-+
203- #ifdef ENABLE_XWAYLAND
204- 	if (!xserver_initialize()) {
205- 		ERROR("Could not initialize xwayland\n");
206--		goto error15;
207-+		goto error16;
208- 	}
209- #endif
210- 
211-@@ -234,9 +241,11 @@ swc_initialize(struct wl_display *display, struct wl_event_loop *event_loop, con
212- 	return true;
213- 
214- #ifdef ENABLE_XWAYLAND
215-+error16:
216-+	wl_global_destroy(swc.wallpaper_manager);
217-+#endif
218- error15:
219- 	wl_global_destroy(swc.snap_manager);
220--#endif
221- error14:
222- 	wl_global_destroy(swc.panel_manager);
223- error13:
224-@@ -275,6 +284,7 @@ swc_finalize(void)
225- #ifdef ENABLE_XWAYLAND
226- 	xserver_finalize();
227- #endif
228-+	wl_global_destroy(swc.wallpaper_manager);
229- 	wl_global_destroy(swc.snap_manager);
230- 	wl_global_destroy(swc.panel_manager);
231- 	wl_global_destroy(swc.xdg_decoration_manager);
232-diff --git a/libswc/swc.h b/libswc/swc.h
233-index 033bdd4..f2a8ae1 100644
234---- a/libswc/swc.h
235-+++ b/libswc/swc.h
236-@@ -35,6 +35,7 @@ extern "C" {
237- struct libinput_device;
238- struct wl_display;
239- struct wl_event_loop;
240-+struct wld_buffer;
241- 
242- /**
243-  * gett the current cursor position
244-@@ -438,19 +439,21 @@ int swc_add_axis_binding(uint32_t modifiers, uint32_t axis, swc_axis_binding_han
245- 
246- /* Wallpaper {{{ */
247- 
248--extern unsigned char *wallpaper;
249--extern struct wld_buffer *wallbuf;
250--
251- /**
252-- * Set wallpaper to image from fs path.
253-- * TODO: tiling, maybe diff image for each screen
254-+ * Set fallback wallpaper buffer for all screens that dom't have an override.
255-  */
256-+void swc_wallpaper_set_buffer(struct wld_buffer *buffer);
257- 
258--void swc_wallpaper_set(char* path);
259-+/**
260-+ * Set wallpaper buffer for specified screen id.
261-+ *
262-+ * Passing NULL clears the override for that screen.
263-+ */
264-+void swc_wallpaper_set_buffer_for_screen(uint8_t screen_id, struct wld_buffer *buffer);
265- 
266- /**
267-  * Set wallpaper to a single color
268-- * pretty much ignored if wallpaper is set to image
269-+ * used when no wallpaper buffer is set
270-  * defaults to black
271-  */
272- 
273-diff --git a/libswc/wallpaper.c b/libswc/wallpaper.c
274-index 7618bf0..deeb5fd 100644
275---- a/libswc/wallpaper.c
276-+++ b/libswc/wallpaper.c
277-@@ -1,77 +1,116 @@
278--#include <pixman.h>
279- #include <wld/wld.h>
280- 
281--#define STB_IMAGE_IMPLEMENTATION
282--#define STBI_NO_HDR
283--#include "../stb/stb_image.h"
284--
285--#define STB_IMAGE_RESIZE_IMPLEMENTATION
286--#include "../stb/stb_image_resize2.h"
287--
288- #include "swc.h"
289--#include "internal.h"
290--#include "drm.h"
291--#include "util.h"
292--#include "shm.h"
293-+#include "compositor.h"
294- #include "screen.h"
295-+#include "util.h"
296-+#include "wayland_buffer.h"
297-+#include "wallpaper.h"
298-+#include "swc_wallpaper-server-protocol.h"
299- 
300--unsigned char *wallpaper = NULL;
301--struct wld_buffer *wallbuf = NULL;
302-+#define MAX_WALLPAPER_SCREENS 32
303- 
304-+static struct wld_buffer *wallbuf = NULL;
305-+static struct wld_buffer *screen_wallbuf[MAX_WALLPAPER_SCREENS];
306- uint32_t bgcolor = 0xff000000;
307- 
308-+static void
309-+set_buffer_slot(struct wld_buffer **slot, struct wld_buffer *buffer)
310-+{
311-+	if (buffer)
312-+		wld_buffer_reference(buffer);
313-+	if (*slot)
314-+		wld_buffer_unreference(*slot);
315-+
316-+	*slot = buffer;
317-+}
318-+
319-+struct wld_buffer *
320-+swc_wallpaper_buffer_for_screen(struct screen *screen)
321-+{
322-+	if (screen
323-+	 && screen->id < ARRAY_LENGTH(screen_wallbuf)
324-+	 && screen_wallbuf[screen->id])
325-+		return screen_wallbuf[screen->id];
326-+
327-+	return wallbuf;
328-+}
329-+
330- EXPORT void
331--swc_wallpaper_set(char* path)
332-+swc_wallpaper_set_buffer(struct wld_buffer *buffer)
333- {
334--	int width, height, chan;
335--	unsigned char *loaded;
336--	struct screen *screen;
337--	int target_width = 0, target_height = 0;
338-+	set_buffer_slot(&wallbuf, buffer);
339-+	compositor_damage_all();
340-+}
341- 
342--	loaded = stbi_load(path, &width, &height, &chan, 4);
343--	if (!loaded)
344-+EXPORT void
345-+swc_wallpaper_set_buffer_for_screen(uint8_t screen_id, struct wld_buffer *buffer)
346-+{
347-+	if (screen_id >= ARRAY_LENGTH(screen_wallbuf))
348- 		return;
349- 
350--	/* get screen dimensions */
351--	wl_list_for_each(screen, &swc.screens, link) {
352--		target_width = screen->base.geometry.width;
353--		target_height = screen->base.geometry.height;
354--		break;
355--	}
356-+	set_buffer_slot(&screen_wallbuf[screen_id], buffer);
357-+	compositor_damage_all();
358-+}
359-+
360-+EXPORT void
361-+swc_wallpaper_color_set(uint32_t color)
362-+{
363-+	bgcolor = color;
364-+	compositor_damage_all();
365-+}
366-+
367-+static void
368-+set_buffer(struct wl_client *client, struct wl_resource *resource,
369-+           int32_t screen_id, struct wl_resource *buffer_resource)
370-+{
371-+	struct wld_buffer *buffer = NULL;
372- 
373--	/* If we have a screen and dimensions wrong  scale  */
374--	if (target_width > 0 && target_height > 0 &&
375--	    (width != target_width || height != target_height)) {
376--		wallpaper = stbir_resize_uint8_srgb(loaded, width, height, 0,
377--		                                     NULL, target_width, target_height, 0,
378--		                                     STBIR_RGBA);
379--		stbi_image_free(loaded);
380--		width = target_width;
381--		height = target_height;
382--	} else {
383--		wallpaper = loaded;
384-+	(void)client;
385-+
386-+	if (buffer_resource) {
387-+		buffer = wayland_buffer_get(buffer_resource);
388-+		if (!buffer) {
389-+			wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
390-+			                       "buffer is not a wl_buffer");
391-+			return;
392-+		}
393- 	}
394- 
395--	/* swap color channels to be compatible */
396--	for(int i = 0; i < width * height; i++) {
397--		unsigned char r = wallpaper[i*4];
398--		wallpaper[i*4] = wallpaper[(i*4)+2];
399--		wallpaper[(i*4)+2] = r;
400-+	if (screen_id == -1) {
401-+		swc_wallpaper_set_buffer(buffer);
402-+		return;
403- 	}
404- 
405--	union wld_object obj;
406--	obj.ptr = (uint32_t*)wallpaper;
407-+	if (screen_id < 0 || screen_id >= ARRAY_LENGTH(screen_wallbuf))
408-+		return;
409- 
410--	wallbuf = wld_import_buffer(swc.shm->context,
411--			WLD_OBJECT_DATA,
412--			obj,
413--			width, height,
414--			WLD_FORMAT_ARGB8888,
415--			width * 4);
416-+	swc_wallpaper_set_buffer_for_screen((uint8_t)screen_id, buffer);
417- }
418- 
419--EXPORT void
420--swc_wallpaper_color_set(uint32_t color)
421-+static const struct swc_wallpaper_interface wallpaper_impl = {
422-+	.destroy = destroy_resource,
423-+	.set_buffer = set_buffer,
424-+};
425-+
426-+static void
427-+bind_wallpaper(struct wl_client *client, void *data, uint32_t version, uint32_t id)
428- {
429--	bgcolor = color;
430-+	(void)data;
431-+
432-+	struct wl_resource *resource;
433-+
434-+	resource = wl_resource_create(client, &swc_wallpaper_interface, version, id);
435-+	if (!resource) {
436-+		wl_client_post_no_memory(client);
437-+		return;
438-+	}
439-+
440-+	wl_resource_set_implementation(resource, &wallpaper_impl, NULL, NULL);
441-+}
442-+
443-+struct wl_global *
444-+swc_wallpaper_manager_create(struct wl_display *display)
445-+{
446-+	return wl_global_create(display, &swc_wallpaper_interface, 1, NULL, bind_wallpaper);
447- }
448-diff --git a/libswc/wallpaper.h b/libswc/wallpaper.h
449-new file mode 100644
450-index 0000000..8c4d834
451---- /dev/null
452-+++ b/libswc/wallpaper.h
453-@@ -0,0 +1,12 @@
454-+#ifndef SWC_WALLPAPER_H
455-+#define SWC_WALLPAPER_H
456-+
457-+struct wl_display;
458-+struct wl_global;
459-+struct wld_buffer;
460-+struct screen;
461-+
462-+struct wl_global *swc_wallpaper_manager_create(struct wl_display *display);
463-+struct wld_buffer *swc_wallpaper_buffer_for_screen(struct screen *screen);
464-+
465-+#endif
466-diff --git a/protocol/swc_wallpaper.xml b/protocol/swc_wallpaper.xml
467-new file mode 100644
468-index 0000000..dd7479a
469---- /dev/null
470-+++ b/protocol/swc_wallpaper.xml
471-@@ -0,0 +1,19 @@
472-+<?xml version="1.0" encoding="UTF-8"?>
473-+<protocol name="swc_wallpaper">
474-+    <interface name="swc_wallpaper" version="1">
475-+        <description summary="set compositor wallpaper">
476-+            Allows clients to set wallpaper buffers.
477-+        </description>
478-+
479-+        <request name="destroy" type="destructor" />
480-+
481-+        <request name="set_buffer">
482-+            <description summary="set or clear wallpaper buffer">
483-+                screen_id=-1 targets global fallback wallpaper.
484-+                Any other screen_id targets that id.
485-+            </description>
486-+            <arg name="screen_id" type="int" />
487-+            <arg name="buffer" type="object" interface="wl_buffer" allow-null="true" />
488-+        </request>
489-+    </interface>
490-+</protocol>
491--- 
492-2.49.0
493-
+1, -1
1@@ -1 +1 @@
2-9a20014 r4
3+9a20014 r5