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