From 8b16e02de55ab2ebe1be91dbd2dbb5d186587a2c Mon Sep 17 00:00:00 2001 From: Raheman Vaiya Date: Fri, 26 Aug 2022 03:51:45 -0400 Subject: [PATCH] Add listen command (#294, #217) Add a new listen command which prints layer state changes in real time. This makes it possible to write things like custom visual indicators. E.G $ keyd listen Sample output: +shift -shift +layer -layer ... --- data/keyd-application-mapper.1.gz | Bin 1488 -> 1490 bytes data/keyd.1.gz | Bin 7280 -> 7279 bytes docs/keyd.scdoc | 5 +- src/daemon.c | 112 +++++++++++++++++++++++++----- src/keyboard.c | 11 ++- src/keyboard.h | 5 +- src/keyd.c | 29 +++++++- src/keyd.h | 2 + 8 files changed, 142 insertions(+), 22 deletions(-) diff --git a/data/keyd-application-mapper.1.gz b/data/keyd-application-mapper.1.gz index 30aaa22cc742aa5f076a12c3da369f68b5f0a413..37fdccb48623c6af39096cc4159050f265169582 100644 GIT binary patch delta 1370 zcmV-g1*Q7X3(^ZYABzY80000000Wg)?@t^z5dEC}E2dOR0C9T(wNVw(NSBC`lk!V} zB&|3;_;$T}7T)#7_8wdn^^rL&0p*cqCx3UJ$h4KCPSrZgjiyDro7a?=fo5N6v8%pj zxwe#khSeuo-mbD-m1G6NOI@3m>RXtFYa@U>&+_xvv~#65`AG7nAaGJpBIi$;Z$0lP~-|13TVykvgtd zrdW~J3V0XQ{I6-gcnnaLsi*973OB>bqWVaxElHy20LnClUOvifY1-1Xb$~zQwnk({ z*I0AclSTm}8%3@ntpG3RLMkP72s-k{on$F=x-gX~5F}iMlY{{xe-6{c1yu$zSgDkC z<*aqroC~DWJQsf3D$75f(NEc-kMtS(+{`V2S7mxGwFUuwnFrS{#ehYW_d65y)?JbsZ~&41|&El!qnw~Y&U8{u47RtYd6xK z3PR~ybAu2Y%N}i6D35m8-DjZm8^(veQ9N1>81hBkLfhZ=f1$E+|B~^+#HKKavH3?| zBxtBAOvUoH&jy+Y3|#8ea?6w}8*q%RUQh7*^gB6TnHV>$MP(y|c1gd|t4UPLap6K1 z&h9S7aS2uzAx`$U=7)s6Mit)e&f8#00P0l1{O|GMV&z?9`i2b{xOY~J{?$ef>mT<1 zKeTwSU6@|4e=+fxlG)^t8ANTFEnzIt^AQHI*(l zh1CPw^A?~>WP1BDfts(Kzd9hsLQNl7p0}7qVQ|-jptQMCYZS65bdV{B2|HaYKZSwl z0&#*Xu5^?cZ1AbsA1-O#EK}*&AI$hPAF93Uf4jX=-z*{ z#ggUN?_m?7Zh(`OBv&c-kFe&dO8Y>;ntH|ImRe+Lf*Ujpg?5mF_;+yT(@^Qy3Qdal ze@)E53=kM4y*h@*80%}E?KJ=J3j4)MwI(#Qi#Og%o*>vl!mU^_Hz8YE)uI4*;7^h8 zR8pVrOG#y+mzBK(G7&?EwFUClm|{FwGAC_{+bM2DENThwV!UN7;&I1MP2s-mH9vsR zU2QSS?QD2G@r`$I2he}2X7KM?VKQYWG}L9$LxK2H^1Rii2x#o4I}TXJy2gDvB9tm( cP-1zVU}j35rkDxZZZEsP0jwL$vF{520NFyTi2wiq delta 1368 zcmV-e1*iJb3(yNWABzY80000000Wg)VNV=45dEC}6;moDfVjPY+Nz3Zq)SA}Nhyf} zNm_Ay@a=l{EWGQD?LD|E>XA7t0qv1yCx5e#WZKG6r)r($M$@9*&1=fbK(jBj*i~P$ zTw6*%!|JmvZ&z8aO0ojsm99-o^&QN@wGlvGWckG#+PT)7a-zD`GImy_~pJpAs{$%jw#lh6D;13TVykveWx zrdW~J3V0XQ{I6-gcnVOJsi*973OB>bqWVCpElHy20LnClUOvifY1-1Xb$~zQwnk({ zH&}BwlSTm}8%?evtpG3RLMkP72s-k{y<{nLx-gX~5F}iOlY{{xe*x3P1yu$zSgDkC z<*ao#oC~DWybyldD$75d(+}B`%VQjl+P}QYvM0y-w(~j!MtS)1;r9`~7fDly<~dIH*T!Pd$e$i~;wbA*cH&o93`lT9gsIB|*>2Q^T*snP)^4Od z6@=2a<`y9~mOa|CP#*2FyU#%BSBwvRt9Y~=Fyza+g|@%$e?w*E{uSeciA`Y;WAl%` zNYGGKn2P0HpA9q*7`W7_<(4T|HsBary`JFr={It`HZg8ki^@g_?TUV(*ORE0@ ze`xVRyD+_8e`De?C9}yPGl<$UTf$hP>)GPz7(L=|nN;6Qsnic~r6Qz(Q616-&VvTF z+SMSrh6{}0p9*K4-xHN%K&^R;5~9adC*uvWb4#vjx~UMS;}JKM*t(irAG==0vz08e zQt1Xm1F?MHiW&we$Qhn2O)7e1dc)tnu9`&&(m3hHf8P)2{05?Dl&37e)J>S{&f|g_ zZ20<^pPgUK-oBk(%-@~y8pw=jk~D#((SzY8V5MVdv7|?&Q3h$#4DI{aWi??fX8|B? zWCEh`Hm>(nn$THb4BGQVM=wN!net}}jqEV_w`2$jw|vMKtOVz?yVa0}%)kcCD-IwT zk5Gk!e*??UyoRxuiI!E?|ojV6$Q_rq{~raRc0N*9~L z>VfTf3(zGpy?dEJ%{R_p9}r`qrVlL7JItamxa&bs+FYqM3Rx67$dto`ovxLi!a#I^ zIKdUyI?4<-_|)tVm$Yt{sdVfQW_+3t)!y~pf8MBXmcxwSNRV;Mw{!1J+@z8HYK9Rf zNK(EmcU<#$?-X(DvhkrMeO4L|?GM8i^MYRmEd5fi4HC%0%JsGsd?DZpDVYsPu-csB~R6r-f2?=Gm)+lQNyaws3jhGn0JJ#( diff --git a/data/keyd.1.gz b/data/keyd.1.gz index 89857346992805cbb354bbf97f1cd8f228c0071b..93cee272cab896250cc17eab3090dc059ea6f29f 100644 GIT binary patch literal 7279 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 [...]* Add the supplied bindings to all active configs. See _Bindings_ for details. +*listen* + Print layer state changes of the running keyd daemon to stdout. Useful for scripting. + *list-keys* List valid key names. diff --git a/src/daemon.c b/src/daemon.c index f88c17a..2da469c 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -17,6 +17,9 @@ static size_t nr_devices; static uint8_t keystate[256]; +static int listeners[32]; +static size_t nr_listeners = 0; + static void free_configs() { struct config_ent *ent = configs; @@ -53,6 +56,57 @@ static void send_key(uint8_t code, uint8_t state) vkbd_send_key(vkbd, code, state); } +static void add_listener(int con) +{ + struct timeval tv; + + /* + * In order to avoid blocking the main event loop, allow up to 50ms for + * slow clients to relieve back pressure before dropping them. + */ + tv.tv_usec = 50000; + tv.tv_sec = 0; + + if (nr_listeners == ARRAY_SIZE(listeners)) { + char s[] = "Max listeners exceeded\n"; + xwrite(con, &s, sizeof s); + + close(con); + return; + } + + setsockopt(con, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); + + listeners[nr_listeners++] = con; +} + +static void layer_observer(const char *name, int state) +{ + if (!nr_listeners) + return; + + char buf[MAX_LAYER_NAME_LEN+2]; + ssize_t bufsz = snprintf(buf, sizeof(buf), "%c%s\n", state ? '+' : '-', name); + size_t i; + + int keep[ARRAY_SIZE(listeners)]; + size_t n = 0; + + for (i = 0; i < nr_listeners; i++) { + ssize_t nw = write(listeners[i], buf, bufsz); + + if (nw == bufsz) + keep[n++] = listeners[i]; + else + close(listeners[i]); + } + + if (n != nr_listeners) { + nr_listeners = n; + memcpy(listeners, keep, n * sizeof(int)); + } +} + static void load_configs() { DIR *dh = opendir(CONFIG_DIR); @@ -82,7 +136,7 @@ static void load_configs() if (config_parse(&ent->config, path)) die("failed to parse %s", path); - ent->kbd = new_keyboard(&ent->config, send_key); + ent->kbd = new_keyboard(&ent->config, send_key, layer_observer); ent->next = configs; configs = ent; } @@ -154,10 +208,36 @@ static void reload() clear_vkbd(); } +static void send_success(int con) +{ + struct ipc_message msg; + + msg.type = IPC_SUCCESS;; + msg.sz = sprintf(msg.data, "Success"); + + xwrite(con, &msg, sizeof msg); + close(con); +} + +static void send_fail(int con, const char *fmt, ...) +{ + struct ipc_message msg; + va_list args; + + va_start(args, fmt); + + msg.type = IPC_FAIL; + msg.sz = vsnprintf(msg.data, sizeof(msg.data), fmt, args); + + xwrite(con, &msg, sizeof msg); + close(con); + + va_end(args); +} + static void handle_client(int con) { struct ipc_message msg; - size_t sz; xread(con, &msg, sizeof msg); @@ -166,36 +246,31 @@ static void handle_client(int con) int success; case IPC_RELOAD: - msg.type = IPC_SUCCESS;; - strcpy(msg.data, "Success"); - msg.sz = strlen(msg.data); - reload(); - xwrite(con, &msg, sizeof msg); + send_success(con); + break; + case IPC_LAYER_LISTEN: + add_listener(con); break; case IPC_BIND: success = 0; - msg.data[msg.sz] = 0; + for (ent = configs; ent; ent = ent->next) { if (!kbd_eval(ent->kbd, msg.data)) success = 1; } - if (success) { - msg.type = IPC_SUCCESS; - msg.sz = 0; - } else { - msg.type = IPC_FAIL; - msg.sz = snprintf(msg.data, sizeof msg.data, "ERROR: %s", errstr); - } + if (success) + send_success(con); + else + send_fail(con, "%s", errstr); + - xwrite(con, &msg, sizeof msg); break; default: + send_fail(con, "Unknown command"); break; } - - close(con); } static void remove_device(struct device *dev) @@ -307,6 +382,7 @@ int run_daemon(int argc, char *argv[]) setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stderr, NULL, _IOLBF, 0); + nice(-20); evloop_add_fd(ipcfd); diff --git a/src/keyboard.c b/src/keyboard.c index 4d1540e..f942b99 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -309,6 +309,9 @@ static void deactivate_layer(struct keyboard *kbd, int idx) assert(kbd->layer_state[idx].active > 0); kbd->layer_state[idx].active--; + + if (kbd->layer_observer) + kbd->layer_observer(kbd->config.layers[idx].name, 0); } /* @@ -323,6 +326,9 @@ static void activate_layer(struct keyboard *kbd, uint8_t code, int idx) kbd->layer_state[idx].activation_time = get_time(); kbd->layer_state[idx].active++; kbd->last_layer_code = code; + + if (kbd->layer_observer) + kbd->layer_observer(kbd->config.layers[idx].name, 1); } static void execute_command(const char *cmd) @@ -634,7 +640,9 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, return timeout; } -struct keyboard *new_keyboard(struct config *config, void (*sink) (uint8_t, uint8_t)) +struct keyboard *new_keyboard(struct config *config, + void (*sink) (uint8_t, uint8_t), + void (*layer_observer)(const char *name, int state)) { size_t i; struct keyboard *kbd; @@ -669,6 +677,7 @@ struct keyboard *new_keyboard(struct config *config, void (*sink) (uint8_t, uint } kbd->output = sink; + kbd->layer_observer = layer_observer; return kbd; } diff --git a/src/keyboard.h b/src/keyboard.h index b7f8800..a98344d 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -63,9 +63,12 @@ struct keyboard { uint8_t keystate[256]; void (*output) (uint8_t code, uint8_t state); + void (*layer_observer) (const char *layer, int state); }; -struct keyboard *new_keyboard(struct config *config, void (*sink) (uint8_t, uint8_t)); +struct keyboard *new_keyboard(struct config *config, + void (*sink) (uint8_t, uint8_t), + void (*layer_observer)(const char *name, int state)); long kbd_process_key_event(struct keyboard *kbd, uint8_t code, int pressed); int kbd_eval(struct keyboard *kbd, const char *exp); diff --git a/src/keyd.c b/src/keyd.c index f7c8606..af412f5 100644 --- a/src/keyd.c +++ b/src/keyd.c @@ -4,7 +4,6 @@ * © 2019 Raheman Vaiya (see also: LICENSE). */ - #include "keyd.h" static int ipc_exec(int type, const char *data, size_t sz) @@ -51,6 +50,7 @@ static int help(int argc, char *argv[]) " monitor Print key events in real time.\n" " list-keys Print a list of valid key names.\n" " reload Trigger a reload .\n" + " listen Print layer state changes of the running keyd daemon to stdout.\n" " bind [...] Add the supplied bindings to all loaded configs.\n" "Options:\n" " -v, --version Print the current version and exit.\n" @@ -93,6 +93,31 @@ static int add_binding(int argc, char *argv[]) return ret; } +static int layer_listen(int argc, char *argv[]) +{ + struct ipc_message msg; + + int con = ipc_connect(); + + if (con < 0) { + perror("connect"); + exit(-1); + } + + msg.type = IPC_LAYER_LISTEN; + xwrite(con, &msg, sizeof msg); + + while (1) { + char buf[512]; + ssize_t sz = read(con, buf, sizeof buf); + + if (sz <= 0) + return -1; + + write(1, buf, sz); + } +} + static int reload() { ipc_exec(IPC_RELOAD, NULL, 0); @@ -114,6 +139,8 @@ struct { {"monitor", "-m", "--monitor", monitor}, {"bind", "-e", "--expression", add_binding}, + {"listen", "", "", layer_listen}, + {"reload", "", "", reload}, {"list-keys", "", "", list_keys}, }; diff --git a/src/keyd.h b/src/keyd.h index d966e21..efe443a 100644 --- a/src/keyd.h +++ b/src/keyd.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,7 @@ struct ipc_message { IPC_BIND, IPC_RELOAD, + IPC_LAYER_LISTEN, } type; char data[MAX_IPC_MESSAGE_SIZE];