commit 0b0d907
wf
·
2026-05-02 08:35:22 +0000 UTC
parent 2d74b9d
Almost working decorations
7 files changed,
+211,
-96
M
Makefile
+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, "" };