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