From c9e4da2c6f6325c21c9f9462d124bf98956ecc98 Mon Sep 17 00:00:00 2001 From: Raheman Vaiya Date: Thu, 11 May 2023 17:46:50 -0400 Subject: [PATCH] Fix oneshot tap bug (#478) overload() and timeout() rely on being able to store and execute descriptors without tying them to specific key states. To achieve idempotence, oneshot() currently neutralizes the upstroke of the activating key if the layer in question is already active. This can yield an activation asymmetry in the rare event that the oneshot descriptor is not associated with a physical key and is activated while the associated layer is already active. To fix this, we activate the layer for each oneshot depression and keep track of the oneshot activation depth. --- TODO | 1 + src/keyboard.c | 23 +++++++++-------------- src/keyboard.h | 2 +- t/overload-oneshot.t | 13 +++++++++++++ 4 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 t/overload-oneshot.t diff --git a/TODO b/TODO index b00e681..066adbc 100644 --- a/TODO +++ b/TODO @@ -5,3 +5,4 @@ cleanup manpage improved mouse support (integrate moused?) multi-user support (remove keyd group) split up the man page + add FAQ +add descriptor state + isolate descriptor logic diff --git a/src/keyboard.c b/src/keyboard.c index 1954b6b..2f39d8d 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -400,9 +400,9 @@ static void clear_oneshot(struct keyboard *kbd) size_t i = 0; for (i = 0; i < kbd->config.nr_layers; i++) - if (kbd->layer_state[i].oneshot) { - kbd->layer_state[i].oneshot = 0; + while (kbd->layer_state[i].oneshot_depth) { deactivate_layer(kbd, i); + kbd->layer_state[i].oneshot_depth--; } kbd->oneshot_latch = 0; @@ -614,17 +614,12 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, idx = d->args[0].idx; if (pressed) { - if (kbd->layer_state[idx].active) { - /* Neutralize the upstroke. */ - cache_set(kbd, code, NULL); - } else { - activate_layer(kbd, code, idx); - update_mods(kbd, dl, 0); - kbd->oneshot_latch = 1; - } + activate_layer(kbd, code, idx); + update_mods(kbd, dl, 0); + kbd->oneshot_latch = 1; } else { if (kbd->oneshot_latch) { - kbd->layer_state[idx].oneshot = 1; + kbd->layer_state[idx].oneshot_depth++; if (kbd->config.oneshot_timeout) { kbd->oneshot_timeout = time + kbd->config.oneshot_timeout; schedule_timeout(kbd, kbd->oneshot_timeout); @@ -717,12 +712,12 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, activate_layer(kbd, 0, idx); kbd->layer_state[idx].toggled = 1; update_mods(kbd, -1, 0); - } else if (kbd->layer_state[dl].oneshot) { + } else if (kbd->layer_state[dl].oneshot_depth) { deactivate_layer(kbd, dl); - kbd->layer_state[dl].oneshot = 0; + kbd->layer_state[dl].oneshot_depth--; activate_layer(kbd, 0, idx); - kbd->layer_state[idx].oneshot = 1; + kbd->layer_state[idx].oneshot_depth++; update_mods(kbd, -1, 0); } else { for (i = 0; i < CACHE_SIZE; i++) { diff --git a/src/keyboard.h b/src/keyboard.h index 81742a5..8bb8af3 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -117,7 +117,7 @@ struct keyboard { uint8_t active; uint8_t toggled; - uint8_t oneshot; + uint8_t oneshot_depth; } layer_state[MAX_LAYERS]; uint8_t keystate[256]; diff --git a/t/overload-oneshot.t b/t/overload-oneshot.t new file mode 100644 index 0000000..a1967ab --- /dev/null +++ b/t/overload-oneshot.t @@ -0,0 +1,13 @@ +control down +7 down +7 up +control up +x down +x up + +control down +meta down +meta up +x down +x up +control up