commit 49ebfc3

shrub  ·  2026-04-28 17:57:02 +0000 UTC
parent 5b23c48
decor: add configurable window decorations api
7 files changed,  +527, -24
+51, -14
  1@@ -29,6 +29,7 @@
  2 
  3 #include "compositor.h"
  4 #include "data_device_manager.h"
  5+#include "decor.h"
  6 #include "drm.h"
  7 #include "event.h"
  8 #include "internal.h"
  9@@ -354,6 +355,7 @@ repaint_view(struct target *target, struct compositor_view *view,
 10 		}
 11 		pixman_region32_fini(&decor_region);
 12 		pixman_region32_fini(&content_region);
 13+		decor_repaint(swc.drm->renderer, target_geom, view, damage);
 14 	}
 15 }
 16 
 17@@ -1300,6 +1302,7 @@ compositor_create_view(struct surface *surface)
 18 	view->decor.right = 0;
 19 	view->decor.bottom = 0;
 20 	view->decor.left = 0;
 21+	decor_view_initialize(view);
 22 	view->decor.damaged = false;
 23 	pixman_region32_init(&view->clip);
 24 	wl_signal_init(&view->destroy_signal);
 25@@ -1316,6 +1319,7 @@ compositor_view_destroy(struct compositor_view *view)
 26 	compositor_view_hide(view);
 27 	surface_set_view(view->surface, NULL);
 28 	view_finalize(&view->base);
 29+	decor_view_finalize(view);
 30 	pixman_region32_fini(&view->clip);
 31 	wl_list_remove(&view->link);
 32 	free(view);
 33@@ -1467,24 +1471,23 @@ compositor_view_set_border_color(struct compositor_view *view,
 34 }
 35 
 36 void
 37-compositor_view_set_decor(struct compositor_view *view, uint32_t color,
 38-                          uint32_t top, uint32_t right, uint32_t bottom,
 39-                          uint32_t left)
 40+compositor_view_set_decor(struct compositor_view *view,
 41+                            const struct swc_decor *decor)
 42 {
 43-	if (view->decor.color == color && view->decor.top == top &&
 44-	    view->decor.right == right && view->decor.bottom == bottom &&
 45-	    view->decor.left == left) {
 46+	decor_view_set(view, decor);
 47+	update_extents(view);
 48+	update(&view->base);
 49+}
 50+
 51+void
 52+compositor_view_damage_decor(struct compositor_view *view)
 53+{
 54+	if (!view->decor.top && !view->decor.right && !view->decor.bottom &&
 55+	    !view->decor.left) {
 56 		return;
 57 	}
 58 
 59-	view->decor.color = color;
 60-	view->decor.top = top;
 61-	view->decor.right = right;
 62-	view->decor.bottom = bottom;
 63-	view->decor.left = left;
 64-	view->decor.damaged = true;
 65-
 66-	update_extents(view);
 67+	decor_view_damage(view);
 68 	update(&view->base);
 69 }
 70 
 71@@ -1799,6 +1802,9 @@ compositor_initialize(void)
 72 	compositor.updating = false;
 73 	compositor.zoom = 1.0f;
 74 	compositor.zoom_buffer = NULL;
 75+	if (!decor_initialize()) {
 76+		return false;
 77+	}
 78 	pixman_region32_init(&compositor.damage);
 79 	pixman_region32_init(&compositor.opaque);
 80 	wl_list_init(&compositor.views);
 81@@ -1834,6 +1840,7 @@ compositor_finalize(void)
 82 		wld_buffer_unreference(compositor.zoom_buffer);
 83 		compositor.zoom_buffer = NULL;
 84 	}
 85+	decor_finalize();
 86 	pixman_region32_fini(&compositor.damage);
 87 	pixman_region32_fini(&compositor.opaque);
 88 	wl_global_destroy(compositor.global);
 89@@ -1971,6 +1978,36 @@ compositor_render_to_shm(struct screen *screen)
 90 			pixman_region32_fini(&out_border);
 91 			pixman_region32_fini(&in_border);
 92 		}
 93+
 94+		if (view->decor.top || view->decor.right || view->decor.bottom ||
 95+		    view->decor.left) {
 96+			pixman_region32_t decor_region, content_region;
 97+			const struct swc_rectangle *geom = &view->base.geometry;
 98+			const struct swc_rectangle *target_geom = &screen->base.geometry;
 99+			int32_t x = geom->x - (int32_t)view->decor.left;
100+			int32_t y = geom->y - (int32_t)view->decor.top;
101+			uint32_t decor_width =
102+			    geom->width + view->decor.left + view->decor.right;
103+			uint32_t decor_height =
104+			    geom->height + view->decor.top + view->decor.bottom;
105+
106+			pixman_region32_init_rect(&decor_region, x, y, decor_width,
107+			                          decor_height);
108+			pixman_region32_init_rect(&content_region, geom->x, geom->y,
109+			                          geom->width, geom->height);
110+			pixman_region32_subtract(&decor_region, &decor_region, &content_region);
111+			pixman_region32_intersect(&decor_region, &decor_region, &damage);
112+			pixman_region32_subtract(&decor_region, &decor_region, &view->clip);
113+			if (pixman_region32_not_empty(&decor_region)) {
114+				pixman_region32_translate(&decor_region, -target_geom->x,
115+				                          -target_geom->y);
116+				wld_fill_region(swc.shm->renderer, view->decor.color,
117+				                &decor_region);
118+			}
119+			pixman_region32_fini(&decor_region);
120+			pixman_region32_fini(&content_region);
121+			decor_repaint(swc.shm->renderer, target_geom, view, &damage);
122+		}
123 	}
124 
125 	draw_overlays(swc.shm->renderer, &screen->base.geometry);
+9, -3
 1@@ -32,6 +32,7 @@
 2 
 3 struct screen;
 4 struct wld_buffer;
 5+struct wld_font;
 6 
 7 struct swc_compositor {
 8 	struct pointer_handler *const pointer_handler;
 9@@ -91,6 +92,10 @@ struct compositor_view {
10 	struct {
11 		uint32_t color;
12 		uint32_t top, right, bottom, left;
13+		struct swc_decor_text text;
14+		char *string;
15+		char *font_name;
16+		struct wld_font *font;
17 		bool damaged;
18 	} decor;
19 
20@@ -129,9 +134,10 @@ void
21 compositor_view_set_border_width(struct compositor_view *view,
22                                  uint32_t outwidth, uint32_t inwidth);
23 void
24-compositor_view_set_decor(struct compositor_view *view, uint32_t color,
25-                          uint32_t top, uint32_t right, uint32_t bottom,
26-                          uint32_t left);
27+compositor_view_set_decor(struct compositor_view *view,
28+                           const struct swc_decor *decor);
29+void
30+compositor_view_damage_decor(struct compositor_view *view);
31 
32 /**
33  * get the current composited buffer for a screen for screenshotss.
+361, -0
  1@@ -0,0 +1,361 @@
  2+#include "decor.h"
  3+
  4+#include "compositor.h"
  5+#include "swc.h"
  6+#include "window.h"
  7+
  8+#include <stdlib.h>
  9+#include <string.h>
 10+#include <wld/wld.h>
 11+
 12+#define DEFAULT_DECOR_FONT "sans-serif:size=10"
 13+
 14+static struct wld_font_context *font_context;
 15+
 16+static uint32_t
 17+decor_title_length(struct wld_font *font, const char *title, uint32_t max_width)
 18+{
 19+	uint32_t len = 0;
 20+	struct wld_extents extents;
 21+
 22+	if (!title || !max_width) {
 23+		return 0;
 24+	}
 25+
 26+	while (title[len]) {
 27+		uint32_t next = len + 1;
 28+
 29+		while ((title[next] & 0xc0) == 0x80) {
 30+			next++;
 31+		}
 32+
 33+		wld_font_text_extents_n(font, title, (int32_t)next, &extents);
 34+		if (extents.advance > max_width) {
 35+			break;
 36+		}
 37+		len = next;
 38+	}
 39+
 40+	return len;
 41+}
 42+
 43+static uint32_t
 44+utf8_next_len(const char *text, uint32_t offset)
 45+{
 46+	uint32_t next = offset + 1;
 47+
 48+	while ((text[next] & 0xc0) == 0x80) {
 49+		next++;
 50+	}
 51+
 52+	return next - offset;
 53+}
 54+
 55+static uint32_t
 56+decor_title_stacked_length(struct wld_font *font, const char *title,
 57+                           uint32_t max_height, uint32_t *glyph_count)
 58+{
 59+	uint32_t len = 0, count = 0;
 60+
 61+	if (!title || !font->height) {
 62+		*glyph_count = 0;
 63+		return 0;
 64+	}
 65+
 66+	while (title[len] && (count + 1) * font->height <= max_height) {
 67+		len += utf8_next_len(title, len);
 68+		count++;
 69+	}
 70+
 71+	*glyph_count = count;
 72+	return len;
 73+}
 74+
 75+static bool
 76+streq(const char *a, const char *b)
 77+{
 78+	if (!a || !b) {
 79+		return a == b;
 80+	}
 81+	return strcmp(a, b) == 0;
 82+}
 83+
 84+static void
 85+close_decor_font(struct compositor_view *view)
 86+{
 87+	if (view->decor.font) {
 88+		wld_font_close(view->decor.font);
 89+		view->decor.font = NULL;
 90+	}
 91+	free(view->decor.font_name);
 92+	view->decor.font_name = NULL;
 93+}
 94+
 95+static void
 96+close_decor_string(struct compositor_view *view)
 97+{
 98+	free(view->decor.string);
 99+	view->decor.string = NULL;
100+}
101+
102+bool
103+decor_initialize(void)
104+{
105+	font_context = wld_font_create_context();
106+	return font_context != NULL;
107+}
108+
109+void
110+decor_finalize(void)
111+{
112+	if (font_context) {
113+		wld_font_destroy_context(font_context);
114+		font_context = NULL;
115+	}
116+}
117+
118+void
119+decor_view_initialize(struct compositor_view *view)
120+{
121+	memset(&view->decor.text, 0, sizeof(view->decor.text));
122+	view->decor.string = NULL;
123+	view->decor.font_name = NULL;
124+	view->decor.font = NULL;
125+}
126+
127+void
128+decor_view_finalize(struct compositor_view *view)
129+{
130+	close_decor_string(view);
131+	close_decor_font(view);
132+}
133+
134+void
135+decor_repaint(struct wld_renderer *renderer,
136+              const struct swc_rectangle *target_geom,
137+              struct compositor_view *view, pixman_region32_t *damage)
138+{
139+	const struct swc_rectangle *geom = &view->base.geometry;
140+	const struct swc_decor_text *text = &view->decor.text;
141+	struct wld_font *font = view->decor.font;
142+	const char *title = view->decor.string;
143+	uint32_t title_len, max_width, decor_size;
144+	int32_t x, y, base_x, base_y, advance, available_width;
145+	struct wld_extents extents;
146+	pixman_region32_t title_region;
147+
148+	if (!title || !font || !text->enabled) {
149+		return;
150+	}
151+
152+	switch (text->edge) {
153+	case SWC_DECOR_EDGE_TOP:
154+		if (!view->decor.top) {
155+			return;
156+		}
157+		base_x = geom->x - (int32_t)view->decor.left;
158+		base_y = geom->y - (int32_t)view->decor.top;
159+		max_width = geom->width + view->decor.left + view->decor.right;
160+		decor_size = view->decor.top;
161+		break;
162+	case SWC_DECOR_EDGE_BOTTOM:
163+		if (!view->decor.bottom) {
164+			return;
165+		}
166+		base_x = geom->x - (int32_t)view->decor.left;
167+		base_y = geom->y + (int32_t)geom->height;
168+		max_width = geom->width + view->decor.left + view->decor.right;
169+		decor_size = view->decor.bottom;
170+		break;
171+	case SWC_DECOR_EDGE_LEFT:
172+		if (!view->decor.left) {
173+			return;
174+		}
175+		base_x = geom->x - (int32_t)view->decor.left;
176+		base_y = geom->y - (int32_t)view->decor.top;
177+		max_width = view->decor.left;
178+		decor_size = geom->height + view->decor.top + view->decor.bottom;
179+		break;
180+	case SWC_DECOR_EDGE_RIGHT:
181+		if (!view->decor.right) {
182+			return;
183+		}
184+		base_x = geom->x + (int32_t)geom->width;
185+		base_y = geom->y - (int32_t)view->decor.top;
186+		max_width = view->decor.right;
187+		decor_size = geom->height + view->decor.top + view->decor.bottom;
188+		break;
189+	default:
190+		return;
191+	}
192+
193+	x = base_x;
194+	y = base_y;
195+
196+	pixman_region32_init_rect(&title_region, x, y, max_width, decor_size);
197+	pixman_region32_intersect(&title_region, &title_region, damage);
198+	pixman_region32_subtract(&title_region, &title_region, &view->clip);
199+	if (!pixman_region32_not_empty(&title_region)) {
200+		pixman_region32_fini(&title_region);
201+		return;
202+	}
203+	pixman_region32_fini(&title_region);
204+
205+	if (max_width <= text->padding * 2 || decor_size < font->height) {
206+		return;
207+	}
208+	max_width -= text->padding * 2;
209+
210+	if (text->edge == SWC_DECOR_EDGE_LEFT || text->edge == SWC_DECOR_EDGE_RIGHT) {
211+		uint32_t glyph_count, glyph_offset = 0, available_height, total_height;
212+
213+		if (decor_size <= text->padding * 2) {
214+			return;
215+		}
216+
217+		available_height = decor_size - text->padding * 2;
218+		title_len = decor_title_stacked_length(font, title, available_height,
219+		                                      &glyph_count);
220+		if (!title_len) {
221+			return;
222+		}
223+
224+		total_height = glyph_count * font->height;
225+		y += (int32_t)text->padding;
226+		switch (text->align) {
227+		case SWC_DECOR_ALIGN_START:
228+			break;
229+		case SWC_DECOR_ALIGN_CENTER:
230+			y += (int32_t)((available_height - total_height) / 2);
231+			break;
232+		case SWC_DECOR_ALIGN_END:
233+			y += (int32_t)(available_height - total_height);
234+			break;
235+		}
236+
237+		while (glyph_offset < title_len) {
238+			uint32_t glyph_len = utf8_next_len(title, glyph_offset);
239+			int32_t glyph_x;
240+
241+			wld_font_text_extents_n(font, title + glyph_offset,
242+			                        (int32_t)glyph_len, &extents);
243+			advance = extents.advance > 0 ? extents.advance : 0;
244+			glyph_x = x + (int32_t)text->padding;
245+			if ((uint32_t)advance < max_width) {
246+				glyph_x += ((int32_t)max_width - advance) / 2;
247+			}
248+
249+			wld_draw_text(renderer, font, text->color,
250+			              glyph_x + text->offset_x - target_geom->x,
251+			              y + (int32_t)font->ascent + text->offset_y -
252+			                  target_geom->y,
253+			              title + glyph_offset, glyph_len, NULL);
254+
255+			glyph_offset += glyph_len;
256+			y += (int32_t)font->height;
257+		}
258+
259+		return;
260+	}
261+
262+	title_len = decor_title_length(font, title, max_width);
263+	if (!title_len) {
264+		return;
265+	}
266+	wld_font_text_extents_n(font, title, (int32_t)title_len, &extents);
267+	advance = extents.advance > 0 ? extents.advance : 0;
268+	available_width = (int32_t)max_width;
269+
270+	switch (text->align) {
271+	case SWC_DECOR_ALIGN_START:
272+		x += text->padding;
273+		break;
274+	case SWC_DECOR_ALIGN_CENTER:
275+		x += (int32_t)text->padding + (available_width - advance) / 2;
276+		break;
277+	case SWC_DECOR_ALIGN_END:
278+		x += (int32_t)text->padding + (int32_t)max_width - advance;
279+		break;
280+	}
281+
282+	y += (int32_t)((decor_size - font->height) / 2) + (int32_t)font->ascent;
283+
284+	x += text->offset_x;
285+	y += text->offset_y;
286+
287+	wld_draw_text(renderer, font, text->color, x - target_geom->x,
288+	              y - target_geom->y, title, title_len, NULL);
289+}
290+
291+void
292+decor_view_set(struct compositor_view *view, const struct swc_decor *decor)
293+{
294+	const char *font_name = NULL;
295+	struct wld_font *font = NULL;
296+	struct swc_decor_text text = { 0 };
297+	char *owned_string = NULL;
298+	char *owned_font_name = NULL;
299+	uint32_t color = 0, top = 0, right = 0, bottom = 0, left = 0;
300+
301+	if (!decor) {
302+		goto apply;
303+	}
304+
305+	color = decor->color;
306+	top = decor->top;
307+	right = decor->right;
308+	bottom = decor->bottom;
309+	left = decor->left;
310+	text = decor->title;
311+	if (text.enabled) {
312+		font_name = text.font ? text.font : DEFAULT_DECOR_FONT;
313+	}
314+
315+	if (view->decor.color == color && view->decor.top == top &&
316+	    view->decor.right == right && view->decor.bottom == bottom &&
317+	    view->decor.left == left &&
318+	    view->decor.text.enabled == text.enabled &&
319+	    view->decor.text.edge == text.edge &&
320+	    view->decor.text.align == text.align &&
321+	    streq(view->decor.string, text.string) &&
322+	    view->decor.text.color == text.color &&
323+	    view->decor.text.padding == text.padding &&
324+	    view->decor.text.offset_x == text.offset_x &&
325+	    view->decor.text.offset_y == text.offset_y &&
326+	    streq(view->decor.font_name, font_name)) {
327+		return;
328+	}
329+
330+	if (text.string) {
331+		owned_string = strdup(text.string);
332+	}
333+
334+	if (font_name) {
335+		owned_font_name = strdup(font_name);
336+		if (owned_font_name && font_context) {
337+			font = wld_font_open_name(font_context, font_name);
338+		}
339+	}
340+
341+apply:
342+	view->decor.color = color;
343+	view->decor.top = top;
344+	view->decor.right = right;
345+	view->decor.bottom = bottom;
346+	view->decor.left = left;
347+	view->decor.text = text;
348+	view->decor.text.string = NULL;
349+	view->decor.text.font = NULL;
350+	close_decor_string(view);
351+	view->decor.string = owned_string;
352+	close_decor_font(view);
353+	view->decor.font_name = owned_font_name;
354+	view->decor.font = font;
355+	view->decor.damaged = true;
356+}
357+
358+void
359+decor_view_damage(struct compositor_view *view)
360+{
361+	view->decor.damaged = true;
362+}
+31, -0
 1@@ -0,0 +1,31 @@
 2+#ifndef SWC_DECOR_H
 3+#define SWC_DECOR_H
 4+
 5+#include <stdbool.h>
 6+#include <pixman.h>
 7+
 8+struct compositor_view;
 9+struct swc_decor;
10+struct swc_rectangle;
11+struct wld_renderer;
12+
13+bool
14+decor_initialize(void);
15+void
16+decor_finalize(void);
17+
18+void
19+decor_view_initialize(struct compositor_view *view);
20+void
21+decor_view_finalize(struct compositor_view *view);
22+
23+void
24+decor_repaint(struct wld_renderer *renderer,
25+              const struct swc_rectangle *target_geom,
26+              struct compositor_view *view, pixman_region32_t *damage);
27+void
28+decor_view_set(struct compositor_view *view, const struct swc_decor *decor);
29+void
30+decor_view_damage(struct compositor_view *view);
31+
32+#endif
+1, -0
1@@ -6,6 +6,7 @@ libswc_src = files(
2   'data.c',
3   'data_device.c',
4   'data_device_manager.c',
5+  'decor.c',
6   'dmabuf.c',
7   'drm.c',
8   'input.c',
+72, -4
 1@@ -390,15 +390,83 @@ swc_window_set_border(struct swc_window *window, uint32_t inner_border_color,
 2                       uint32_t inner_border_width, uint32_t outer_border_color,
 3                       uint32_t outer_border_width);
 4 
 5+/*window decor things*/
 6+
 7 /**
 8- * Set decor around the window content
 9+ * Select which decor edge a text slot is rendered on
10+ */
11+enum swc_decor_edge {
12+	SWC_DECOR_EDGE_TOP,
13+	SWC_DECOR_EDGE_RIGHT,
14+	SWC_DECOR_EDGE_BOTTOM,
15+	SWC_DECOR_EDGE_LEFT,
16+};
17+
18+/**
19+ * choose text alignment within the selected decor edge.
20+ *
21+ * for top and bottom edges, alignment is horizontal
22+ * for left and right edges, alignment is vertical
23+ */
24+enum swc_decor_align {
25+	SWC_DECOR_ALIGN_START,
26+	SWC_DECOR_ALIGN_CENTER,
27+	SWC_DECOR_ALIGN_END,
28+};
29+
30+/**
31+ * describe a window decor text slot.
32+ *
33+ * if enabled is false, no text is drawn.
34+ *
35+ * the wm supplies some string to be rendered in this decor slot. swc
36+ * copies the string when swc_window_set_decor() is called, so caller doesnt
37+ * need to keep it after the call returns.
38+ *
39+ * for top and bottom edges, text is drawn horizontally
40+ * for left and right edges, text is drawn as stacked glyphs, one glyph
41+ * per row.
42+ *
43+ * the font field accepts some fontconfig pattern such as "sans-serif:size=10".
44+ * if font is NULL, a default font is used (dont be a loser, pick a cool font)
45+ */
46+struct swc_decor_text {
47+	bool enabled;
48+	enum swc_decor_edge edge;
49+	enum swc_decor_align align;
50+	const char *string;
51+	uint32_t color;
52+	uint32_t padding;
53+	int32_t offset_x, offset_y;
54+	const char *font;
55+};
56+
57+/**
58+ * describe decor around a window's edges.
59+ *
60+ * The edge sizes extend outward from the window content geometry. 
61+ * if you put 0 it won't be visible
62+ *
63+ * the title field controls an optional text slot rendered on one edge
64+ */
65+struct swc_decor {
66+	uint32_t color;
67+	uint32_t top, right, bottom, left;
68+	struct swc_decor_text title;
69+};
70+
71+/**
72+ * set window decor around the window.
73  *
74  * the dimensions are independent for each edge and extend outward from the
75- * window content geometry. passing all dimensions as 0 disables this decor.
76+ * window content geometry.
77+ *
78+ * swc copies any decor text string needed by the configuration
79+ *
80+ * passing NULL disables window dcor
81  */
82 void
83-swc_window_set_decor(struct swc_window *window, uint32_t color, uint32_t top,
84-                     uint32_t right, uint32_t bottom, uint32_t left);
85+swc_window_set_decor(struct swc_window *window, const struct swc_decor *decor);
86 
87 /**
88  * Begin an interactive move of the specified window.
+2, -3
 1@@ -368,12 +368,11 @@ swc_window_set_border(struct swc_window *window, uint32_t inner_border_color,
 2 }
 3 
 4 EXPORT void
 5-swc_window_set_decor(struct swc_window *window, uint32_t color, uint32_t top,
 6-                     uint32_t right, uint32_t bottom, uint32_t left)
 7+swc_window_set_decor(struct swc_window *window, const struct swc_decor *decor)
 8 {
 9 	struct compositor_view *view = INTERNAL(window)->view;
10 
11-	compositor_view_set_decor(view, color, top, right, bottom, left);
12+	compositor_view_set_decor(view, decor);
13 }
14 
15 EXPORT void