From bc1c91a2f65c4c81e796afbb2fec0fe5fa11ea62 Mon Sep 17 00:00:00 2001 From: Raheman Vaiya Date: Thu, 19 Aug 2021 23:43:14 -0400 Subject: [PATCH] Added layert() + resolve mapping conflicts. --- CHANGELOG.md | 5 ++ Makefile | 2 +- keyd.1.gz | Bin 3309 -> 3363 bytes man.md | 4 ++ src/config.c | 12 ++++ src/config.h | 5 ++ src/main.c | 160 ++++++++++++++++++++++++++++++--------------------- 7 files changed, 123 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5562df1..134f7aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# v1.0.1 + +- Added layert(). +- No resolve layer conflicts by picking the most recently activated one in the case of multiple mappings. + # v1.0.0 Major version update: diff --git a/Makefile b/Makefile index e33e3fc..f4671df 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ DESTDIR= PREFIX=/usr -VERSION=1.0.0 +VERSION=1.0.1 GIT_HASH=$(shell git describe --no-match --always --abbrev=40 --dirty) CFLAGS=-DVERSION=\"$(VERSION)\" -DGIT_COMMIT_HASH=\"$(GIT_HASH)\" diff --git a/keyd.1.gz b/keyd.1.gz index ce951ef1e51ad3f2c0b3fa0c3b072ac7bd4407cf..c725f111e803168d59b03c2c5d2a6824279dc563 100644 GIT binary patch delta 3356 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 delta 3302 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 diff --git a/man.md b/man.md index e556008..b034dee 100644 --- a/man.md +++ b/man.md @@ -150,6 +150,10 @@ like modifier stacking and pointer combos. **layer(\)**: Activates the given layer while held. +**layert(\)**: Toggle the state of the given layer. Note this is +intended for transient layers and is distinct from layout() which should +be used for letter layouts. + **overload(\,\)**: Activates the given layer while held and emits the given key sequence when tapped. **layout(\)**: Sets the current layout to the given layer. You will likely want diff --git a/src/config.c b/src/config.c index 2743d0e..f163b4f 100644 --- a/src/config.c +++ b/src/config.c @@ -522,6 +522,18 @@ static int parse_descriptor(const char *_s, struct key_descriptor *desc) desc->action = ACTION_LAYER; desc->arg.layer = idx; + goto cleanup; + } else if(!strcmp(fn, "layert") && nargs == 1) { + int idx = lookup_layer(args[0]); + + if(idx < 0) { + err("%s is not a valid layer.", args[0]); + goto fail; + } + + desc->action = ACTION_LAYER_TOGGLE; + desc->arg.layer = idx; + goto cleanup; } else if(!strcmp(fn, "layout") && nargs > 0) { int idx; diff --git a/src/config.h b/src/config.h index d96d4de..a6465b8 100644 --- a/src/config.h +++ b/src/config.h @@ -41,6 +41,7 @@ enum action { ACTION_LAYOUT, ACTION_KEYSEQ, ACTION_LAYER, + ACTION_LAYER_TOGGLE, ACTION_ONESHOT, }; @@ -66,6 +67,10 @@ struct key_descriptor struct layer { uint16_t mods; struct key_descriptor *keymap; + + //State + uint8_t active; + uint64_t timestamp; }; struct keyboard_config { diff --git a/src/main.c b/src/main.c index 3149268..b418174 100644 --- a/src/main.c +++ b/src/main.c @@ -62,16 +62,14 @@ struct keyboard { int fd; char devnode[256]; - uint8_t active_layers[MAX_LAYERS]; - uint16_t mods; //The active modifier set + struct layer **layers; + size_t nlayers; //The layer to which modifiers are applied, //this may be distinct from the main layout - int modlayout; + struct layer *modlayout; + struct layer *layout; - int layout; - - struct keyboard_config *cfg; struct keyboard *next; }; @@ -100,6 +98,13 @@ static void _die(char *fmt, ...) #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; @@ -259,66 +264,75 @@ static void setmods(uint16_t mods) 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; - int active_layers = 0; + struct layer *layer = NULL; uint16_t mods = 0; - size_t i; + size_t nactive = 0; - for(i = 0;i < kbd->cfg->nlayers;i++) { - if(kbd->active_layers[i]) { - struct layer *layer = kbd->cfg->layers[i]; - struct key_descriptor *d = &layer->keymap[code]; + //Pick the most recently activated layer in which a mapping is defined. - if(!desc && d->action != ACTION_UNDEFINED) + 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; - else - mods |= layer->mods; + layer = l; + } - active_layers++; + 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 the shift key is the only modifier active, we probably - //want to use the key layout. if(mods == MOD_SHIFT || mods == MOD_ALT_GR) - desc = &kbd->cfg->layers[kbd->layout]->keymap[code]; + desc = &kbd->layout->keymap[code]; else - desc = &kbd->cfg->layers[kbd->modlayout]->keymap[code]; - } else if(active_layers) - return NULL; + desc = &kbd->modlayout->keymap[code]; + } else if(!nactive) //If no layers are active use the default layout + desc = &kbd->layout->keymap[code]; else - desc = &kbd->cfg->layers[kbd->layout]->keymap[code]; + return NULL; } *modsp = mods; return desc; } -static void reify_layer_mods(struct keyboard *kbd) -{ - uint16_t mods = 0; - size_t i; - - for(i = 0;i < kbd->cfg->nlayers;i++) { - struct layer *layer = kbd->cfg->layers[i]; - if(kbd->active_layers[i]) - mods |= layer->mods; - } - - setmods(mods); -} - -static uint64_t get_time() -{ - struct timespec tv; - clock_gettime(CLOCK_MONOTONIC, &tv); - return (tv.tv_sec*1E9)+tv.tv_nsec; -} - //Where the magic happens static void process_event(struct keyboard *kbd, struct input_event *ev) { @@ -366,17 +380,18 @@ static void process_event(struct keyboard *kbd, struct input_event *ev) return; switch(d->action) { - int layer; + struct layer *layer; uint32_t keyseq; case ACTION_OVERLOAD: keyseq = d->arg.keyseq; - layer = d->arg2.layer; + layer = kbd->layers[d->arg2.layer]; if(pressed) { - kbd->active_layers[layer] = 1; + layer->active = 1; + layer->timestamp = get_time(); } else { - kbd->active_layers[layer] = 0; + layer->active = 0; if(lastd == d) { //If tapped uint16_t key = keyseq & 0xFF; @@ -386,6 +401,7 @@ static void process_event(struct keyboard *kbd, struct input_event *ev) send_key(key, 1); send_key(key, 0); + last_keyseq_timestamp = get_time(); goto keyseq_cleanup; } } @@ -393,26 +409,40 @@ static void process_event(struct keyboard *kbd, struct input_event *ev) reify_layer_mods(kbd); break; case ACTION_LAYOUT: - kbd->layout = d->arg.layer; - kbd->modlayout = d->arg2.layer; + 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: - if(pressed) - kbd->active_layers[d->arg.layer] = 1; - else if(pressed_timestamps[code] < last_keyseq_timestamp) - kbd->active_layers[d->arg.layer] = 0; - else //Tapped + 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: - if(pressed) - kbd->active_layers[d->arg.layer] = 1; - else - kbd->active_layers[d->arg.layer] = 0; + layer = kbd->layers[d->arg.layer]; + + if(pressed) { + layer->active = 1; + layer->timestamp = get_time(); + } else + layer->active = 0; reify_layer_mods(kbd); break; @@ -466,9 +496,9 @@ keyseq_cleanup: last_keyseq_timestamp = get_time(); //Clear active oneshot layers. - for(i = 0;i < kbd->cfg->nlayers;i++) { + for(i = 0;i < kbd->nlayers;i++) { if(oneshot_layers[i]) { - kbd->active_layers[i] = 0; + kbd->layers[i]->active = 0; oneshot_layers[i] = 0; } } @@ -594,9 +624,11 @@ static int manage_keyboard(const char *devnode) kbd = calloc(1, sizeof(struct keyboard)); kbd->fd = fd; - kbd->cfg = cfg; - kbd->modlayout = kbd->cfg->default_modlayout; - kbd->layout = kbd->cfg->default_layout; + 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);