commit 97031e7

wf  ·  2026-03-27 20:22:11 +0000 UTC
parent 4c038d5
Extend IPC parsing and implement basic compositor behavior
10 files changed,  +225, -80
M README
M howl.c
R ipc.c
A util.c
+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
+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+}