Chord latches all presses — releasing a key mid-chord should remove it #3

Open
opened 2026-07-04 15:40:05 +08:00 by afiqzudinhadi · 0 comments
afiqzudinhadi commented 2026-07-04 15:40:05 +08:00 (Migrated from github.com)

Problem

Pressing K+A+T then releasing T (while K and A still held) still outputs KAT when all keys are released. Expected: KA only (T was retracted).

Current behavior: on_press ORs the bit into current_chord, on_release only decrements a counter. Once a key is pressed, its bit is permanent for that chord — no way to retract a mispress.

Solution

Change keys_held from a counter to a bitmask (same width as current_chord). On release, clear the bit in both keys_held AND current_chord. The chord fired at all-up time is then only the keys that were held at that final moment.

static int on_steno_binding_pressed(...)
{
    uint32_t key_index = binding->param1;
    state.current_chord |= (1U << key_index);
    state.keys_held |= (1U << key_index);
    return ZMK_BEHAVIOR_OPAQUE;
}

static int on_steno_binding_released(...)
{
    uint32_t key_index = binding->param1;
    state.keys_held &= ~(1U << key_index);
    state.current_chord &= ~(1U << key_index);

    if (state.keys_held == 0 && state.current_chord != 0) {
        process_chord();
    }
    return ZMK_BEHAVIOR_OPAQUE;
}

Also tighten the bounds check from 35 to 22 (only 23 steno keys exist).

## Problem Pressing K+A+T then releasing T (while K and A still held) still outputs KAT when all keys are released. Expected: KA only (T was retracted). Current behavior: `on_press` ORs the bit into `current_chord`, `on_release` only decrements a counter. Once a key is pressed, its bit is permanent for that chord — no way to retract a mispress. ## Solution Change `keys_held` from a counter to a **bitmask** (same width as `current_chord`). On release, clear the bit in both `keys_held` AND `current_chord`. The chord fired at all-up time is then only the keys that were held at that final moment. ```c static int on_steno_binding_pressed(...) { uint32_t key_index = binding->param1; state.current_chord |= (1U << key_index); state.keys_held |= (1U << key_index); return ZMK_BEHAVIOR_OPAQUE; } static int on_steno_binding_released(...) { uint32_t key_index = binding->param1; state.keys_held &= ~(1U << key_index); state.current_chord &= ~(1U << key_index); if (state.keys_held == 0 && state.current_chord != 0) { process_chord(); } return ZMK_BEHAVIOR_OPAQUE; } ``` Also tighten the bounds check from 35 to 22 (only 23 steno keys exist).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: afiqzudinhadi/zmk-steno-engine#3
No description provided.