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 ce951ef..c725f11 100644 Binary files a/keyd.1.gz and b/keyd.1.gz differ 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);