diff --git a/app/src/combo.c b/app/src/combo.c index c3334bdb754..5da2bdc2299 100644 --- a/app/src/combo.c +++ b/app/src/combo.c @@ -459,19 +459,55 @@ static int position_state_down(const zmk_event_t *ev, struct zmk_position_state_ } } +static bool is_key_part_of_candidate(int32_t position) { + for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) { + struct combo_candidate *candidate = &candidates[i]; + if (candidate->combo == NULL) { + continue; + } + + for (int j = 0; j < candidate->combo->key_position_len; j++) { + if (candidate->combo->key_positions[j] == position) { + return true; + } + } + } + return false; +} + +static bool is_key_part_of_active_combo(int32_t position) { + for (int i = 0; i < active_combo_count; i++) { + struct active_combo *active_combo = &active_combos[i]; + for (int j = 0; j < active_combo->key_positions_pressed_count; j++) { + if (active_combo->key_positions_pressed[j].data.position == position) { + return true; + } + } + } + return false; +} + +static bool is_key_part_of_any_combo(int32_t position) { + return is_key_part_of_candidate(position) || is_key_part_of_active_combo(position); +} + static int position_state_up(const zmk_event_t *ev, struct zmk_position_state_changed *data) { - int released_keys = cleanup(); - if (release_combo_key(data->position, data->timestamp)) { - return ZMK_EV_EVENT_HANDLED; - } - if (released_keys > 1) { - // The second and further key down events are re-raised. To preserve - // correct order for e.g. hold-taps, reraise the key up event too. - struct zmk_position_state_changed_event dupe_ev = - copy_raised_zmk_position_state_changed(data); - ZMK_EVENT_RAISE(dupe_ev); - return ZMK_EV_EVENT_CAPTURED; + // Check if the released key is part of any combo candidate or active combo + if (is_key_part_of_any_combo(data->position)) { + int released_keys = cleanup(); + if (release_combo_key(data->position, data->timestamp)) { + return ZMK_EV_EVENT_HANDLED; + } + if (released_keys > 1) { + // The second and further key down events are re-raised. To preserve + // correct order for e.g. hold-taps, reraise the key up event too. + struct zmk_position_state_changed_event dupe_ev = + copy_raised_zmk_position_state_changed(data); + ZMK_EVENT_RAISE(dupe_ev); + return ZMK_EV_EVENT_CAPTURED; + } } + return ZMK_EV_EVENT_BUBBLE; } diff --git a/app/tests/combo/other-key-release/events.patterns b/app/tests/combo/other-key-release/events.patterns new file mode 100644 index 00000000000..b1342af4d97 --- /dev/null +++ b/app/tests/combo/other-key-release/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/combo/other-key-release/keycode_events.snapshot b/app/tests/combo/other-key-release/keycode_events.snapshot new file mode 100644 index 00000000000..3c8dc1381b1 --- /dev/null +++ b/app/tests/combo/other-key-release/keycode_events.snapshot @@ -0,0 +1,4 @@ +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/combo/other-key-release/native_posix_64.keymap b/app/tests/combo/other-key-release/native_posix_64.keymap new file mode 100644 index 00000000000..f0210f17733 --- /dev/null +++ b/app/tests/combo/other-key-release/native_posix_64.keymap @@ -0,0 +1,41 @@ +#include +#include +#include + +/ { + combos { + compatible = "zmk,combos"; + + combo_d { + timeout-ms = <50>; + key-positions = <0 1>; + bindings = <&kp D>; + }; + + // not actuated combo to test behavior + combo_g { + timeout-ms = <50>; + key-positions = <2 3>; + bindings = <&kp G>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = <&kp A &kp B &kp C &kp D>; + }; + }; +}; + +&kscan { + events = < + ZMK_MOCK_PRESS(0,2,10) // C + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_RELEASE(0,2,10) // C + ZMK_MOCK_PRESS(0,1,10) // B + ZMK_MOCK_RELEASE(0,0,0) // A + ZMK_MOCK_RELEASE(0,1,0) // B + >; +}; diff --git a/app/tests/tap-dance/3b-tap-int-seq/keycode_events.snapshot b/app/tests/tap-dance/3b-tap-int-seq/keycode_events.snapshot index 31113ffc4aa..38b560dbfde 100644 --- a/app/tests/tap-dance/3b-tap-int-seq/keycode_events.snapshot +++ b/app/tests/tap-dance/3b-tap-int-seq/keycode_events.snapshot @@ -1,10 +1,10 @@ td_binding_pressed: 2 created new tap dance td_binding_pressed: 2 tap dance pressed +td_binding_released: 2 tap dance keybind released kp_pressed: usage_page 0x07 keycode 0x1E implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x1E implicit_mods 0x00 explicit_mods 0x00 td_binding_pressed: 1 created new tap dance td_binding_pressed: 1 tap dance pressed kp_pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 -td_binding_released: 2 tap dance keybind released -kp_released: usage_page 0x07 keycode 0x1E implicit_mods 0x00 explicit_mods 0x00 td_binding_released: 1 tap dance keybind released kp_released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00