From 7186528f77bf077173927c1c8506b4d434e5c371 Mon Sep 17 00:00:00 2001 From: Cem Aksoylar Date: Wed, 26 Feb 2025 14:54:08 -0800 Subject: [PATCH] fix(behaviors): Make multiple sticky keys work on same key position (#2758) test(behaviors): Add same position sticky key tests fix(behaviors): Make multiple sticky keys work on same key position refactor(behaviors): Remove unused param2 in sticky keys --- app/src/behaviors/behavior_sticky_key.c | 26 ++++++----- .../sticky-keys/12-macro/events.patterns | 1 + .../12-macro/keycode_events.snapshot | 4 ++ .../12-macro/native_posix_64.keymap | 36 +++++++++++++++ .../12-same-position-mods/events.patterns | 1 + .../keycode_events.snapshot | 6 +++ .../native_posix_64.keymap | 39 ++++++++++++++++ .../12-same-position-sk-sl/events.patterns | 1 + .../keycode_events.snapshot | 4 ++ .../native_posix_64.keymap | 45 +++++++++++++++++++ 10 files changed, 153 insertions(+), 10 deletions(-) create mode 100644 app/tests/sticky-keys/12-macro/events.patterns create mode 100644 app/tests/sticky-keys/12-macro/keycode_events.snapshot create mode 100644 app/tests/sticky-keys/12-macro/native_posix_64.keymap create mode 100644 app/tests/sticky-keys/12-same-position-mods/events.patterns create mode 100644 app/tests/sticky-keys/12-same-position-mods/keycode_events.snapshot create mode 100644 app/tests/sticky-keys/12-same-position-mods/native_posix_64.keymap create mode 100644 app/tests/sticky-keys/12-same-position-sk-sl/events.patterns create mode 100644 app/tests/sticky-keys/12-same-position-sk-sl/keycode_events.snapshot create mode 100644 app/tests/sticky-keys/12-same-position-sk-sl/native_posix_64.keymap diff --git a/app/src/behaviors/behavior_sticky_key.c b/app/src/behaviors/behavior_sticky_key.c index 380964b94..babdeb7da 100644 --- a/app/src/behaviors/behavior_sticky_key.c +++ b/app/src/behaviors/behavior_sticky_key.c @@ -44,7 +44,6 @@ struct active_sticky_key { uint8_t source; #endif uint32_t param1; - uint32_t param2; const struct behavior_sticky_key_config *config; // timer data. bool timer_started; @@ -59,7 +58,7 @@ struct active_sticky_key { struct active_sticky_key active_sticky_keys[ZMK_BHV_STICKY_KEY_MAX_HELD] = {}; static struct active_sticky_key *store_sticky_key(struct zmk_behavior_binding_event *event, - uint32_t param1, uint32_t param2, + uint32_t param1, const struct behavior_sticky_key_config *config) { for (int i = 0; i < ZMK_BHV_STICKY_KEY_MAX_HELD; i++) { struct active_sticky_key *const sticky_key = &active_sticky_keys[i]; @@ -72,7 +71,6 @@ static struct active_sticky_key *store_sticky_key(struct zmk_behavior_binding_ev sticky_key->source = event->source; #endif sticky_key->param1 = param1; - sticky_key->param2 = param2; sticky_key->config = config; sticky_key->release_at = 0; sticky_key->timer_cancelled = false; @@ -85,12 +83,18 @@ static struct active_sticky_key *store_sticky_key(struct zmk_behavior_binding_ev } static void clear_sticky_key(struct active_sticky_key *sticky_key) { + LOG_DBG("clearing sticky key at position %d, param %d", sticky_key->position, + sticky_key->param1); sticky_key->position = ZMK_BHV_STICKY_KEY_POSITION_FREE; } -static struct active_sticky_key *find_sticky_key(uint32_t position) { +static struct active_sticky_key * +find_sticky_key(uint32_t position, struct zmk_behavior_binding behavior, uint32_t binding_param) { for (int i = 0; i < ZMK_BHV_STICKY_KEY_MAX_HELD; i++) { - if (active_sticky_keys[i].position == position && !active_sticky_keys[i].timer_cancelled) { + if (active_sticky_keys[i].position == position && + active_sticky_keys[i].config->behavior.behavior_dev == behavior.behavior_dev && + active_sticky_keys[i].param1 == binding_param && + !active_sticky_keys[i].timer_cancelled) { return &active_sticky_keys[i]; } } @@ -102,7 +106,6 @@ static inline int press_sticky_key_behavior(struct active_sticky_key *sticky_key struct zmk_behavior_binding binding = { .behavior_dev = sticky_key->config->behavior.behavior_dev, .param1 = sticky_key->param1, - .param2 = sticky_key->param2, }; struct zmk_behavior_binding_event event = { .position = sticky_key->position, @@ -119,7 +122,6 @@ static inline int release_sticky_key_behavior(struct active_sticky_key *sticky_k struct zmk_behavior_binding binding = { .behavior_dev = sticky_key->config->behavior.behavior_dev, .param1 = sticky_key->param1, - .param2 = sticky_key->param2, }; struct zmk_behavior_binding_event event = { .position = sticky_key->position, @@ -156,12 +158,13 @@ static int on_sticky_key_binding_pressed(struct zmk_behavior_binding *binding, const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev); const struct behavior_sticky_key_config *cfg = dev->config; struct active_sticky_key *sticky_key; - sticky_key = find_sticky_key(event.position); + sticky_key = find_sticky_key(event.position, cfg->behavior, binding->param1); if (sticky_key != NULL) { + LOG_DBG("found same sticky key pressed at position %d, release it first", event.position); stop_timer(sticky_key); release_sticky_key_behavior(sticky_key, event.timestamp); } - sticky_key = store_sticky_key(&event, binding->param1, binding->param2, cfg); + sticky_key = store_sticky_key(&event, binding->param1, cfg); if (sticky_key == NULL) { LOG_ERR("unable to store sticky key, did you press more than %d sticky_key?", ZMK_BHV_STICKY_KEY_MAX_HELD); @@ -178,7 +181,10 @@ static int on_sticky_key_binding_pressed(struct zmk_behavior_binding *binding, static int on_sticky_key_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { - struct active_sticky_key *sticky_key = find_sticky_key(event.position); + const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev); + const struct behavior_sticky_key_config *cfg = dev->config; + struct active_sticky_key *sticky_key = + find_sticky_key(event.position, cfg->behavior, binding->param1); if (sticky_key == NULL) { LOG_ERR("ACTIVE STICKY KEY CLEARED TOO EARLY"); return ZMK_BEHAVIOR_OPAQUE; diff --git a/app/tests/sticky-keys/12-macro/events.patterns b/app/tests/sticky-keys/12-macro/events.patterns new file mode 100644 index 000000000..b1342af4d --- /dev/null +++ b/app/tests/sticky-keys/12-macro/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/sticky-keys/12-macro/keycode_events.snapshot b/app/tests/sticky-keys/12-macro/keycode_events.snapshot new file mode 100644 index 000000000..6757e8343 --- /dev/null +++ b/app/tests/sticky-keys/12-macro/keycode_events.snapshot @@ -0,0 +1,4 @@ +pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/sticky-keys/12-macro/native_posix_64.keymap b/app/tests/sticky-keys/12-macro/native_posix_64.keymap new file mode 100644 index 000000000..e7ec5b27c --- /dev/null +++ b/app/tests/sticky-keys/12-macro/native_posix_64.keymap @@ -0,0 +1,36 @@ +#include +#include +#include + +/ { + behaviors { + ZMK_MACRO(sk_sl, wait-ms = <1>; tap-ms = <1>; bindings = <&sk LEFT_CONTROL &sl 1>;) + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &sk_sl &mo 1 + &kp Y &kp B>; + }; + + upper_layer { + bindings = < + &kp T &kp X + &kp A &kp Z>; + }; + }; +}; + +&kscan { + events = < + /* tap sk_sl */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + /* tap A */ + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; diff --git a/app/tests/sticky-keys/12-same-position-mods/events.patterns b/app/tests/sticky-keys/12-same-position-mods/events.patterns new file mode 100644 index 000000000..b1342af4d --- /dev/null +++ b/app/tests/sticky-keys/12-same-position-mods/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/sticky-keys/12-same-position-mods/keycode_events.snapshot b/app/tests/sticky-keys/12-same-position-mods/keycode_events.snapshot new file mode 100644 index 000000000..56d45c001 --- /dev/null +++ b/app/tests/sticky-keys/12-same-position-mods/keycode_events.snapshot @@ -0,0 +1,6 @@ +pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/sticky-keys/12-same-position-mods/native_posix_64.keymap b/app/tests/sticky-keys/12-same-position-mods/native_posix_64.keymap new file mode 100644 index 000000000..f3dd4ba9f --- /dev/null +++ b/app/tests/sticky-keys/12-same-position-mods/native_posix_64.keymap @@ -0,0 +1,39 @@ +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &sk LEFT_CONTROL &mo 1 + &kp A &kp B>; + }; + + lower_layer { + bindings = < + &sk LEFT_SHIFT &kp X + &kp Y &kp Z>; + }; + }; +}; + +&kscan { + events = < + /* tap sk LEFT_CONTROL */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + /* switch to lower layer */ + ZMK_MOCK_PRESS(0,1,10) + /* tap sk LEFT_SHIFT */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + /* deactivate lower layer */ + ZMK_MOCK_RELEASE(0,1,10) + /* tap A */ + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; diff --git a/app/tests/sticky-keys/12-same-position-sk-sl/events.patterns b/app/tests/sticky-keys/12-same-position-sk-sl/events.patterns new file mode 100644 index 000000000..b1342af4d --- /dev/null +++ b/app/tests/sticky-keys/12-same-position-sk-sl/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/sticky-keys/12-same-position-sk-sl/keycode_events.snapshot b/app/tests/sticky-keys/12-same-position-sk-sl/keycode_events.snapshot new file mode 100644 index 000000000..6757e8343 --- /dev/null +++ b/app/tests/sticky-keys/12-same-position-sk-sl/keycode_events.snapshot @@ -0,0 +1,4 @@ +pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/sticky-keys/12-same-position-sk-sl/native_posix_64.keymap b/app/tests/sticky-keys/12-same-position-sk-sl/native_posix_64.keymap new file mode 100644 index 000000000..144730479 --- /dev/null +++ b/app/tests/sticky-keys/12-same-position-sk-sl/native_posix_64.keymap @@ -0,0 +1,45 @@ +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &sk LEFT_CONTROL &mo 1 + &kp A &kp B>; + }; + + lower_layer { + bindings = < + &sl 2 &kp X + &kp Y &kp Z>; + }; + + upper_layer { + bindings = < + &kp T &kp X + &kp A &kp Z>; + }; + }; +}; + +&kscan { + events = < + /* tap sk LEFT_CONTROL */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + /* switch to lower layer */ + ZMK_MOCK_PRESS(0,1,10) + /* tap sl upper_layer */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + /* tap A */ + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + /* deactivate lower layer */ + ZMK_MOCK_RELEASE(0,1,10) + >; +};