Skip to content

Commit

Permalink
Fix oneshot tap bug (#478)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rvaiya committed May 11, 2023
1 parent 41bccee commit c9e4da2
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 15 deletions.
1 change: 1 addition & 0 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -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
23 changes: 9 additions & 14 deletions src/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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++) {
Expand Down
2 changes: 1 addition & 1 deletion src/keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
13 changes: 13 additions & 0 deletions t/overload-oneshot.t
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit c9e4da2

Please sign in to comment.