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.
645 lines
13 KiB
645 lines
13 KiB
/* |
|
* keyd - A key remapping daemon. |
|
* |
|
* © 2019 Raheman Vaiya (see also: LICENSE). |
|
*/ |
|
#include <stdint.h> |
|
#include <unistd.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
#include "keyboard.h" |
|
#include "keyd.h" |
|
#include "vkbd.h" |
|
#include "descriptor.h" |
|
#include "layer.h" |
|
|
|
/* |
|
* Here be tiny dragons. |
|
*/ |
|
|
|
static long get_time() |
|
{ |
|
/* Close enough :/. Using a syscall is unnecessary. */ |
|
static long time = 1; |
|
return time++; |
|
} |
|
|
|
static int cache_set(struct keyboard *kbd, uint8_t code, |
|
const struct descriptor *d, struct layer *dl) |
|
{ |
|
size_t i; |
|
int slot = -1; |
|
|
|
for (i = 0; i < CACHE_SIZE; i++) |
|
if (kbd->cache[i].code == code) { |
|
slot = i; |
|
break; |
|
} else if (!kbd->cache[i].code) { |
|
slot = i; |
|
} |
|
|
|
if (slot == -1) |
|
return -1; |
|
|
|
if (d == NULL) { |
|
kbd->cache[slot].code = 0; |
|
} else { |
|
kbd->cache[slot].code = code; |
|
kbd->cache[slot].d = *d; |
|
kbd->cache[slot].dl = dl; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int cache_get(struct keyboard *kbd, uint8_t code, |
|
struct descriptor *d, struct layer **dl) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < CACHE_SIZE; i++) |
|
if (kbd->cache[i].code == code) { |
|
if (d) |
|
*d = kbd->cache[i].d; |
|
if (dl) |
|
*dl = kbd->cache[i].dl; |
|
|
|
return 0; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
|
|
static void send_key(struct keyboard *kbd, uint8_t code, uint8_t pressed) |
|
{ |
|
if (code == KEYD_NOOP || code == KEYD_EXTERNAL_MOUSE_BUTTON) |
|
return; |
|
|
|
if (pressed) |
|
kbd->last_pressed_output_code = code; |
|
|
|
kbd->keystate[code] = pressed; |
|
|
|
switch (code) { |
|
case KEYD_LEFT_MOUSE: |
|
vkbd_send_button(vkbd, 1, pressed); |
|
break; |
|
case KEYD_MIDDLE_MOUSE: |
|
vkbd_send_button(vkbd, 2, pressed); |
|
break; |
|
case KEYD_RIGHT_MOUSE: |
|
vkbd_send_button(vkbd, 3, pressed); |
|
break; |
|
default: |
|
vkbd_send_key(vkbd, code, pressed); |
|
break; |
|
} |
|
} |
|
|
|
static void set_mods(struct keyboard *kbd, uint8_t mods) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < sizeof modifier_table / sizeof(modifier_table[0]); i++) { |
|
uint8_t code = modifier_table[i].code1; |
|
uint8_t mask = modifier_table[i].mask; |
|
|
|
if (mask & mods) { |
|
if (!kbd->keystate[code]) |
|
send_key(kbd, code, 1); |
|
} else { |
|
/* |
|
* Some modifiers have a special meaning when used in |
|
* isolation (e.g meta in Gnome, alt in Firefox). |
|
* In order to prevent spurious key presses we |
|
* avoid adjacent down/up pairs by interposing |
|
* additional control sequences. |
|
*/ |
|
int guard = ((((kbd->last_pressed_output_code == KEYD_LEFTMETA) && mask == MOD_SUPER) || |
|
((kbd->last_pressed_output_code == KEYD_LEFTALT) && mask == MOD_ALT)) && |
|
!kbd->inhibit_modifier_guard); |
|
|
|
if (kbd->keystate[code]) { |
|
if (guard && !kbd->keystate[KEYD_LEFTCTRL]) { |
|
send_key(kbd, KEYD_LEFTCTRL, 1); |
|
send_key(kbd, code, 0); |
|
send_key(kbd, KEYD_LEFTCTRL, 0); |
|
} else { |
|
send_key(kbd, code, 0); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
static void update_mods(struct keyboard *kbd, struct layer *excluded_layer, uint8_t mods) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < (int)kbd->layer_table.nr_layers; i++) { |
|
struct layer *layer = &kbd->layer_table.layers[i]; |
|
int excluded = 0; |
|
|
|
if (!layer->active) |
|
continue; |
|
|
|
if (layer == excluded_layer) { |
|
excluded = 1; |
|
} else if (excluded_layer && excluded_layer->type == LT_COMPOSITE) { |
|
size_t j; |
|
|
|
for (j = 0; j < excluded_layer->nr_layers; j++) |
|
if (excluded_layer->layers[j] == i) |
|
excluded = 1; |
|
} |
|
|
|
if (!excluded) |
|
mods |= layer->mods; |
|
} |
|
|
|
set_mods(kbd, mods); |
|
} |
|
|
|
static void execute_macro(struct keyboard *kbd, struct layer *dl, const struct macro *macro) |
|
{ |
|
size_t i; |
|
int hold_start = -1; |
|
|
|
for (i = 0; i < macro->sz; i++) { |
|
const struct macro_entry *ent = ¯o->entries[i]; |
|
|
|
switch (ent->type) { |
|
size_t j; |
|
uint16_t n; |
|
uint8_t code, mods; |
|
|
|
case MACRO_HOLD: |
|
if (hold_start == -1) { |
|
update_mods(kbd, dl, 0); |
|
hold_start = i; |
|
} |
|
|
|
send_key(kbd, ent->data, 1); |
|
|
|
break; |
|
case MACRO_RELEASE: |
|
if (hold_start != -1) { |
|
size_t j; |
|
|
|
for (j = hold_start; j < i; j++) { |
|
const struct macro_entry *ent = ¯o->entries[j]; |
|
send_key(kbd, ent->data, 0); |
|
} |
|
|
|
hold_start = -1; |
|
} |
|
break; |
|
case MACRO_UNICODE: |
|
n = ent->data; |
|
|
|
send_key(kbd, KEYD_LINEFEED, 1); |
|
send_key(kbd, KEYD_LINEFEED, 0); |
|
|
|
for (j = 10000; j; j /= 10) { |
|
int digit = n / j; |
|
code = digit == 0 ? KEYD_0 : digit - 1 + KEYD_1; |
|
|
|
send_key(kbd, code, 1); |
|
send_key(kbd, code, 0); |
|
n %= j; |
|
} |
|
break; |
|
case MACRO_KEYSEQUENCE: |
|
code = ent->data; |
|
mods = ent->data >> 8; |
|
|
|
if (kbd->keystate[code]) |
|
send_key(kbd, code, 0); |
|
|
|
update_mods(kbd, dl, mods); |
|
send_key(kbd, code, 1); |
|
send_key(kbd, code, 0); |
|
|
|
break; |
|
case MACRO_TIMEOUT: |
|
usleep(ent->data*1E3); |
|
break; |
|
} |
|
|
|
} |
|
|
|
update_mods(kbd, NULL, 0); |
|
} |
|
|
|
static void lookup_descriptor(struct keyboard *kbd, uint8_t code, |
|
struct descriptor *d, struct layer **dl) |
|
{ |
|
size_t max; |
|
size_t i; |
|
uint8_t active[MAX_LAYERS]; |
|
struct layer_table *lt = &kbd->layer_table; |
|
|
|
d->op = OP_UNDEFINED; |
|
|
|
long maxts = 0; |
|
|
|
for (i = 0; i < lt->nr_layers; i++) { |
|
struct layer *layer = <->layers[i]; |
|
|
|
if (layer->active) { |
|
active[i] = 1; |
|
if (layer->keymap[code].op && layer->activation_time >= maxts) { |
|
maxts = layer->activation_time; |
|
*d = layer->keymap[code]; |
|
*dl = layer; |
|
} |
|
} else { |
|
active[i] = 0; |
|
} |
|
} |
|
|
|
max = 0; |
|
/* Scan for any composite matches (which take precedence). */ |
|
for (i = 0; i < lt->nr_layers; i++) { |
|
struct layer *layer = <->layers[i]; |
|
|
|
if (layer->type == LT_COMPOSITE) { |
|
size_t j; |
|
int match = 1; |
|
uint8_t mods = 0; |
|
|
|
for (j = 0; j < layer->nr_layers; j++) { |
|
if (active[layer->layers[j]]) |
|
mods |= lt->layers[layer->layers[j]].mods; |
|
else |
|
match = 0; |
|
} |
|
|
|
if (match && layer->keymap[code].op && (layer->nr_layers > max)) { |
|
*dl = layer; |
|
*d = layer->keymap[code]; |
|
|
|
max = layer->nr_layers; |
|
} |
|
} |
|
} |
|
} |
|
|
|
static void update_leds(struct keyboard *kbd) |
|
{ |
|
struct layer_table *lt = &kbd->layer_table; |
|
|
|
if (!kbd->config.layer_indicator) |
|
return; |
|
|
|
size_t i; |
|
int active = 0; |
|
|
|
for (i = 0; i < lt->nr_layers; i++) { |
|
if (lt->layers[i].active && lt->layers[i].mods) |
|
active = 1; |
|
} |
|
|
|
device_set_led(kbd->dev, 1, active); |
|
} |
|
|
|
static void deactivate_layer(struct keyboard *kbd, struct layer *layer) |
|
{ |
|
if (layer->active > 0) |
|
layer->active--; |
|
} |
|
|
|
/* |
|
* NOTE: Every activation call *must* be paired with a |
|
* corresponding deactivation call. |
|
*/ |
|
|
|
static void activate_layer(struct keyboard *kbd, uint8_t code, struct layer *layer) |
|
{ |
|
layer->active++; |
|
layer->activation_time = get_time(); |
|
|
|
kbd->last_layer_code = code; |
|
} |
|
|
|
static void execute_command(const char *cmd) |
|
{ |
|
int fd; |
|
|
|
dbg("Executing command: %s", cmd); |
|
|
|
if (fork()) |
|
return; |
|
|
|
fd = open("/dev/null", O_RDWR); |
|
|
|
if (fd < 0) { |
|
perror("open"); |
|
exit(-1); |
|
} |
|
|
|
close(0); |
|
close(1); |
|
close(2); |
|
|
|
dup2(fd, 0); |
|
dup2(fd, 1); |
|
dup2(fd, 2); |
|
|
|
execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); |
|
} |
|
|
|
static void clear_oneshot(struct keyboard *kbd) |
|
{ |
|
size_t i = 0; |
|
struct layer *layers = kbd->layer_table.layers; |
|
size_t nr_layers = kbd->layer_table.nr_layers; |
|
|
|
for (i = 0; i < nr_layers; i++) |
|
if (layers[i].flags & LF_ONESHOT) { |
|
layers[i].flags &= ~LF_ONESHOT; |
|
deactivate_layer(kbd, &layers[i]); |
|
} |
|
|
|
kbd->oneshot_latch = 0; |
|
} |
|
|
|
static long process_descriptor(struct keyboard *kbd, uint8_t code, |
|
struct descriptor *d, struct layer *dl, |
|
int pressed) |
|
{ |
|
int timeout = 0; |
|
|
|
struct macro *macros = kbd->layer_table.macros; |
|
struct timeout *timeouts = kbd->layer_table.timeouts; |
|
struct layer *layers = kbd->layer_table.layers; |
|
struct command *commands = kbd->layer_table.commands; |
|
|
|
switch (d->op) { |
|
struct macro *macro; |
|
struct layer *layer; |
|
uint8_t mods; |
|
|
|
case OP_KEYSEQUENCE: |
|
code = d->args[0].code; |
|
mods = d->args[1].mods; |
|
|
|
if (pressed) { |
|
/* |
|
* Permit variations of the same key |
|
* to be actuated next to each other |
|
* E.G [/{ |
|
*/ |
|
if (kbd->keystate[code]) |
|
send_key(kbd, code, 0); |
|
|
|
update_mods(kbd, dl, mods); |
|
|
|
send_key(kbd, code, 1); |
|
clear_oneshot(kbd); |
|
} else { |
|
send_key(kbd, code, 0); |
|
update_mods(kbd, NULL, 0); |
|
} |
|
|
|
break; |
|
case OP_LAYER: |
|
layer = &layers[d->args[0].idx]; |
|
|
|
if (pressed) { |
|
activate_layer(kbd, code, layer); |
|
} else { |
|
deactivate_layer(kbd, layer); |
|
} |
|
|
|
if (kbd->last_pressed_code == code) { |
|
kbd->inhibit_modifier_guard = 1; |
|
update_mods(kbd, NULL, 0); |
|
kbd->inhibit_modifier_guard = 0; |
|
} else { |
|
update_mods(kbd, NULL, 0); |
|
} |
|
|
|
break; |
|
case OP_OVERLOAD: |
|
layer = &layers[d->args[0].idx]; |
|
macro = ¯os[d->args[1].idx]; |
|
|
|
if (pressed) { |
|
activate_layer(kbd, code, layer); |
|
update_mods(kbd, NULL, 0); |
|
} else { |
|
deactivate_layer(kbd, layer); |
|
|
|
if (kbd->last_pressed_code == code) { |
|
clear_oneshot(kbd); |
|
update_mods(kbd, dl, 0); |
|
execute_macro(kbd, dl, macro); |
|
} |
|
|
|
update_mods(kbd, NULL, 0); |
|
} |
|
|
|
break; |
|
case OP_ONESHOT: |
|
layer = &layers[d->args[0].idx]; |
|
|
|
if (pressed) { |
|
if (layer->active) { |
|
/* Neutralize the upstroke. */ |
|
cache_set(kbd, code, NULL, NULL); |
|
} else { |
|
activate_layer(kbd, code, layer); |
|
update_mods(kbd, dl, 0); |
|
kbd->oneshot_latch = 1; |
|
} |
|
} else { |
|
if (kbd->oneshot_latch) { |
|
layer->flags |= LF_ONESHOT; |
|
} else { |
|
deactivate_layer(kbd, layer); |
|
update_mods(kbd, NULL, 0); |
|
} |
|
} |
|
|
|
break; |
|
case OP_MACRO: |
|
macro = ¯os[d->args[0].idx]; |
|
|
|
if (pressed) { |
|
clear_oneshot(kbd); |
|
|
|
execute_macro(kbd, dl, macro); |
|
kbd->active_macro = macro; |
|
kbd->active_macro_layer = dl; |
|
|
|
timeout = macro->timeout == -1 ? |
|
kbd->config.macro_timeout : |
|
macro->timeout; |
|
} |
|
|
|
break; |
|
case OP_TOGGLE: |
|
layer = &layers[d->args[0].idx]; |
|
|
|
if (!pressed) { |
|
layer->flags ^= LF_TOGGLED; |
|
|
|
if (layer->flags & LF_TOGGLED) |
|
activate_layer(kbd, code, layer); |
|
else |
|
deactivate_layer(kbd, layer); |
|
} |
|
|
|
break; |
|
case OP_TIMEOUT: |
|
if (pressed) { |
|
kbd->pending_timeout.t = timeouts[d->args[0].idx]; |
|
kbd->pending_timeout.code = code; |
|
kbd->pending_timeout.dl = dl; |
|
kbd->pending_timeout.active = 1; |
|
|
|
timeout = kbd->pending_timeout.t.timeout; |
|
} |
|
break; |
|
case OP_COMMAND: |
|
if (pressed) |
|
execute_command(commands[d->args[0].idx].cmd); |
|
break; |
|
case OP_SWAP: |
|
layer = &layers[d->args[0].idx]; |
|
macro = d->args[1].idx == -1 ? NULL : ¯os[d->args[1].idx]; |
|
|
|
|
|
if (pressed) { |
|
struct descriptor od; |
|
struct layer *odl; |
|
|
|
if (!cache_get(kbd, kbd->last_layer_code, &od, &odl)) { |
|
struct layer *oldlayer = &layers[od.args[0].idx]; |
|
|
|
cache_set(kbd, kbd->last_layer_code, d, odl); |
|
cache_set(kbd, code, NULL, 0); |
|
|
|
activate_layer(kbd, kbd->last_layer_code, layer); |
|
deactivate_layer(kbd, oldlayer); |
|
update_mods(kbd, layer, 0); |
|
|
|
if (macro) |
|
execute_macro(kbd, layer, macro); |
|
} |
|
} else { |
|
deactivate_layer(kbd, layer); |
|
update_mods(kbd, layer, 0); |
|
} |
|
|
|
break; |
|
case OP_UNDEFINED: |
|
break; |
|
} |
|
|
|
update_leds(kbd); |
|
|
|
if (pressed) |
|
kbd->last_pressed_code = code; |
|
|
|
return timeout; |
|
} |
|
|
|
|
|
/* |
|
* `code` may be 0 in the event of a timeout. |
|
* |
|
* The return value corresponds to a timeout before which the next invocation |
|
* of kbd_process_key_event must take place. A return value of 0 permits the |
|
* main loop to call at liberty. |
|
*/ |
|
long kbd_process_key_event(struct keyboard *kbd, |
|
uint8_t code, int pressed) |
|
{ |
|
struct layer *dl = NULL; |
|
struct descriptor d; |
|
|
|
/* timeout */ |
|
if (!code) { |
|
if (kbd->active_macro) { |
|
execute_macro(kbd, kbd->active_macro_layer, kbd->active_macro); |
|
return kbd->active_macro->repeat_timeout == -1 ? |
|
kbd->config.macro_repeat_timeout : |
|
kbd->active_macro->repeat_timeout; |
|
} |
|
|
|
if (kbd->pending_timeout.active) { |
|
struct layer *dl = kbd->pending_timeout.dl; |
|
uint8_t code = kbd->pending_timeout.code; |
|
struct descriptor *d = &kbd->pending_timeout.t.d2; |
|
|
|
cache_set(kbd, code, d, dl); |
|
|
|
kbd->pending_timeout.active = 0; |
|
return process_descriptor(kbd, code, d, dl, 1); |
|
} |
|
} else { |
|
if (kbd->pending_timeout.active) { |
|
struct layer *dl = kbd->pending_timeout.dl; |
|
uint8_t code = kbd->pending_timeout.code; |
|
struct descriptor *d = &kbd->pending_timeout.t.d1; |
|
|
|
cache_set(kbd, code, d, dl); |
|
process_descriptor(kbd, code, d, dl, 1); |
|
} |
|
|
|
if (kbd->active_macro) { |
|
kbd->active_macro = NULL; |
|
update_mods(kbd, NULL, 0); |
|
} |
|
|
|
kbd->pending_timeout.active = 0; |
|
} |
|
|
|
|
|
if (pressed) { |
|
lookup_descriptor(kbd, code, &d, &dl); |
|
|
|
if (cache_set(kbd, code, &d, dl) < 0) |
|
return 0; |
|
} else { |
|
if (cache_get(kbd, code, &d, &dl) < 0) |
|
return 0; |
|
|
|
cache_set(kbd, code, NULL, NULL); |
|
} |
|
|
|
return process_descriptor(kbd, code, &d, dl, pressed); |
|
} |
|
|
|
int kbd_execute_expression(struct keyboard *kbd, const char *exp) |
|
{ |
|
return layer_table_add_entry(&kbd->layer_table, exp); |
|
} |
|
|
|
void kbd_reset(struct keyboard *kbd) |
|
{ |
|
uint8_t ad[MAX_LAYERS]; |
|
long at[MAX_LAYERS]; |
|
size_t i; |
|
|
|
/* Preserve layer state to facilitate hotswapping (TODO: make this more robust) */ |
|
|
|
for (i = 0; i < kbd->config.layer_table.nr_layers; i++) { |
|
ad[i] = kbd->layer_table.layers[i].active; |
|
at[i] = kbd->layer_table.layers[i].activation_time; |
|
} |
|
|
|
memcpy(&kbd->layer_table, |
|
&kbd->config.layer_table, |
|
sizeof(kbd->layer_table)); |
|
|
|
for (i = 0; i < kbd->config.layer_table.nr_layers; i++) { |
|
kbd->layer_table.layers[i].active = ad[i]; |
|
kbd->layer_table.layers[i].activation_time = at[i]; |
|
} |
|
} |
|
|
|
|