commit 0b0d907

wf  ·  2026-05-02 08:35:22 +0000 UTC
parent 2d74b9d
Almost working decorations
7 files changed,  +211, -96
+7, -7
 1@@ -1,20 +1,20 @@
 2-CC        ?= cc
 3+CC         ?= cc
 4 PKG_CONFIG ?= pkg-config
 5-CFLAGS    += -std=c99 -Wall -Wextra -Oz -pedantic
 6-LDFLAGS   += -Iinclude
 7-LDLIBS    := `${PKG_CONFIG} --libs swc`
 8-PREFIX    ?= /usr/local
 9+CFLAGS     += -std=c99 -Wall -Wextra -Wshadow -Oz -pedantic `pkg-config --cflags swc spng`
10+LDFLAGS    += -Iinclude
11+LDLIBS     := `${PKG_CONFIG} --libs swc spng`
12+PREFIX     ?= /usr/local
13 
14 ###############
15 
16 howl      := howl
17-howl_src  := src/ipc.c src/log.c src/howl.c
18+howl_src  := src/ipc.c src/log.c src/decor.c src/howl.c
19 
20 howlc     := howlc
21 howlc_src := src/client.c
22 
23 version   := 0.1.0
24-CFLAGS    += -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(version)\"
25+CFLAGS    += -D_XOPEN_SOURCE=700 -DVERSION=\"$(version)\"
26 
27 ###############
28 
+1, -0
1@@ -34,6 +34,7 @@ enum cmd {
2 	cmd_outer_unfocus_color,
3 	cmd_inner_border_width,
4 	cmd_outer_border_width,
5+	cmd_set_decor,
6 	cmd_quit,
7 	cmd_config,
8 	cmd_last
+8, -1
 1@@ -45,7 +45,14 @@ struct decor {
 2 		struct swc_decor_part bottom_left, bottom, bottom_right;
 3 	} inactive;
 4 	
 5-	/* TODO: what should this contain? */
 6+	bool     enabled;
 7+	enum     swc_decor_edge edge;
 8+	enum     swc_decor_align align;
 9+	uint32_t foreground, background;
10+	uint32_t padding;
11+	int32_t  offset_x, offset_y;
12+	uint32_t edge_left, edge_right, edge_top, edge_bottom;
13+	char     *fontname;
14 };
15 
16 struct grab {
+1, -0
1@@ -49,6 +49,7 @@ static const struct command commands[] = {
2 	{ "outer_unfocus_color", cmd_outer_unfocus_color, 1, true  },
3 	{ "inner_border_width",  cmd_inner_border_width,  1, true  },
4 	{ "outer_border_width",  cmd_outer_border_width,  1, true  },
5+	{ "set_decor",           cmd_set_decor,           1, true  },
6 	{ "quit",                cmd_quit,                0, false }
7 };
8 
+136, -84
  1@@ -4,6 +4,7 @@
  2 #include <stdbool.h>
  3 #include <errno.h>
  4 #include <limits.h>
  5+#include <libgen.h>
  6 #include <dirent.h>
  7 
  8 #include <wayland-server.h>
  9@@ -16,23 +17,29 @@
 10 #define COMMA(x) if (x[strlen(x) - 1] == ',') x[strlen(x) - 1] = '\0'
 11 
 12 extern struct wm wm;
 13-extern struct decor decor;
 14+extern struct decor *decor;
 15 
 16 static bool
 17-decode_part(FILE *fd, struct swc_decor_part *out) {
 18-	spng_ctx *ctx = NULL;
 19+decode_part(FILE *fd, struct swc_decor_part out) {
 20+	spng_ctx *ctx = spng_ctx_new(0);
 21 	int ret;
 22 
 23 	uint32_t limit = 256 * 256 * 64;
 24 	spng_set_image_limits(ctx, limit, limit);
 25-	spng_set_png_file(ctx, fd);
 26+	if ((ret = spng_set_png_file(ctx, fd)) != 0) {
 27+		_wrn("couldn't set PNG input: %s", spng_strerror(ret));
 28+		return false;
 29+	}
 30 
 31 	struct spng_ihdr header;
 32-	ret = spng_get_ihdr(ctx, &header);
 33+	if ((ret = spng_get_ihdr(ctx, &header)) != 0) {
 34+		_wrn("couldn't get image header: %s", spng_strerror(ret));
 35+		return false;
 36+	}
 37 
 38-	out->width = header.width;
 39-	out->height = header.height;
 40-	out->stride = header.width * 4;
 41+	out.width = header.width;
 42+	out.height = header.height;
 43+	out.stride = header.width * 4;
 44 
 45 	size_t imglen = 0;
 46 	if ((ret = spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &imglen)) != 0) {
 47@@ -41,14 +48,14 @@ decode_part(FILE *fd, struct swc_decor_part *out) {
 48 		return false;
 49 	}
 50 
 51-	/* assuming we are writing into a preallocated buffer, which should be the case, but who knows */
 52-	if ((ret = spng_decode_image(ctx, buf, imglen, SPNG_FMT_RGBA8, SPNG_DECODE_TRNS)) != 0) {
 53+	/* TODO: this is totally incorrect */
 54+	if ((ret = spng_decode_image(ctx, (void *)out.data, imglen, SPNG_FMT_RGBA8, 0)) != 0) {
 55 		_wrn("couldn't decode image: %s", spng_strerror(ret));
 56 		spng_ctx_free(ctx);
 57 		return false;
 58 	}
 59 
 60-	out->data = buf;
 61+	_inf("DECOR: got some sort of texture into a buffer");
 62 
 63 	spng_ctx_free(ctx);
 64 	return true;
 65@@ -70,136 +77,181 @@ load_decor(char *path) {
 66 	 */
 67 
 68 	DIR *dir;
 69-	char realpath[PATH_MAX];
 70-	if (!(realpath(path, realpath))) {
 71+	char real[PATH_MAX];
 72+	if (!(realpath(path, real))) {
 73 		_wrn("couldn't get decoration real path: %s", strerror(errno));
 74 		goto error;
 75 	}
 76-	if (!(dir = opendir(realpath))) {
 77+	if (!(dir = opendir(real))) {
 78 		_wrn("couldn't open decoration at %s", path);
 79 		goto error;
 80 	}
 81 
 82-	struct decor *decor = malloc(sizeof(struct decor *));
 83-	/* TODO: see types.h, need to set defaults */
 84-
 85 	struct dirent *de;
 86 	while ((de = readdir(dir))) {
 87-		if ((strcmp(de->d_name, "info") == 0) {
 88+		if ((strcmp(de->d_name, "info")) == 0) {
 89+			_inf("DECOR: parsing info");
 90 			FILE *fp;
 91 			char line[256], fullpath[512];
 92-			snprintf(fullpath, 512 * sizeof(char), "%s/%s", realpath, de->d_name);
 93+			snprintf(fullpath, 512 * sizeof(char), "%s/%s", real, de->d_name);
 94 
 95 			if (!(fp = fopen(fullpath, "r"))) {
 96 				_wrn("couldn't open %s: %s", fullpath, strerror(errno));
 97 				goto error;
 98 			}
 99 			while (fgets(line, 256, fp)) {
100-				int off = strcspn(line, " =");
101+				int off = strcspn(line, "=");
102 				char *key = line + off;
103-				COMMA(key);
104-
105-				if ((strncmp(key, "left", 4)) == 0)
106-					edge_left = strtoul(key, NULL, 0);
107-				else if ((strncmp(key, "right", 5)) == 0)
108-					edge_right = strtoul(key, NULL, 0);
109-				else if ((strncmp(key, "top", 3)) == 0)
110-					edge_top = strtoul(key, NULL, 0);
111-				else if ((strncmp(key, "bottom", 6)) == 0)
112-					edge_bottom = strtoul(key, NULL, 0);
113-				/* TODO: need to parse title.* keys */
114+				// COMMA(key);
115+
116+				if ((strncmp(line, "left", 4)) == 0)
117+					decor->edge_left = strtoul(key, NULL, 0);
118+				else if ((strncmp(line, "right", 5)) == 0)
119+					decor->edge_right = strtoul(key, NULL, 0);
120+				else if ((strncmp(line, "top", 3)) == 0)
121+					decor->edge_top = strtoul(key, NULL, 0);
122+				else if ((strncmp(line, "bottom", 6)) == 0)
123+					decor->edge_bottom = strtoul(key, NULL, 0);
124+				else if ((strncmp(line, "title.enabled", 12)) == 0)
125+					decor->enabled = (strcmp(key, "true") == 0);
126+				else if ((strncmp(line, "title.edge", 10)) == 0) {
127+					if ((strcmp(key, "left")) == 0)
128+						decor->edge = SWC_DECOR_EDGE_LEFT;
129+					else if ((strcmp(key, "right")) == 0)
130+						decor->edge = SWC_DECOR_EDGE_RIGHT;
131+					else if ((strcmp(key, "top")) == 0)
132+						decor->edge = SWC_DECOR_EDGE_TOP;
133+					else if ((strcmp(key, "bottom")) == 0)
134+						decor->edge = SWC_DECOR_EDGE_BOTTOM;
135+				}
136+				else if ((strncmp(line, "title.align", 11)) == 0) {
137+					if ((strcmp(key, "start")) == 0)
138+						decor->align = SWC_DECOR_ALIGN_START;
139+					else if ((strcmp(key, "center")) == 0)
140+						decor->align = SWC_DECOR_ALIGN_CENTER;
141+					else if ((strcmp(key, "end")) == 0)
142+						decor->align = SWC_DECOR_ALIGN_END;
143+				}
144+				else if ((strncmp(line, "title.foreground", 16)) == 0)
145+					decor->foreground = strtoul(key, NULL, 16);
146+				else if ((strncmp(line, "title.background", 16)) == 0)
147+					decor->background = strtoul(key, NULL, 16);
148+				else if ((strncmp(line, "title.offset_x", 14)) == 0)
149+					decor->offset_x = atoi(key);
150+				else if ((strncmp(line, "title.offset_y", 14)) == 0)
151+					decor->offset_y = atoi(key);
152+				else if ((strncmp(line, "title.fontname", 14)) == 0)
153+					decor->fontname = key;
154 			}
155+			fclose(fp);
156 		}
157 
158 		else if ((strcmp(de->d_name, "active")) == 0) {
159-			DIR *dir;
160+			_inf("DECOR: active textures");
161+			DIR *active;
162 			char fullpath[512];
163-			snprintf(fullpath, 512 * sizeof(char), "%s/%s", realpath, de->d_name);
164+			snprintf(fullpath, 512 * sizeof(char), "%s/%s", real, de->d_name);
165 
166-			if (!(dir = opendir(fullpath))) {
167-				_wrn("could not open %s: %s", fullpath, strerror(errno));
168+			if (!(active = opendir(fullpath))) {
169+				_wrn("couldn't opendir %s: %s", fullpath, strerror(errno));
170 				goto error;
171 			}
172 
173-			struct dirent *de;
174-			while ((de = readdir(dir))) {
175+			struct dirent *de2;
176+			while ((de2 = readdir(active))) {
177 				char full2[512];
178-				snprintf(full2, 512 * sizeof(char), "%s/%s", fullpath, de->d_name);
179+				snprintf(full2, 512 * sizeof(char), "%s/%s", fullpath, de2->d_name);
180 
181 				FILE *fp;
182 				if (!(fp = fopen(full2, "rb"))) {
183 					_wrn("couldn't open %s: %s", full2, strerror(errno));
184+					closedir(active);
185 					goto error;
186 				}
187+				fseek(fp, 0, SEEK_SET);
188+
189+				if ((strcmp(de2->d_name, "top_left.png")) == 0)
190+					decode_part(fp, decor->active.top_left);
191+				else if ((strcmp(de2->d_name, "top.png")) == 0)
192+					decode_part(fp, decor->active.top);
193+				else if ((strcmp(de2->d_name, "top_right.png")) == 0)
194+					decode_part(fp, decor->active.top_right);
195+				else if ((strcmp(de2->d_name, "left.png")) == 0)
196+					decode_part(fp, decor->active.left);
197+				else if ((strcmp(de2->d_name, "right.png")) == 0)
198+					decode_part(fp, decor->active.right);
199+				else if ((strcmp(de2->d_name, "bottom_left.png")) == 0)
200+					decode_part(fp, decor->active.bottom_left);
201+				else if ((strcmp(de2->d_name, "bottom.png")) == 0)
202+					decode_part(fp, decor->active.bottom);
203+				else if ((strcmp(de2->d_name, "bottom_right.png")) == 0)
204+					decode_part(fp, decor->active.bottom_right);
205 
206-				if ((strcmp(de->d_name, "top_left.png")) == 0)
207-					decode_part(fp, decor.active.top_left);
208-				else if ((strcmp(de->d_name, "top.png")) == 0)
209-					decode_part(fp, decor.active.top);
210-				else if ((strcmp(de->d_name, "top_right.png")) == 0)
211-					decode_part(fp, decor.active.top_right);
212-				else if ((strcmp(de->d_name, "left.png")) == 0)
213-					decode_part(fp, decor.active.left;
214-				else if ((strcmp(de->d_name, "right.png")) == 0)
215-					decode_part(fp, decor.active.right);
216-				else if ((strcmp(de->d_name, "bottom_left.png")) == 0)
217-					decode_part(fp, decor.active.bottom_left);
218-				else if ((strcmp(de->d_name, "bottom.png")) == 0)
219-					decode_part(fp, decor.active.bottom);
220-				else if ((strcmp(de->d_name, "bottom_right.png")) == 0)
221-					decode_part(fp, decor.active.bottom_right);
222+				fclose(fp);
223 			}
224+
225+			closedir(active);
226 		}
227 
228 		else if ((strcmp(de->d_name, "inactive")) == 0) {
229-			DIR *dir;
230+			_inf("DECOR: inactive textures");
231+			DIR *inactive;
232 			char fullpath[512];
233-			snprintf(fullpath, 512 * sizeof(char), "%s/%s", realpath, de->d_name);
234+			snprintf(fullpath, 512 * sizeof(char), "%s/%s", real, de->d_name);
235 
236-			if (!(dir = opendir(fullpath))) {
237-				/* TODO: fall back to active tiles instead */
238-				_wrn("could not open %s: %s", fullpath, strerror(errno));
239-				goto error;
240+			if (!(inactive = opendir(fullpath))) {
241+				/* fallback to active textures instead */
242+				decor->inactive.top_left = decor->active.top_left;
243+				decor->inactive.top = decor->active.top;
244+				decor->inactive.top_right = decor->active.top_right;
245+				decor->inactive.left = decor->active.left;
246+				decor->inactive.right = decor->active.right;
247+				decor->inactive.bottom_left = decor->active.bottom_left;
248+				decor->inactive.bottom = decor->active.bottom;
249+				decor->inactive.bottom_right = decor->active.bottom_right;
250+				closedir(inactive);
251+				break;
252 			}
253 
254-			struct dirent *de;
255-			while ((de = readdir(dir))) {
256+			struct dirent *de2;
257+			while ((de2 = readdir(inactive))) {
258 				char full2[512];
259-				snprintf(full2, 512 * sizeof(char), "%s/%s", fullpath, de->d_name);
260+				snprintf(full2, 512 * sizeof(char), "%s/%s", fullpath, de2->d_name);
261 
262 				FILE *fp;
263 				if (!(fp = fopen(full2, "rb"))) {
264-					_wrn("couldn't open %s: %s", full2, strerror(errno));
265+					_wrn("hi im here!!! couldn't open %s: %s", full2, strerror(errno));
266 					goto error;
267 				}
268+				fseek(fp, 0, SEEK_SET);
269+
270+				if ((strcmp(de2->d_name, "top_left.png")) == 0)
271+					decode_part(fp, decor->inactive.top_left);
272+				else if ((strcmp(de2->d_name, "top.png")) == 0)
273+					decode_part(fp, decor->inactive.top);
274+				else if ((strcmp(de2->d_name, "top_right.png")) == 0)
275+					decode_part(fp, decor->inactive.top_right);
276+				else if ((strcmp(de2->d_name, "left.png")) == 0)
277+					decode_part(fp, decor->inactive.left);
278+				else if ((strcmp(de2->d_name, "right.png")) == 0)
279+					decode_part(fp, decor->inactive.right);
280+				else if ((strcmp(de2->d_name, "bottom_left.png")) == 0)
281+					decode_part(fp, decor->inactive.bottom_left);
282+				else if ((strcmp(de2->d_name, "bottom.png")) == 0)
283+					decode_part(fp, decor->inactive.bottom);
284+				else if ((strcmp(de2->d_name, "bottom_right.png")) == 0)
285+					decode_part(fp, decor->inactive.bottom_right);
286 
287-				if ((strcmp(de->d_name, "top_left.png")) == 0)
288-					decode_part(fp, decor.inactive.top_left);
289-				else if ((strcmp(de->d_name, "top.png")) == 0)
290-					decode_part(fp, decor.inactive.top);
291-				else if ((strcmp(de->d_name, "top_right.png")) == 0)
292-					decode_part(fp, decor.inactive.top_right);
293-				else if ((strcmp(de->d_name, "left.png")) == 0)
294-					decode_part(fp, decor.inactive.left;
295-				else if ((strcmp(de->d_name, "right.png")) == 0)
296-					decode_part(fp, decor.inactive.right);
297-				else if ((strcmp(de->d_name, "bottom_left.png")) == 0)
298-					decode_part(fp, decor.inactive.bottom_left);
299-				else if ((strcmp(de->d_name, "bottom.png")) == 0)
300-					decode_part(fp, decor.inactive.bottom);
301-				else if ((strcmp(de->d_name, "bottom_right.png")) == 0)
302-					decode_part(fp, decor.inactive.bottom_right);
303+				fclose(fp);
304 			}
305+
306+			closedir(inactive);
307 		}
308 	}
309 
310-	/* TODO: actually utilize parts */
311-	free(decor);
312 	return true;
313 
314 error:
315-	if (decor)
316-		free(decor);
317 	if (dir)
318 		closedir(dir);
319 	return false;
+48, -2
 1@@ -53,6 +53,7 @@ extern status ipc_config(char **);
 2 
 3 struct wm wm;
 4 struct config config;
 5+struct decor *decor;
 6 
 7 static struct swc_manager mgr = {
 8 	.new_screen = &new_screen,
 9@@ -243,6 +244,16 @@ setup(void) {
10 	config.ib_width = 3;
11 	config.ob_width = 3;
12 
13+	decor = calloc(1, sizeof(struct decor));
14+	decor->enabled    = true;
15+	decor->edge       = SWC_DECOR_EDGE_TOP;
16+	decor->align      = SWC_DECOR_ALIGN_START;
17+	decor->foreground = 0xFFFFFFFF;
18+	decor->background = 0xFF000000;
19+	decor->offset_x   = 0;
20+	decor->offset_y   = 0;
21+	decor->fontname   = "sans-serif:size=10";
22+
23 	wm.loop = wl_display_get_event_loop(wm.dpy);
24 	if (!swc_initialize(wm.dpy, wm.loop, &mgr))
25 		_err(1, "couldn't initialize swc");
26@@ -262,6 +273,7 @@ setup(void) {
27 
28 void
29 cleanup(void) {
30+	free(decor);
31 	wl_display_terminate(wm.dpy);
32 	close(sfd);
33 	unlink(SOCK_PATH);
34@@ -291,7 +303,7 @@ bind_handler(void *data, uint32_t time, uint32_t value, uint32_t state) {
35 	}
36 }
37 
38-static void
39+void
40 decorate(struct client *c, bool focus) {
41 	if (!c)
42 		return;
43@@ -300,9 +312,43 @@ decorate(struct client *c, bool focus) {
44 		swc_window_set_decor(c->win, NULL);
45 		return;
46 	}
47+
48+	const struct swc_decor_parts parts = {
49+		.top_left = focus ? decor->active.top_left : decor->inactive.top_left,
50+		.top = focus ? decor->active.top : decor->inactive.top,
51+		.top_right = focus ? decor->active.top_right : decor->inactive.top_right,
52+		.left = focus ? decor->active.left : decor->inactive.left,
53+		.right = focus ? decor->active.right : decor->inactive.right,
54+		.bottom_left = focus ? decor->active.bottom_left : decor->inactive.bottom_left,
55+		.bottom = focus ? decor->active.bottom : decor->inactive.bottom,
56+		.bottom_right = focus ? decor->active.bottom_right : decor->inactive.bottom_right
57+	};
58+
59+	struct swc_decor new = {
60+		.color = decor->background,
61+		.left = decor->edge_left,
62+		.right = decor->edge_right,
63+		.top = decor->edge_top,
64+		.bottom = decor->edge_bottom,
65+		.parts = &parts,
66+		.title = {
67+			.enabled = decor->enabled,
68+			.edge = decor->edge,
69+			.align = decor->align,
70+			.string = c->win->title, /* TODO: title formatting */
71+			.color = decor->foreground,
72+			.padding = decor->padding,
73+			.offset_x = decor->offset_x,
74+			.offset_y = decor->offset_y,
75+			.font = (const char *)decor->fontname
76+		}
77+	};
78+
79+	swc_window_set_decor(c->win, &new);
80+	_inf("decorated c=%p focus=%d", (void*)c, focus);
81 }
82 
83-static void
84+void
85 undecorate(struct client *c) {
86 	swc_window_set_decor(c->win, NULL);
87 }
+10, -2
 1@@ -67,6 +67,8 @@ fn_bind(char *s) {
 2 }
 3 
 4 extern void bind_handler(void *, uint32_t, uint32_t, uint32_t);
 5+extern void decorate(struct client *, bool);
 6+extern bool load_decor(char *);
 7 extern void ws_go_to(int8_t);
 8 extern void ws_move_to(int8_t);
 9 extern void focus_prev(void);
10@@ -482,24 +484,30 @@ ipc_config(char **arg) {
11 		case cmd_outer_border_width:
12 			config.ob_width = fn_int(arg[2]);
13 			break;
14+		case cmd_set_decor:
15+			load_decor(arg[2]);
16+			break;
17 		default:
18 			break;
19 	}
20 
21 	struct client *c;
22 	wl_list_for_each(c, &wm.clients, link) {
23-		if (c == wm.cur)
24+		if (c == wm.cur) {
25 			swc_window_set_border(
26 				c->win,
27 				config.if_color, config.ib_width,
28 				config.of_color, config.ob_width
29 			);
30-		else
31+			decorate(c, true);
32+		} else {
33 			swc_window_set_border(
34 				c->win,
35 				config.iu_color, config.ib_width,
36 				config.ou_color, config.ob_width
37 			);
38+			decorate(c, false);
39+		}
40 	}
41 
42 	return (status){ true, "" };