commit 97031e7
wf
·
2026-03-27 20:22:11 +0000 UTC
parent 4c038d5
Extend IPC parsing and implement basic compositor behavior
M
Makefile
+3,
-3
1@@ -1,14 +1,14 @@
2 CC ?= cc
3 CFLAGS := -std=c99 -Wall -Wimplicit-function-declaration -O3 -pedantic
4-LDFLAGS := `pkg-config --cflags --libs swc` -Iinclude/
5+LDFLAGS := `pkg-config --cflags --libs swc` -Iinclude
6 LDLIBS := `pkg-config --libs swc`
7 PREFIX ?= /usr/local
8
9 howl := howl
10-howl_src := window.c log.c howl.c
11+howl_src := ipc.c log.c util.c howl.c
12
13 howlc := howlc
14-howlc_src := window.c client.c
15+howlc_src := ipc.c util.c client.c
16
17 .PHONY: all install uninstall clean
18
M
README
+4,
-1
1@@ -1,7 +1,8 @@
2 howl
3 ====
4
5-howl is a floating wayland compositor written in neuswc[1] that implements an IPC interface, allowing for scriptability and advanced usage. howl's code is largely based on that of tohu[2]'s.
6+howl is a floating wayland compositor written in neuswc[1] that implements an IPC interface, allowing for scriptability and advanced usage. howl's code is largely based on that of tohu[2]'s and berry[3]'s.
7+to build howl, use any POSIX-compliant make. the repo provides a Makefile compatible with most implementations, including bmake.
8
9 dependencies
10 ------------
11@@ -12,8 +13,10 @@ dependencies
12 todo
13 ----
14
15+ - fix IPC socket deadloop
16 - configuration
17 - document IPC interface
18
19 [1]: https://git.sr.ht/~shrub900/neuswc
20 [2]: https://git.sr.ht/~shrub900/tohu
21+[3]: https://github.com/JLErvin/berry
M
client.c
+19,
-19
1@@ -4,43 +4,42 @@
2 #include <sys/socket.h>
3 #include <sys/un.h>
4
5-#include <swc.h>
6-
7 #include "howl.h"
8 #include "types.h"
9-#include "window.h"
10+#include "ipc.h"
11
12 static void
13 ipc_msg(char **cmd) {
14 struct sockaddr_un addr;
15 char msg[256];
16
17- if (sock == -1) {
18- perror("couldn't connect to IPC socket");
19- return;
20- }
21-
22 memset(&addr, 0, sizeof(struct sockaddr_un));
23 addr.sun_family = AF_LOCAL;
24 strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1);
25
26- if (connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
27+ csock = socket(AF_LOCAL, SOCK_STREAM, 0);
28+ if (csock == -1) {
29+ perror("couldn't open client socket");
30+ return;
31+ }
32+
33+ if (connect(csock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
34 perror("couldn't connect to IPC socket");
35- close(sock);
36+ close(csock);
37 return;
38 }
39
40 snprintf(msg, sizeof(msg), "%s", cmd[0]);
41- for (int i = 0; cmd[i] != NULL; i++) {
42+ for (int i = 1; cmd[i] != NULL; i++) {
43 strcat(msg, " ");
44 strcat(msg, cmd[i]);
45 }
46
47- if (send(sock, msg, strlen(msg), 0) == -1) {
48+ if (send(csock, msg, strlen(msg), 0) == -1) {
49 perror("couldn't send IPC message");
50 }
51
52- close(sock);
53+ close(csock);
54 }
55
56 int
57@@ -49,16 +48,17 @@ main(int argc, char **argv) {
58 fprintf(stderr, "usage: %s cmd [args]\n", argv[0]);
59 return 1;
60 }
61- argc--;
62- argv++;
63+
64+ int u_argc = argc - 2;
65+ char **u_argv = argv + 2;
66
67 for (int i = 0; i < (int)(sizeof commands / sizeof commands[0]); i++) {
68- if (strcmp(commands[i].name, argv[0]) == 0) {
69- if (commands[i].argc != argc) {
70- fprintf(stderr, "not enough arguments for %s (need %d))\n", commands[i].name, commands[i].argc);
71+ if (strcmp(commands[i].name, argv[1]) == 0) {
72+ if (commands[i].argc != u_argc) {
73+ fprintf(stderr, "wrong number of arguments for %s (need %d)\n", commands[i].name, commands[i].argc);
74 return 1;
75 }
76- ipc_msg(argv);
77+ ipc_msg(u_argv);
78 return 0;
79 }
80 }
M
howl.c
+87,
-18
1@@ -4,6 +4,7 @@
2 #include <errno.h>
3 #include <signal.h>
4 #include <unistd.h>
5+#include <getopt.h>
6 #include <sys/socket.h>
7 #include <sys/un.h>
8
9@@ -11,7 +12,7 @@
10 #include <swc.h>
11
12 #include "howl.h"
13-#include "window.h"
14+#include "ipc.h"
15
16 static void new_screen(struct swc_screen *);
17 static void new_window(struct swc_window *);
18@@ -19,20 +20,23 @@ static void on_win_destroy(void *);
19 static void on_win_entered(void *);
20 static void on_scr_destroy(void *);
21 static bool is_ws_client(const struct client *, const struct screen *);
22+static void reload_config(void);
23
24+bool have_config = true;
25 struct wm wm;
26+struct config config;
27
28-struct swc_manager mgr = {
29+static struct swc_manager mgr = {
30 .new_screen = &new_screen,
31 .new_window = &new_window
32 };
33
34-struct swc_window_handler win_handler = {
35+static struct swc_window_handler win_handler = {
36 .destroy = on_win_destroy,
37 .entered = on_win_entered
38 };
39
40-struct swc_screen_handler scr_handler = {
41+static struct swc_screen_handler scr_handler = {
42 .destroy = on_scr_destroy
43 };
44
45@@ -41,6 +45,8 @@ handler(int fd, uint32_t mask, void *data) {
46 UNUSED(mask);
47 UNUSED(data);
48
49+ _inf("handling :333333333333333333");
50+
51 char buf[MAXSIZE];
52 ssize_t res = read(fd, buf, sizeof(buf) - 1);
53
54@@ -50,6 +56,8 @@ handler(int fd, uint32_t mask, void *data) {
55 }
56 buf[res] = '\0';
57
58+ _inf("read bullshit from socket: %s", buf);
59+
60 char *tok;
61 int argc = 0;
62 char **argv;
63@@ -68,6 +76,7 @@ handler(int fd, uint32_t mask, void *data) {
64 if (strcmp(commands[i].name, argv[0]) == 0) {
65 argv++;
66 commands[i].fn(argv);
67+ if (commands[i].config) reload_config();
68 }
69 }
70
71@@ -78,21 +87,24 @@ static void
72 setup_ipc(void) {
73 struct sockaddr_un addr;
74
75- sock = socket(AF_LOCAL, SOCK_STREAM, 0);
76- if (sock == -1)
77- _err(1, "couldn't create IPC socket: %s", strerror(errno));
78+ ssock = socket(AF_LOCAL, SOCK_STREAM, 0);
79+ if (ssock == -1)
80+ _err(1, "couldn't open IPC socket: %s", strerror(errno));
81
82 memset(&addr, 0, sizeof(addr));
83 addr.sun_family = AF_LOCAL;
84 strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1);
85
86- if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
87+ unlink(SOCK_PATH);
88+ if (bind(ssock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
89 _err(1, "couldn't bind IPC socket: %s", strerror(errno));
90
91- if (listen(sock, 5) == -1)
92+ if (listen(ssock, 5) == -1)
93 _err(1, "couldn't listen on IPC socket: %s", strerror(errno));
94
95- wl_event_loop_add_fd(wm.loop, sock, WL_EVENT_READABLE, handler, NULL);
96+ wl_event_loop_add_fd(wm.loop, ssock, WL_EVENT_READABLE, handler, NULL);
97+
98+ _inf("set up IPC socket");
99 }
100
101 static void
102@@ -101,7 +113,36 @@ sig_handler(int s) {
103 if (wm.dpy) {
104 swc_finalize();
105 wl_display_terminate(wm.dpy);
106- close(sock);
107+ close(ssock);
108+ unlink(SOCK_PATH);
109+ }
110+}
111+
112+static void
113+load_config(char *conf_path) {
114+ if (fork() == 0) {
115+ setsid();
116+ execl("/bin/sh", "sh", conf_path, NULL);
117+ }
118+}
119+
120+static void
121+reload_config(void) {
122+ struct client *c;
123+
124+ wl_list_for_each(c, &wm.clients, link) {
125+ if (c == wm.cur)
126+ swc_window_set_border(
127+ c->win,
128+ config.focus_color, config.border_width,
129+ 0, 0
130+ );
131+ else
132+ swc_window_set_border(
133+ c->win,
134+ config.unfocus_color, config.border_width,
135+ 0, 0
136+ );
137 }
138 }
139
140@@ -109,6 +150,8 @@ static void
141 setup(void) {
142 wm.dpy = wl_display_create();
143 if (!wm.dpy) _err(1, "couldn't create Wayland display");
144+ char *conf_path = malloc(MAXSIZE * sizeof(char));
145+ conf_path[0] = '\0';
146
147 wl_list_init(&wm.screens);
148 wl_list_init(&wm.clients);
149@@ -119,12 +162,14 @@ setup(void) {
150 wm.grab.c = NULL;
151 wm.ws = 1;
152
153+ config.focus_color = 0xFFE16C87;
154+ config.unfocus_color = 0xFF444059;
155+ config.border_width = 2;
156+
157 wm.loop = wl_display_get_event_loop(wm.dpy);
158 if (!swc_initialize(wm.dpy, wm.loop, &mgr))
159 _err(1, "couldn't initialize swc");
160
161- /* TODO: Parse config. */
162-
163 const char *sock = wl_display_add_socket_auto(wm.dpy);
164 if (!sock) _err(1, "couldn't add Wayland display socket");
165
166@@ -133,6 +178,24 @@ setup(void) {
167
168 setup_ipc();
169
170+ /* taken from berrywm, thanks! */
171+ if (conf_path[0] == '\0') {
172+ char *xdg_home = getenv("XDG_CONFIG_HOME");
173+ if (xdg_home != NULL) {
174+ snprintf(conf_path, MAXSIZE * sizeof(char), "%s/%s/%s", xdg_home, "howl", "autostart");
175+ have_config = true;
176+ } else {
177+ char *home = getenv("HOME");
178+ if (home == NULL) {
179+ _wrn("could not find autostart file!");
180+ have_config = false;
181+ }
182+ snprintf(conf_path, MAXSIZE * sizeof(char), "%s/%s/%s/%s", home, ".config", "howl", "autostart");
183+ }
184+ }
185+
186+ if (have_config) load_config(conf_path);
187+
188 signal(SIGINT, sig_handler);
189 signal(SIGTERM, sig_handler);
190 signal(SIGQUIT, sig_handler);
191@@ -142,7 +205,7 @@ static void
192 cleanup(void) {
193 swc_finalize();
194 wl_display_destroy(wm.dpy);
195- close(sock);
196+ close(ssock);
197 }
198
199 static void
200@@ -150,14 +213,14 @@ focus(struct client *c) {
201 if (wm.cur)
202 swc_window_set_border(
203 wm.cur->win,
204- 0xFF333333, 2,
205+ config.unfocus_color, config.border_width,
206 0, 0
207 );
208
209 if (c)
210 swc_window_set_border(
211- wm.cur->win,
212- 0xFFFF0000, 2,
213+ c->win,
214+ config.focus_color, config.border_width,
215 0, 0
216 );
217
218@@ -224,7 +287,13 @@ new_window(struct swc_window *win) {
219 wl_list_insert(&wm.clients, &c->link);
220 swc_window_set_handler(win, &win_handler, c);
221 swc_window_set_stacked(win);
222- do_center((char **){0});
223+ {
224+ int32_t cx = 0;
225+ int32_t cy = 0;
226+
227+ if (swc_cursor_position(&cx, &cy))
228+ swc_window_set_position(win, cx / 256, cy / 256);
229+ }
230 swc_window_show(win);
231 focus(c);
232 _inf("new_window=%p", (void *)win);
+11,
-4
1@@ -1,15 +1,22 @@
2 #ifndef HOWL_H
3 #define HOWL_H
4
5-#define UNUSED(x) (void)x
6-#define SOCK_PATH "/tmp/.howl.sock"
7-#define MAXSIZE 256
8+#include <stdint.h>
9+
10+#define UNUSED(x) (void)x
11+#define SOCK_PATH "/tmp/.howl.sock"
12+#define MAXSIZE 256
13
14 /* IPC socket */
15-static int sock;
16+static int ssock;
17+static int csock;
18
19 extern void _inf(const char *, ...);
20 extern void _wrn(const char *, ...);
21 extern void _err(int, const char *, ...);
22
23+extern int str_int(char *);
24+extern uint32_t str_uint(char *);
25+extern uint32_t str_color(char *);
26+
27 #endif /* HOWL_H */
+44,
-0
1@@ -0,0 +1,44 @@
2+#ifndef IPC_H
3+#define IPC_H
4+
5+#include "howl.h"
6+#include "types.h"
7+
8+extern int do_move(char **);
9+extern int do_resize(char **);
10+extern int do_teleport(char **);
11+extern int do_center(char **);
12+extern int do_hide(char **);
13+extern int do_show(char **);
14+extern int do_close(char **);
15+extern int do_title(char **);
16+
17+extern int get_geometry(char **);
18+extern int get_pid(char **);
19+extern int get_title(char **);
20+extern int get_app_id(char **);
21+
22+extern int ipc_focus_color(char **);
23+extern int ipc_unfocus_color(char **);
24+extern int ipc_border_width(char **);
25+
26+static const struct command commands[MAXSIZE] = {
27+ { "move", 2, false, do_move },
28+ { "resize", 2, false, do_resize },
29+ { "teleport", 4, false, do_teleport },
30+ { "center", 0, false, do_center },
31+ { "hide", 0, false, do_hide },
32+ { "show", 0, false, do_show },
33+ { "close", 0, false, do_close },
34+ { "geometry", 0, false, get_geometry },
35+ { "pid", 0, false, get_pid },
36+ { "title", 0, false, get_title },
37+ { "app_id", 0, false, get_app_id },
38+
39+ /* configuration */
40+ { "focus_color", 1, true, ipc_focus_color },
41+ { "unfocus_color", 1, true, ipc_unfocus_color },
42+ { "border_width", 1, true, ipc_border_width }
43+};
44+
45+#endif /* IPC_H */
+7,
-0
1@@ -10,9 +10,16 @@
2 struct command {
3 const char *name;
4 int argc;
5+ bool config;
6 int (*fn)(char **);
7 };
8
9+struct config {
10+ uint32_t focus_color;
11+ uint32_t unfocus_color;
12+ int32_t border_width;
13+};
14+
15 struct client {
16 struct wl_list link;
17 struct swc_window *win;
+0,
-34
1@@ -1,34 +0,0 @@
2-#ifndef WINDOW_H
3-#define WINDOW_H
4-
5-#include "types.h"
6-
7-extern int do_move(char **);
8-extern int do_resize(char **);
9-extern int do_teleport(char **);
10-extern int do_center(char **);
11-extern int do_hide(char **);
12-extern int do_show(char **);
13-extern int do_close(char **);
14-extern int do_title(char **);
15-
16-extern int get_geometry(char **);
17-extern int get_pid(char **);
18-extern int get_title(char **);
19-extern int get_app_id(char **);
20-
21-static const struct command commands[] = {
22- { "move", 2, do_move },
23- { "resize", 2, do_resize },
24- { "teleport", 4, do_teleport },
25- { "center", 0, do_center },
26- { "hide", 0, do_hide },
27- { "show", 0, do_show },
28- { "close", 0, do_close },
29- { "geometry", 0, get_geometry },
30- { "pid", 0, get_pid },
31- { "title", 0, get_title },
32- { "app_id", 0, get_app_id }
33-};
34-
35-#endif /* WINDOW_H */
R window.c =>
ipc.c
+31,
-1
1@@ -1,13 +1,15 @@
2 #include <stdio.h>
3+#include <string.h>
4 #include <stdlib.h>
5
6 #include <swc.h>
7
8 #include "howl.h"
9 #include "types.h"
10-#include "window.h"
11+#include "ipc.h"
12
13 struct wm wm;
14+struct config config;
15
16 int
17 do_move(char **arg) {
18@@ -116,3 +118,31 @@ get_app_id(char **arg) {
19 printf("%s\n", wm.cur->win->app_id);
20 return 0;
21 }
22+
23+/*
24+ * configuration
25+ */
26+
27+int
28+ipc_focus_color(char **arg) {
29+ if (arg[0] == NULL) return 1;
30+
31+ config.focus_color = str_color(arg[0]);
32+ return 0;
33+}
34+
35+int
36+ipc_unfocus_color(char **arg) {
37+ if (arg[0] == NULL) return 1;
38+
39+ config.unfocus_color = str_color(arg[0]);
40+ return 0;
41+}
42+
43+int
44+ipc_border_width(char **arg) {
45+ if (arg[0] == NULL) return 1;
46+
47+ config.border_width = str_int(arg[0]);
48+ return 0;
49+}
A
util.c
+19,
-0
1@@ -0,0 +1,19 @@
2+#include <stdlib.h>
3+#include <stdint.h>
4+#include <string.h>
5+
6+int
7+str_int(char *s) {
8+ return atoi(s);
9+}
10+
11+uint32_t
12+str_uint(char *s) {
13+ return strtoul(s, NULL, 0);
14+}
15+
16+uint32_t
17+str_color(char *s) {
18+ if (s[0] == '#') s++;
19+ return strtoul(s, NULL, 16); /* 16 = base16 */
20+}