diff --git a/TODO b/TODO index 52f2a82..eb9adaf 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,7 @@ +streamline test logic +organize tests cleanup manpage [idea] tmux like layer timeouts -nested timeouts? (perhaps not...) toggleable composite layers? improved mouse support (integrate moused?) -organize tests multi-user support (remove keyd group) diff --git a/src/config.c b/src/config.c index d3feac7..52209f8 100644 --- a/src/config.c +++ b/src/config.c @@ -49,19 +49,19 @@ static int config_add_layer(struct config *config, const char *s) if (name && layer_table_lookup(&config->layer_table, name) != -1) return 1; - if (config->layer_table.nr >= MAX_LAYERS) { + if (config->layer_table.nr_layers >= MAX_LAYERS) { err("max layers (%d) exceeded", MAX_LAYERS); return -1; } - ret = create_layer(&config->layer_table.layers[config->layer_table.nr], + ret = create_layer(&config->layer_table.layers[config->layer_table.nr_layers], s, &config->layer_table); if (ret < 0) return -1; - config->layer_table.nr++; + config->layer_table.nr_layers++; return 0; } @@ -82,8 +82,9 @@ static void config_init(struct config *config) km = config->layer_table.layers[0].keymap; for (i = 0; i < 256; i++) { - km[i].op = OP_KEYCODE; + km[i].op = OP_KEYSEQUENCE; km[i].args[0].code = i; + km[i].args[1].mods = 0; } for (i = 0; i < MAX_MOD; i++) { @@ -101,8 +102,7 @@ static void config_init(struct config *config) ent2->args[0].idx = idx; } - config->layer_table.layers[0].flags = LF_ACTIVE; - + config->layer_table.layers[0].active = 1; /* In ms */ config->macro_timeout = 600; config->macro_repeat_timeout = 50; diff --git a/src/config.h b/src/config.h index 0752009..59e91c4 100644 --- a/src/config.h +++ b/src/config.h @@ -19,7 +19,7 @@ struct config { long layer_indicator; }; -const char *config_find_path(const char *dir, uint16_t vendor, uint16_t product); -int config_parse(struct config *config, const char *path); +int config_parse(struct config *config, const char *path); +const char *config_find_path(const char *dir, uint16_t vendor, uint16_t product); #endif diff --git a/src/descriptor.c b/src/descriptor.c index 1b19fc5..3f3455c 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -85,21 +85,6 @@ exit: } } -static uint8_t parse_code(const char *s) -{ - size_t i; - - for (i = 0; i < 256; i++) { - const struct keycode_table_ent *ent = &keycode_table[i]; - - if ((ent->name && !strcmp(ent->name, s)) || - (ent->alt_name && !strcmp(ent->alt_name, s))) - return i; - } - - return 0; -} - static int parse_sequence(const char *s, uint8_t *codep, uint8_t *modsp) { const char *c = s; @@ -390,7 +375,7 @@ int layer_table_lookup(const struct layer_table *lt, const char *name) { size_t i; - for (i = 0; i < lt->nr; i++) + for (i = 0; i < lt->nr_layers; i++) if (!strcmp(lt->layers[i].name, name)) return i; @@ -566,7 +551,7 @@ int parse_descriptor(const char *descstr, char *fn = NULL; char *args[MAX_ARGS]; size_t nargs = 0; - uint8_t code; + uint8_t code, mods; int idx; int ret; @@ -579,9 +564,10 @@ int parse_descriptor(const char *descstr, strcpy(fnstr, descstr); - if ((code = parse_code(descstr))) { - d->op = OP_KEYCODE; + if (!parse_sequence(descstr, &code, &mods)) { + d->op = OP_KEYSEQUENCE; d->args[0].code = code; + d->args[1].mods = mods; /* TODO: fixme. */ if (keycode_to_mod(code)) diff --git a/src/descriptor.h b/src/descriptor.h index 820c903..d882cc4 100644 --- a/src/descriptor.h +++ b/src/descriptor.h @@ -20,7 +20,7 @@ struct layer_table; enum op { OP_UNDEFINED, - OP_KEYCODE, + OP_KEYSEQUENCE, OP_ONESHOT, OP_SWAP, @@ -39,6 +39,7 @@ struct descriptor { union { uint8_t code; + uint8_t mods; int16_t idx; uint16_t sz; uint16_t timeout; diff --git a/src/device.h b/src/device.h index 16f0376..7ea2c8c 100644 --- a/src/device.h +++ b/src/device.h @@ -41,14 +41,14 @@ struct device_event { }; -struct device_event *device_read_event(struct device *dev); +struct device_event *device_read_event(struct device *dev); -int device_scan(struct device devices[MAX_DEVICES]); -int device_grab(struct device *dev); -int device_ungrab(struct device *dev); +int device_scan(struct device devices[MAX_DEVICES]); +int device_grab(struct device *dev); +int device_ungrab(struct device *dev); -int devmon_create(); -struct device *devmon_read_device(int fd); -void device_set_led(const struct device *dev, int led, int state); +int devmon_create(); +struct device *devmon_read_device(int fd); +void device_set_led(const struct device *dev, int led, int state); #endif diff --git a/src/ini.h b/src/ini.h index 07c10ee..b9653ba 100644 --- a/src/ini.h +++ b/src/ini.h @@ -56,8 +56,8 @@ struct ini { * freed. */ -struct ini *ini_parse_file(const char *path, const char *default_section_name); +struct ini *ini_parse_file(const char *path, const char *default_section_name); -int parse_kvp(char *s, char **key, char **value); +int parse_kvp(char *s, char **key, char **value); #endif diff --git a/src/ipc.h b/src/ipc.h index 1e73657..f12acbf 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -22,9 +22,8 @@ #define MAX_MESSAGE_SIZE 4096 - -int ipc_create_server(const char *path); -void ipc_server_process_connection(int sd, int (*handler) (int fd, const char *input)); -int ipc_run(const char *socket, const char *input); +int ipc_create_server(const char *path); +void ipc_server_process_connection(int sd, int (*handler) (int fd, const char *input)); +int ipc_run(const char *socket, const char *input); #endif diff --git a/src/keyboard.c b/src/keyboard.c index f4e016d..ef365c7 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -14,18 +14,65 @@ #include "descriptor.h" #include "layer.h" -static struct macro *active_macro = NULL; -static uint8_t active_macro_mods = 0; -static uint8_t oneshot_latch = 0; +/* + * Here be tiny dragons. + */ static long get_time() { - /* close enough :/. using a syscall is unnecessary. */ + /* Close enough :/. Using a syscall is unnecessary. */ static long time = 1; return time++; } -static void kbd_send_key(struct keyboard *kbd, uint8_t code, uint8_t pressed) +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; @@ -51,14 +98,7 @@ static void kbd_send_key(struct keyboard *kbd, uint8_t code, uint8_t pressed) } } -/* - * refcounted to account for overlapping active mods without adding manual - * accounting to the calling code, each send_mods(foo, 1) *must* be accompanied - * by a corresponding send_mods(foo, 0) at some point. Failure to do so is - * considered a bug (i.e treat this like malloc). - */ - -static void send_mods(struct keyboard *kbd, uint8_t mods, int press) +static void set_mods(struct keyboard *kbd, uint8_t mods) { size_t i; @@ -67,63 +107,66 @@ static void send_mods(struct keyboard *kbd, uint8_t mods, int press) uint8_t mask = modifier_table[i].mask; if (mask & mods) { - kbd->modstate[i] += press ? 1 : -1; - - if (kbd->modstate[i] == 0) - kbd_send_key(kbd, code, 0); - else if (kbd->modstate[i] == 1) - kbd_send_key(kbd, code, 1); + 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); + } + } } } } -/* intelligently disarm active mods to avoid spurious alt/meta keypresses. */ -static void disarm_mods(struct keyboard *kbd, uint8_t mods) +static void update_mods(struct keyboard *kbd, struct layer *excluded_layer, uint8_t mods) { - uint8_t dmods = (MOD_ALT|MOD_SUPER) & mods; - /* - * We interpose a control sequence to prevent ' ' - * from being interpreted as an alt keypress. - */ - if (dmods && ((kbd->last_pressed_output_code == KEYD_LEFTMETA) || (kbd->last_pressed_output_code == KEYD_LEFTALT))) { - if (kbd->keystate[KEYD_LEFTCTRL]) - send_mods(kbd, dmods, 0); - else { - kbd_send_key(kbd, KEYD_LEFTCTRL, 1); - send_mods(kbd, dmods, 0); - kbd_send_key(kbd, KEYD_LEFTCTRL, 0); + 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; } - mods &= ~dmods; + if (!excluded) + mods |= layer->mods; } - send_mods(kbd, mods, 0); + set_mods(kbd, mods); } -static void execute_macro(struct keyboard *kbd, const struct macro *macro, uint8_t disable_mods) +static void execute_macro(struct keyboard *kbd, struct layer *dl, const struct macro *macro) { size_t i; int hold_start = -1; - /* - * Minimize unnecessary noise by avoiding redundant modifier key up/down - * events in the case that the requisite modifiers are already present - * in the layer modifier set and the macro is a simple key sequence. - * - * This makes common cases like: - * - * [meta] - * - * a = M-b - * - * less likely to produce undesirable side effects as a consequence of additional - * meta up/down presses. - */ - if (macro->sz == 1 && macro->entries[0].type == MACRO_KEYSEQUENCE) - disable_mods &= ~(macro->entries[0].data >> 8); - - disarm_mods(kbd, disable_mods); - for (i = 0; i < macro->sz; i++) { const struct macro_entry *ent = ¯o->entries[i]; @@ -133,10 +176,12 @@ static void execute_macro(struct keyboard *kbd, const struct macro *macro, uint8 uint8_t code, mods; case MACRO_HOLD: - if (hold_start == -1) + if (hold_start == -1) { + update_mods(kbd, dl, 0); hold_start = i; + } - kbd_send_key(kbd, ent->data, 1); + send_key(kbd, ent->data, 1); break; case MACRO_RELEASE: @@ -145,7 +190,7 @@ static void execute_macro(struct keyboard *kbd, const struct macro *macro, uint8 for (j = hold_start; j < i; j++) { const struct macro_entry *ent = ¯o->entries[j]; - kbd_send_key(kbd, ent->data, 0); + send_key(kbd, ent->data, 0); } hold_start = -1; @@ -154,15 +199,15 @@ static void execute_macro(struct keyboard *kbd, const struct macro *macro, uint8 case MACRO_UNICODE: n = ent->data; - kbd_send_key(kbd, KEYD_LINEFEED, 1); - kbd_send_key(kbd, KEYD_LINEFEED, 0); + 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; - kbd_send_key(kbd, code, 1); - kbd_send_key(kbd, code, 0); + send_key(kbd, code, 1); + send_key(kbd, code, 0); n %= j; } break; @@ -171,12 +216,11 @@ static void execute_macro(struct keyboard *kbd, const struct macro *macro, uint8 mods = ent->data >> 8; if (kbd->keystate[code]) - kbd_send_key(kbd, code, 0); + send_key(kbd, code, 0); - send_mods(kbd, mods, 1); - kbd_send_key(kbd, code, 1); - kbd_send_key(kbd, code, 0); - send_mods(kbd, mods, 0); + update_mods(kbd, dl, mods); + send_key(kbd, code, 1); + send_key(kbd, code, 0); break; case MACRO_TIMEOUT: @@ -186,58 +230,30 @@ static void execute_macro(struct keyboard *kbd, const struct macro *macro, uint8 } - send_mods(kbd, disable_mods, 1); -} - -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 flags[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; i++) { - flags[i] = kbd->layer_table.layers[i].flags; - 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; i++) { - kbd->layer_table.layers[i].flags = flags[i]; - kbd->layer_table.layers[i].activation_time = at[i]; - } - + update_mods(kbd, NULL, 0); } -static void lookup_descriptor(struct keyboard *kbd, uint8_t code, uint8_t *layer_mods, struct descriptor *d) +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; - *layer_mods = 0; long maxts = 0; - for (i = 0; i < lt->nr; i++) { + for (i = 0; i < lt->nr_layers; i++) { struct layer *layer = <->layers[i]; - if (layer->flags) { + if (layer->active) { active[i] = 1; if (layer->keymap[code].op && layer->activation_time >= maxts) { maxts = layer->activation_time; - *layer_mods = layer->mods; *d = layer->keymap[code]; + *dl = layer; } } else { active[i] = 0; @@ -246,7 +262,7 @@ static void lookup_descriptor(struct keyboard *kbd, uint8_t code, uint8_t *layer max = 0; /* Scan for any composite matches (which take precedence). */ - for (i = 0; i < lt->nr; i++) { + for (i = 0; i < lt->nr_layers; i++) { struct layer *layer = <->layers[i]; if (layer->type == LT_COMPOSITE) { @@ -262,7 +278,7 @@ static void lookup_descriptor(struct keyboard *kbd, uint8_t code, uint8_t *layer } if (match && layer->keymap[code].op && (layer->nr_layers > max)) { - *layer_mods = mods; + *dl = layer; *d = layer->keymap[code]; max = layer->nr_layers; @@ -271,231 +287,180 @@ static void lookup_descriptor(struct keyboard *kbd, uint8_t code, uint8_t *layer } } -static int cache_set(struct keyboard *kbd, uint8_t code, const struct descriptor *d, uint8_t mods) +static void update_leds(struct keyboard *kbd) { - size_t i; - int slot = -1; + struct layer_table *lt = &kbd->layer_table; - 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 (!kbd->config.layer_indicator) + return; - if (slot == -1) - return -1; + size_t i; + int active = 0; - if (d == NULL) { - kbd->cache[slot].code = 0; - } else { - kbd->cache[slot].code = code; - kbd->cache[slot].d = *d; - kbd->cache[slot].layermods = mods; + for (i = 0; i < lt->nr_layers; i++) { + if (lt->layers[i].active && lt->layers[i].mods) + active = 1; } - return 0; + device_set_led(kbd->dev, 1, active); } -static int cache_get(struct keyboard *kbd, uint8_t code, struct descriptor *d, uint8_t *mods) +static void deactivate_layer(struct keyboard *kbd, struct layer *layer) { - size_t i; - - for (i = 0; i < CACHE_SIZE; i++) - if (kbd->cache[i].code == code) { - if (d) - *d = kbd->cache[i].d; - if (mods) - *mods = kbd->cache[i].layermods; - - return 0; - } - - return -1; + if (layer->active > 0) + layer->active--; } -static void activate_layer(struct keyboard *kbd, struct layer *layer) -{ - layer->flags |= LF_ACTIVE; - send_mods(kbd, layer->mods, 1); - layer->activation_time = get_time(); -} +/* + * NOTE: Every activation call *must* be paired with a + * corresponding deactivation call. + */ -static void deactivate_layer(struct keyboard *kbd, struct layer *layer, int disarm_p) +static void activate_layer(struct keyboard *kbd, uint8_t code, struct layer *layer) { - layer->flags &= ~LF_ACTIVE; + layer->active++; + layer->activation_time = get_time(); - if (disarm_p) - disarm_mods(kbd, layer->mods); - else - send_mods(kbd, layer->mods, 0); + kbd->last_layer_code = code; } -static void update_leds(struct keyboard *kbd) +static void clear_oneshot(struct keyboard *kbd) { - struct layer_table *lt = &kbd->layer_table; - - if (!kbd->config.layer_indicator) - return; - - size_t i; - int active = 0; + size_t i = 0; + struct layer *layers = kbd->layer_table.layers; + size_t nr_layers = kbd->layer_table.nr_layers; - for (i = 0; i < lt->nr; i++) { - if (lt->layers[i].flags && lt->layers[i].mods) - active = 1; - } + for (i = 0; i < nr_layers; i++) + if (layers[i].flags & LF_ONESHOT) { + layers[i].flags &= ~LF_ONESHOT; + deactivate_layer(kbd, &layers[i]); + } - if (active) { - device_set_led(kbd->dev, 1, 1); - } else { - device_set_led(kbd->dev, 1, 0); - } + kbd->oneshot_latch = 0; } -static long process_descriptor(struct keyboard *kbd, uint8_t code, struct descriptor *d, int descriptor_layer_mods, int pressed) +static long process_descriptor(struct keyboard *kbd, uint8_t code, + struct descriptor *d, struct layer *dl, + int pressed) { int timeout = 0; - uint8_t clear_oneshot = 0; struct macro *macros = kbd->layer_table.macros; struct timeout *timeouts = kbd->layer_table.timeouts; struct layer *layers = kbd->layer_table.layers; - size_t nr_layers = kbd->layer_table.nr; switch (d->op) { struct macro *macro; struct layer *layer; + uint8_t mods; - case OP_MACRO: - macro = ¯os[d->args[0].idx]; + case OP_KEYSEQUENCE: + code = d->args[0].code; + mods = d->args[1].mods; if (pressed) { - execute_macro(kbd, macro, descriptor_layer_mods); + /* + * Permit variations of the same key + * to be actuated next to each other + * E.G [/{ + */ + if (kbd->keystate[code]) + send_key(kbd, code, 0); - active_macro = macro; - active_macro_mods = descriptor_layer_mods; + update_mods(kbd, dl, mods); - timeout = kbd->config.macro_timeout; + send_key(kbd, code, 1); + clear_oneshot(kbd); + } else { + send_key(kbd, code, 0); + update_mods(kbd, NULL, 0); } - oneshot_latch = 0; - clear_oneshot = 1; break; - case OP_ONESHOT: + case OP_LAYER: layer = &layers[d->args[0].idx]; if (pressed) { - if (layer->flags & LF_ONESHOT_HELD) { - /* Neutralize key up */ - cache_set(kbd, code, NULL, 0); - } else { - if (layer->flags & LF_ONESHOT) { - disarm_mods(kbd, layer->mods); - layer->flags &= ~LF_ONESHOT; - } - - send_mods(kbd, layer->mods, 1); - - oneshot_latch = 1; - layer->flags |= LF_ONESHOT_HELD; - layer->activation_time = get_time(); - } - } else if (oneshot_latch) { - if (layer->flags & LF_ONESHOT) { - /* - * If oneshot is already set for the layer we can't - * rely on the clear logic to mirror our send_mod() - * call. - */ - disarm_mods(kbd, layer->mods); - } else { - layer->flags |= LF_ONESHOT; - layer->flags &= ~LF_ONESHOT_HELD; - } + activate_layer(kbd, code, layer); } else { - send_mods(kbd, layer->mods, 0); + deactivate_layer(kbd, layer); + } - layer->flags &= ~LF_ONESHOT_HELD; + 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_TOGGLE: + case OP_OVERLOAD: layer = &layers[d->args[0].idx]; + macro = ¯os[d->args[1].idx]; - if (!pressed) { - layer->flags ^= LF_TOGGLE; - - if (layer->flags & LF_TOGGLE) - activate_layer(kbd, layer); - else - deactivate_layer(kbd, layer, 0); - } - - clear_oneshot = 1; - break; - case OP_KEYCODE: if (pressed) { - disarm_mods(kbd, descriptor_layer_mods); - kbd_send_key(kbd, d->args[0].code, 1); + activate_layer(kbd, code, layer); + update_mods(kbd, NULL, 0); } else { - kbd_send_key(kbd, d->args[0].code, 0); - send_mods(kbd, descriptor_layer_mods, 1); - clear_oneshot = 1; + 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); } - oneshot_latch = 0; break; - case OP_LAYER: + case OP_ONESHOT: layer = &layers[d->args[0].idx]; if (pressed) { - activate_layer(kbd, layer); - kbd->last_layer_code = code; + 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 { - deactivate_layer(kbd, layer, kbd->last_pressed_keycode != code); + if (kbd->oneshot_latch) { + layer->flags |= LF_ONESHOT; + } else { + deactivate_layer(kbd, layer); + update_mods(kbd, NULL, 0); + } } + break; - case OP_SWAP: - layer = &layers[d->args[0].idx]; - macro = d->args[1].idx == -1 ? NULL : ¯os[d->args[1].idx]; + case OP_MACRO: + macro = ¯os[d->args[0].idx]; if (pressed) { - struct descriptor od; - - if (!cache_get(kbd, kbd->last_layer_code, &od, NULL)) { - struct layer *oldlayer = &layers[od.args[0].idx]; + clear_oneshot(kbd); - cache_set(kbd, kbd->last_layer_code, d, descriptor_layer_mods); - cache_set(kbd, code, NULL, 0); + execute_macro(kbd, dl, macro); + kbd->active_macro = macro; + kbd->active_macro_layer = dl; - activate_layer(kbd, layer); - deactivate_layer(kbd, oldlayer, 1); - - if (macro) - execute_macro(kbd, macro, layer->mods); - } - } else - deactivate_layer(kbd, layer, 1); + timeout = kbd->config.macro_timeout; + } break; - case OP_OVERLOAD: + case OP_TOGGLE: layer = &layers[d->args[0].idx]; - macro = ¯os[d->args[1].idx]; - - if (pressed) { - activate_layer(kbd, layer); - kbd->last_layer_code = code; - } else { - deactivate_layer(kbd, layer, 1); - if (kbd->last_pressed_keycode == code) { - execute_macro(kbd, macro, descriptor_layer_mods); + if (!pressed) { + layer->flags ^= LF_TOGGLED; - oneshot_latch = 0; - clear_oneshot = 1; - } + if (layer->flags & LF_TOGGLED) + activate_layer(kbd, code, layer); + else + deactivate_layer(kbd, layer); } break; @@ -503,44 +468,52 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, struct descri if (pressed) { kbd->pending_timeout.t = timeouts[d->args[0].idx]; kbd->pending_timeout.code = code; - kbd->pending_timeout.mods = descriptor_layer_mods; + kbd->pending_timeout.dl = dl; + kbd->pending_timeout.active = 1; timeout = kbd->pending_timeout.t.timeout; } break; - case OP_UNDEFINED: - if (pressed) - clear_oneshot = 1; - break; - } + case OP_SWAP: + layer = &layers[d->args[0].idx]; + macro = d->args[1].idx == -1 ? NULL : ¯os[d->args[1].idx]; - if (clear_oneshot) { - size_t i = 0; - for (i = 0; i < nr_layers; i++) { - struct layer *layer = &layers[i]; + 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]; - if (layer->flags & LF_ONESHOT) { - layer->flags &= ~LF_ONESHOT; + 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); - send_mods(kbd, layer->mods, 0); + if (macro) + execute_macro(kbd, layer, macro); } + } else { + deactivate_layer(kbd, layer); + update_mods(kbd, layer, mods); } - oneshot_latch = 0; + break; } + update_leds(kbd); + if (pressed) - kbd->last_pressed_keycode = code; + kbd->last_pressed_code = code; - update_leds(kbd); return timeout; } /* - * Here be tiny dragons. - * * `code` may be 0 in the event of a timeout. * * The return value corresponds to a timeout before which the next invocation @@ -548,54 +521,87 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, struct descri * main loop to call at liberty. */ long kbd_process_key_event(struct keyboard *kbd, - uint8_t code, - int pressed) + uint8_t code, int pressed) { - uint8_t descriptor_layer_mods; + struct layer *dl; struct descriptor d; /* timeout */ if (!code) { - if (active_macro) { - execute_macro(kbd, active_macro, active_macro_mods); + if (kbd->active_macro) { + execute_macro(kbd, kbd->active_macro_layer, kbd->active_macro); return kbd->config.macro_repeat_timeout; - } else if (kbd->pending_timeout.code) { - uint8_t mods = kbd->pending_timeout.mods; + } + + 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, mods); + cache_set(kbd, code, d, dl); - kbd->pending_timeout.code = 0; - return process_descriptor(kbd, code, d, mods, 1); + 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; - if (kbd->pending_timeout.code) { - uint8_t mods = kbd->pending_timeout.mods; - 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); + } - cache_set(kbd, code, d, mods); - process_descriptor(kbd, code, d, mods, 1); + if (kbd->active_macro) { + kbd->active_macro = NULL; + update_mods(kbd, NULL, 0); + } - kbd->pending_timeout.code = 0; + kbd->pending_timeout.active = 0; } - if (active_macro) - active_macro = NULL; if (pressed) { - lookup_descriptor(kbd, code, &descriptor_layer_mods, &d); + lookup_descriptor(kbd, code, &d, &dl); - if (cache_set(kbd, code, &d, descriptor_layer_mods) < 0) + if (cache_set(kbd, code, &d, dl) < 0) return 0; } else { - if (cache_get(kbd, code, &d, &descriptor_layer_mods) < 0) + if (cache_get(kbd, code, &d, &dl) < 0) return 0; - cache_set(kbd, code, NULL, 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; } - return process_descriptor(kbd, code, &d, descriptor_layer_mods, pressed); + 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]; + } } + diff --git a/src/keyboard.h b/src/keyboard.h index 3944865..9c54536 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -15,7 +15,7 @@ struct cache_entry { uint8_t code; struct descriptor d; - uint8_t layermods; + struct layer *dl; }; struct keyboard { @@ -25,23 +25,34 @@ struct keyboard { struct layer_table layer_table; - /* state*/ - - /* for key up events */ + /* + * Cache descriptors to preserve code->descriptor + * mappings in the event of mid-stroke layer changes. + */ struct cache_entry cache[CACHE_SIZE]; + uint8_t transient_mods; + uint8_t last_pressed_output_code; - uint8_t last_pressed_keycode; + uint8_t last_pressed_code; uint8_t last_layer_code; + uint8_t oneshot_latch; + + uint8_t inhibit_modifier_guard; + + struct macro *active_macro; + struct layer *active_macro_layer; + struct { + uint8_t active; + uint8_t code; - uint8_t mods; + struct layer *dl; struct timeout t; } pending_timeout; uint8_t keystate[256]; - uint8_t modstate[MAX_MOD]; }; long kbd_process_key_event(struct keyboard *kbd, uint8_t code, int pressed); diff --git a/src/keys.c b/src/keys.c index 4155068..b00152c 100644 --- a/src/keys.c +++ b/src/keys.c @@ -7,12 +7,12 @@ #include "keys.h" const struct modifier_table_ent modifier_table[MAX_MOD] = { - {"control", MOD_CTRL, KEYD_LEFTCTRL, KEYD_RIGHTCTRL}, + {"alt", MOD_ALT, KEYD_LEFTALT, 0}, + {"altgr", MOD_ALT_GR, KEYD_RIGHTALT, 0}, {"shift", MOD_SHIFT, KEYD_LEFTSHIFT, KEYD_RIGHTSHIFT}, {"meta", MOD_SUPER, KEYD_LEFTMETA, KEYD_RIGHTMETA}, - {"alt", MOD_ALT, KEYD_LEFTALT, 0}, - {"altgr", MOD_ALT_GR, KEYD_RIGHTALT, 0}, + {"control", MOD_CTRL, KEYD_LEFTCTRL, KEYD_RIGHTCTRL}, }; const struct keycode_table_ent keycode_table[256] = { diff --git a/src/keys.h b/src/keys.h index 072d26d..0e02f17 100644 --- a/src/keys.h +++ b/src/keys.h @@ -10,17 +10,14 @@ #include #include -#define MOD_ALT_GR 0x10 -#define MOD_CTRL 0x8 -#define MOD_SHIFT 0x4 -#define MOD_SUPER 0x2 -#define MOD_ALT 0x1 +#define MOD_ALT_GR 0x10 +#define MOD_CTRL 0x8 +#define MOD_SHIFT 0x4 +#define MOD_SUPER 0x2 +#define MOD_ALT 0x1 #define MAX_MOD 5 -uint8_t keycode_to_mod(uint8_t code); -int parse_modset(const char *s, uint8_t *mods); - struct keycode_table_ent { const char *name; const char *alt_name; @@ -291,6 +288,9 @@ struct modifier_table_ent { #define KEYD_MOUSE_2 253 #define KEYD_FN 254 +uint8_t keycode_to_mod(uint8_t code); +int parse_modset(const char *s, uint8_t *mods); + extern const struct modifier_table_ent modifier_table[MAX_MOD]; extern const struct keycode_table_ent keycode_table[256]; diff --git a/src/layer.h b/src/layer.h index 427ec03..9b6ab49 100644 --- a/src/layer.h +++ b/src/layer.h @@ -20,10 +20,8 @@ #define LT_LAYOUT 1 #define LT_COMPOSITE 2 -#define LF_ACTIVE 0x1 -#define LF_TOGGLE 0x2 -#define LF_ONESHOT 0x4 -#define LF_ONESHOT_HELD 0x8 +#define LF_TOGGLED 0x1 +#define LF_ONESHOT 0x2 /* * A layer is a map from keycodes to descriptors. It may optionally @@ -45,6 +43,7 @@ struct layer { struct descriptor keymap[256]; /* state */ + uint8_t active; uint8_t flags; long activation_time; }; @@ -78,7 +77,7 @@ struct macro { struct layer_table { struct layer layers[MAX_LAYERS]; - size_t nr; + size_t nr_layers; struct timeout timeouts[MAX_TIMEOUTS]; struct macro macros[MAX_MACROS]; diff --git a/src/vkbd.h b/src/vkbd.h index bb72148..a92ad84 100644 --- a/src/vkbd.h +++ b/src/vkbd.h @@ -10,11 +10,12 @@ struct vkbd; -struct vkbd *vkbd_init(const char *name); -void vkbd_move_mouse(const struct vkbd *vkbd, int x, int y); -void vkbd_send_key(const struct vkbd *vkbd, uint8_t code, int state); -void vkbd_send_button(const struct vkbd *vkbd, uint8_t btn, int state); +struct vkbd *vkbd_init(const char *name); -void free_vkbd(struct vkbd *vkbd); +void vkbd_move_mouse(const struct vkbd *vkbd, int x, int y); +void vkbd_send_key(const struct vkbd *vkbd, uint8_t code, int state); +void vkbd_send_button(const struct vkbd *vkbd, uint8_t btn, int state); + +void free_vkbd(struct vkbd *vkbd); #endif diff --git a/t/composite.t b/t/composite.t index 952c177..9d67f80 100644 --- a/t/composite.t +++ b/t/composite.t @@ -8,14 +8,12 @@ alt up control down alt down alt up -control up shift down +control up h down h up +alt down shift up control down -alt down control up -control down alt up -control up diff --git a/t/composite2.t b/t/composite2.t index 6edc1d8..2cd455a 100644 --- a/t/composite2.t +++ b/t/composite2.t @@ -11,15 +11,13 @@ control down shift down alt down alt up -control up shift up +control up left down left up -control down -shift down alt down +shift down +control down control up shift up -control down alt up -control up diff --git a/t/layout-seq.t b/t/layout-seq.t index d332d5d..5caea94 100644 --- a/t/layout-seq.t +++ b/t/layout-seq.t @@ -1,11 +1,11 @@ 9 down 9 up -control down shift down meta down +control down x down x up -control up shift up meta up +control up diff --git a/t/layout.t b/t/layout.t index 8128a64..09211b9 100644 --- a/t/layout.t +++ b/t/layout.t @@ -14,14 +14,14 @@ x up o down o up alt down -control down shift down meta down +control down x down x up -control up shift up meta up +control up alt up x down x up diff --git a/t/oneshot-single-key.t b/t/oneshot-single-key.t new file mode 100644 index 0000000..ceec1c8 --- /dev/null +++ b/t/oneshot-single-key.t @@ -0,0 +1,13 @@ +2 down +2 up +u down +i down +u up +i up + +shift down +u down +shift up +i down +u up +i up diff --git a/t/oneshot10.t b/t/oneshot10.t index a740479..c42f3d3 100644 --- a/t/oneshot10.t +++ b/t/oneshot10.t @@ -11,7 +11,5 @@ o down o up n down n up -shift down -shift up x down x up diff --git a/t/oneshot12.t b/t/oneshot12.t index 7424dac..a529264 100644 --- a/t/oneshot12.t +++ b/t/oneshot12.t @@ -16,8 +16,6 @@ o up n down n up shift down -shift up -shift down a down a up shift up diff --git a/t/oneshot14.t b/t/oneshot14.t index cafa121..2751e5f 100644 --- a/t/oneshot14.t +++ b/t/oneshot14.t @@ -11,7 +11,5 @@ shift down shift up a down a up -shift down -shift up b down b up diff --git a/t/oneshot5.t b/t/oneshot5.t index 9e1e7a0..a186c24 100644 --- a/t/oneshot5.t +++ b/t/oneshot5.t @@ -9,5 +9,5 @@ shift down control down i down i up -control up shift up +control up diff --git a/t/oneshot6.t b/t/oneshot6.t index 5d09d01..dcf0278 100644 --- a/t/oneshot6.t +++ b/t/oneshot6.t @@ -9,5 +9,5 @@ shift down control down i down i up -control up shift up +control up diff --git a/t/oneshotn.t b/t/oneshotn.t index e1de12d..3fed490 100644 --- a/t/oneshotn.t +++ b/t/oneshotn.t @@ -7,5 +7,3 @@ control down control up b down b up -control down -control up diff --git a/t/oneshotn3.t b/t/oneshotn3.t index edc2a13..f613880 100644 --- a/t/oneshotn3.t +++ b/t/oneshotn3.t @@ -9,5 +9,5 @@ control down shift down i down i up -control up shift up +control up diff --git a/t/swap9.t b/t/swap9.t index ccd2971..becb323 100644 --- a/t/swap9.t +++ b/t/swap9.t @@ -6,14 +6,12 @@ a up alt up alt down -meta down control down alt up control up +meta down a down a up b down b up -control down meta up -control up diff --git a/t/toggle2.t b/t/toggle2.t index 74cd815..063fb5a 100644 --- a/t/toggle2.t +++ b/t/toggle2.t @@ -11,8 +11,6 @@ t up b down b up -shift down -shift up shift down shift up a down