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
+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
M
client.c
+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 }