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;