commit 7a39712
wf
·
2026-04-01 15:39:34 +0000 UTC
parent 8d2626d
Add keybindings
6 files changed,
+110,
-8
+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 ----------------
M
client.c
+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;