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
	...
master
Raheman Vaiya 4 years ago
parent 53f0a85781
commit 8b16e02de5
  1. BIN
      data/keyd-application-mapper.1.gz
  2. BIN
      data/keyd.1.gz
  3. 5
      docs/keyd.scdoc
  4. 112
      src/daemon.c
  5. 11
      src/keyboard.c
  6. 5
      src/keyboard.h
  7. 29
      src/keyd.c
  8. 2
      src/keyd.h

Binary file not shown.

Binary file not shown.

@ -11,11 +11,14 @@ keyd(1)
# COMMANDS
*monitor*
Print key events in real time. Useful for debugging.
Print key events. Useful for discovering key names and debugging.
*bind <binding> [<binding>...]*
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.

@ -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);

@ -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;
}

@ -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);

@ -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 <binding> [<binding>...] 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},
};

@ -16,6 +16,7 @@
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
@ -71,6 +72,7 @@ struct ipc_message {
IPC_BIND,
IPC_RELOAD,
IPC_LAYER_LISTEN,
} type;
char data[MAX_IPC_MESSAGE_SIZE];

Loading…
Cancel
Save