You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
916 lines
19 KiB
916 lines
19 KiB
/* 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 <stdio.h> |
|
#include <sys/time.h> |
|
#include <signal.h> |
|
#include <sys/types.h> |
|
#include <sys/file.h> |
|
#include <dirent.h> |
|
#include <assert.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <linux/input.h> |
|
#include <libudev.h> |
|
#include <stdint.h> |
|
#include <stdarg.h> |
|
#include <linux/uinput.h> |
|
#include <time.h> |
|
#include <stdlib.h> |
|
#include <fcntl.h> |
|
#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(); |
|
}
|
|
|