commit 0b9f5bc

hovercats  ·  2026-03-10 11:13:13 +0000 UTC
parent 0141787
swc: port patches from neuswc, so that we can build hevel etc without adding neuswc as an additional package
22 files changed,  +4525, -2
+1, -0
1@@ -183,6 +183,7 @@
2 [submodule "pkg/swc/src"]
3 	path = pkg/swc/src
4 	url = https://github.com/michaelforney/swc
5+	ignore = all
6 [submodule "pkg/utf8proc/src"]
7 	path = pkg/utf8proc/src
8 	url = https://github.com/JuliaLang/utf8proc
+9, -0
 1@@ -30,6 +30,12 @@ waylandproto('protocol/server-decoration.xml', {
 2 	code='server-decoration-protocol.c',
 3 })
 4 
 5+waylandproto('protocol/swc_wallpaper.xml', {
 6+       client='include/swc_wallpaper-client-protocol.h',
 7+       server='include/swc_wallpaper-server-protocol.h',
 8+       code='swc_wallpaper-protocol.c',
 9+})
10+
11 pkg.hdrs = {
12 	copy('$outdir/include', '$srcdir/libswc', {'swc.h'}),
13 	'$outdir/include/server-decoration-server-protocol.h',
14@@ -50,6 +56,7 @@ build('convert_font', '$outdir/cursor/cursor_data.h', {'$srcdir/cursor/cursor.pc
15 
16 pkg.deps = {
17 	'$gendir/headers',
18+	'$outdir/include/swc_wallpaper-server-protocol.h',
19 	'$outdir/wayland-drm-server-protocol.h',
20 	'$outdir/cursor/cursor_data.h',
21 	'pkg/fontconfig/headers',
22@@ -95,6 +102,7 @@ lib('libswc.a', [[
23 		swc.c
24 		util.c
25 		view.c
26+		wallpaper.c
27 		wayland_buffer.c
28 		window.c
29 		xdg_decoration.c
30@@ -103,6 +111,7 @@ lib('libswc.a', [[
31 	launch/protocol.c.o
32 	server-decoration-protocol.c.o
33 	swc-protocol.c.o
34+	swc_wallpaper-protocol.c.o
35 	$builddir/(
36 		pkg/libinput/libinput.a.d
37 		pkg/libxkbcommon/libxkbcommon.a
+283, -0
  1@@ -0,0 +1,283 @@
  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+
+170, -0
  1@@ -0,0 +1,170 @@
  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+
+59, -0
 1@@ -0,0 +1,59 @@
 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+
+264, -0
  1@@ -0,0 +1,264 @@
  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+
+383, -0
  1@@ -0,0 +1,383 @@
  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+
+215, -0
  1@@ -0,0 +1,215 @@
  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+
+134, -0
  1@@ -0,0 +1,134 @@
  2+From 6c4cfa23f28c67ae588423bdb2f3842848972ac7 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 2aa06c8..3776929 100644
 18+--- a/libswc/swc.h
 19++++ b/libswc/swc.h
 20+@@ -192,6 +192,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 b1fdeb9..bfbbbbd 100644
 30+--- a/libswc/window.c
 31++++ b/libswc/window.c
 32+@@ -36,8 +36,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+@@ -299,6 +314,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+@@ -313,6 +332,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+@@ -389,9 +411,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+@@ -405,6 +429,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+@@ -491,6 +516,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+@@ -509,6 +535,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 08c9d36..d6684a8 100644
115+--- a/libswc/window.h
116++++ b/libswc/window.h
117+@@ -61,6 +61,7 @@ struct window {
118+ 
119+ 		bool pending;
120+ 		int32_t x, y;
121++		uint32_t last_time;
122+ 	} move;
123+ 
124+ 	struct {
125+@@ -69,6 +70,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+
+99, -0
  1@@ -0,0 +1,99 @@
  2+From af4e4ebba3d7fd5840c200c26e339246242bbee7 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 3776929..dde7ef0 100644
 16+--- a/libswc/swc.h
 17++++ b/libswc/swc.h
 18+@@ -193,6 +193,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 bfbbbbd..60432ae 100644
 31+--- a/libswc/window.c
 32++++ b/libswc/window.c
 33+@@ -53,6 +53,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+@@ -241,6 +271,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+@@ -345,6 +377,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+@@ -412,6 +445,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+
+232, -0
  1@@ -0,0 +1,232 @@
  2+From b89f8f1c30e8d92e33d977ebd8a014281aa4d340 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 dde7ef0..29638d9 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+
+85, -0
 1@@ -0,0 +1,85 @@
 2+From d7cc5eb216965f3a9ed14542d25f557aab6042d1 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 29638d9..f3bd453 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+@@ -325,6 +326,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 60432ae..3e3a714 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+@@ -583,3 +585,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+
+333, -0
  1@@ -0,0 +1,333 @@
  2+From 6d3e048dfc11209f32891ef9fc39586b83eff974 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 263da39..0f978d9 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+@@ -452,6 +456,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+@@ -499,6 +514,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+@@ -992,14 +1203,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+@@ -1158,6 +1387,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+@@ -1181,6 +1412,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 f3bd453..505391d 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+
+62, -0
 1@@ -0,0 +1,62 @@
 2+From 3a2c4c613b15106497ea464b2ee613a30ca2f112 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 3e3a714..6322502 100644
14+--- a/libswc/window.c
15++++ b/libswc/window.c
16+@@ -239,12 +239,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 d6684a8..71a597f 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+
+124, -0
  1@@ -0,0 +1,124 @@
  2+From 73322f31c68b0ef38e16534b80aae31a75ef712c 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.
  6+
  7+---
  8+ libswc/compositor.c |  8 ++++++--
  9+ libswc/swc.h        | 23 ++++++++++++++++++++++
 10+ libswc/wallpaper.c  | 48 +++++++++++++++++++++++++++++++++++++++++++++
 11+ 3 files changed, 77 insertions(+), 2 deletions(-)
 12+ create mode 100644 libswc/wallpaper.c
 13+
 14+diff --git a/libswc/compositor.c b/libswc/compositor.c
 15+index 0f978d9..d70377d 100644
 16+--- a/libswc/compositor.c
 17++++ b/libswc/compositor.c
 18+@@ -287,10 +287,14 @@ renderer_repaint(struct target *target, pixman_region32_t *damage, pixman_region
 19+ 
 20+ 	wld_set_target_surface(swc.drm->renderer, target->surface);
 21+ 
 22+-	/* Paint base damage black. */
 23+ 	if (pixman_region32_not_empty(base_damage)) {
 24+ 		pixman_region32_translate(base_damage, -target->view->geometry.x, -target->view->geometry.y);
 25+-		wld_fill_region(swc.drm->renderer, 0xff000000, base_damage);
 26++		
 27++		if(wallbuf)
 28++			wld_copy_region(swc.drm->renderer, wallbuf, 0, 0, base_damage);
 29++
 30++		else
 31++			wld_fill_region(swc.drm->renderer, bgcolor, base_damage);
 32+ 	}
 33+ 
 34+ 	wl_list_for_each_reverse (view, views, link) {
 35+diff --git a/libswc/swc.h b/libswc/swc.h
 36+index 505391d..a23e556 100644
 37+--- a/libswc/swc.h
 38++++ b/libswc/swc.h
 39+@@ -436,6 +436,29 @@ int swc_add_axis_binding(uint32_t modifiers, uint32_t axis, swc_axis_binding_han
 40+ 
 41+ /* }}} */
 42+ 
 43++/* Wallpaper {{{ */
 44++
 45++extern unsigned char *wallpaper;
 46++extern struct wld_buffer *wallbuf;
 47++
 48++/**
 49++ * Set wallpaper to image from fs path.
 50++ * TODO: scaling, tiling, maybe diff image for each screen
 51++ */
 52++
 53++void swc_wallpaper_init(char* path);
 54++
 55++/**
 56++ * Set wallpaper to a single color
 57++ * pretty much ignored if wallpaper is set to image
 58++ * defaults to black
 59++ */
 60++
 61++extern uint32_t bgcolor;
 62++void swc_wallpaper_color_set(uint32_t color);
 63++
 64++/* }}} */
 65++
 66+ /**
 67+  * This is a user-provided structure that swc will use to notify the display
 68+  * server of new windows, screens and input devices.
 69+diff --git a/libswc/wallpaper.c b/libswc/wallpaper.c
 70+new file mode 100644
 71+index 0000000..53f8a0c
 72+--- /dev/null
 73++++ b/libswc/wallpaper.c
 74+@@ -0,0 +1,48 @@
 75++#include <pixman.h>
 76++#include <wld/wld.h>
 77++
 78++#define STB_IMAGE_IMPLEMENTATION
 79++#define STBI_NO_HDR
 80++#include "../stb/stb_image.h"
 81++
 82++#include "swc.h"
 83++#include "internal.h"
 84++#include "drm.h"
 85++#include "util.h"
 86++#include "shm.h"
 87++
 88++unsigned char *wallpaper = NULL;
 89++struct wld_buffer *wallbuf = NULL;
 90++
 91++uint32_t bgcolor = 0xff000000;
 92++
 93++EXPORT void
 94++swc_wallpaper_init(char* path)
 95++{
 96++	int width, height, chan;
 97++
 98++	wallpaper = stbi_load(path, &width, &height, &chan, 4);
 99++
100++	/* swap color channels to be compatible */
101++	for(int i = 0; i < width * height; i++) {
102++		unsigned char r = wallpaper[i*4];
103++		wallpaper[i*4] = wallpaper[(i*4)+2];
104++		wallpaper[(i*4)+2] = r;
105++	}
106++
107++	union wld_object obj;
108++	obj.ptr = (uint32_t*)wallpaper;
109++
110++	wallbuf = wld_import_buffer(swc.shm->context,
111++			WLD_OBJECT_DATA,
112++			obj,
113++			width, height,
114++			WLD_FORMAT_ARGB8888,
115++			width * 4);
116++}
117++
118++EXPORT void
119++swc_wallpaper_color_set(uint32_t color)
120++{
121++	bgcolor = color;
122++}
123+-- 
124+2.49.0
125+
+68, -0
 1@@ -0,0 +1,68 @@
 2+From 5be9ee4cbc06aefed8fe11360df8cc9d045c3587 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] this adds 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+
+44, -0
 1@@ -0,0 +1,44 @@
 2+From 108719f480ad292252721ac3c90b75081e1152e5 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 a23e556..033bdd4 100644
15+--- a/libswc/swc.h
16++++ b/libswc/swc.h
17+@@ -443,10 +443,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+
+678, -0
  1@@ -0,0 +1,678 @@
  2+From c04d6b835bc440cb56babfc63e45c705c1723820 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 d70377d..d2321b9 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 9046f0b..e079d92 100644
194+--- a/libswc/compositor.h
195++++ b/libswc/compositor.h
196+@@ -52,6 +52,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 6322502..728243d 100644
628+--- a/libswc/window.c
629++++ b/libswc/window.c
630+@@ -450,8 +450,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+
+850, -0
  1@@ -0,0 +1,850 @@
  2+From 13a2200f15510e837ce2631592856f4f0a0ede2b 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 d2321b9..4875af8 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 e079d92..529c360 100644
314+--- a/libswc/compositor.h
315++++ b/libswc/compositor.h
316+@@ -92,6 +92,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+
+430, -0
  1@@ -0,0 +1,430 @@
  2+From 40e72dcbb90be5d92615aea647810a56dd0c878a 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+---
  8+ libswc/compositor.c        |  39 ++++++++--
  9+ libswc/compositor.h        |   1 +
 10+ libswc/internal.h          |   1 +
 11+ libswc/swc.c               |   1 +
 12+ libswc/swc.h               |  17 +++--
 13+ libswc/wallpaper.c         | 147 +++++++++++++++++++++++--------------
 14+ libswc/wallpaper.h         |  12 +++
 15+ protocol/swc_wallpaper.xml |  19 +++++
 16+ 8 files changed, 170 insertions(+), 67 deletions(-)
 17+ create mode 100644 libswc/wallpaper.h
 18+ create mode 100644 protocol/swc_wallpaper.xml
 19+
 20+diff --git a/libswc/compositor.c b/libswc/compositor.c
 21+index 4875af8..cb7863b 100644
 22+--- a/libswc/compositor.c
 23++++ b/libswc/compositor.c
 24+@@ -44,6 +44,7 @@
 25+ #include "subsurface.h"
 26+ #include "util.h"
 27+ #include "view.h"
 28++#include "wallpaper.h"
 29+ #include "window.h"
 30+ 
 31+ #include <errno.h>
 32+@@ -109,6 +110,7 @@ static struct {
 33+ 
 34+ 	bool updating;
 35+ 	struct wl_global *global;
 36++	bool initialized;
 37+ 
 38+ 	/* zoom level (1.0 = normal, >1 = zoomed in, <1 = zoomed out) */
 39+ 	float zoom;
 40+@@ -302,7 +304,7 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
 41+ }
 42+ 
 43+ static void
 44+-renderer_repaint(struct target *target, pixman_region32_t *damage, pixman_region32_t *base_damage, struct wl_list *views)
 45++renderer_repaint(struct target *target, pixman_region32_t *damage, pixman_region32_t *base_damage, struct wl_list *views, struct screen *screen)
 46+ {
 47+ 	struct compositor_view *view;
 48+ 	const struct swc_rectangle *target_geom = &target->view->geometry;
 49+@@ -314,10 +316,12 @@ renderer_repaint(struct target *target, pixman_region32_t *damage, pixman_region
 50+ 	wld_set_target_surface(swc.drm->renderer, target->surface);
 51+ 
 52+ 	if (pixman_region32_not_empty(base_damage)) {
 53++		struct wld_buffer *background = swc_wallpaper_buffer_for_screen(screen);
 54++
 55+ 		pixman_region32_translate(base_damage, -target->view->geometry.x, -target->view->geometry.y);
 56+ 		
 57+-		if(wallbuf)
 58+-			wld_copy_region(swc.drm->renderer, wallbuf, 0, 0, base_damage);
 59++		if (background)
 60++			wld_copy_region(swc.drm->renderer, background, 0, 0, base_damage);
 61+ 
 62+ 		else
 63+ 			wld_fill_region(swc.drm->renderer, bgcolor, base_damage);
 64+@@ -508,6 +512,23 @@ schedule_updates(uint32_t screens)
 65+ 	compositor.scheduled_updates |= screens;
 66+ }
 67+ 
 68++void
 69++compositor_damage_all(void)
 70++{
 71++	struct screen *screen;
 72++
 73++	if (!compositor.initialized)
 74++		return;
 75++
 76++	wl_list_for_each (screen, &swc.screens, link) {
 77++		pixman_region32_union_rect(&compositor.damage, &compositor.damage,
 78++			screen->base.geometry.x, screen->base.geometry.y,
 79++			screen->base.geometry.width, screen->base.geometry.height);
 80++	}
 81++
 82++	schedule_updates(-1);
 83++}
 84++
 85+ static void
 86+ overlay_damage_region(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t border_width)
 87+ {
 88+@@ -597,6 +618,7 @@ render_zoomed_to_shm(struct screen *screen, float zoom)
 89+ 	int32_t cx = screen_x + width / 2;
 90+ 	int32_t cy = screen_y + height / 2;
 91+ 	struct compositor_view *view;
 92++	struct wld_buffer *background;
 93+ 
 94+ 	struct wld_buffer *buffer = wld_create_buffer(swc.shm->context,
 95+ 		width, height, WLD_FORMAT_XRGB8888, WLD_FLAG_MAP);
 96+@@ -610,8 +632,9 @@ render_zoomed_to_shm(struct screen *screen, float zoom)
 97+ 
 98+ 	pixman_region32_t full;
 99+ 	pixman_region32_init_rect(&full, 0, 0, width, height);
100+-	if (wallbuf)
101+-		wld_copy_region(swc.shm->renderer, wallbuf, 0, 0, &full);
102++	background = swc_wallpaper_buffer_for_screen(screen);
103++	if (background)
104++		wld_copy_region(swc.shm->renderer, background, 0, 0, &full);
105+ 	else
106+ 		wld_fill_region(swc.shm->renderer, bgcolor, &full);
107+ 	pixman_region32_fini(&full);
108+@@ -1326,7 +1349,7 @@ update_screen(struct screen *screen)
109+ 		pixman_region32_translate(&damage, geom->x, geom->y);
110+ 		pixman_region32_init(&base_damage);
111+ 		pixman_region32_subtract(&base_damage, &damage, &compositor.opaque);
112+-		renderer_repaint(target, &damage, &base_damage, &compositor.views);
113++		renderer_repaint(target, &damage, &base_damage, &compositor.views, screen);
114+ 		pixman_region32_fini(&damage);
115+ 		pixman_region32_fini(&base_damage);
116+ 	}
117+@@ -1507,12 +1530,16 @@ compositor_initialize(void)
118+ 	for (keysym = XKB_KEY_XF86Switch_VT_1; keysym <= XKB_KEY_XF86Switch_VT_12; ++keysym)
119+ 		swc_add_binding(SWC_BINDING_KEY, SWC_MOD_ANY, keysym, &handle_switch_vt, NULL);
120+ 
121++	compositor.initialized = true;
122++
123+ 	return true;
124+ }
125+ 
126+ void
127+ compositor_finalize(void)
128+ {
129++	compositor.initialized = false;
130++
131+ 	if (compositor.zoom_buffer)
132+ 		wld_buffer_unreference(compositor.zoom_buffer);
133+ 	pixman_region32_fini(&compositor.damage);
134+diff --git a/libswc/compositor.h b/libswc/compositor.h
135+index 529c360..6c938ac 100644
136+--- a/libswc/compositor.h
137++++ b/libswc/compositor.h
138+@@ -45,6 +45,7 @@ struct swc_compositor {
139+ 
140+ bool compositor_initialize(void);
141+ void compositor_finalize(void);
142++void compositor_damage_all(void);
143+ 
144+ struct compositor_view {
145+ 	struct view base;
146+diff --git a/libswc/internal.h b/libswc/internal.h
147+index 8efba6d..6d2b0a1 100644
148+--- a/libswc/internal.h
149++++ b/libswc/internal.h
150+@@ -50,6 +50,7 @@ struct swc {
151+ 	struct wl_global *panel_manager;
152+ 	struct wl_global *shell;
153+ 	struct wl_global *subcompositor;
154++	struct wl_global *wallpaper_manager;
155+ 	struct wl_global *xdg_decoration_manager;
156+ 	struct wl_global *xdg_shell;
157+ 
158+diff --git a/libswc/swc.c b/libswc/swc.c
159+index 8814c13..1035ef3 100644
160+--- a/libswc/swc.c
161++++ b/libswc/swc.c
162+@@ -39,6 +39,7 @@
163+ #include "shm.h"
164+ #include "subcompositor.h"
165+ #include "util.h"
166++#include "wallpaper.h"
167+ #include "window.h"
168+ #include "xdg_decoration.h"
169+ #include "xdg_shell.h"
170+diff --git a/libswc/swc.h b/libswc/swc.h
171+index 033bdd4..f2a8ae1 100644
172+--- a/libswc/swc.h
173++++ b/libswc/swc.h
174+@@ -35,6 +35,7 @@ extern "C" {
175+ struct libinput_device;
176+ struct wl_display;
177+ struct wl_event_loop;
178++struct wld_buffer;
179+ 
180+ /**
181+  * gett the current cursor position
182+@@ -438,19 +439,21 @@ int swc_add_axis_binding(uint32_t modifiers, uint32_t axis, swc_axis_binding_han
183+ 
184+ /* Wallpaper {{{ */
185+ 
186+-extern unsigned char *wallpaper;
187+-extern struct wld_buffer *wallbuf;
188+-
189+ /**
190+- * Set wallpaper to image from fs path.
191+- * TODO: tiling, maybe diff image for each screen
192++ * Set fallback wallpaper buffer for all screens that dom't have an override.
193+  */
194++void swc_wallpaper_set_buffer(struct wld_buffer *buffer);
195+ 
196+-void swc_wallpaper_set(char* path);
197++/**
198++ * Set wallpaper buffer for specified screen id.
199++ *
200++ * Passing NULL clears the override for that screen.
201++ */
202++void swc_wallpaper_set_buffer_for_screen(uint8_t screen_id, struct wld_buffer *buffer);
203+ 
204+ /**
205+  * Set wallpaper to a single color
206+- * pretty much ignored if wallpaper is set to image
207++ * used when no wallpaper buffer is set
208+  * defaults to black
209+  */
210+ 
211+diff --git a/libswc/wallpaper.c b/libswc/wallpaper.c
212+index 7618bf0..deeb5fd 100644
213+--- a/libswc/wallpaper.c
214++++ b/libswc/wallpaper.c
215+@@ -1,77 +1,116 @@
216+-#include <pixman.h>
217+ #include <wld/wld.h>
218+ 
219+-#define STB_IMAGE_IMPLEMENTATION
220+-#define STBI_NO_HDR
221+-#include "../stb/stb_image.h"
222+-
223+-#define STB_IMAGE_RESIZE_IMPLEMENTATION
224+-#include "../stb/stb_image_resize2.h"
225+-
226+ #include "swc.h"
227+-#include "internal.h"
228+-#include "drm.h"
229+-#include "util.h"
230+-#include "shm.h"
231++#include "compositor.h"
232+ #include "screen.h"
233++#include "util.h"
234++#include "wayland_buffer.h"
235++#include "wallpaper.h"
236++#include "swc_wallpaper-server-protocol.h"
237+ 
238+-unsigned char *wallpaper = NULL;
239+-struct wld_buffer *wallbuf = NULL;
240++#define MAX_WALLPAPER_SCREENS 32
241+ 
242++static struct wld_buffer *wallbuf = NULL;
243++static struct wld_buffer *screen_wallbuf[MAX_WALLPAPER_SCREENS];
244+ uint32_t bgcolor = 0xff000000;
245+ 
246++static void
247++set_buffer_slot(struct wld_buffer **slot, struct wld_buffer *buffer)
248++{
249++	if (buffer)
250++		wld_buffer_reference(buffer);
251++	if (*slot)
252++		wld_buffer_unreference(*slot);
253++
254++	*slot = buffer;
255++}
256++
257++struct wld_buffer *
258++swc_wallpaper_buffer_for_screen(struct screen *screen)
259++{
260++	if (screen
261++	 && screen->id < ARRAY_LENGTH(screen_wallbuf)
262++	 && screen_wallbuf[screen->id])
263++		return screen_wallbuf[screen->id];
264++
265++	return wallbuf;
266++}
267++
268+ EXPORT void
269+-swc_wallpaper_set(char* path)
270++swc_wallpaper_set_buffer(struct wld_buffer *buffer)
271+ {
272+-	int width, height, chan;
273+-	unsigned char *loaded;
274+-	struct screen *screen;
275+-	int target_width = 0, target_height = 0;
276++	set_buffer_slot(&wallbuf, buffer);
277++	compositor_damage_all();
278++}
279+ 
280+-	loaded = stbi_load(path, &width, &height, &chan, 4);
281+-	if (!loaded)
282++EXPORT void
283++swc_wallpaper_set_buffer_for_screen(uint8_t screen_id, struct wld_buffer *buffer)
284++{
285++	if (screen_id >= ARRAY_LENGTH(screen_wallbuf))
286+ 		return;
287+ 
288+-	/* get screen dimensions */
289+-	wl_list_for_each(screen, &swc.screens, link) {
290+-		target_width = screen->base.geometry.width;
291+-		target_height = screen->base.geometry.height;
292+-		break;
293+-	}
294++	set_buffer_slot(&screen_wallbuf[screen_id], buffer);
295++	compositor_damage_all();
296++}
297++
298++EXPORT void
299++swc_wallpaper_color_set(uint32_t color)
300++{
301++	bgcolor = color;
302++	compositor_damage_all();
303++}
304++
305++static void
306++set_buffer(struct wl_client *client, struct wl_resource *resource,
307++           int32_t screen_id, struct wl_resource *buffer_resource)
308++{
309++	struct wld_buffer *buffer = NULL;
310+ 
311+-	/* If we have a screen and dimensions wrong  scale  */
312+-	if (target_width > 0 && target_height > 0 &&
313+-	    (width != target_width || height != target_height)) {
314+-		wallpaper = stbir_resize_uint8_srgb(loaded, width, height, 0,
315+-		                                     NULL, target_width, target_height, 0,
316+-		                                     STBIR_RGBA);
317+-		stbi_image_free(loaded);
318+-		width = target_width;
319+-		height = target_height;
320+-	} else {
321+-		wallpaper = loaded;
322++	(void)client;
323++
324++	if (buffer_resource) {
325++		buffer = wayland_buffer_get(buffer_resource);
326++		if (!buffer) {
327++			wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
328++			                       "buffer is not a wl_buffer");
329++			return;
330++		}
331+ 	}
332+ 
333+-	/* swap color channels to be compatible */
334+-	for(int i = 0; i < width * height; i++) {
335+-		unsigned char r = wallpaper[i*4];
336+-		wallpaper[i*4] = wallpaper[(i*4)+2];
337+-		wallpaper[(i*4)+2] = r;
338++	if (screen_id == -1) {
339++		swc_wallpaper_set_buffer(buffer);
340++		return;
341+ 	}
342+ 
343+-	union wld_object obj;
344+-	obj.ptr = (uint32_t*)wallpaper;
345++	if (screen_id < 0 || screen_id >= ARRAY_LENGTH(screen_wallbuf))
346++		return;
347+ 
348+-	wallbuf = wld_import_buffer(swc.shm->context,
349+-			WLD_OBJECT_DATA,
350+-			obj,
351+-			width, height,
352+-			WLD_FORMAT_ARGB8888,
353+-			width * 4);
354++	swc_wallpaper_set_buffer_for_screen((uint8_t)screen_id, buffer);
355+ }
356+ 
357+-EXPORT void
358+-swc_wallpaper_color_set(uint32_t color)
359++static const struct swc_wallpaper_interface wallpaper_impl = {
360++	.destroy = destroy_resource,
361++	.set_buffer = set_buffer,
362++};
363++
364++static void
365++bind_wallpaper(struct wl_client *client, void *data, uint32_t version, uint32_t id)
366+ {
367+-	bgcolor = color;
368++	(void)data;
369++
370++	struct wl_resource *resource;
371++
372++	resource = wl_resource_create(client, &swc_wallpaper_interface, version, id);
373++	if (!resource) {
374++		wl_client_post_no_memory(client);
375++		return;
376++	}
377++
378++	wl_resource_set_implementation(resource, &wallpaper_impl, NULL, NULL);
379++}
380++
381++struct wl_global *
382++swc_wallpaper_manager_create(struct wl_display *display)
383++{
384++	return wl_global_create(display, &swc_wallpaper_interface, 1, NULL, bind_wallpaper);
385+ }
386+diff --git a/libswc/wallpaper.h b/libswc/wallpaper.h
387+new file mode 100644
388+index 0000000..8c4d834
389+--- /dev/null
390++++ b/libswc/wallpaper.h
391+@@ -0,0 +1,12 @@
392++#ifndef SWC_WALLPAPER_H
393++#define SWC_WALLPAPER_H
394++
395++struct wl_display;
396++struct wl_global;
397++struct wld_buffer;
398++struct screen;
399++
400++struct wl_global *swc_wallpaper_manager_create(struct wl_display *display);
401++struct wld_buffer *swc_wallpaper_buffer_for_screen(struct screen *screen);
402++
403++#endif
404+diff --git a/protocol/swc_wallpaper.xml b/protocol/swc_wallpaper.xml
405+new file mode 100644
406+index 0000000..dd7479a
407+--- /dev/null
408++++ b/protocol/swc_wallpaper.xml
409+@@ -0,0 +1,19 @@
410++<?xml version="1.0" encoding="UTF-8"?>
411++<protocol name="swc_wallpaper">
412++    <interface name="swc_wallpaper" version="1">
413++        <description summary="set compositor wallpaper">
414++            Allows clients to set wallpaper buffers.
415++        </description>
416++
417++        <request name="destroy" type="destructor" />
418++
419++        <request name="set_buffer">
420++            <description summary="set or clear wallpaper buffer">
421++                screen_id=-1 targets global fallback wallpaper.
422++                Any other screen_id targets that id.
423++            </description>
424++            <arg name="screen_id" type="int" />
425++            <arg name="buffer" type="object" interface="wl_buffer" allow-null="true" />
426++        </request>
427++    </interface>
428++</protocol>
429+-- 
430+2.49.0
431+
+1, -1
1@@ -1 +1 @@
2-Subproject commit 19893a5ca3239082b904a04fdb9ac7c3a1fb62e2
3+Subproject commit 9a20014528f6bb61333a404b81136d918595ee22
+1, -1
1@@ -1 +1 @@
2-19893a5ca3 r0
3+9a20014 r3