/* Copyright © 2019 Raheman Vaiya. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "keys.h" #include "config.h" #define UINPUT_DEVICE_NAME "keyd virtual keyboard" #define MAX_KEYBOARDS 256 #define LOCK_FILE "/var/lock/keyd.lock" #define LOG_FILE "/var/log/keyd.log" //Only used when running as a daemon. #ifdef DEBUG #define dbg(fmt, ...) warn("%s:%d: "fmt, __FILE__, __LINE__, ## __VA_ARGS__) #else #define dbg(...) #endif static int ufd = -1; static struct udev *udev; static struct udev_monitor *udevmon; static uint8_t keystate[KEY_CNT] = {0}; //Active keyboard state. struct keyboard { int fd; char devnode[256]; struct layer **layers; size_t nlayers; //The layer to which modifiers are applied, //this may be distinct from the main layout struct layer *modlayout; struct layer *layout; struct keyboard *next; }; static struct keyboard *keyboards = NULL; static void warn(char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); } static void _die(char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); exit(-1); } #define die(fmt, ...) _die("%s:%d: "fmt, __FILE__, __LINE__, ## __VA_ARGS__) static uint64_t get_time() { struct timespec tv; clock_gettime(CLOCK_MONOTONIC, &tv); return (tv.tv_sec*1E9)+tv.tv_nsec; } static int is_keyboard(struct udev_device *dev) { int is_keyboard = 0; const char *path = udev_device_get_devnode(dev); if(!path || !strstr(path, "event")) //Filter out non evdev devices. return 0; struct udev_list_entry *prop; udev_list_entry_foreach(prop, udev_device_get_properties_list_entry(dev)) { //Some mice can also send keypresses, ignore these if(!strcmp(udev_list_entry_get_name(prop), "ID_INPUT_MOUSE") && !strcmp(udev_list_entry_get_value(prop), "1")) { return 0; } if(!strcmp(udev_list_entry_get_name(prop), "ID_INPUT_KEYBOARD") && !strcmp(udev_list_entry_get_value(prop), "1")) { is_keyboard = 1; } } return is_keyboard; } static void get_keyboard_nodes(char *nodes[MAX_KEYBOARDS], int *sz) { struct udev *udev; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *ent; udev = udev_new(); if (!udev) die("Cannot create udev context."); enumerate = udev_enumerate_new(udev); if (!enumerate) die("Cannot create enumerate context."); udev_enumerate_add_match_subsystem(enumerate, "input"); udev_enumerate_add_match_subsystem(enumerate, "input"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); if (!devices) die("Failed to get device list."); *sz = 0; udev_list_entry_foreach(ent, devices) { const char *name = udev_list_entry_get_name(ent);; struct udev_device *dev = udev_device_new_from_syspath(udev, name); const char *path = udev_device_get_devnode(dev); if(is_keyboard(dev)) { nodes[*sz] = malloc(strlen(path)+1); strcpy(nodes[*sz], path); (*sz)++; assert(*sz <= MAX_KEYBOARDS); } udev_device_unref(dev); } udev_enumerate_unref(enumerate); udev_unref(udev); } static int create_uinput_fd() { size_t i; struct uinput_setup usetup; int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); if(fd < 0) { perror("open"); exit(-1); } ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_SYN); for(i = 0;i < KEY_MAX;i++) { if(keycode_table[i].name) ioctl(fd, UI_SET_KEYBIT, i); } memset(&usetup, 0, sizeof(usetup)); usetup.id.bustype = BUS_USB; usetup.id.vendor = 0x046d; usetup.id.product = 0xc52b; strcpy(usetup.name, UINPUT_DEVICE_NAME); ioctl(fd, UI_DEV_SETUP, &usetup); ioctl(fd, UI_DEV_CREATE); return fd; } static void syn() { static struct input_event ev = { .type = EV_SYN, .code = 0, .value = 0, }; write(ufd, &ev, sizeof(ev)); } static void send_repetitions() { size_t i; struct input_event ev = { .type = EV_KEY, .value = 2, .time.tv_sec = 0, .time.tv_usec = 0 }; //Inefficient, but still reasonably fast (<100us) for(i = 0; i < sizeof keystate / sizeof keystate[0];i++) { if(keystate[i]) { ev.code = i; write(ufd, &ev, sizeof(ev)); syn(); } } } static void send_key(uint16_t code, int is_pressed) { keystate[code] = is_pressed; struct input_event ev; ev.type = EV_KEY; ev.code = code; ev.value = is_pressed; ev.time.tv_sec = 0; ev.time.tv_usec = 0; write(ufd, &ev, sizeof(ev)); syn(); } static void setmods(uint16_t mods) { if(!!(mods & MOD_CTRL) != keystate[KEY_LEFTCTRL]) send_key(KEY_LEFTCTRL, !keystate[KEY_LEFTCTRL]); if(!!(mods & MOD_SHIFT) != keystate[KEY_LEFTSHIFT]) send_key(KEY_LEFTSHIFT, !keystate[KEY_LEFTSHIFT]); if(!!(mods & MOD_SUPER) != keystate[KEY_LEFTMETA]) send_key(KEY_LEFTMETA, !keystate[KEY_LEFTMETA]); if(!!(mods & MOD_ALT) != keystate[KEY_LEFTALT]) send_key(KEY_LEFTALT, !keystate[KEY_LEFTALT]); if(!!(mods & MOD_ALT_GR) != keystate[KEY_RIGHTALT]) send_key(KEY_RIGHTALT, !keystate[KEY_RIGHTALT]); } static void reify_layer_mods(struct keyboard *kbd) { uint16_t mods = 0; size_t i; for(i = 0;i < kbd->nlayers;i++) { struct layer *layer = kbd->layers[i]; if(layer->active) mods |= layer->mods; } setmods(mods); } static struct key_descriptor *kbd_lookup_descriptor(struct keyboard *kbd, uint16_t code, uint16_t *modsp) { size_t i; struct key_descriptor *desc = NULL; struct layer *layer = NULL; uint16_t mods = 0; size_t nactive = 0; //Pick the most recently activated layer in which a mapping is defined. for(i = 0;i < kbd->nlayers;i++) { struct layer *l = kbd->layers[i]; struct key_descriptor *d = &l->keymap[code]; if(l->active) { if(d->action && (!desc || (l->timestamp > layer->timestamp))) { desc = d; layer = l; } nactive++; } } //Calculate the modifier union of active layers, excluding the mods for //the layer in which the mapping is defined. mods = 0; for(i = 0;i < kbd->nlayers;i++) { struct layer *l = kbd->layers[i]; if(l->active && l != layer) mods |= l->mods; } if(!desc) { //If any modifier layers are active and do not explicitly //define a mapping, obtain the target from modlayout. if(mods) { if(mods == MOD_SHIFT || mods == MOD_ALT_GR) desc = &kbd->layout->keymap[code]; else desc = &kbd->modlayout->keymap[code]; } else if(!nactive) //If no layers are active use the default layout desc = &kbd->layout->keymap[code]; else return NULL; } *modsp = mods; return desc; } //Where the magic happens static void process_event(struct keyboard *kbd, struct input_event *ev) { size_t i; uint16_t code = ev->code; uint8_t pressed = ev->value; struct key_descriptor *d; static struct key_descriptor *lastd = NULL; static uint8_t oneshot_layers[MAX_LAYERS] = {0}; static uint64_t pressed_timestamps[KEY_CNT] = {0}; static uint64_t last_keyseq_timestamp = 0; uint16_t mods = 0; //Cache the descriptor to ensure consistency upon up/down event pairs since layers can change midkey. static struct key_descriptor *dcache[KEY_CNT] ={0}; static uint16_t mcache[KEY_CNT] ={0}; if(ev->type != EV_KEY) return; //Wayland and X both ignore repeat events but VTs seem to require them. if(pressed == 2) { send_repetitions(); return; } if(!pressed) { d = dcache[code]; mods = mcache[code]; dcache[code] = NULL; mcache[code] = 0; } else { pressed_timestamps[code] = get_time(); d = kbd_lookup_descriptor(kbd, code, &mods); dcache[code] = d; mcache[code] = mods; } if(!d) return; switch(d->action) { struct layer *layer; uint32_t keyseq; case ACTION_OVERLOAD: keyseq = d->arg.keyseq; layer = kbd->layers[d->arg2.layer]; if(pressed) { layer->active = 1; layer->timestamp = get_time(); } else { layer->active = 0; if(lastd == d) { //If tapped uint16_t key = keyseq & 0xFF; mods |= keyseq >> 16; setmods(mods); send_key(key, 1); send_key(key, 0); last_keyseq_timestamp = get_time(); goto keyseq_cleanup; } } reify_layer_mods(kbd); break; case ACTION_LAYOUT: kbd->layout = kbd->layers[d->arg.layer]; kbd->modlayout = kbd->layers[d->arg2.layer]; dbg("layer: %d, modlayout: %d", kbd->layout, kbd->modlayout); break; case ACTION_ONESHOT: layer = kbd->layers[d->arg.layer]; if(pressed) { layer->active = 1; layer->timestamp = get_time(); } else if(pressed_timestamps[code] < last_keyseq_timestamp) { layer->active = 0; } else //Tapped oneshot_layers[d->arg.layer] = 1; reify_layer_mods(kbd); break; case ACTION_LAYER_TOGGLE: if(pressed) { layer = kbd->layers[d->arg.layer]; layer->active = !layer->active; reify_layer_mods(kbd); } break; case ACTION_LAYER: layer = kbd->layers[d->arg.layer]; if(pressed) { layer->active = 1; layer->timestamp = get_time(); } else layer->active = 0; reify_layer_mods(kbd); break; case ACTION_KEYSEQ: mods |= d->arg.keyseq >> 16; if(pressed) { setmods(mods); send_key(d->arg.keyseq & 0xFF, 1); } else { reify_layer_mods(kbd); send_key(d->arg.keyseq & 0xFF, 0); } goto keyseq_cleanup; break; case ACTION_MACRO: if(pressed) { uint32_t *macro = d->arg.macro; size_t sz = d->arg2.sz; for(i = 0; i < sz;i++) { uint32_t seq = macro[i]; uint16_t mods = macro[i] >> 16; uint16_t key = macro[i] & 0xFF; if(mods & MOD_TIMEOUT) { usleep(GET_TIMEOUT(seq)*1000); } else { setmods(mods); send_key(key, 1); send_key(key, 0); } } reify_layer_mods(kbd); } break; case ACTION_UNDEFINED: goto keyseq_cleanup; break; } lastd = d; return; keyseq_cleanup: lastd = d; if(pressed) last_keyseq_timestamp = get_time(); //Clear active oneshot layers. for(i = 0;i < kbd->nlayers;i++) { if(oneshot_layers[i]) { kbd->layers[i]->active = 0; oneshot_layers[i] = 0; } } } static const char *evdev_device_name(const char *devnode) { static char name[256]; int fd = open(devnode, O_RDONLY); if(fd < 0) { perror("open"); exit(-1); } if(ioctl(fd, EVIOCGNAME(sizeof(name)), &name) == -1) { perror("ioctl"); exit(-1); } close(fd); return name; } //Block on the given keyboard nodes until no keys are depressed. static void await_keyboard_neutrality(char **devs, int n) { int fds[MAX_KEYBOARDS]; int maxfd = 0; int i; dbg("Awaiting keyboard neutrality."); for(i = 0;i < n;i++) { if((fds[i] = open(devs[i], O_RDONLY | O_NONBLOCK)) < 0) die("open"); if(fds[i] > maxfd) maxfd = fds[i]; } //There is a race condition here since it is possible for a key down //event to be generated before keyd is launched, in that case we hope a //repeat event is generated within the first 300ms. If we miss the //keydown event and the repeat event is not generated within the first //300ms it is possible for this to yield a false positive. In practice //this seems to work fine. Given the stateless nature of evdev I am not //aware of a better way to achieve this. while(1) { struct timeval tv = { .tv_usec = 300000 }; struct input_event ev; int i; fd_set fdset; FD_ZERO(&fdset); for(i = 0;i < n;i++) FD_SET(fds[i], &fdset); select(maxfd+1, &fdset, NULL, NULL, &tv); for(i = 0;i < n;i++) { if(FD_ISSET(fds[i], &fdset)) { while(read(fds[i], &ev, sizeof ev) > 0) { if(ev.type == EV_KEY) { keystate[ev.code] = ev.value; dbg("keystate[%d]: %d", ev.code, ev.value); } } } } for(i = 0;i < KEY_CNT;i++) if(keystate[i]) break; if(i == KEY_CNT) break; } for(i = 0;i < n;i++) close(fds[i]); dbg("Keyboard neutrality achieved"); } static int manage_keyboard(const char *devnode) { int fd; const char *name = evdev_device_name(devnode); struct keyboard *kbd; struct keyboard_config *cfg = NULL; struct keyboard_config *default_cfg = NULL; if(!strcmp(name, UINPUT_DEVICE_NAME)) //Don't manage virtual keyboard return 0; for(cfg = configs;cfg;cfg = cfg->next) { if(!strcmp(cfg->name, "default")) default_cfg = cfg; if(!strcmp(cfg->name, name)) break; } if(!cfg) { if(default_cfg) { warn("No config found for %s (%s), falling back to default.cfg", name, devnode); cfg = default_cfg; } else { //Don't manage keyboards for which there is no configuration. warn("No config found for %s (%s), ignoring", name, devnode); return 0; } } if((fd = open(devnode, O_RDONLY | O_NONBLOCK)) < 0) { perror("open"); exit(1); } kbd = calloc(1, sizeof(struct keyboard)); kbd->fd = fd; kbd->layers = cfg->layers; kbd->nlayers = cfg->nlayers; kbd->modlayout = cfg->layers[cfg->default_modlayout]; kbd->layout = cfg->layers[cfg->default_layout]; strcpy(kbd->devnode, devnode); //Grab the keyboard. if(ioctl(fd, EVIOCGRAB, (void *)1) < 0) { perror("EVIOCGRAB"); exit(-1); } kbd->next = keyboards; keyboards = kbd; warn("Managing %s", evdev_device_name(devnode)); return 1; } static int destroy_keyboard(const char *devnode) { struct keyboard **ent = &keyboards; while(*ent) { if(!strcmp((*ent)->devnode, devnode)) { dbg("Destroying %s", devnode); struct keyboard *kbd = *ent; *ent = kbd->next; //Attempt to ungrab the the keyboard (assuming it still exists) if(ioctl(kbd->fd, EVIOCGRAB, (void *)1) < 0) { perror("EVIOCGRAB"); } close(kbd->fd); free(kbd); return 1; } ent = &(*ent)->next; } return 0; } static void evdev_monitor_loop(int *fds, int sz) { struct input_event ev; fd_set fdset; int i; char names[256][256]; for(i = 0;i < sz;i++) { int fd = fds[i]; if(ioctl(fd, EVIOCGNAME(sizeof(names[fd])), names[fd]) == -1) { perror("ioctl"); exit(-1); } } while(1) { int i; int maxfd = fds[0]; FD_ZERO(&fdset); for(i = 0;i < sz;i++) { if(maxfd < fds[i]) maxfd = fds[i]; FD_SET(fds[i], &fdset); } select(maxfd+1, &fdset, NULL, NULL, NULL); for(i = 0;i < sz;i++) { int fd = fds[i]; if(FD_ISSET(fd, &fdset)) { while(read(fd, &ev, sizeof(ev)) > 0) { if(ev.type == EV_KEY && ev.value != 2) { assert(keycode_table[ev.code].name); fprintf(stderr, "%s: %s %s\n", names[fd], keycode_table[ev.code].name, ev.value == 0 ? "up" : "down"); } } } } } } static int monitor_loop() { char *devnodes[256]; int sz, i; int fd = -1; int fds[256]; int nfds = 0; get_keyboard_nodes(devnodes, &sz); for(i = 0;i < sz;i++) { fd = open(devnodes[i], O_RDONLY | O_NONBLOCK); if(fd < 0) { perror("open"); exit(-1); } fds[nfds++] = fd; } evdev_monitor_loop(fds, nfds); return 0; } static void main_loop() { struct keyboard *kbd; int monfd; int i, n; char *devs[MAX_KEYBOARDS]; get_keyboard_nodes(devs, &n); await_keyboard_neutrality(devs, n); for(i = 0;i < n;i++) { manage_keyboard(devs[i]); free(devs[i]); } udev = udev_new(); udevmon = udev_monitor_new_from_netlink(udev, "udev"); if (!udev) die("Can't create udev."); udev_monitor_filter_add_match_subsystem_devtype(udevmon, "input", NULL); udev_monitor_enable_receiving(udevmon); monfd = udev_monitor_get_fd(udevmon); while(1) { int maxfd; fd_set fds; struct udev_device *dev; FD_ZERO(&fds); FD_SET(monfd, &fds); maxfd = monfd; for(kbd = keyboards;kbd;kbd=kbd->next) { int fd = kbd->fd; maxfd = maxfd > fd ? maxfd : fd; FD_SET(fd, &fds); } if(select(maxfd+1, &fds, NULL, NULL, NULL) > 0) { if(FD_ISSET(monfd, &fds)) { dev = udev_monitor_receive_device(udevmon); const char *devnode = udev_device_get_devnode(dev); if(devnode && is_keyboard(dev)) { const char *action = udev_device_get_action(dev); if(!strcmp(action, "add")) manage_keyboard(devnode); else if(!strcmp(action, "remove")) destroy_keyboard(devnode); } udev_device_unref(dev); } for(kbd = keyboards;kbd;kbd=kbd->next) { int fd = kbd->fd; if(FD_ISSET(fd, &fds)) { struct input_event ev; while(read(fd, &ev, sizeof(ev)) > 0) { process_event(kbd, &ev); } } } } } } static void cleanup() { struct keyboard *kbd = keyboards; config_free(); while(kbd) { struct keyboard *tmp = kbd; kbd = kbd->next; free(tmp); } udev_unref(udev); udev_monitor_unref(udevmon); } static void lock() { int fd; if((fd=open(LOCK_FILE, O_CREAT | O_RDWR, 0600)) == -1) { perror("flock open"); exit(1); } if(flock(fd, LOCK_EX | LOCK_NB) == -1) { warn("Another instance of keyd is already running."); exit(-1); } } static void exit_signal_handler(int sig) { warn("%s received, cleaning up and terminating...", sig == SIGINT ? "SIGINT" : "SIGTERM"); cleanup(); exit(0); } static void daemonize() { int fd = open(LOG_FILE, O_APPEND|O_WRONLY); warn("Daemonizing."); warn("Log output will be stored in %s", LOG_FILE); if(fork()) exit(0); if(fork()) exit(0); close(0); close(1); close(2); dup2(fd, 1); dup2(fd, 2); } int main(int argc, char *argv[]) { if(argc > 1) { if(!strcmp(argv[1], "-v")) { fprintf(stderr, "keyd version: %s (%s)\n", VERSION, GIT_COMMIT_HASH); return 0; } else if(!strcmp(argv[1], "-m")) { return monitor_loop(); } else if(!strcmp(argv[1], "-l")) { size_t i; for(i = 0; i < KEY_MAX;i++) if(keycode_table[i].name) { const struct keycode_table_ent *ent = &keycode_table[i]; printf("%s\n", ent->name); if(ent->alt_name) printf("%s\n", ent->alt_name); if(ent->shifted_name) printf("%s\n", ent->shifted_name); } return 0; } } lock(); signal(SIGINT, exit_signal_handler); signal(SIGTERM, exit_signal_handler); if(argc > 1 && !strcmp(argv[1], "-d")) daemonize(); warn("Starting keyd v%s (%s).", VERSION, GIT_COMMIT_HASH); config_generate(); ufd = create_uinput_fd(); main_loop(); }