commit 62af971

wf  ·  2026-04-13 19:41:28 +0000 UTC
parent 46b63a8
Add ability to fetch (basic) info from compositor
5 files changed,  +240, -119
M howl.c
M ipc.c
+1, -1
1@@ -10,7 +10,7 @@ See the `howl` and `howlc` manual pages for more information on usage.
2 TODO
3 ----
4 
5- * Expose compositor internals through the client
6+ * Update documentation
7  * Eyecandy: titlebars and double borders
8 
9 CREDITS
+38, -7
 1@@ -45,13 +45,13 @@ 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-	{ "quit",            cmd_quit,           0, false }
 6+	{ "quit",                cmd_quit,                0, false }
 7 };
 8 
 9-static void
10+static int
11 ipc_msg(const struct command *cmd, int argc, char **argv) {
12 	struct sockaddr_un addr;
13-	char msg[256] = "", *data[8];
14+	char msg[MAXSIZE] = "", *data[8];
15 
16 	memset(&addr, 0, sizeof(struct sockaddr_un));
17 	addr.sun_family = AF_UNIX;
18@@ -60,13 +60,13 @@ ipc_msg(const struct command *cmd, int argc, char **argv) {
19 	cfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
20 	if (cfd == -1) {
21 		perror("couldn't open IPC socket");
22-		return;
23+		return 1;
24 	}
25 
26 	if (connect(cfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
27 		perror("couldn't connect to IPC socket");
28 		close(cfd);
29-		return;
30+		return 1;
31 	}
32 
33 	if (cmd->config) {
34@@ -95,9 +95,40 @@ ipc_msg(const struct command *cmd, int argc, char **argv) {
35 
36 	if (send(cfd, msg, strlen(msg), 0) == -1) {
37 		perror("couldn't send IPC message");
38+		return 1;
39+	}
40+
41+	char resp[MAXSIZE];
42+	ssize_t n;
43+
44+	if ((n = read(cfd, resp, sizeof(resp) - 1)) < 0) {
45+		/* read error */
46+		perror("couldn't read from IPC socket");
47+		return 1;
48+	}
49+
50+	if (n == 0) {
51+		/* EOF */
52+		perror("unexpected EOF on IPC socket");
53+		return 1;
54+	}
55+
56+	if (n > 0) {
57+		char *msg;
58+		long ret = strtol(resp, &msg, 10);
59+		if (msg == resp) {
60+			ret = 0;
61+		}
62+
63+		while (*msg == ' ') msg++;
64+
65+		if (msg != NULL)
66+			printf("%s\n", msg);
67+		return (int)ret;
68 	}
69 
70 	close(cfd);
71+	return 1; /* NOTREACHED */
72 }
73 
74 int
75@@ -113,8 +144,8 @@ main(int argc, char **argv) {
76 				fprintf(stderr, "Wrong number of arguments for %s (need %d)\n", commands[i].name, commands[i].argc);
77 				return 1;
78 			}
79-			ipc_msg(&commands[i], argc, argv);
80-			return 0;
81+			int ret = ipc_msg(&commands[i], argc, argv);
82+			return ret;
83 		}
84 	}
85 	
M howl.c
+45, -37
  1@@ -22,29 +22,29 @@ static void on_win_entered(void *);
  2 static void on_scr_destroy(void *);
  3 static bool is_ws_client(const struct client *, const struct screen *);
  4 
  5-extern void ipc_move(char **);
  6-extern void ipc_move_absolute(char **);
  7-extern void ipc_resize(char **);
  8-extern void ipc_resize_absolute(char **);
  9-extern void ipc_teleport(char **);
 10-extern void ipc_center(char **);
 11-extern void ipc_fullscreen(char **);
 12-extern void ipc_hide(char **);
 13-extern void ipc_show(char **);
 14-extern void ipc_lower(char **);
 15-extern void ipc_raise(char **);
 16-extern void ipc_focus_prev(char **);
 17-extern void ipc_focus_next(char **);
 18-extern void ipc_close(char **);
 19-extern void ipc_workspace(char **);
 20-extern void ipc_move_workspace(char **);
 21-extern void ipc_get_geometry(char **);
 22-extern void ipc_get_pid(char **);
 23-extern void ipc_get_title(char **);
 24-extern void ipc_get_app_id(char **);
 25-extern void ipc_bind(char **);
 26-extern void ipc_quit(char **);
 27-extern void ipc_config(char **);
 28+extern status ipc_move(char **);
 29+extern status ipc_move_absolute(char **);
 30+extern status ipc_resize(char **);
 31+extern status ipc_resize_absolute(char **);
 32+extern status ipc_teleport(char **);
 33+extern status ipc_center(char **);
 34+extern status ipc_fullscreen(char **);
 35+extern status ipc_hide(char **);
 36+extern status ipc_show(char **);
 37+extern status ipc_lower(char **);
 38+extern status ipc_raise(char **);
 39+extern status ipc_focus_prev(char **);
 40+extern status ipc_focus_next(char **);
 41+extern status ipc_close(char **);
 42+extern status ipc_workspace(char **);
 43+extern status ipc_move_workspace(char **);
 44+extern status ipc_get_geometry(char **);
 45+extern status ipc_get_pid(char **);
 46+extern status ipc_get_title(char **);
 47+extern status ipc_get_app_id(char **);
 48+extern status ipc_bind(char **);
 49+extern status ipc_quit(char **);
 50+extern status ipc_config(char **);
 51 
 52 struct wm wm;
 53 struct config config;
 54@@ -63,7 +63,7 @@ static struct swc_screen_handler scr_handler = {
 55 	.destroy = on_scr_destroy
 56 };
 57 
 58-typedef void (*cmd_handler_t)(char **);
 59+typedef status (*cmd_handler_t)(char **);
 60 static const cmd_handler_t cmd_handler [cmd_last] = {
 61 	[cmd_move]            = ipc_move,
 62 	[cmd_move_absolute]   = ipc_move,
 63@@ -111,7 +111,7 @@ ipc_handler(int fd, uint32_t mask, void *data) {
 64 			int afd = accept(sfd, NULL, NULL);
 65 			if (afd == -1) {
 66 				_wrn("couldn't accept incoming connection on IPC socket: %s", strerror(errno));
 67-				return 0;
 68+				goto ipcerr;
 69 			}
 70 
 71 			char buf[MAXSIZE];
 72@@ -120,13 +120,13 @@ ipc_handler(int fd, uint32_t mask, void *data) {
 73 			if ((n = read(afd, buf, sizeof(buf) - 1)) < 0) {
 74 				/* read error */
 75 				_wrn("couldn't read from IPC socket: %s", strerror(errno));
 76-				return 0;
 77+				goto ipcerr;
 78 			}
 79 
 80 			if (n == 0) {
 81 				/* EOF */
 82 				_wrn("unexpected EOF on IPC socket: %s", strerror(errno));
 83-				return 0;
 84+				goto ipcerr;
 85 			}
 86 
 87 			if (n > 0) {
 88@@ -144,15 +144,29 @@ ipc_handler(int fd, uint32_t mask, void *data) {
 89 				}
 90 				argv[argc] = NULL;
 91 
 92-				if (argc == 0) return 0;
 93+				if (argc == 0)
 94+					goto ipcerr;
 95 
 96 				int cmd = atoi(argv[0]);
 97-				void (*fn)(char **) = cmd_handler[cmd];
 98+				if (cmd < 0 || cmd > cmd_last) {
 99+					_wrn("IPC command index out of bounds");
100+					goto ipcerr;
101+				}
102+
103 				/* TODO: this is hard to debug */
104-				if (!fn) _wrn("no such command #%d", cmd);
105+				if (!cmd_handler[cmd]) {
106+					_wrn("no such command #%d", cmd);
107+					goto ipcerr;
108+				}
109 	
110-				cmd_handler[cmd](argv);
111+				status s = cmd_handler[cmd](argv);
112+				char answer[MAXSIZE];
113+				snprintf(answer, MAXSIZE * sizeof(char), "%d %s", s.ok, s.msg);
114+
115+				if (send(afd, answer, strlen(answer), 0) == -1)
116+					_wrn("couldn't send answer to client");
117 			}
118+ipcerr:
119 			close(afd);
120 			return 0;
121 		}
122@@ -359,8 +373,6 @@ focus_prev(void) {
123 	if (wl_list_empty(&wm.clients))
124 		return;
125 
126-	_inf("focus_prev");
127-
128 	struct client *c = NULL;
129 	if (!wm.cur || !is_ws_client(wm.cur, wm.scr)) {
130 		c = first_client(wm.scr);
131@@ -389,7 +401,6 @@ focus_prev(void) {
132 	c = first_client(wm.scr);
133 	if (!c) c = first_client(NULL);
134 	focus(c);
135-	_inf("DONE focus_prev");
136 }
137 
138 void
139@@ -397,8 +408,6 @@ focus_next(void) {
140 	if (wl_list_empty(&wm.clients))
141 		return;
142 
143-	_inf("focus_next");
144-
145 	struct client *c = NULL;
146 	if (!wm.cur || !is_ws_client(wm.cur, wm.scr)) {
147 		c = first_client(wm.scr);
148@@ -427,7 +436,6 @@ focus_next(void) {
149 	c = first_client(wm.scr);
150 	if (!c) c = first_client(NULL);
151 	focus(c);
152-	_inf("DONE focus_next");
153 }
154 
155 static void
+31, -23
 1@@ -7,14 +7,16 @@
 2 #include <wayland-server.h>
 3 #include <swc.h>
 4 
 5+#include "howl.h"
 6+
 7 struct config {
 8-	uint32_t  mod;
 9-	uint32_t  if_color;
10-	uint32_t  iu_color;
11-	uint32_t  of_color;
12-	uint32_t  ou_color;
13-	int32_t   ib_width;
14-	int32_t   ob_width;
15+	uint32_t mod;
16+	uint32_t if_color;
17+	uint32_t iu_color;
18+	uint32_t of_color;
19+	uint32_t ou_color;
20+	int32_t  ib_width;
21+	int32_t  ob_width;
22 };
23 
24 struct client {
25@@ -30,29 +32,35 @@ struct client {
26 	uint8_t           ws;
27 };
28 
29-struct screen {
30-	struct wl_list     link;
31-	struct swc_screen  *scr;
32-	int32_t            x, y;
33-	uint32_t           width, height;
34+struct grab {
35+	bool          active, resize;
36+	struct client *client;
37 };
38 
39-struct grab {
40-	bool           active, resize;
41-	struct client  *client;
42+struct status {
43+	bool ok;
44+	char msg[MAXSIZE];
45+};
46+typedef struct status status;
47+
48+struct screen {
49+	struct wl_list    link;
50+	struct swc_screen *scr;
51+	int32_t           x, y;
52+	uint32_t          width, height;
53 };
54 
55 struct wm {
56-	struct wl_display     *dpy;
57-	struct wl_event_loop  *loop;
58+	struct wl_display    *dpy;
59+	struct wl_event_loop *loop;
60 
61-	struct wl_list        clients;
62-	struct wl_list        screens;
63+	struct wl_list       clients;
64+	struct wl_list       screens;
65 
66-	struct screen         *scr;
67-	struct client         *cur;
68-	struct grab           grab;
69-	uint8_t               ws;
70+	struct screen        *scr;
71+	struct client        *cur;
72+	struct grab          grab;
73+	uint8_t              ws;
74 };
75 
76 #endif /* TYPES_H */
M ipc.c
+125, -51
  1@@ -68,10 +68,10 @@ extern void cleanup(void);
  2 
  3 /*********/
  4 
  5-void
  6+status
  7 ipc_move(char **arg) {
  8 	if (arg[1] == NULL || arg[2] == NULL || !wm.cur)
  9-		return;
 10+		return (status){ false, "" };
 11 
 12 	struct swc_rectangle geom;
 13 	if (swc_window_get_geometry(wm.cur->win, &geom)) {
 14@@ -82,19 +82,24 @@ ipc_move(char **arg) {
 15 	}
 16 
 17 	swc_window_set_position(wm.cur->win, wm.cur->x + atoi(arg[1]), wm.cur->y + atoi(arg[2]));
 18+
 19+	return (status){ true, "" };
 20 }
 21 
 22-void
 23+status
 24 ipc_move_absolute(char **arg) {
 25 	if (arg[1] == NULL || arg[2] == NULL || !wm.cur)
 26-		return;
 27+		return (status){ false, "" };
 28 
 29 	swc_window_set_position(wm.cur->win, atoi(arg[1]), atoi(arg[2]));
 30+
 31+	return (status){ true, "" };
 32 }
 33 
 34-void
 35+status
 36 ipc_resize(char **arg) {
 37-	if (arg[1] == NULL || arg[2] == NULL || !wm.cur) return;
 38+	if (arg[1] == NULL || arg[2] == NULL || !wm.cur)
 39+		return (status){ false, "" };
 40 
 41 	struct swc_rectangle geom;
 42 	if (swc_window_get_geometry(wm.cur->win, &geom)) {
 43@@ -105,21 +110,25 @@ ipc_resize(char **arg) {
 44 	}
 45 
 46 	swc_window_set_size(wm.cur->win, wm.cur->width + strtoul(arg[1], NULL, 0), wm.cur->height + strtoul(arg[2], NULL, 0));
 47+
 48+	return (status){ true, "" };
 49 }
 50 
 51-void
 52+status
 53 ipc_resize_absolute(char **arg) {
 54 	if (arg[1] == NULL || arg[2] == NULL || !wm.cur)
 55-		return;
 56+		return (status){ false, "" };
 57 
 58 	swc_window_set_size(wm.cur->win, strtoul(arg[1], NULL, 0), strtoul(arg[2], NULL, 0));
 59+
 60+	return (status){ true, "" };
 61 }
 62 
 63-void
 64+status
 65 ipc_teleport(char **arg) {
 66 	if (arg[1] == NULL || arg[2] == NULL ||
 67 	    arg[3] == NULL || arg[4] == NULL || !wm.cur)
 68-		return;
 69+		return (status){ false, "" };
 70 
 71 	struct swc_rectangle rect = {
 72 		.x      = atoi(arg[1]),
 73@@ -129,12 +138,15 @@ ipc_teleport(char **arg) {
 74 	};
 75 
 76 	swc_window_set_geometry(wm.cur->win, &rect);
 77+
 78+	return (status){ true, "" };
 79 }
 80 
 81-void
 82+status
 83 ipc_center(char **arg) {
 84 	UNUSED(arg);
 85-	if (!wm.cur) return;
 86+	if (!wm.cur)
 87+		return (status){ false, "" };
 88 
 89 	struct swc_rectangle geom;
 90 	if (swc_window_get_geometry(wm.cur->win, &geom)) {
 91@@ -149,12 +161,15 @@ ipc_center(char **arg) {
 92 		wm.scr->width / 2 - wm.cur->width / 2,
 93 		wm.scr->height / 2 - wm.cur->height / 2
 94 	);
 95+
 96+	return (status){ true, "" };
 97 }
 98 
 99-void
100+status
101 ipc_fullscreen(char **arg) {
102 	UNUSED(arg);
103-	if (!wm.cur) return;
104+	if (!wm.cur)
105+		return (status){ false, "" };
106 
107 	struct swc_rectangle geom;
108 
109@@ -169,11 +184,11 @@ ipc_fullscreen(char **arg) {
110 			geom.height = wm.cur->height;
111 			swc_window_set_geometry(wm.cur->win, &geom);
112 		}
113-		return;
114+		return (status){ true, "" };
115 	}
116 
117 	if (!wm.cur->scr || !wm.cur->scr->scr)
118-		return;
119+		return (status){ false, "" };
120 
121 	if (swc_window_get_geometry(wm.cur->win, &geom)) {
122 		wm.cur->x = geom.x;
123@@ -184,128 +199,180 @@ ipc_fullscreen(char **arg) {
124 
125 	wm.cur->fullscreen = true;
126 	swc_window_set_fullscreen(wm.cur->win, wm.scr->scr);
127+
128+	return (status){ true, "" };
129 }
130 
131-void
132+status
133 ipc_hide(char **arg) {
134 	UNUSED(arg);
135-	if (!wm.cur) return;
136+	if (!wm.cur)
137+		return (status){ false, "" };
138 
139 	swc_window_hide(wm.cur->win);
140+
141+	return (status){ true, "" };
142 }
143 
144-void
145+status
146 ipc_show(char **arg) {
147 	UNUSED(arg);
148-	if (!wm.cur) return;
149+	if (!wm.cur)
150+		return (status){ false, "" };
151 
152 	swc_window_show(wm.cur->win);
153+
154+	return (status){ true, "" };
155 }
156 
157 /* I really doubt anyone even has 99 clients opened at the same time */
158-void
159+status
160 ipc_lower(char **arg) {
161 	UNUSED(arg);
162-	if (!wm.cur) return;
163+	if (!wm.cur)
164+		return (status){ false, "" };
165 
166 	swc_window_stack(wm.cur->win, 99);
167+
168+	return (status){ true, "" };
169 }
170 
171-void
172+status
173 ipc_raise(char **arg) {
174 	UNUSED(arg);
175-	if (!wm.cur) return;
176+	if (!wm.cur)
177+		return (status){ false, "" };
178 
179 	swc_window_stack(wm.cur->win, -99);
180+
181+	return (status){ true, "" };
182 }
183 
184-void
185+status
186 ipc_focus_prev(char **arg) {
187 	UNUSED(arg);
188 
189 	focus_prev();
190+
191+	return (status){ true, "" };
192 }
193 
194-void
195+status
196 ipc_focus_next(char **arg) {
197 	UNUSED(arg);
198 
199 	focus_next();
200+
201+	return (status){ true, "" };
202 }
203 
204-void
205+status
206 ipc_close(char **arg) {
207 	UNUSED(arg);
208-	if (!wm.cur) return;
209+	if (!wm.cur)
210+		return (status){ false, "" };
211 
212 	swc_window_close(wm.cur->win);
213+
214+	return (status){ true, "" };
215 }
216 
217-void
218+status
219 ipc_workspace(char **arg) {
220-	if (arg[1] == NULL) return;
221+	if (arg[1] == NULL)
222+		return (status){ false, "" };
223 
224 	ws_go_to(atoi(arg[1]));
225+
226+	return (status){ true, "" };
227 }
228 
229-void
230+status
231 ipc_move_workspace(char **arg) {
232-	if (arg[1] == NULL || !wm.cur) return;
233+	if (arg[1] == NULL || !wm.cur)
234+		return (status){ false, "" };
235 
236 	ws_move_to(atoi(arg[1]));
237+
238+	return (status){ true, "" };
239 }
240 
241 /*********/
242 
243-void
244+status
245 ipc_get_geometry(char **arg) {
246+	status s = {0};
247+
248 	UNUSED(arg);
249-	if (!wm.cur) return;
250+	if (!wm.cur)
251+		return (status){ false, "" };
252 
253 	struct swc_rectangle geom;
254 	if (swc_window_get_geometry(wm.cur->win, &geom)) {
255-		printf("%d %d %u %u\n", geom.x, geom.y, geom.width, geom.height);  
256-		fflush(stdout);
257+		snprintf(s.msg, MAXSIZE * sizeof(char), "%d %d %u %u", geom.x, geom.y, geom.width, geom.height);  
258 	}
259+
260+	s.ok = true;
261+	return s;
262 }
263 
264-void
265+status
266 ipc_get_pid(char **arg) {
267+	status s = {0};
268+
269 	UNUSED(arg);
270-	if (!wm.cur) return;
271+	if (!wm.cur)
272+		return (status){ false, "" };
273 
274 	pid_t pid = swc_window_get_pid(wm.cur->win);
275-	printf("%d\n", pid);
276+	snprintf(s.msg, MAXSIZE * sizeof(char), "%d", pid);
277+
278+	s.ok = true;
279+	return s;
280 }
281 
282-void
283+status
284 ipc_get_title(char **arg) {
285+	status s = {0};
286+
287 	UNUSED(arg);
288-	if (!wm.cur) return;
289+	if (!wm.cur)
290+		return (status){ false, "" };
291+
292+	snprintf(s.msg, MAXSIZE * sizeof(char), "%s", wm.cur->win->title);
293 
294-	printf("%s\n", wm.cur->win->title);
295+	s.ok = true;
296+	return s;
297 }
298 
299-void
300+status
301 ipc_get_app_id(char **arg) {
302+	status s = {0};
303+
304 	UNUSED(arg);
305-	if (!wm.cur) return;
306+	if (!wm.cur)
307+		return (status){ false, "" };
308 
309-	printf("%s\n", wm.cur->win->app_id);
310+	snprintf(s.msg, MAXSIZE * sizeof(char), "%s", wm.cur->win->app_id);
311+
312+	s.ok = true;
313+	return s;
314 }
315 
316 /*********/
317 
318-void
319+status
320 ipc_bind(char **arg) {
321-	if (arg[1] == NULL || arg[2] == NULL) return;
322+	if (arg[1] == NULL || arg[2] == NULL)
323+		return (status){ false, "" };
324 
325 	struct bind tmp = fn_bind(arg[1]);
326 	int count = 0;
327 	for (char **p = arg + 2; *p != NULL; p++) count++;
328 
329 	char **cmd = malloc((count + 1) * sizeof(char*));
330-	if (!cmd) return;
331+	if (!cmd)
332+		return (status){ false, "" };
333 
334 	for (int i = 0; i < count; i++)
335 		cmd[i] = strdup(arg[i + 2]);
336@@ -318,15 +385,19 @@ ipc_bind(char **arg) {
337 		bind_handler,
338 		cmd
339 	);
340+
341+	return (status){ true, "" };
342 }
343 
344-void
345+status
346 ipc_config(char **arg) {
347-	if (arg[1] == NULL || arg[2] == NULL) return;
348+	if (arg[1] == NULL || arg[2] == NULL)
349+		return (status){ false, "" };
350 
351 	enum cmd cmd = atoi(arg[1]);
352 	switch (cmd) {
353 		case cmd_modkey:
354+			; /* smh */
355 			struct bind tmp = fn_bind(arg[2]);
356 			config.mod = tmp.mod;
357 			break;
358@@ -367,10 +438,13 @@ ipc_config(char **arg) {
359 				config.ou_color, config.ob_width
360 			);
361 	}
362+
363+	return (status){ true, "" };
364 }
365 
366-void
367+status
368 ipc_quit(char **arg) {
369 	UNUSED(arg);
370 	cleanup();
371+	return (status){ true, "" }; /* NOTREACHED */
372 }