commit 2d74b9d

wf  ·  2026-05-01 18:21:53 +0000 UTC
parent 073b60d
Halfway to decorations
4 files changed,  +251, -2
+8, -0
 1@@ -14,9 +14,17 @@ TODO
 2  * Add optional window ID flags to `get_*` commands
 3  * Titlebars
 4 
 5+REQUIREMENTS
 6+------------
 7+
 8+ * A c99 compiler
 9+ * neuswc
10+ * libspng for decorations
11+
12 CREDITS
13 -------
14 
15  * neuswc authors
16+ * libspng authors
17  * uint23, shrub: For developing wsxwm / tohu
18  * JLErvin: For developing berry, which is the inspiration and reference for this project
+15, -0
 1@@ -33,6 +33,21 @@ struct client {
 2 	uint32_t id;
 3 };
 4 
 5+struct decor {
 6+	struct {
 7+		struct swc_decor_part top_left, top, top_right;
 8+		struct swc_decor_part left, right;
 9+		struct swc_decor_part bottom_left, bottom, bottom_right;
10+	} active;
11+	struct {
12+		struct swc_decor_part top_left, top, top_right;
13+		struct swc_decor_part left, right;
14+		struct swc_decor_part bottom_left, bottom, bottom_right;
15+	} inactive;
16+	
17+	/* TODO: what should this contain? */
18+};
19+
20 struct grab {
21 	bool          active, resize;
22 	struct client *client;
+206, -0
  1@@ -0,0 +1,206 @@
  2+#include <stdlib.h>
  3+#include <string.h>
  4+#include <stdint.h>
  5+#include <stdbool.h>
  6+#include <errno.h>
  7+#include <limits.h>
  8+#include <dirent.h>
  9+
 10+#include <wayland-server.h>
 11+#include <swc.h>
 12+#include <spng.h>
 13+
 14+#include "howl.h"
 15+#include "types.h"
 16+
 17+#define COMMA(x) if (x[strlen(x) - 1] == ',') x[strlen(x) - 1] = '\0'
 18+
 19+extern struct wm wm;
 20+extern struct decor decor;
 21+
 22+static bool
 23+decode_part(FILE *fd, struct swc_decor_part *out) {
 24+	spng_ctx *ctx = NULL;
 25+	int ret;
 26+
 27+	uint32_t limit = 256 * 256 * 64;
 28+	spng_set_image_limits(ctx, limit, limit);
 29+	spng_set_png_file(ctx, fd);
 30+
 31+	struct spng_ihdr header;
 32+	ret = spng_get_ihdr(ctx, &header);
 33+
 34+	out->width = header.width;
 35+	out->height = header.height;
 36+	out->stride = header.width * 4;
 37+
 38+	size_t imglen = 0;
 39+	if ((ret = spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &imglen)) != 0) {
 40+		_wrn("couldn't get image size: %s", spng_strerror(ret));
 41+		spng_ctx_free(ctx);
 42+		return false;
 43+	}
 44+
 45+	/* assuming we are writing into a preallocated buffer, which should be the case, but who knows */
 46+	if ((ret = spng_decode_image(ctx, buf, imglen, SPNG_FMT_RGBA8, SPNG_DECODE_TRNS)) != 0) {
 47+		_wrn("couldn't decode image: %s", spng_strerror(ret));
 48+		spng_ctx_free(ctx);
 49+		return false;
 50+	}
 51+
 52+	out->data = buf;
 53+
 54+	spng_ctx_free(ctx);
 55+	return true;
 56+}
 57+
 58+bool
 59+load_decor(char *path) {
 60+	if (path == NULL)
 61+		return false;
 62+
 63+	/**
 64+	 * path is a path to a decoration folder. the structure of the folder is as follows:
 65+	 *
 66+	 *  info:      contains the decoration metadata
 67+	 *  active/:   contains the PNG textures to draw on the focused window's decoration
 68+	 *  inactive/: same as above, but for unfocused windows
 69+	 *
 70+	 * anything else in the directory is ignored, meaning you can put stuff like previews and readmes in it.
 71+	 */
 72+
 73+	DIR *dir;
 74+	char realpath[PATH_MAX];
 75+	if (!(realpath(path, realpath))) {
 76+		_wrn("couldn't get decoration real path: %s", strerror(errno));
 77+		goto error;
 78+	}
 79+	if (!(dir = opendir(realpath))) {
 80+		_wrn("couldn't open decoration at %s", path);
 81+		goto error;
 82+	}
 83+
 84+	struct decor *decor = malloc(sizeof(struct decor *));
 85+	/* TODO: see types.h, need to set defaults */
 86+
 87+	struct dirent *de;
 88+	while ((de = readdir(dir))) {
 89+		if ((strcmp(de->d_name, "info") == 0) {
 90+			FILE *fp;
 91+			char line[256], fullpath[512];
 92+			snprintf(fullpath, 512 * sizeof(char), "%s/%s", realpath, de->d_name);
 93+
 94+			if (!(fp = fopen(fullpath, "r"))) {
 95+				_wrn("couldn't open %s: %s", fullpath, strerror(errno));
 96+				goto error;
 97+			}
 98+			while (fgets(line, 256, fp)) {
 99+				int off = strcspn(line, " =");
100+				char *key = line + off;
101+				COMMA(key);
102+
103+				if ((strncmp(key, "left", 4)) == 0)
104+					edge_left = strtoul(key, NULL, 0);
105+				else if ((strncmp(key, "right", 5)) == 0)
106+					edge_right = strtoul(key, NULL, 0);
107+				else if ((strncmp(key, "top", 3)) == 0)
108+					edge_top = strtoul(key, NULL, 0);
109+				else if ((strncmp(key, "bottom", 6)) == 0)
110+					edge_bottom = strtoul(key, NULL, 0);
111+				/* TODO: need to parse title.* keys */
112+			}
113+		}
114+
115+		else if ((strcmp(de->d_name, "active")) == 0) {
116+			DIR *dir;
117+			char fullpath[512];
118+			snprintf(fullpath, 512 * sizeof(char), "%s/%s", realpath, de->d_name);
119+
120+			if (!(dir = opendir(fullpath))) {
121+				_wrn("could not open %s: %s", fullpath, strerror(errno));
122+				goto error;
123+			}
124+
125+			struct dirent *de;
126+			while ((de = readdir(dir))) {
127+				char full2[512];
128+				snprintf(full2, 512 * sizeof(char), "%s/%s", fullpath, de->d_name);
129+
130+				FILE *fp;
131+				if (!(fp = fopen(full2, "rb"))) {
132+					_wrn("couldn't open %s: %s", full2, strerror(errno));
133+					goto error;
134+				}
135+
136+				if ((strcmp(de->d_name, "top_left.png")) == 0)
137+					decode_part(fp, decor.active.top_left);
138+				else if ((strcmp(de->d_name, "top.png")) == 0)
139+					decode_part(fp, decor.active.top);
140+				else if ((strcmp(de->d_name, "top_right.png")) == 0)
141+					decode_part(fp, decor.active.top_right);
142+				else if ((strcmp(de->d_name, "left.png")) == 0)
143+					decode_part(fp, decor.active.left;
144+				else if ((strcmp(de->d_name, "right.png")) == 0)
145+					decode_part(fp, decor.active.right);
146+				else if ((strcmp(de->d_name, "bottom_left.png")) == 0)
147+					decode_part(fp, decor.active.bottom_left);
148+				else if ((strcmp(de->d_name, "bottom.png")) == 0)
149+					decode_part(fp, decor.active.bottom);
150+				else if ((strcmp(de->d_name, "bottom_right.png")) == 0)
151+					decode_part(fp, decor.active.bottom_right);
152+			}
153+		}
154+
155+		else if ((strcmp(de->d_name, "inactive")) == 0) {
156+			DIR *dir;
157+			char fullpath[512];
158+			snprintf(fullpath, 512 * sizeof(char), "%s/%s", realpath, de->d_name);
159+
160+			if (!(dir = opendir(fullpath))) {
161+				/* TODO: fall back to active tiles instead */
162+				_wrn("could not open %s: %s", fullpath, strerror(errno));
163+				goto error;
164+			}
165+
166+			struct dirent *de;
167+			while ((de = readdir(dir))) {
168+				char full2[512];
169+				snprintf(full2, 512 * sizeof(char), "%s/%s", fullpath, de->d_name);
170+
171+				FILE *fp;
172+				if (!(fp = fopen(full2, "rb"))) {
173+					_wrn("couldn't open %s: %s", full2, strerror(errno));
174+					goto error;
175+				}
176+
177+				if ((strcmp(de->d_name, "top_left.png")) == 0)
178+					decode_part(fp, decor.inactive.top_left);
179+				else if ((strcmp(de->d_name, "top.png")) == 0)
180+					decode_part(fp, decor.inactive.top);
181+				else if ((strcmp(de->d_name, "top_right.png")) == 0)
182+					decode_part(fp, decor.inactive.top_right);
183+				else if ((strcmp(de->d_name, "left.png")) == 0)
184+					decode_part(fp, decor.inactive.left;
185+				else if ((strcmp(de->d_name, "right.png")) == 0)
186+					decode_part(fp, decor.inactive.right);
187+				else if ((strcmp(de->d_name, "bottom_left.png")) == 0)
188+					decode_part(fp, decor.inactive.bottom_left);
189+				else if ((strcmp(de->d_name, "bottom.png")) == 0)
190+					decode_part(fp, decor.inactive.bottom);
191+				else if ((strcmp(de->d_name, "bottom_right.png")) == 0)
192+					decode_part(fp, decor.inactive.bottom_right);
193+			}
194+		}
195+	}
196+
197+	/* TODO: actually utilize parts */
198+	free(decor);
199+	return true;
200+
201+error:
202+	if (decor)
203+		free(decor);
204+	if (dir)
205+		closedir(dir);
206+	return false;
207+}
+22, -2
 1@@ -291,21 +291,41 @@ bind_handler(void *data, uint32_t time, uint32_t value, uint32_t state) {
 2 	}
 3 }
 4 
 5+static void
 6+decorate(struct client *c, bool focus) {
 7+	if (!c)
 8+		return;
 9+
10+	if (c->fullscreen) {
11+		swc_window_set_decor(c->win, NULL);
12+		return;
13+	}
14+}
15+
16+static void
17+undecorate(struct client *c) {
18+	swc_window_set_decor(c->win, NULL);
19+}
20+
21 static void
22 focus(struct client *c) {
23-	if (wm.cur)
24+	if (wm.cur) {
25 		swc_window_set_border(
26 			wm.cur->win,
27 			config.iu_color, config.ib_width,
28 			config.ou_color, config.ob_width
29 		);
30+		decorate(wm.cur, false);
31+	}
32 
33-	if (c)
34+	if (c) {
35 		swc_window_set_border(
36 			c->win,
37 			config.if_color, config.ib_width,
38 			config.of_color, config.ob_width
39 		);
40+		decorate(c, true);
41+	}
42 
43 	swc_window_focus(c ? c->win : NULL);
44 	wm.cur = c;