commit 7a39712

wf  ·  2026-04-01 15:39:34 +0000 UTC
parent 8d2626d
Add keybindings
6 files changed,  +110, -8
M howl.c
M ipc.c
+3, -2
 1@@ -13,9 +13,10 @@ dependencies
 2 todo
 3 ----
 4 
 5- - titlebars(?), keybindings
 6+ - titlebars(?)
 7+ - unbinding keys
 8  - allow for more scriptability, assign pseudorandom IDs to windows etc.
 9- - document IPC interface
10+ - documentation
11 
12 acknowledgements
13 ----------------
+5, -3
 1@@ -29,9 +29,11 @@ static const struct command commands[] = {
 2 	{ "title",    cmd_title,    0, false },
 3 	{ "appid",    cmd_app_id,   0, false },
 4 
 5-	{ "focus_color",   cmd_focus_color,   1, true },
 6-	{ "unfocus_color", cmd_unfocus_color, 1, true },
 7-	{ "border_width",  cmd_border_width,  1, true },
 8+	{ "bind",          cmd_bind,          2, false },
 9+	{ "modkey",        cmd_modkey,        1, true  },
10+	{ "focus_color",   cmd_focus_color,   1, true  },
11+	{ "unfocus_color", cmd_unfocus_color, 1, true  },
12+	{ "border_width",  cmd_border_width,  1, true  },
13 };
14 
15 static void
M howl.c
+21, -3
 1@@ -34,6 +34,7 @@ extern void ipc_get_geometry(char **);
 2 extern void ipc_get_pid(char **);
 3 extern void ipc_get_title(char **);
 4 extern void ipc_get_app_id(char **);
 5+extern void ipc_bind(char **);
 6 extern void ipc_config(char **);
 7 
 8 bool have_config = true;
 9@@ -67,11 +68,12 @@ static const cmd_handler_t cmd_handler [cmd_last] = {
10 	[cmd_pid]      = ipc_get_pid,
11 	[cmd_title]    = ipc_get_title,
12 	[cmd_app_id]   = ipc_get_app_id,
13+	[cmd_bind]     = ipc_bind,
14 	[cmd_config]   = ipc_config
15 };
16 
17 static int
18-handler(int fd, uint32_t mask, void *data) {
19+ipc_handler(int fd, uint32_t mask, void *data) {
20 	UNUSED(data);
21 
22 	if (mask & WL_EVENT_HANGUP) {
23@@ -128,7 +130,7 @@ handler(int fd, uint32_t mask, void *data) {
24 
25 				int cmd = atoi(argv[0]);
26 				void *fn = cmd_handler[cmd];
27-				if (!fn) _wrn("no such command %s", argv[0]);
28+				if (!fn) _wrn("no such command #%s", argv[0]);
29 	
30 				cmd_handler[cmd](argv);
31 			}
32@@ -159,7 +161,7 @@ setup_ipc(void) {
33 	if (listen(sfd, 5) == -1)
34 		_err(1, "couldn't listen on IPC socket: %s", strerror(errno));
35 
36-	wl_event_loop_add_fd(wm.loop, sfd, WL_EVENT_READABLE | WL_EVENT_HANGUP | WL_EVENT_ERROR, handler, NULL);
37+	wl_event_loop_add_fd(wm.loop, sfd, WL_EVENT_READABLE | WL_EVENT_HANGUP | WL_EVENT_ERROR, ipc_handler, NULL);
38 
39 	_inf("set up IPC socket");
40 }
41@@ -181,6 +183,22 @@ load_config(char *conf_path) {
42 	}
43 }
44 
45+void
46+bind_handler(void *data, uint32_t time, uint32_t value, uint32_t state) {
47+	UNUSED(time);
48+	UNUSED(value);
49+
50+	if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
51+		return;
52+
53+	char *const *cmd = (char *const *)data;
54+	if (fork() == 0) {
55+		setsid();
56+		execvp(cmd[0], cmd);
57+		_exit(127);
58+	}
59+}
60+
61 static void
62 setup(void) {
63 	wm.dpy = wl_display_create();
+2, -0
1@@ -13,6 +13,8 @@ enum cmd {
2 	cmd_pid,
3 	cmd_title,
4 	cmd_app_id,
5+	cmd_bind,
6+	cmd_modkey,
7 	cmd_focus_color,
8 	cmd_unfocus_color,
9 	cmd_border_width,
+8, -0
 1@@ -7,7 +7,15 @@
 2 #include <wayland-server.h>
 3 #include <swc.h>
 4 
 5+union arg {
 6+	int        i;
 7+	uint32_t   ui;
 8+	float      f;
 9+	const void *v;
10+};
11+
12 struct config {
13+	uint32_t  modkey;
14 	uint32_t  focus_color;
15 	uint32_t  unfocus_color;
16 	int32_t   border_width;
M ipc.c
+71, -0
  1@@ -3,6 +3,7 @@
  2 #include <stdlib.h>
  3 
  4 #include <swc.h>
  5+#include <xkbcommon/xkbcommon.h>
  6 
  7 #include "howl.h"
  8 #include "types.h"
  9@@ -11,6 +12,10 @@
 10 struct wm wm;
 11 struct config config;
 12 
 13+struct bind {
 14+	uint32_t mod, key;
 15+};
 16+
 17 static int
 18 fn_int(char *s) {
 19 	return atoi(s);
 20@@ -27,6 +32,40 @@ fn_hex(char *s) {
 21 	return strtoul(s, NULL, 16);
 22 }
 23 
 24+static struct bind
 25+fn_bind(char *s) {
 26+	uint32_t mods = 0, key = 0;
 27+	char *tok = strtok(s, "+");
 28+	while (tok != NULL) {
 29+		uint32_t tmp = 0;
 30+	
 31+		if (strcmp(tok, "mod") == 0) tmp = config.modkey;
 32+		else if (strcmp(tok, "alt") == 0) tmp = SWC_MOD_ALT;
 33+		else if (strcmp(tok, "win") == 0) tmp = SWC_MOD_LOGO;
 34+		else if (strcmp(tok, "ctrl") == 0) tmp = SWC_MOD_CTRL;
 35+		else if (strcmp(tok, "shift") == 0) tmp = SWC_MOD_SHIFT;
 36+		else if (strcmp(tok, "any") == 0) tmp = SWC_MOD_ANY;
 37+
 38+		/* if not a modifier, check if it's a key */
 39+		if (tmp == 0) {
 40+			tmp = xkb_keysym_from_name(tok, XKB_KEYSYM_CASE_INSENSITIVE);
 41+			if (tmp == 0) {
 42+				_wrn("no such key: %s", tok);
 43+				return (struct bind){ 0, 0 };
 44+			}
 45+			key = tmp;
 46+		} else {
 47+			mods |= tmp;
 48+		}
 49+
 50+		tok = strtok(NULL, "+");
 51+	}
 52+
 53+	return (struct bind){ mods, key };
 54+}
 55+
 56+extern void bind_handler(void *, uint32_t, uint32_t, uint32_t);
 57+
 58 /*********/
 59 
 60 void
 61@@ -161,12 +200,44 @@ ipc_get_app_id(char **arg) {
 62 
 63 /*********/
 64 
 65+void
 66+ipc_bind(char **arg) {
 67+	/*
 68+	 * NOTE: here arg[2] is not the full command, but rather the first token, which
 69+	 * is still the minimum amount, but i thought it would be confusing without this note
 70+	 */
 71+	if (arg[1] == NULL || arg[2] == NULL) return;
 72+
 73+	struct bind tmp = fn_bind(arg[1]);
 74+	int count = 0;
 75+	for (char **p = arg + 2; *p != NULL; p++) count++;
 76+
 77+	char **cmd = malloc((count + 1) * sizeof(char*));
 78+	if (!cmd) return;
 79+
 80+	for (int i = 0; i < count; i++)
 81+		cmd[i] = strdup(arg[i + 2]);
 82+	cmd[count] = NULL;
 83+
 84+	swc_add_binding(
 85+		SWC_BINDING_KEY,
 86+		tmp.mod,
 87+		tmp.key,
 88+		bind_handler,
 89+		cmd
 90+	);
 91+}
 92+
 93 void
 94 ipc_config(char **arg) {
 95 	if (arg[1] == NULL || arg[2] == NULL) return;
 96 
 97 	enum cmd cmd = atoi(arg[1]);
 98 	switch (cmd) {
 99+		case cmd_modkey:
100+			struct bind tmp = fn_bind(arg[2]);
101+			config.modkey = tmp.mod;
102+			break;
103 		case cmd_focus_color:
104 			config.focus_color = fn_hex(arg[2]);
105 			break;