From 055ecab826c2a8e0ea67f0ddf3fd5e283ddedb51 Mon Sep 17 00:00:00 2001 From: Aaron Nunley Date: Fri, 29 Jan 2021 17:52:33 -0800 Subject: [PATCH 01/21] Added support for 2% Milk --- .github/workflows/build.yml | 1 + .../two_percent_milk/Kconfig.defconfig | 9 +++++++ .../shields/two_percent_milk/Kconfig.shield | 5 ++++ .../two_percent_milk/two_percent_milk.keymap | 24 +++++++++++++++++++ .../two_percent_milk/two_percent_milk.overlay | 24 +++++++++++++++++++ 5 files changed, 63 insertions(+) create mode 100644 app/boards/shields/two_percent_milk/Kconfig.defconfig create mode 100644 app/boards/shields/two_percent_milk/Kconfig.shield create mode 100644 app/boards/shields/two_percent_milk/two_percent_milk.keymap create mode 100644 app/boards/shields/two_percent_milk/two_percent_milk.overlay diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d7011283..08713147 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,6 +46,7 @@ jobs: - lily58_right - microdox_left - microdox_right + - two_percent_milk - nibble - qaz - quefrency_left diff --git a/app/boards/shields/two_percent_milk/Kconfig.defconfig b/app/boards/shields/two_percent_milk/Kconfig.defconfig new file mode 100644 index 00000000..82126ca5 --- /dev/null +++ b/app/boards/shields/two_percent_milk/Kconfig.defconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SHIELD_TWO_PERCENT_MILK + +config ZMK_KEYBOARD_NAME + default "2% Milk" + +endif diff --git a/app/boards/shields/two_percent_milk/Kconfig.shield b/app/boards/shields/two_percent_milk/Kconfig.shield new file mode 100644 index 00000000..700eaa13 --- /dev/null +++ b/app/boards/shields/two_percent_milk/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SHIELD_TWO_PERCENT_MILK + def_bool $(shields_list_contains,two_percent_milk) diff --git a/app/boards/shields/two_percent_milk/two_percent_milk.keymap b/app/boards/shields/two_percent_milk/two_percent_milk.keymap new file mode 100644 index 00000000..c2bf02a7 --- /dev/null +++ b/app/boards/shields/two_percent_milk/two_percent_milk.keymap @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + + #include + #include + #include + + #define DEFAULT 0 + + / { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp X + &kp Z + >; + }; + }; + }; \ No newline at end of file diff --git a/app/boards/shields/two_percent_milk/two_percent_milk.overlay b/app/boards/shields/two_percent_milk/two_percent_milk.overlay new file mode 100644 index 00000000..1cc7afbe --- /dev/null +++ b/app/boards/shields/two_percent_milk/two_percent_milk.overlay @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + chosen { + zmk,kscan = &kscan0; + }; + + kscan0: kscan { + compatible = "zmk,kscan-gpio-direct"; + + label = "KSCAN"; + + input-gpios + = <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + ; + + }; + +}; \ No newline at end of file From 131d001e8d17ec2fc5dc261c43396a9b0b565091 Mon Sep 17 00:00:00 2001 From: Okke Formsma Date: Sun, 14 Mar 2021 21:39:31 +0100 Subject: [PATCH 02/21] refactor(hid): Move hid logic into hid.c Move the logic for picking the correct hid function into hid.c. --- app/include/zmk/hid.h | 4 ++++ app/src/hid.c | 20 ++++++++++++++++++++ app/src/hid_listener.c | 39 +++++++++------------------------------ 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index 5aa004c2..458e9b96 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -173,6 +173,7 @@ int zmk_hid_register_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_unregister_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers); int zmk_hid_implicit_modifiers_release(); + int zmk_hid_keyboard_press(zmk_key_t key); int zmk_hid_keyboard_release(zmk_key_t key); void zmk_hid_keyboard_clear(); @@ -181,5 +182,8 @@ int zmk_hid_consumer_press(zmk_key_t key); int zmk_hid_consumer_release(zmk_key_t key); void zmk_hid_consumer_clear(); +int zmk_hid_press(uint8_t usage_page, zmk_key_t code); +int zmk_hid_release(uint8_t usage_page, zmk_key_t code); + struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(); struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(); diff --git a/app/src/hid.c b/app/src/hid.c index 7ab080e5..e5e7f728 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -129,6 +129,26 @@ int zmk_hid_consumer_release(zmk_key_t code) { return 0; }; +int zmk_hid_press(uint8_t usage_page, zmk_key_t code) { + switch (usage_page) { + case HID_USAGE_KEY: + return zmk_hid_keyboard_press(code); + case HID_USAGE_CONSUMER: + return zmk_hid_consumer_press(code); + } + return -EINVAL; +} + +int zmk_hid_release(uint8_t usage_page, zmk_key_t code) { + switch (usage_page) { + case HID_USAGE_KEY: + return zmk_hid_keyboard_release(code); + case HID_USAGE_CONSUMER: + return zmk_hid_consumer_release(code); + } + return -EINVAL; +} + void zmk_hid_consumer_clear() { memset(&consumer_report.body, 0, sizeof(consumer_report.body)); } struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() { diff --git a/app/src/hid_listener.c b/app/src/hid_listener.c index d582c169..1766f965 100644 --- a/app/src/hid_listener.c +++ b/app/src/hid_listener.c @@ -20,21 +20,10 @@ static int hid_listener_keycode_pressed(const struct zmk_keycode_state_changed * int err; LOG_DBG("usage_page 0x%02X keycode 0x%02X implicit_mods 0x%02X explicit_mods 0x%02X", ev->usage_page, ev->keycode, ev->implicit_modifiers, ev->explicit_modifiers); - switch (ev->usage_page) { - case HID_USAGE_KEY: - err = zmk_hid_keyboard_press(ev->keycode); - if (err) { - LOG_ERR("Unable to press keycode"); - return err; - } - break; - case HID_USAGE_CONSUMER: - err = zmk_hid_consumer_press(ev->keycode); - if (err) { - LOG_ERR("Unable to press keycode"); - return err; - } - break; + err = zmk_hid_press(ev->usage_page, ev->keycode); + if (err) { + LOG_DBG("Unable to press keycode"); + return err; } zmk_hid_register_mods(ev->explicit_modifiers); zmk_hid_implicit_modifiers_press(ev->implicit_modifiers); @@ -45,20 +34,10 @@ static int hid_listener_keycode_released(const struct zmk_keycode_state_changed int err; LOG_DBG("usage_page 0x%02X keycode 0x%02X implicit_mods 0x%02X explicit_mods 0x%02X", ev->usage_page, ev->keycode, ev->implicit_modifiers, ev->explicit_modifiers); - switch (ev->usage_page) { - case HID_USAGE_KEY: - err = zmk_hid_keyboard_release(ev->keycode); - if (err) { - LOG_ERR("Unable to release keycode"); - return err; - } - break; - case HID_USAGE_CONSUMER: - err = zmk_hid_consumer_release(ev->keycode); - if (err) { - LOG_ERR("Unable to release keycode"); - return err; - } + err = zmk_hid_release(ev->usage_page, ev->keycode); + if (err) { + LOG_DBG("Unable to release keycode"); + return err; } zmk_hid_unregister_mods(ev->explicit_modifiers); // There is a minor issue with this code. @@ -83,4 +62,4 @@ int hid_listener(const zmk_event_t *eh) { } ZMK_LISTENER(hid_listener, hid_listener); -ZMK_SUBSCRIPTION(hid_listener, zmk_keycode_state_changed); \ No newline at end of file +ZMK_SUBSCRIPTION(hid_listener, zmk_keycode_state_changed); From ff739a9918a60793c7262c644e2e6df57de2e1dd Mon Sep 17 00:00:00 2001 From: Okke Formsma Date: Tue, 16 Mar 2021 20:46:24 +0100 Subject: [PATCH 03/21] refactor(hid): add mask to HID_USAGE_PAGE macro --- app/include/dt-bindings/zmk/hid_usage_pages.h | 2 +- app/include/zmk/events/keycode_state_changed.h | 2 +- app/src/behaviors/behavior_sticky_key.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/include/dt-bindings/zmk/hid_usage_pages.h b/app/include/dt-bindings/zmk/hid_usage_pages.h index f38f4fd3..830641f0 100644 --- a/app/include/dt-bindings/zmk/hid_usage_pages.h +++ b/app/include/dt-bindings/zmk/hid_usage_pages.h @@ -12,7 +12,7 @@ #define HID_USAGE(page, id) ((page << 16) | id) #define HID_USAGE_ID(usage) (usage & 0xFFFF) -#define HID_USAGE_PAGE(usage) (usage >> 16) +#define HID_USAGE_PAGE(usage) ((usage >> 16) & 0xFF) /* WARNING: DEPRECATED from dt-bindings/zmk/keys.h */ #define USAGE_KEYPAD (0x07) // WARNING: DEPRECATED (DO NOT USE) diff --git a/app/include/zmk/events/keycode_state_changed.h b/app/include/zmk/events/keycode_state_changed.h index 466bbd76..924eb223 100644 --- a/app/include/zmk/events/keycode_state_changed.h +++ b/app/include/zmk/events/keycode_state_changed.h @@ -23,7 +23,7 @@ ZMK_EVENT_DECLARE(zmk_keycode_state_changed); static inline struct zmk_keycode_state_changed_event * zmk_keycode_state_changed_from_encoded(uint32_t encoded, bool pressed, int64_t timestamp) { - uint16_t page = HID_USAGE_PAGE(encoded) & 0xFF; + uint16_t page = HID_USAGE_PAGE(encoded); uint16_t id = HID_USAGE_ID(encoded); uint8_t implicit_modifiers = 0x00; uint8_t explicit_modifiers = 0x00; diff --git a/app/src/behaviors/behavior_sticky_key.c b/app/src/behaviors/behavior_sticky_key.c index 20af93a8..d2331a78 100644 --- a/app/src/behaviors/behavior_sticky_key.c +++ b/app/src/behaviors/behavior_sticky_key.c @@ -190,7 +190,7 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) { if (strcmp(sticky_key->config->behavior.behavior_dev, "KEY_PRESS") == 0 && HID_USAGE_ID(sticky_key->param1) == ev->keycode && - (HID_USAGE_PAGE(sticky_key->param1) & 0xFF) == ev->usage_page && + HID_USAGE_PAGE(sticky_key->param1) == ev->usage_page && SELECT_MODS(sticky_key->param1) == ev->implicit_modifiers) { // don't catch key down events generated by the sticky key behavior itself continue; From 4dbf309b30c12f6ba873b3998a3380fe7033904b Mon Sep 17 00:00:00 2001 From: Okke Formsma Date: Tue, 16 Mar 2021 21:38:36 +0100 Subject: [PATCH 04/21] feat(behavior): Add key toggle --- app/CMakeLists.txt | 1 + app/dts/behaviors.dtsi | 1 + app/dts/behaviors/key_toggle.dtsi | 15 ++++++ .../behaviors/zmk,behavior-key-toggle.yaml | 8 +++ app/include/zmk/hid.h | 5 ++ app/src/behaviors/behavior_key_toggle.c | 50 +++++++++++++++++++ app/src/hid.c | 39 ++++++++++++++- app/tests/keytoggle/behavior_keymap.dtsi | 17 +++++++ .../kt-press-release/events.patterns | 1 + .../kt-press-release/keycode_events.snapshot | 2 + .../kt-press-release/native_posix.keymap | 10 ++++ 11 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 app/dts/behaviors/key_toggle.dtsi create mode 100644 app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml create mode 100644 app/src/behaviors/behavior_key_toggle.c create mode 100644 app/tests/keytoggle/behavior_keymap.dtsi create mode 100644 app/tests/keytoggle/kt-press-release/events.patterns create mode 100644 app/tests/keytoggle/kt-press-release/keycode_events.snapshot create mode 100644 app/tests/keytoggle/kt-press-release/native_posix.keymap diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e283487f..ad0d97a8 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -43,6 +43,7 @@ target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed target_sources_ifdef(CONFIG_USB app PRIVATE src/events/usb_conn_state_changed.c) if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) + target_sources(app PRIVATE src/behaviors/behavior_key_toggle.c) target_sources(app PRIVATE src/behaviors/behavior_reset.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 4333ceea..0aa281bd 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/app/dts/behaviors/key_toggle.dtsi b/app/dts/behaviors/key_toggle.dtsi new file mode 100644 index 00000000..304bd03c --- /dev/null +++ b/app/dts/behaviors/key_toggle.dtsi @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + behaviors { + /omit-if-no-ref/ kt: behavior_key_toggle { + compatible = "zmk,behavior-key-toggle"; + label = "KEY_TOGGLE"; + #binding-cells = <1>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml b/app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml new file mode 100644 index 00000000..01bdae1e --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Key toggle behavior + +compatible: "zmk,behavior-key-toggle" + +include: one_param.yaml diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index 458e9b96..5043083c 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -169,6 +169,8 @@ struct zmk_hid_consumer_report { zmk_mod_flags_t zmk_hid_get_explicit_mods(); int zmk_hid_register_mod(zmk_mod_t modifier); int zmk_hid_unregister_mod(zmk_mod_t modifier); +bool zmk_hid_mod_is_pressed(zmk_mod_t modifier); + int zmk_hid_register_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_unregister_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers); @@ -177,13 +179,16 @@ int zmk_hid_implicit_modifiers_release(); int zmk_hid_keyboard_press(zmk_key_t key); int zmk_hid_keyboard_release(zmk_key_t key); void zmk_hid_keyboard_clear(); +bool zmk_hid_keyboard_is_pressed(); int zmk_hid_consumer_press(zmk_key_t key); int zmk_hid_consumer_release(zmk_key_t key); void zmk_hid_consumer_clear(); +bool zmk_hid_consumer_is_pressed(zmk_key_t key); int zmk_hid_press(uint8_t usage_page, zmk_key_t code); int zmk_hid_release(uint8_t usage_page, zmk_key_t code); +bool zmk_hid_is_pressed(uint8_t usage_page, zmk_key_t code); struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(); struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(); diff --git a/app/src/behaviors/behavior_key_toggle.c b/app/src/behaviors/behavior_key_toggle.c new file mode 100644 index 00000000..1bd23c43 --- /dev/null +++ b/app/src/behaviors/behavior_key_toggle.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_key_toggle + +#include +#include +#include + +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +static int behavior_key_toggle_init(const struct device *dev) { return 0; }; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + bool pressed = + zmk_hid_is_pressed(HID_USAGE_PAGE(binding->param1), HID_USAGE_ID(binding->param1)); + return ZMK_EVENT_RAISE( + zmk_keycode_state_changed_from_encoded(binding->param1, !pressed, event.timestamp)); +} + +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + return 0; +} + +static const struct behavior_driver_api behavior_key_toggle_driver_api = { + .binding_pressed = on_keymap_binding_pressed, + .binding_released = on_keymap_binding_released, +}; + +#define KP_INST(n) \ + DEVICE_AND_API_INIT(behavior_key_toggle_##n, DT_INST_LABEL(n), behavior_key_toggle_init, NULL, \ + NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_key_toggle_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/src/hid.c b/app/src/hid.c index e5e7f728..39c2c880 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: MIT */ +#include "zmk/keys.h" #include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -51,6 +52,11 @@ int zmk_hid_unregister_mod(zmk_mod_t modifier) { return 0; } +bool zmk_hid_mod_is_pressed(zmk_mod_t modifier) { + zmk_mod_flags_t mod_flag = 1 << modifier; + return (zmk_hid_get_explicit_mods() & mod_flag) == mod_flag; +} + int zmk_hid_register_mods(zmk_mod_flags_t modifiers) { for (zmk_mod_t i = 0; i < 8; i++) { if (modifiers & (1 << i)) { @@ -117,6 +123,18 @@ int zmk_hid_keyboard_release(zmk_key_t code) { return 0; }; +bool zmk_hid_keyboard_is_pressed(zmk_key_t code) { + if (code >= HID_USAGE_KEY_KEYBOARD_LEFTCONTROL && code <= HID_USAGE_KEY_KEYBOARD_RIGHT_GUI) { + return zmk_hid_mod_is_pressed(code - HID_USAGE_KEY_KEYBOARD_LEFTCONTROL); + } + for (int idx = 0; idx < ZMK_HID_KEYBOARD_NKRO_SIZE; idx++) { + if (keyboard_report.body.keys[idx] == code) { + return true; + } + } + return false; +} + void zmk_hid_keyboard_clear() { memset(&keyboard_report.body, 0, sizeof(keyboard_report.body)); } int zmk_hid_consumer_press(zmk_key_t code) { @@ -129,6 +147,17 @@ int zmk_hid_consumer_release(zmk_key_t code) { return 0; }; +void zmk_hid_consumer_clear() { memset(&consumer_report.body, 0, sizeof(consumer_report.body)); } + +bool zmk_hid_consumer_is_pressed(zmk_key_t key) { + for (int idx = 0; idx < ZMK_HID_CONSUMER_NKRO_SIZE; idx++) { + if (consumer_report.body.keys[idx] == key) { + return true; + } + } + return false; +} + int zmk_hid_press(uint8_t usage_page, zmk_key_t code) { switch (usage_page) { case HID_USAGE_KEY: @@ -149,7 +178,15 @@ int zmk_hid_release(uint8_t usage_page, zmk_key_t code) { return -EINVAL; } -void zmk_hid_consumer_clear() { memset(&consumer_report.body, 0, sizeof(consumer_report.body)); } +bool zmk_hid_is_pressed(uint8_t usage_page, zmk_key_t code) { + switch (usage_page) { + case HID_USAGE_KEY: + return zmk_hid_keyboard_is_pressed(code); + case HID_USAGE_CONSUMER: + return zmk_hid_consumer_is_pressed(code); + } + return false; +} struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() { return &keyboard_report; diff --git a/app/tests/keytoggle/behavior_keymap.dtsi b/app/tests/keytoggle/behavior_keymap.dtsi new file mode 100644 index 00000000..32712a8d --- /dev/null +++ b/app/tests/keytoggle/behavior_keymap.dtsi @@ -0,0 +1,17 @@ +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kt B &none + &none &none + >; + }; + }; +}; diff --git a/app/tests/keytoggle/kt-press-release/events.patterns b/app/tests/keytoggle/kt-press-release/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/keytoggle/kt-press-release/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/keytoggle/kt-press-release/keycode_events.snapshot b/app/tests/keytoggle/kt-press-release/keycode_events.snapshot new file mode 100644 index 00000000..259501ba --- /dev/null +++ b/app/tests/keytoggle/kt-press-release/keycode_events.snapshot @@ -0,0 +1,2 @@ +pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/keytoggle/kt-press-release/native_posix.keymap b/app/tests/keytoggle/kt-press-release/native_posix.keymap new file mode 100644 index 00000000..644caa26 --- /dev/null +++ b/app/tests/keytoggle/kt-press-release/native_posix.keymap @@ -0,0 +1,10 @@ +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; \ No newline at end of file From 8fbaeddea5f12628b2d18a8fea49ea92329d7ee9 Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Tue, 18 May 2021 22:16:36 -0700 Subject: [PATCH 05/21] Update west.yml - Add macros (PR 685) --- app/west.yml | 42 +++++++----------------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/app/west.yml b/app/west.yml index ca5efad2..20e68709 100644 --- a/app/west.yml +++ b/app/west.yml @@ -1,41 +1,13 @@ manifest: remotes: - - name: zephyrproject-rtos - url-base: https://github.com/zephyrproject-rtos - name: zmkfirmware url-base: https://github.com/zmkfirmware - - name: microsoft - url-base: https://github.com/microsoft + - name: okke-formsma + url-base: https://github.com/okke-formsma projects: - - name: zephyr - remote: zmkfirmware - revision: v2.4.0+zmk-fixes - clone-depth: 1 - import: - # TODO: Rename once upstream offers option like `exclude` or `denylist` - name-blacklist: - - ci-tools - - hal_altera - - hal_cypress - - hal_infineon - - hal_microchip - - hal_nxp - - hal_openisa - - hal_silabs - - hal_xtensa - - hal_st - - hal_ti - - loramac-node - - mcuboot - - mcumgr - - net-tools - - segger - - openthread - - edtt - - trusted-firmware-m - - name: uf2 - remote: microsoft - path: tools/uf2 - clone-depth: 1 + - name: zmk + remote: okke-formsma + revision: macros + import: app/west.yml self: - west-commands: scripts/west-commands.yml + path: config \ No newline at end of file From 0f4dbeee2a297528d7ca17b768fb2f59237f273d Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Thu, 20 May 2021 21:04:42 -0700 Subject: [PATCH 06/21] Revert "Merge branch 'pr/684' into testing" This reverts commit a02aba653951c2e2ce41d8ac84651dd4ccebb7e5, reversing changes made to 8fbaeddea5f12628b2d18a8fea49ea92329d7ee9. --- .github/workflows/build.yml | 1 - .../two_percent_milk/Kconfig.defconfig | 9 ------- .../shields/two_percent_milk/Kconfig.shield | 5 ---- .../two_percent_milk/two_percent_milk.keymap | 24 ------------------- .../two_percent_milk/two_percent_milk.overlay | 24 ------------------- 5 files changed, 63 deletions(-) delete mode 100644 app/boards/shields/two_percent_milk/Kconfig.defconfig delete mode 100644 app/boards/shields/two_percent_milk/Kconfig.shield delete mode 100644 app/boards/shields/two_percent_milk/two_percent_milk.keymap delete mode 100644 app/boards/shields/two_percent_milk/two_percent_milk.overlay diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 08713147..d7011283 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,6 @@ jobs: - lily58_right - microdox_left - microdox_right - - two_percent_milk - nibble - qaz - quefrency_left diff --git a/app/boards/shields/two_percent_milk/Kconfig.defconfig b/app/boards/shields/two_percent_milk/Kconfig.defconfig deleted file mode 100644 index 82126ca5..00000000 --- a/app/boards/shields/two_percent_milk/Kconfig.defconfig +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2021 The ZMK Contributors -# SPDX-License-Identifier: MIT - -if SHIELD_TWO_PERCENT_MILK - -config ZMK_KEYBOARD_NAME - default "2% Milk" - -endif diff --git a/app/boards/shields/two_percent_milk/Kconfig.shield b/app/boards/shields/two_percent_milk/Kconfig.shield deleted file mode 100644 index 700eaa13..00000000 --- a/app/boards/shields/two_percent_milk/Kconfig.shield +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) 2021 The ZMK Contributors -# SPDX-License-Identifier: MIT - -config SHIELD_TWO_PERCENT_MILK - def_bool $(shields_list_contains,two_percent_milk) diff --git a/app/boards/shields/two_percent_milk/two_percent_milk.keymap b/app/boards/shields/two_percent_milk/two_percent_milk.keymap deleted file mode 100644 index c2bf02a7..00000000 --- a/app/boards/shields/two_percent_milk/two_percent_milk.keymap +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2021 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - - #include - #include - #include - - #define DEFAULT 0 - - / { - keymap { - compatible = "zmk,keymap"; - - default_layer { - bindings = < - &kp X - &kp Z - >; - }; - }; - }; \ No newline at end of file diff --git a/app/boards/shields/two_percent_milk/two_percent_milk.overlay b/app/boards/shields/two_percent_milk/two_percent_milk.overlay deleted file mode 100644 index 1cc7afbe..00000000 --- a/app/boards/shields/two_percent_milk/two_percent_milk.overlay +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2021 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -/ { - chosen { - zmk,kscan = &kscan0; - }; - - kscan0: kscan { - compatible = "zmk,kscan-gpio-direct"; - - label = "KSCAN"; - - input-gpios - = <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> - , <&pro_micro_d 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> - ; - - }; - -}; \ No newline at end of file From 48ed9fe01d3861689e037f6dab69149e82c08189 Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Thu, 20 May 2021 21:16:31 -0700 Subject: [PATCH 07/21] Revert "Revert "Merge branch 'pr/684' into testing"" This reverts commit 0f4dbeee2a297528d7ca17b768fb2f59237f273d. --- .github/workflows/build.yml | 1 + .../two_percent_milk/Kconfig.defconfig | 9 +++++++ .../shields/two_percent_milk/Kconfig.shield | 5 ++++ .../two_percent_milk/two_percent_milk.keymap | 24 +++++++++++++++++++ .../two_percent_milk/two_percent_milk.overlay | 24 +++++++++++++++++++ 5 files changed, 63 insertions(+) create mode 100644 app/boards/shields/two_percent_milk/Kconfig.defconfig create mode 100644 app/boards/shields/two_percent_milk/Kconfig.shield create mode 100644 app/boards/shields/two_percent_milk/two_percent_milk.keymap create mode 100644 app/boards/shields/two_percent_milk/two_percent_milk.overlay diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d7011283..08713147 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,6 +46,7 @@ jobs: - lily58_right - microdox_left - microdox_right + - two_percent_milk - nibble - qaz - quefrency_left diff --git a/app/boards/shields/two_percent_milk/Kconfig.defconfig b/app/boards/shields/two_percent_milk/Kconfig.defconfig new file mode 100644 index 00000000..82126ca5 --- /dev/null +++ b/app/boards/shields/two_percent_milk/Kconfig.defconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SHIELD_TWO_PERCENT_MILK + +config ZMK_KEYBOARD_NAME + default "2% Milk" + +endif diff --git a/app/boards/shields/two_percent_milk/Kconfig.shield b/app/boards/shields/two_percent_milk/Kconfig.shield new file mode 100644 index 00000000..700eaa13 --- /dev/null +++ b/app/boards/shields/two_percent_milk/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SHIELD_TWO_PERCENT_MILK + def_bool $(shields_list_contains,two_percent_milk) diff --git a/app/boards/shields/two_percent_milk/two_percent_milk.keymap b/app/boards/shields/two_percent_milk/two_percent_milk.keymap new file mode 100644 index 00000000..c2bf02a7 --- /dev/null +++ b/app/boards/shields/two_percent_milk/two_percent_milk.keymap @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + + #include + #include + #include + + #define DEFAULT 0 + + / { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp X + &kp Z + >; + }; + }; + }; \ No newline at end of file diff --git a/app/boards/shields/two_percent_milk/two_percent_milk.overlay b/app/boards/shields/two_percent_milk/two_percent_milk.overlay new file mode 100644 index 00000000..1cc7afbe --- /dev/null +++ b/app/boards/shields/two_percent_milk/two_percent_milk.overlay @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + chosen { + zmk,kscan = &kscan0; + }; + + kscan0: kscan { + compatible = "zmk,kscan-gpio-direct"; + + label = "KSCAN"; + + input-gpios + = <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro_d 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + ; + + }; + +}; \ No newline at end of file From 07c236e310837c5e14a02c23232fa8b9eb573f31 Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Sun, 23 May 2021 12:57:11 -0700 Subject: [PATCH 08/21] Beginning work of Tap Dance --- app/CMakeLists.txt | 1 + .../behaviors/zmk,behavior-tap-dance.yaml | 16 +++ app/src/behaviors/behavior_tap_dance.c | 99 +++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml create mode 100644 app/src/behaviors/behavior_tap_dance.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index ad0d97a8..1412d810 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -46,6 +46,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_key_toggle.c) target_sources(app PRIVATE src/behaviors/behavior_reset.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) + target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c) target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c) target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c) target_sources(app PRIVATE src/behaviors/behavior_mod_morph.c) diff --git a/app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml b/app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml new file mode 100644 index 00000000..42145be4 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Tap Dance Behavior + +compatible: "zmk,behavior-tap-dance" + +include: zero_param.yaml + +properties: + bindings: + type: phandle-array + required: true + tapping_term_ms: + type: int + default: 175 \ No newline at end of file diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c new file mode 100644 index 00000000..df651e0b --- /dev/null +++ b/app/src/behaviors/behavior_tap_dance.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_tap_dance + +#include +#include +#include +#include + +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +static bool tap_dance_started = false; +static int tap_counter = 0; +int64_t timestamp; + +struct behavior_tap_dance_config { + int tapping_term_ms; +}; + +static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct behavior_tap_dance_config *cfg = dev->config; + if (!tap_dance_started) { + tap_dance_started = true; + timestamp = k_uptime_get(); + LOG_DBG("timestamp at: %lld", timestamp); + } + else { + if (k_uptime_get() <= (timestamp + cfg->tapping_term_ms)){ + timestamp = k_uptime_get(); + LOG_DBG("Updated timestamp: %lld", timestamp); + } + } + int32_t tapping_term_ms_left = (timestamp + cfg->tapping_term_ms) - k_uptime_get(); + LOG_DBG("time left: %d", tapping_term_ms_left); + + if (tapping_term_ms_left <= 0) { + LOG_DBG("TIME'S UP"); + LOG_DBG("Counter reached on press: %d", tap_counter); + tap_dance_started = false; + tap_counter = 0; + } + else { + ++ tap_counter; + } + LOG_DBG("Counter beginning at: %d", tap_counter); + + + return ZMK_BEHAVIOR_OPAQUE; + + +} + +static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct behavior_tap_dance_config *cfg = dev->config; + int32_t tapping_term_ms_left = (timestamp + cfg->tapping_term_ms) - k_uptime_get(); + if (tapping_term_ms_left <= 0) { + LOG_DBG("Counter reached on release: %d", tap_counter); + tap_dance_started = false; + tap_counter = 0; + } + return ZMK_BEHAVIOR_OPAQUE; +} + +static const struct behavior_driver_api behavior_tap_dance_driver_api = { + .binding_pressed = on_tap_dance_binding_pressed, + .binding_released = on_tap_dance_binding_released, +}; + +static int behavior_tap_dance_init(const struct device *dev) { return 0; }; + +struct behavior_tap_dance_data {}; +static struct behavior_tap_dance_data behavior_tap_dance_data; + +#define KP_INST(n) \ + static struct behavior_tap_dance_config behavior_tap_dance_config_##n = { \ + .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ + }; \ + DEVICE_AND_API_INIT(behavior_tap_dance_##n, DT_INST_LABEL(n), behavior_tap_dance_init, &behavior_tap_dance_data, \ + &behavior_tap_dance_config_##n, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_tap_dance_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif \ No newline at end of file From 65091e43eefab3074e23adab53e8d805d6daf1dc Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Sun, 23 May 2021 22:50:39 -0700 Subject: [PATCH 09/21] Working Timer --- app/src/behaviors/behavior_tap_dance.c | 166 +++++++++++++++++++------ 1 file changed, 128 insertions(+), 38 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index df651e0b..ad8d7f21 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -20,58 +20,123 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) -static bool tap_dance_started = false; -static int tap_counter = 0; -int64_t timestamp; +#define ZMK_BHV_TAP_DANCE_POSITION_FREE ULONG_MAX struct behavior_tap_dance_config { - int tapping_term_ms; + uint32_t tapping_term_ms; + struct zmk_behavior_binding behavior; }; +struct active_tap_dance { + // tap dance data + int tap_counter; + uint32_t position; + const struct behavior_tap_dance_config *config; + // timer data + bool timer_started; + bool timer_cancelled; + int64_t release_at; + struct k_delayed_work release_timer; +}; + +struct active_tap_dance the_tap_dance; + +static struct active_tap_dance *store_tap_dance(uint32_t position, + const struct behavior_tap_dance_config *config) { + struct active_tap_dance *const tap_dance = &the_tap_dance; + tap_dance->tap_counter = 0; + tap_dance->position = position; + tap_dance->config = config; + tap_dance->release_at = 0; + tap_dance->timer_cancelled = false; + tap_dance->timer_started = false; + return tap_dance; + return NULL; +} + + +static void clear_tap_dance(struct active_tap_dance *tap_dance) { + LOG_DBG("Clearing Tap Dance"); + tap_dance-> position = ZMK_BHV_TAP_DANCE_POSITION_FREE; +} + +static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, + int64_t timestamp) { + LOG_DBG("Press Tap Dance Behavior"); + struct zmk_behavior_binding binding = { + .behavior_dev = tap_dance->config->behavior.behavior_dev, + }; + struct zmk_behavior_binding_event event = { + .position = tap_dance->position, + .timestamp = timestamp, + }; + return 0; + //return behavior_keymap_binding_pressed(&binding, event); +} + +static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, + int64_t timestamp) { + LOG_DBG("Release Tap Dance Behavior"); + struct zmk_behavior_binding binding = { + .behavior_dev = tap_dance->config->behavior.behavior_dev, + }; + struct zmk_behavior_binding_event event = { + .position = tap_dance->position, + .timestamp = timestamp, + }; + + clear_tap_dance(tap_dance); + return 0; + //return behavior_keymap_binding_released(&binding, event); +} + +static int stop_timer(struct active_tap_dance *tap_dance) { + LOG_DBG("Stop Timer"); + int timer_cancel_result = k_delayed_work_cancel(&tap_dance->release_timer); + if (timer_cancel_result == -EINPROGRESS) { + tap_dance->timer_cancelled = true; + } + return timer_cancel_result; +} + static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { + LOG_DBG("On Binding Pressed"); const struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_tap_dance_config *cfg = dev->config; - if (!tap_dance_started) { - tap_dance_started = true; - timestamp = k_uptime_get(); - LOG_DBG("timestamp at: %lld", timestamp); + struct active_tap_dance *tap_dance = &the_tap_dance; + if (tap_dance != NULL) { + stop_timer(tap_dance); + release_tap_dance_behavior(tap_dance, event.timestamp); } - else { - if (k_uptime_get() <= (timestamp + cfg->tapping_term_ms)){ - timestamp = k_uptime_get(); - LOG_DBG("Updated timestamp: %lld", timestamp); - } + tap_dance = store_tap_dance(event.position, cfg); + if (tap_dance == NULL) { + LOG_ERR("unable to store tap dance"); + return ZMK_BEHAVIOR_OPAQUE; } - int32_t tapping_term_ms_left = (timestamp + cfg->tapping_term_ms) - k_uptime_get(); - LOG_DBG("time left: %d", tapping_term_ms_left); - if (tapping_term_ms_left <= 0) { - LOG_DBG("TIME'S UP"); - LOG_DBG("Counter reached on press: %d", tap_counter); - tap_dance_started = false; - tap_counter = 0; - } - else { - ++ tap_counter; - } - LOG_DBG("Counter beginning at: %d", tap_counter); - - - return ZMK_BEHAVIOR_OPAQUE; - - + press_tap_dance_behavior(tap_dance, event.timestamp); + LOG_DBG("%d new tap_dance", event.position); + return ZMK_BEHAVIOR_OPAQUE; } static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { - const struct device *dev = device_get_binding(binding->behavior_dev); - const struct behavior_tap_dance_config *cfg = dev->config; - int32_t tapping_term_ms_left = (timestamp + cfg->tapping_term_ms) - k_uptime_get(); - if (tapping_term_ms_left <= 0) { - LOG_DBG("Counter reached on release: %d", tap_counter); - tap_dance_started = false; - tap_counter = 0; + LOG_DBG("On Binding Released"); + struct active_tap_dance *tap_dance = &the_tap_dance; + if (tap_dance == NULL) { + LOG_ERR("ACTIVE STICKY KEY CLEARED TOO EARLY"); + return ZMK_BEHAVIOR_OPAQUE; + } + + // No other key was pressed. Start the timer. + tap_dance->timer_started = true; + tap_dance->release_at = event.timestamp + tap_dance->config->tapping_term_ms; + // adjust timer in case this behavior was queued by a hold-tap + int32_t ms_left = tap_dance->release_at - k_uptime_get(); + LOG_DBG("ms_left equal to: %d", ms_left); + if (ms_left > 0) { + k_delayed_work_submit(&tap_dance->release_timer, K_MSEC(ms_left)); } return ZMK_BEHAVIOR_OPAQUE; } @@ -81,7 +146,32 @@ static const struct behavior_driver_api behavior_tap_dance_driver_api = { .binding_released = on_tap_dance_binding_released, }; -static int behavior_tap_dance_init(const struct device *dev) { return 0; }; +void behavior_tap_dance_timer_handler(struct k_work *item) { + LOG_DBG("Timer Handler Called"); + LOG_DBG("Counter reached: %d", the_tap_dance.tap_counter); + struct active_tap_dance *tap_dance = + CONTAINER_OF(item, struct active_tap_dance, release_timer); + if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { + return; + } + if (tap_dance->timer_cancelled) { + tap_dance->timer_cancelled = false; + } else { + release_tap_dance_behavior(tap_dance, tap_dance->release_at); + } +} + +static int behavior_tap_dance_init(const struct device *dev) { + static bool init_first_run = true; + if (init_first_run) { + k_delayed_work_init(&the_tap_dance.release_timer, + behavior_tap_dance_timer_handler); + LOG_DBG("Hello World"); + the_tap_dance.position = ZMK_BHV_TAP_DANCE_POSITION_FREE; + } + init_first_run = false; + return 0; +} struct behavior_tap_dance_data {}; static struct behavior_tap_dance_data behavior_tap_dance_data; From 536db5b3e83e6ceebf91166174de1ad55a00149b Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Sun, 23 May 2021 23:45:14 -0700 Subject: [PATCH 10/21] Working Timer, Working Counter --- app/src/behaviors/behavior_tap_dance.c | 61 +++++++++++--------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index ad8d7f21..c3447d04 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -29,7 +29,7 @@ struct behavior_tap_dance_config { struct active_tap_dance { // tap dance data - int tap_counter; + int counter; uint32_t position; const struct behavior_tap_dance_config *config; // timer data @@ -41,25 +41,13 @@ struct active_tap_dance { struct active_tap_dance the_tap_dance; -static struct active_tap_dance *store_tap_dance(uint32_t position, - const struct behavior_tap_dance_config *config) { - struct active_tap_dance *const tap_dance = &the_tap_dance; - tap_dance->tap_counter = 0; - tap_dance->position = position; - tap_dance->config = config; - tap_dance->release_at = 0; - tap_dance->timer_cancelled = false; - tap_dance->timer_started = false; - return tap_dance; - return NULL; -} - - static void clear_tap_dance(struct active_tap_dance *tap_dance) { LOG_DBG("Clearing Tap Dance"); tap_dance-> position = ZMK_BHV_TAP_DANCE_POSITION_FREE; } + + static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, int64_t timestamp) { LOG_DBG("Press Tap Dance Behavior"); @@ -90,33 +78,40 @@ static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, //return behavior_keymap_binding_released(&binding, event); } -static int stop_timer(struct active_tap_dance *tap_dance) { +/*static int stop_timer(struct active_tap_dance *tap_dance) { LOG_DBG("Stop Timer"); int timer_cancel_result = k_delayed_work_cancel(&tap_dance->release_timer); if (timer_cancel_result == -EINPROGRESS) { tap_dance->timer_cancelled = true; } return timer_cancel_result; -} + + static struct active_tap_dance *tap_dance +}*/ + + static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("On Binding Pressed"); const struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_tap_dance_config *cfg = dev->config; - struct active_tap_dance *tap_dance = &the_tap_dance; - if (tap_dance != NULL) { - stop_timer(tap_dance); - release_tap_dance_behavior(tap_dance, event.timestamp); - } - tap_dance = store_tap_dance(event.position, cfg); - if (tap_dance == NULL) { - LOG_ERR("unable to store tap dance"); - return ZMK_BEHAVIOR_OPAQUE; - } - press_tap_dance_behavior(tap_dance, event.timestamp); - LOG_DBG("%d new tap_dance", event.position); + if (!the_tap_dance.timer_started){ + LOG_DBG("Initializing Tap Dance"); + the_tap_dance.timer_started = true; + the_tap_dance.counter = 1; + the_tap_dance.position = event.position; + the_tap_dance.config = cfg; + the_tap_dance.release_at = 0; + the_tap_dance.timer_cancelled = false; + the_tap_dance.timer_started = false; + } + else { + LOG_DBG("Incrementing Tap Dance"); + the_tap_dance.counter ++; + } + LOG_DBG("Counter is now at value: %d", the_tap_dance.counter); return ZMK_BEHAVIOR_OPAQUE; } @@ -124,11 +119,6 @@ static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("On Binding Released"); struct active_tap_dance *tap_dance = &the_tap_dance; - if (tap_dance == NULL) { - LOG_ERR("ACTIVE STICKY KEY CLEARED TOO EARLY"); - return ZMK_BEHAVIOR_OPAQUE; - } - // No other key was pressed. Start the timer. tap_dance->timer_started = true; tap_dance->release_at = event.timestamp + tap_dance->config->tapping_term_ms; @@ -148,7 +138,6 @@ static const struct behavior_driver_api behavior_tap_dance_driver_api = { void behavior_tap_dance_timer_handler(struct k_work *item) { LOG_DBG("Timer Handler Called"); - LOG_DBG("Counter reached: %d", the_tap_dance.tap_counter); struct active_tap_dance *tap_dance = CONTAINER_OF(item, struct active_tap_dance, release_timer); if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { @@ -159,6 +148,8 @@ void behavior_tap_dance_timer_handler(struct k_work *item) { } else { release_tap_dance_behavior(tap_dance, tap_dance->release_at); } + the_tap_dance.counter = 0; + the_tap_dance.timer_started = false; } static int behavior_tap_dance_init(const struct device *dev) { From 4461167d82e55ef391a6c7716c4ebe7945b237bb Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Mon, 24 May 2021 11:03:52 -0700 Subject: [PATCH 11/21] First version of full tap dance functionality --- app/src/behaviors/behavior_tap_dance.c | 96 +++++++++++--------------- 1 file changed, 39 insertions(+), 57 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index c3447d04..b26a6ec4 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -24,7 +24,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); struct behavior_tap_dance_config { uint32_t tapping_term_ms; - struct zmk_behavior_binding behavior; + int behavior_count; + struct zmk_behavior_binding *behaviors; }; struct active_tap_dance { @@ -37,6 +38,7 @@ struct active_tap_dance { bool timer_cancelled; int64_t release_at; struct k_delayed_work release_timer; + struct zmk_behavior_binding_event event; }; struct active_tap_dance the_tap_dance; @@ -46,51 +48,6 @@ static void clear_tap_dance(struct active_tap_dance *tap_dance) { tap_dance-> position = ZMK_BHV_TAP_DANCE_POSITION_FREE; } - - -static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, - int64_t timestamp) { - LOG_DBG("Press Tap Dance Behavior"); - struct zmk_behavior_binding binding = { - .behavior_dev = tap_dance->config->behavior.behavior_dev, - }; - struct zmk_behavior_binding_event event = { - .position = tap_dance->position, - .timestamp = timestamp, - }; - return 0; - //return behavior_keymap_binding_pressed(&binding, event); -} - -static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, - int64_t timestamp) { - LOG_DBG("Release Tap Dance Behavior"); - struct zmk_behavior_binding binding = { - .behavior_dev = tap_dance->config->behavior.behavior_dev, - }; - struct zmk_behavior_binding_event event = { - .position = tap_dance->position, - .timestamp = timestamp, - }; - - clear_tap_dance(tap_dance); - return 0; - //return behavior_keymap_binding_released(&binding, event); -} - -/*static int stop_timer(struct active_tap_dance *tap_dance) { - LOG_DBG("Stop Timer"); - int timer_cancel_result = k_delayed_work_cancel(&tap_dance->release_timer); - if (timer_cancel_result == -EINPROGRESS) { - tap_dance->timer_cancelled = true; - } - return timer_cancel_result; - - static struct active_tap_dance *tap_dance -}*/ - - - static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("On Binding Pressed"); @@ -100,17 +57,19 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, if (!the_tap_dance.timer_started){ LOG_DBG("Initializing Tap Dance"); the_tap_dance.timer_started = true; - the_tap_dance.counter = 1; + the_tap_dance.counter = 0; the_tap_dance.position = event.position; the_tap_dance.config = cfg; the_tap_dance.release_at = 0; the_tap_dance.timer_cancelled = false; the_tap_dance.timer_started = false; + } else { LOG_DBG("Incrementing Tap Dance"); - the_tap_dance.counter ++; } + the_tap_dance.counter ++; + the_tap_dance.event = event; LOG_DBG("Counter is now at value: %d", the_tap_dance.counter); return ZMK_BEHAVIOR_OPAQUE; } @@ -131,14 +90,9 @@ static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } -static const struct behavior_driver_api behavior_tap_dance_driver_api = { - .binding_pressed = on_tap_dance_binding_pressed, - .binding_released = on_tap_dance_binding_released, -}; - void behavior_tap_dance_timer_handler(struct k_work *item) { LOG_DBG("Timer Handler Called"); - struct active_tap_dance *tap_dance = + struct active_tap_dance *tap_dance = CONTAINER_OF(item, struct active_tap_dance, release_timer); if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { return; @@ -146,18 +100,41 @@ void behavior_tap_dance_timer_handler(struct k_work *item) { if (tap_dance->timer_cancelled) { tap_dance->timer_cancelled = false; } else { - release_tap_dance_behavior(tap_dance, tap_dance->release_at); + if (the_tap_dance.counter <= the_tap_dance.config->behavior_count) { + behavior_keymap_binding_pressed(&the_tap_dance.config->behaviors[the_tap_dance.counter-1], the_tap_dance.event); + behavior_keymap_binding_released(&the_tap_dance.config->behaviors[the_tap_dance.counter-1], the_tap_dance.event); + } + else{ + LOG_DBG("Counter exceeded number of keybinds"); + } + clear_tap_dance(tap_dance); } the_tap_dance.counter = 0; the_tap_dance.timer_started = false; } +static const struct behavior_driver_api behavior_tap_dance_driver_api = { + .binding_pressed = on_tap_dance_binding_pressed, + .binding_released = on_tap_dance_binding_released, +}; + +#define _TRANSFORM_ENTRY(idx, node) \ + { \ + .behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \ + .param1 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param1), (0), \ + (DT_INST_PHA_BY_IDX(node, bindings, idx, param1))), \ + .param2 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param2), (0), \ + (DT_INST_PHA_BY_IDX(node, bindings, idx, param2))), \ + }, + +#define TRANSFORMED_BINDINGS(node) \ + { UTIL_LISTIFY(DT_INST_PROP_LEN(node, bindings), _TRANSFORM_ENTRY, node) } + static int behavior_tap_dance_init(const struct device *dev) { static bool init_first_run = true; if (init_first_run) { k_delayed_work_init(&the_tap_dance.release_timer, behavior_tap_dance_timer_handler); - LOG_DBG("Hello World"); the_tap_dance.position = ZMK_BHV_TAP_DANCE_POSITION_FREE; } init_first_run = false; @@ -168,8 +145,13 @@ struct behavior_tap_dance_data {}; static struct behavior_tap_dance_data behavior_tap_dance_data; #define KP_INST(n) \ + static struct zmk_behavior_binding \ + behavior_tap_dance_config_##n##_bindings[DT_INST_PROP_LEN(n, bindings)] = \ + TRANSFORMED_BINDINGS(n); \ static struct behavior_tap_dance_config behavior_tap_dance_config_##n = { \ - .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ + .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ + .behaviors = behavior_tap_dance_config_##n##_bindings, \ + .behavior_count = DT_INST_PROP_LEN(n, bindings) \ }; \ DEVICE_AND_API_INIT(behavior_tap_dance_##n, DT_INST_LABEL(n), behavior_tap_dance_init, &behavior_tap_dance_data, \ &behavior_tap_dance_config_##n, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ From 2d0613c3724e04b363b8258926065c19c8b51f76 Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Mon, 24 May 2021 16:00:59 -0700 Subject: [PATCH 12/21] Beginning support for multiple TDs --- app/src/behaviors/behavior_tap_dance.c | 158 +++++++++++++++++-------- 1 file changed, 107 insertions(+), 51 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index b26a6ec4..e78781bd 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -20,9 +20,11 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +#define ZMK_BHV_TAP_DANCE_MAX_HELD 10 + #define ZMK_BHV_TAP_DANCE_POSITION_FREE ULONG_MAX -struct behavior_tap_dance_config { +struct behavior_tap_dance_config { uint32_t tapping_term_ms; int behavior_count; struct zmk_behavior_binding *behaviors; @@ -31,6 +33,7 @@ struct behavior_tap_dance_config { struct active_tap_dance { // tap dance data int counter; + bool dance_begun; uint32_t position; const struct behavior_tap_dance_config *config; // timer data @@ -41,11 +44,69 @@ struct active_tap_dance { struct zmk_behavior_binding_event event; }; -struct active_tap_dance the_tap_dance; +struct active_tap_dance active_tap_dances[ZMK_BHV_TAP_DANCE_MAX_HELD] = {}; + +static struct active_tap_dance *store_tap_dance(uint32_t position, + const struct behavior_tap_dance_config *config) { + LOG_DBG("Position Detected at %d", position); + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + struct active_tap_dance *const tap_dance = &active_tap_dances[i]; + if (tap_dance->position != ZMK_BHV_TAP_DANCE_POSITION_FREE || tap_dance->timer_cancelled) { + tap_dance->counter++; + LOG_DBG("Counter balue currently at: %d", tap_dance->counter); + continue; + } + tap_dance->counter = 1; + tap_dance->position = position; + tap_dance->config = config; + tap_dance->release_at = 0; + tap_dance->timer_started = false; + tap_dance->timer_cancelled = false; + return tap_dance; + } + return NULL; +} + +static struct active_tap_dance *find_tap_dance(uint32_t position) { + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + if (active_tap_dances[i].position == position && !active_tap_dances[i].timer_cancelled) { + return &active_tap_dances[i]; + } + } + return NULL; +} static void clear_tap_dance(struct active_tap_dance *tap_dance) { LOG_DBG("Clearing Tap Dance"); - tap_dance-> position = ZMK_BHV_TAP_DANCE_POSITION_FREE; + tap_dance->position = ZMK_BHV_TAP_DANCE_POSITION_FREE; +} + +static int stop_timer(struct active_tap_dance *tap_dance) { + int timer_cancel_result = k_delayed_work_cancel(&tap_dance->release_timer); + if (timer_cancel_result == -EINPROGRESS) { + // too late to cancel, we'll let the timer handler clear up. + tap_dance->timer_cancelled = true; + } + return timer_cancel_result; +} + +static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, + int64_t timestamp) { + LOG_DBG("Release Tap Dance Behavior"); + LOG_DBG("Counter finally value: %d", tap_dance->counter); + struct zmk_behavior_binding binding = tap_dance->config->behaviors[(tap_dance->counter) - 1]; + struct zmk_behavior_binding_event event = { + .position = tap_dance->position, + .timestamp = timestamp, + }; + if (tap_dance->counter <= tap_dance->config->behavior_count) { + behavior_keymap_binding_pressed(&binding, event); + behavior_keymap_binding_released(&binding, event); + } else { + LOG_DBG("Counter exceeded number of keybinds"); + } + clear_tap_dance(tap_dance); + return 0; } static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, @@ -53,31 +114,35 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, LOG_DBG("On Binding Pressed"); const struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_tap_dance_config *cfg = dev->config; - - if (!the_tap_dance.timer_started){ - LOG_DBG("Initializing Tap Dance"); - the_tap_dance.timer_started = true; - the_tap_dance.counter = 0; - the_tap_dance.position = event.position; - the_tap_dance.config = cfg; - the_tap_dance.release_at = 0; - the_tap_dance.timer_cancelled = false; - the_tap_dance.timer_started = false; - + struct active_tap_dance *tap_dance; + tap_dance = find_tap_dance(event.position); + if (tap_dance != NULL) { + stop_timer(tap_dance); + // release_tap_dance_behavior(tap_dance, event.timestamp); } - else { - LOG_DBG("Incrementing Tap Dance"); + tap_dance = store_tap_dance(event.position, cfg); + if (tap_dance == NULL) { + LOG_ERR("unable to store tap dance, did you press more than %d tap_dance?", + ZMK_BHV_TAP_DANCE_MAX_HELD); + return ZMK_BEHAVIOR_OPAQUE; } - the_tap_dance.counter ++; - the_tap_dance.event = event; - LOG_DBG("Counter is now at value: %d", the_tap_dance.counter); - return ZMK_BEHAVIOR_OPAQUE; + return ZMK_BEHAVIOR_OPAQUE; } static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event) { + struct zmk_behavior_binding_event event) { LOG_DBG("On Binding Released"); - struct active_tap_dance *tap_dance = &the_tap_dance; + struct active_tap_dance *tap_dance = find_tap_dance(event.position); + if (tap_dance == NULL) { + LOG_ERR("ACTIVE TAP DANCE CLEARED TOO EARLY"); + return ZMK_BEHAVIOR_OPAQUE; + } + + /*if (tap_dance->position != 0 && tap_dance->modified_key_keycode != 0) { + LOG_DBG("Another key was pressed while the sticky key was pressed. Act like a normal key."); + return release_sticky_key_behavior(tap_dance, event.timestamp); + }*/ + // No other key was pressed. Start the timer. tap_dance->timer_started = true; tap_dance->release_at = event.timestamp + tap_dance->config->tapping_term_ms; @@ -92,25 +157,15 @@ static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, void behavior_tap_dance_timer_handler(struct k_work *item) { LOG_DBG("Timer Handler Called"); - struct active_tap_dance *tap_dance = - CONTAINER_OF(item, struct active_tap_dance, release_timer); + struct active_tap_dance *tap_dance = CONTAINER_OF(item, struct active_tap_dance, release_timer); if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { return; } if (tap_dance->timer_cancelled) { tap_dance->timer_cancelled = false; } else { - if (the_tap_dance.counter <= the_tap_dance.config->behavior_count) { - behavior_keymap_binding_pressed(&the_tap_dance.config->behaviors[the_tap_dance.counter-1], the_tap_dance.event); - behavior_keymap_binding_released(&the_tap_dance.config->behaviors[the_tap_dance.counter-1], the_tap_dance.event); - } - else{ - LOG_DBG("Counter exceeded number of keybinds"); - } - clear_tap_dance(tap_dance); + release_tap_dance_behavior(tap_dance, tap_dance->release_at); } - the_tap_dance.counter = 0; - the_tap_dance.timer_started = false; } static const struct behavior_driver_api behavior_tap_dance_driver_api = { @@ -130,12 +185,14 @@ static const struct behavior_driver_api behavior_tap_dance_driver_api = { #define TRANSFORMED_BINDINGS(node) \ { UTIL_LISTIFY(DT_INST_PROP_LEN(node, bindings), _TRANSFORM_ENTRY, node) } -static int behavior_tap_dance_init(const struct device *dev) { +static int behavior_tap_dance_init(const struct device *dev) { static bool init_first_run = true; if (init_first_run) { - k_delayed_work_init(&the_tap_dance.release_timer, - behavior_tap_dance_timer_handler); - the_tap_dance.position = ZMK_BHV_TAP_DANCE_POSITION_FREE; + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + k_delayed_work_init(&active_tap_dances[i].release_timer, + behavior_tap_dance_timer_handler); + active_tap_dances[i].position = ZMK_BHV_TAP_DANCE_POSITION_FREE; + } } init_first_run = false; return 0; @@ -144,19 +201,18 @@ static int behavior_tap_dance_init(const struct device *dev) { struct behavior_tap_dance_data {}; static struct behavior_tap_dance_data behavior_tap_dance_data; -#define KP_INST(n) \ - static struct zmk_behavior_binding \ - behavior_tap_dance_config_##n##_bindings[DT_INST_PROP_LEN(n, bindings)] = \ - TRANSFORMED_BINDINGS(n); \ - static struct behavior_tap_dance_config behavior_tap_dance_config_##n = { \ - .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ - .behaviors = behavior_tap_dance_config_##n##_bindings, \ - .behavior_count = DT_INST_PROP_LEN(n, bindings) \ - }; \ - DEVICE_AND_API_INIT(behavior_tap_dance_##n, DT_INST_LABEL(n), behavior_tap_dance_init, &behavior_tap_dance_data, \ - &behavior_tap_dance_config_##n, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ - &behavior_tap_dance_driver_api); - +#define KP_INST(n) \ + static struct zmk_behavior_binding \ + behavior_tap_dance_config_##n##_bindings[DT_INST_PROP_LEN(n, bindings)] = \ + TRANSFORMED_BINDINGS(n); \ + static struct behavior_tap_dance_config behavior_tap_dance_config_##n = { \ + .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ + .behaviors = behavior_tap_dance_config_##n##_bindings, \ + .behavior_count = DT_INST_PROP_LEN(n, bindings)}; \ + DEVICE_AND_API_INIT(behavior_tap_dance_##n, DT_INST_LABEL(n), behavior_tap_dance_init, \ + &behavior_tap_dance_data, &behavior_tap_dance_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_tap_dance_driver_api); + DT_INST_FOREACH_STATUS_OKAY(KP_INST) #endif \ No newline at end of file From 43633160b632cc71fe12f2a663956314b3ff2a25 Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Mon, 24 May 2021 18:04:41 -0700 Subject: [PATCH 13/21] Beginning support for multiple TDs --- app/src/behaviors/behavior_tap_dance.c | 158 +++++++++++++++++-------- 1 file changed, 107 insertions(+), 51 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index b26a6ec4..e78781bd 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -20,9 +20,11 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +#define ZMK_BHV_TAP_DANCE_MAX_HELD 10 + #define ZMK_BHV_TAP_DANCE_POSITION_FREE ULONG_MAX -struct behavior_tap_dance_config { +struct behavior_tap_dance_config { uint32_t tapping_term_ms; int behavior_count; struct zmk_behavior_binding *behaviors; @@ -31,6 +33,7 @@ struct behavior_tap_dance_config { struct active_tap_dance { // tap dance data int counter; + bool dance_begun; uint32_t position; const struct behavior_tap_dance_config *config; // timer data @@ -41,11 +44,69 @@ struct active_tap_dance { struct zmk_behavior_binding_event event; }; -struct active_tap_dance the_tap_dance; +struct active_tap_dance active_tap_dances[ZMK_BHV_TAP_DANCE_MAX_HELD] = {}; + +static struct active_tap_dance *store_tap_dance(uint32_t position, + const struct behavior_tap_dance_config *config) { + LOG_DBG("Position Detected at %d", position); + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + struct active_tap_dance *const tap_dance = &active_tap_dances[i]; + if (tap_dance->position != ZMK_BHV_TAP_DANCE_POSITION_FREE || tap_dance->timer_cancelled) { + tap_dance->counter++; + LOG_DBG("Counter balue currently at: %d", tap_dance->counter); + continue; + } + tap_dance->counter = 1; + tap_dance->position = position; + tap_dance->config = config; + tap_dance->release_at = 0; + tap_dance->timer_started = false; + tap_dance->timer_cancelled = false; + return tap_dance; + } + return NULL; +} + +static struct active_tap_dance *find_tap_dance(uint32_t position) { + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + if (active_tap_dances[i].position == position && !active_tap_dances[i].timer_cancelled) { + return &active_tap_dances[i]; + } + } + return NULL; +} static void clear_tap_dance(struct active_tap_dance *tap_dance) { LOG_DBG("Clearing Tap Dance"); - tap_dance-> position = ZMK_BHV_TAP_DANCE_POSITION_FREE; + tap_dance->position = ZMK_BHV_TAP_DANCE_POSITION_FREE; +} + +static int stop_timer(struct active_tap_dance *tap_dance) { + int timer_cancel_result = k_delayed_work_cancel(&tap_dance->release_timer); + if (timer_cancel_result == -EINPROGRESS) { + // too late to cancel, we'll let the timer handler clear up. + tap_dance->timer_cancelled = true; + } + return timer_cancel_result; +} + +static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, + int64_t timestamp) { + LOG_DBG("Release Tap Dance Behavior"); + LOG_DBG("Counter finally value: %d", tap_dance->counter); + struct zmk_behavior_binding binding = tap_dance->config->behaviors[(tap_dance->counter) - 1]; + struct zmk_behavior_binding_event event = { + .position = tap_dance->position, + .timestamp = timestamp, + }; + if (tap_dance->counter <= tap_dance->config->behavior_count) { + behavior_keymap_binding_pressed(&binding, event); + behavior_keymap_binding_released(&binding, event); + } else { + LOG_DBG("Counter exceeded number of keybinds"); + } + clear_tap_dance(tap_dance); + return 0; } static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, @@ -53,31 +114,35 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, LOG_DBG("On Binding Pressed"); const struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_tap_dance_config *cfg = dev->config; - - if (!the_tap_dance.timer_started){ - LOG_DBG("Initializing Tap Dance"); - the_tap_dance.timer_started = true; - the_tap_dance.counter = 0; - the_tap_dance.position = event.position; - the_tap_dance.config = cfg; - the_tap_dance.release_at = 0; - the_tap_dance.timer_cancelled = false; - the_tap_dance.timer_started = false; - + struct active_tap_dance *tap_dance; + tap_dance = find_tap_dance(event.position); + if (tap_dance != NULL) { + stop_timer(tap_dance); + // release_tap_dance_behavior(tap_dance, event.timestamp); } - else { - LOG_DBG("Incrementing Tap Dance"); + tap_dance = store_tap_dance(event.position, cfg); + if (tap_dance == NULL) { + LOG_ERR("unable to store tap dance, did you press more than %d tap_dance?", + ZMK_BHV_TAP_DANCE_MAX_HELD); + return ZMK_BEHAVIOR_OPAQUE; } - the_tap_dance.counter ++; - the_tap_dance.event = event; - LOG_DBG("Counter is now at value: %d", the_tap_dance.counter); - return ZMK_BEHAVIOR_OPAQUE; + return ZMK_BEHAVIOR_OPAQUE; } static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event) { + struct zmk_behavior_binding_event event) { LOG_DBG("On Binding Released"); - struct active_tap_dance *tap_dance = &the_tap_dance; + struct active_tap_dance *tap_dance = find_tap_dance(event.position); + if (tap_dance == NULL) { + LOG_ERR("ACTIVE TAP DANCE CLEARED TOO EARLY"); + return ZMK_BEHAVIOR_OPAQUE; + } + + /*if (tap_dance->position != 0 && tap_dance->modified_key_keycode != 0) { + LOG_DBG("Another key was pressed while the sticky key was pressed. Act like a normal key."); + return release_sticky_key_behavior(tap_dance, event.timestamp); + }*/ + // No other key was pressed. Start the timer. tap_dance->timer_started = true; tap_dance->release_at = event.timestamp + tap_dance->config->tapping_term_ms; @@ -92,25 +157,15 @@ static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, void behavior_tap_dance_timer_handler(struct k_work *item) { LOG_DBG("Timer Handler Called"); - struct active_tap_dance *tap_dance = - CONTAINER_OF(item, struct active_tap_dance, release_timer); + struct active_tap_dance *tap_dance = CONTAINER_OF(item, struct active_tap_dance, release_timer); if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { return; } if (tap_dance->timer_cancelled) { tap_dance->timer_cancelled = false; } else { - if (the_tap_dance.counter <= the_tap_dance.config->behavior_count) { - behavior_keymap_binding_pressed(&the_tap_dance.config->behaviors[the_tap_dance.counter-1], the_tap_dance.event); - behavior_keymap_binding_released(&the_tap_dance.config->behaviors[the_tap_dance.counter-1], the_tap_dance.event); - } - else{ - LOG_DBG("Counter exceeded number of keybinds"); - } - clear_tap_dance(tap_dance); + release_tap_dance_behavior(tap_dance, tap_dance->release_at); } - the_tap_dance.counter = 0; - the_tap_dance.timer_started = false; } static const struct behavior_driver_api behavior_tap_dance_driver_api = { @@ -130,12 +185,14 @@ static const struct behavior_driver_api behavior_tap_dance_driver_api = { #define TRANSFORMED_BINDINGS(node) \ { UTIL_LISTIFY(DT_INST_PROP_LEN(node, bindings), _TRANSFORM_ENTRY, node) } -static int behavior_tap_dance_init(const struct device *dev) { +static int behavior_tap_dance_init(const struct device *dev) { static bool init_first_run = true; if (init_first_run) { - k_delayed_work_init(&the_tap_dance.release_timer, - behavior_tap_dance_timer_handler); - the_tap_dance.position = ZMK_BHV_TAP_DANCE_POSITION_FREE; + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + k_delayed_work_init(&active_tap_dances[i].release_timer, + behavior_tap_dance_timer_handler); + active_tap_dances[i].position = ZMK_BHV_TAP_DANCE_POSITION_FREE; + } } init_first_run = false; return 0; @@ -144,19 +201,18 @@ static int behavior_tap_dance_init(const struct device *dev) { struct behavior_tap_dance_data {}; static struct behavior_tap_dance_data behavior_tap_dance_data; -#define KP_INST(n) \ - static struct zmk_behavior_binding \ - behavior_tap_dance_config_##n##_bindings[DT_INST_PROP_LEN(n, bindings)] = \ - TRANSFORMED_BINDINGS(n); \ - static struct behavior_tap_dance_config behavior_tap_dance_config_##n = { \ - .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ - .behaviors = behavior_tap_dance_config_##n##_bindings, \ - .behavior_count = DT_INST_PROP_LEN(n, bindings) \ - }; \ - DEVICE_AND_API_INIT(behavior_tap_dance_##n, DT_INST_LABEL(n), behavior_tap_dance_init, &behavior_tap_dance_data, \ - &behavior_tap_dance_config_##n, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ - &behavior_tap_dance_driver_api); - +#define KP_INST(n) \ + static struct zmk_behavior_binding \ + behavior_tap_dance_config_##n##_bindings[DT_INST_PROP_LEN(n, bindings)] = \ + TRANSFORMED_BINDINGS(n); \ + static struct behavior_tap_dance_config behavior_tap_dance_config_##n = { \ + .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ + .behaviors = behavior_tap_dance_config_##n##_bindings, \ + .behavior_count = DT_INST_PROP_LEN(n, bindings)}; \ + DEVICE_AND_API_INIT(behavior_tap_dance_##n, DT_INST_LABEL(n), behavior_tap_dance_init, \ + &behavior_tap_dance_data, &behavior_tap_dance_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_tap_dance_driver_api); + DT_INST_FOREACH_STATUS_OKAY(KP_INST) #endif \ No newline at end of file From 2fa0ed3cb081aca27281722d2eafa565c52e326a Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Mon, 24 May 2021 18:52:06 -0700 Subject: [PATCH 14/21] TD immediately resolves when max count is reached --- app/src/behaviors/behavior_tap_dance.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index e78781bd..e8c501cf 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -46,14 +46,14 @@ struct active_tap_dance { struct active_tap_dance active_tap_dances[ZMK_BHV_TAP_DANCE_MAX_HELD] = {}; +static int stop_timer(struct active_tap_dance *tap_dance); + static struct active_tap_dance *store_tap_dance(uint32_t position, const struct behavior_tap_dance_config *config) { LOG_DBG("Position Detected at %d", position); for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { struct active_tap_dance *const tap_dance = &active_tap_dances[i]; if (tap_dance->position != ZMK_BHV_TAP_DANCE_POSITION_FREE || tap_dance->timer_cancelled) { - tap_dance->counter++; - LOG_DBG("Counter balue currently at: %d", tap_dance->counter); continue; } tap_dance->counter = 1; @@ -93,7 +93,6 @@ static int stop_timer(struct active_tap_dance *tap_dance) { static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, int64_t timestamp) { LOG_DBG("Release Tap Dance Behavior"); - LOG_DBG("Counter finally value: %d", tap_dance->counter); struct zmk_behavior_binding binding = tap_dance->config->behaviors[(tap_dance->counter) - 1]; struct zmk_behavior_binding_event event = { .position = tap_dance->position, @@ -109,6 +108,7 @@ static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, return 0; } + static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("On Binding Pressed"); @@ -118,13 +118,17 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, tap_dance = find_tap_dance(event.position); if (tap_dance != NULL) { stop_timer(tap_dance); - // release_tap_dance_behavior(tap_dance, event.timestamp); + if (++tap_dance->counter >= cfg->behavior_count){ + release_tap_dance_behavior(tap_dance, event.timestamp); + } } - tap_dance = store_tap_dance(event.position, cfg); - if (tap_dance == NULL) { - LOG_ERR("unable to store tap dance, did you press more than %d tap_dance?", - ZMK_BHV_TAP_DANCE_MAX_HELD); - return ZMK_BEHAVIOR_OPAQUE; + else{ + tap_dance = store_tap_dance(event.position, cfg); + if (tap_dance == NULL) { + LOG_ERR("unable to store tap dance, did you press more than %d tap_dance?", + ZMK_BHV_TAP_DANCE_MAX_HELD); + return ZMK_BEHAVIOR_OPAQUE; + } } return ZMK_BEHAVIOR_OPAQUE; } From 11917b16b1779e57017d1a68d9b6616a542ef97e Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Mon, 24 May 2021 21:00:33 -0700 Subject: [PATCH 15/21] Attempting to resolve other keypress mid-tap-dance --- app/src/behaviors/behavior_tap_dance.c | 90 ++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 6 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index e8c501cf..f997de57 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -33,15 +33,19 @@ struct behavior_tap_dance_config { struct active_tap_dance { // tap dance data int counter; - bool dance_begun; uint32_t position; + uint32_t param1; + uint32_t param2; const struct behavior_tap_dance_config *config; // timer data bool timer_started; bool timer_cancelled; int64_t release_at; struct k_delayed_work release_timer; - struct zmk_behavior_binding_event event; + // usage page and keycode for the key that is being modified by this sticky key + uint8_t modified_key_usage_page; + uint32_t modified_key_keycode; + }; struct active_tap_dance active_tap_dances[ZMK_BHV_TAP_DANCE_MAX_HELD] = {}; @@ -62,6 +66,8 @@ static struct active_tap_dance *store_tap_dance(uint32_t position, tap_dance->release_at = 0; tap_dance->timer_started = false; tap_dance->timer_cancelled = false; + tap_dance->modified_key_usage_page = 0; + tap_dance->modified_key_keycode = 0; return tap_dance; } return NULL; @@ -90,6 +96,23 @@ static int stop_timer(struct active_tap_dance *tap_dance) { return timer_cancel_result; } +static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, + int64_t timestamp) { + LOG_DBG("Press Tap Dance Behavior"); + struct zmk_behavior_binding binding = tap_dance->config->behaviors[(tap_dance->counter) - 1]; + struct zmk_behavior_binding_event event = { + .position = tap_dance->position, + .timestamp = timestamp, + }; + if (tap_dance->counter <= tap_dance->config->behavior_count) { + behavior_keymap_binding_pressed(&binding, event); + } else { + LOG_DBG("Counter exceeded number of keybinds"); + } + clear_tap_dance(tap_dance); + return 0; +} + static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, int64_t timestamp) { LOG_DBG("Release Tap Dance Behavior"); @@ -99,7 +122,6 @@ static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, .timestamp = timestamp, }; if (tap_dance->counter <= tap_dance->config->behavior_count) { - behavior_keymap_binding_pressed(&binding, event); behavior_keymap_binding_released(&binding, event); } else { LOG_DBG("Counter exceeded number of keybinds"); @@ -119,6 +141,7 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, if (tap_dance != NULL) { stop_timer(tap_dance); if (++tap_dance->counter >= cfg->behavior_count){ + press_tap_dance_behavior(tap_dance, event.timestamp); release_tap_dance_behavior(tap_dance, event.timestamp); } } @@ -142,10 +165,11 @@ static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } - /*if (tap_dance->position != 0 && tap_dance->modified_key_keycode != 0) { + if (tap_dance->position != 0 && tap_dance->modified_key_keycode != 0) { LOG_DBG("Another key was pressed while the sticky key was pressed. Act like a normal key."); - return release_sticky_key_behavior(tap_dance, event.timestamp); - }*/ + press_tap_dance_behavior(tap_dance, event.timestamp); + return release_tap_dance_behavior(tap_dance, event.timestamp); + } // No other key was pressed. Start the timer. tap_dance->timer_started = true; @@ -168,6 +192,7 @@ void behavior_tap_dance_timer_handler(struct k_work *item) { if (tap_dance->timer_cancelled) { tap_dance->timer_cancelled = false; } else { + press_tap_dance_behavior(tap_dance, tap_dance->release_at); release_tap_dance_behavior(tap_dance, tap_dance->release_at); } } @@ -177,6 +202,59 @@ static const struct behavior_driver_api behavior_tap_dance_driver_api = { .binding_released = on_tap_dance_binding_released, }; +static int tap_dance_keycode_state_changed_listener(const zmk_event_t *eh); + +ZMK_LISTENER(behavior_tap_dance, tap_dance_keycode_state_changed_listener); +ZMK_SUBSCRIPTION(behavior_tap_dance, zmk_keycode_state_changed); + +static int tap_dance_keycode_state_changed_listener(const zmk_event_t *eh) { + struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh); + if (ev == NULL) { + return ZMK_EV_EVENT_BUBBLE; + } + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + struct active_tap_dance *tap_dance = &active_tap_dances[i]; + if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { + continue; + } + + if (strcmp(tap_dance->config->behaviors->behavior_dev, "KEY_PRESS") == 0 && + HID_USAGE_ID(tap_dance->param1) == ev->keycode && + HID_USAGE_PAGE(tap_dance->param1) == ev->usage_page && + SELECT_MODS(tap_dance->param1) == ev->implicit_modifiers) { + // don't catch key down events generated by the sticky key behavior itself + continue; + } + + // If events were queued, the timer event may be queued late or not at all. + // Release the sticky key if the timer should've run out in the meantime. + if (tap_dance->release_at != 0 && ev->timestamp > tap_dance->release_at) { + stop_timer(tap_dance); + release_tap_dance_behavior(tap_dance, tap_dance->release_at); + continue; + } + if (ev->state) { // key down + if (tap_dance->modified_key_usage_page != 0 || tap_dance->modified_key_keycode != 0) { + // this sticky key is already in use for a keycode + continue; + } + if (tap_dance->timer_started) { + stop_timer(tap_dance); + } + tap_dance->modified_key_usage_page = ev->usage_page; + tap_dance->modified_key_keycode = ev->keycode; + } else { // key up + if (tap_dance->timer_started && + tap_dance->modified_key_usage_page == ev->usage_page && + tap_dance->modified_key_keycode == ev->keycode) { + stop_timer(tap_dance); + release_tap_dance_behavior(tap_dance, ev->timestamp); + } + } + } + return ZMK_EV_EVENT_BUBBLE; +} + #define _TRANSFORM_ENTRY(idx, node) \ { \ .behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \ From 45deb58d7a3b66de0114db2a23595d405d1ed746 Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Mon, 24 May 2021 21:04:51 -0700 Subject: [PATCH 16/21] Attempt resolving dance before keypress mid-dance --- app/src/behaviors/behavior_tap_dance.c | 90 ++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 6 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index e8c501cf..f997de57 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -33,15 +33,19 @@ struct behavior_tap_dance_config { struct active_tap_dance { // tap dance data int counter; - bool dance_begun; uint32_t position; + uint32_t param1; + uint32_t param2; const struct behavior_tap_dance_config *config; // timer data bool timer_started; bool timer_cancelled; int64_t release_at; struct k_delayed_work release_timer; - struct zmk_behavior_binding_event event; + // usage page and keycode for the key that is being modified by this sticky key + uint8_t modified_key_usage_page; + uint32_t modified_key_keycode; + }; struct active_tap_dance active_tap_dances[ZMK_BHV_TAP_DANCE_MAX_HELD] = {}; @@ -62,6 +66,8 @@ static struct active_tap_dance *store_tap_dance(uint32_t position, tap_dance->release_at = 0; tap_dance->timer_started = false; tap_dance->timer_cancelled = false; + tap_dance->modified_key_usage_page = 0; + tap_dance->modified_key_keycode = 0; return tap_dance; } return NULL; @@ -90,6 +96,23 @@ static int stop_timer(struct active_tap_dance *tap_dance) { return timer_cancel_result; } +static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, + int64_t timestamp) { + LOG_DBG("Press Tap Dance Behavior"); + struct zmk_behavior_binding binding = tap_dance->config->behaviors[(tap_dance->counter) - 1]; + struct zmk_behavior_binding_event event = { + .position = tap_dance->position, + .timestamp = timestamp, + }; + if (tap_dance->counter <= tap_dance->config->behavior_count) { + behavior_keymap_binding_pressed(&binding, event); + } else { + LOG_DBG("Counter exceeded number of keybinds"); + } + clear_tap_dance(tap_dance); + return 0; +} + static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, int64_t timestamp) { LOG_DBG("Release Tap Dance Behavior"); @@ -99,7 +122,6 @@ static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, .timestamp = timestamp, }; if (tap_dance->counter <= tap_dance->config->behavior_count) { - behavior_keymap_binding_pressed(&binding, event); behavior_keymap_binding_released(&binding, event); } else { LOG_DBG("Counter exceeded number of keybinds"); @@ -119,6 +141,7 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, if (tap_dance != NULL) { stop_timer(tap_dance); if (++tap_dance->counter >= cfg->behavior_count){ + press_tap_dance_behavior(tap_dance, event.timestamp); release_tap_dance_behavior(tap_dance, event.timestamp); } } @@ -142,10 +165,11 @@ static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } - /*if (tap_dance->position != 0 && tap_dance->modified_key_keycode != 0) { + if (tap_dance->position != 0 && tap_dance->modified_key_keycode != 0) { LOG_DBG("Another key was pressed while the sticky key was pressed. Act like a normal key."); - return release_sticky_key_behavior(tap_dance, event.timestamp); - }*/ + press_tap_dance_behavior(tap_dance, event.timestamp); + return release_tap_dance_behavior(tap_dance, event.timestamp); + } // No other key was pressed. Start the timer. tap_dance->timer_started = true; @@ -168,6 +192,7 @@ void behavior_tap_dance_timer_handler(struct k_work *item) { if (tap_dance->timer_cancelled) { tap_dance->timer_cancelled = false; } else { + press_tap_dance_behavior(tap_dance, tap_dance->release_at); release_tap_dance_behavior(tap_dance, tap_dance->release_at); } } @@ -177,6 +202,59 @@ static const struct behavior_driver_api behavior_tap_dance_driver_api = { .binding_released = on_tap_dance_binding_released, }; +static int tap_dance_keycode_state_changed_listener(const zmk_event_t *eh); + +ZMK_LISTENER(behavior_tap_dance, tap_dance_keycode_state_changed_listener); +ZMK_SUBSCRIPTION(behavior_tap_dance, zmk_keycode_state_changed); + +static int tap_dance_keycode_state_changed_listener(const zmk_event_t *eh) { + struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh); + if (ev == NULL) { + return ZMK_EV_EVENT_BUBBLE; + } + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + struct active_tap_dance *tap_dance = &active_tap_dances[i]; + if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { + continue; + } + + if (strcmp(tap_dance->config->behaviors->behavior_dev, "KEY_PRESS") == 0 && + HID_USAGE_ID(tap_dance->param1) == ev->keycode && + HID_USAGE_PAGE(tap_dance->param1) == ev->usage_page && + SELECT_MODS(tap_dance->param1) == ev->implicit_modifiers) { + // don't catch key down events generated by the sticky key behavior itself + continue; + } + + // If events were queued, the timer event may be queued late or not at all. + // Release the sticky key if the timer should've run out in the meantime. + if (tap_dance->release_at != 0 && ev->timestamp > tap_dance->release_at) { + stop_timer(tap_dance); + release_tap_dance_behavior(tap_dance, tap_dance->release_at); + continue; + } + if (ev->state) { // key down + if (tap_dance->modified_key_usage_page != 0 || tap_dance->modified_key_keycode != 0) { + // this sticky key is already in use for a keycode + continue; + } + if (tap_dance->timer_started) { + stop_timer(tap_dance); + } + tap_dance->modified_key_usage_page = ev->usage_page; + tap_dance->modified_key_keycode = ev->keycode; + } else { // key up + if (tap_dance->timer_started && + tap_dance->modified_key_usage_page == ev->usage_page && + tap_dance->modified_key_keycode == ev->keycode) { + stop_timer(tap_dance); + release_tap_dance_behavior(tap_dance, ev->timestamp); + } + } + } + return ZMK_EV_EVENT_BUBBLE; +} + #define _TRANSFORM_ENTRY(idx, node) \ { \ .behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \ From 491a8a06bffd87d09246c41ad282d3913dcccaf8 Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Tue, 25 May 2021 21:31:52 -0700 Subject: [PATCH 17/21] Tap Dance Revision 2 --- app/src/behaviors/behavior_tap_dance.c | 105 ++++++------------------- 1 file changed, 24 insertions(+), 81 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index f997de57..5455308b 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -42,19 +43,12 @@ struct active_tap_dance { bool timer_cancelled; int64_t release_at; struct k_delayed_work release_timer; - // usage page and keycode for the key that is being modified by this sticky key - uint8_t modified_key_usage_page; - uint32_t modified_key_keycode; - }; struct active_tap_dance active_tap_dances[ZMK_BHV_TAP_DANCE_MAX_HELD] = {}; -static int stop_timer(struct active_tap_dance *tap_dance); - static struct active_tap_dance *store_tap_dance(uint32_t position, const struct behavior_tap_dance_config *config) { - LOG_DBG("Position Detected at %d", position); for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { struct active_tap_dance *const tap_dance = &active_tap_dances[i]; if (tap_dance->position != ZMK_BHV_TAP_DANCE_POSITION_FREE || tap_dance->timer_cancelled) { @@ -64,10 +58,8 @@ static struct active_tap_dance *store_tap_dance(uint32_t position, tap_dance->position = position; tap_dance->config = config; tap_dance->release_at = 0; - tap_dance->timer_started = false; + tap_dance->timer_started = true; tap_dance->timer_cancelled = false; - tap_dance->modified_key_usage_page = 0; - tap_dance->modified_key_keycode = 0; return tap_dance; } return NULL; @@ -87,17 +79,7 @@ static void clear_tap_dance(struct active_tap_dance *tap_dance) { tap_dance->position = ZMK_BHV_TAP_DANCE_POSITION_FREE; } -static int stop_timer(struct active_tap_dance *tap_dance) { - int timer_cancel_result = k_delayed_work_cancel(&tap_dance->release_timer); - if (timer_cancel_result == -EINPROGRESS) { - // too late to cancel, we'll let the timer handler clear up. - tap_dance->timer_cancelled = true; - } - return timer_cancel_result; -} - -static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, - int64_t timestamp) { +static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, int64_t timestamp) { LOG_DBG("Press Tap Dance Behavior"); struct zmk_behavior_binding binding = tap_dance->config->behaviors[(tap_dance->counter) - 1]; struct zmk_behavior_binding_event event = { @@ -109,7 +91,6 @@ static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, } else { LOG_DBG("Counter exceeded number of keybinds"); } - clear_tap_dance(tap_dance); return 0; } @@ -130,7 +111,6 @@ static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, return 0; } - static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("On Binding Pressed"); @@ -139,13 +119,11 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, struct active_tap_dance *tap_dance; tap_dance = find_tap_dance(event.position); if (tap_dance != NULL) { - stop_timer(tap_dance); - if (++tap_dance->counter >= cfg->behavior_count){ + if (++tap_dance->counter >= cfg->behavior_count) { press_tap_dance_behavior(tap_dance, event.timestamp); release_tap_dance_behavior(tap_dance, event.timestamp); } - } - else{ + } else { tap_dance = store_tap_dance(event.position, cfg); if (tap_dance == NULL) { LOG_ERR("unable to store tap dance, did you press more than %d tap_dance?", @@ -153,6 +131,14 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } } + // No other key was pressed. Start the timer. + tap_dance->release_at = event.timestamp + tap_dance->config->tapping_term_ms; + // adjust timer in case this behavior was queued by a hold-tap + int32_t ms_left = tap_dance->release_at - k_uptime_get(); + LOG_DBG("ms_left equal to: %d", ms_left); + if (ms_left > 0) { + k_delayed_work_submit(&tap_dance->release_timer, K_MSEC(ms_left)); + } return ZMK_BEHAVIOR_OPAQUE; } @@ -162,23 +148,6 @@ static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, struct active_tap_dance *tap_dance = find_tap_dance(event.position); if (tap_dance == NULL) { LOG_ERR("ACTIVE TAP DANCE CLEARED TOO EARLY"); - return ZMK_BEHAVIOR_OPAQUE; - } - - if (tap_dance->position != 0 && tap_dance->modified_key_keycode != 0) { - LOG_DBG("Another key was pressed while the sticky key was pressed. Act like a normal key."); - press_tap_dance_behavior(tap_dance, event.timestamp); - return release_tap_dance_behavior(tap_dance, event.timestamp); - } - - // No other key was pressed. Start the timer. - tap_dance->timer_started = true; - tap_dance->release_at = event.timestamp + tap_dance->config->tapping_term_ms; - // adjust timer in case this behavior was queued by a hold-tap - int32_t ms_left = tap_dance->release_at - k_uptime_get(); - LOG_DBG("ms_left equal to: %d", ms_left); - if (ms_left > 0) { - k_delayed_work_submit(&tap_dance->release_timer, K_MSEC(ms_left)); } return ZMK_BEHAVIOR_OPAQUE; } @@ -202,53 +171,27 @@ static const struct behavior_driver_api behavior_tap_dance_driver_api = { .binding_released = on_tap_dance_binding_released, }; -static int tap_dance_keycode_state_changed_listener(const zmk_event_t *eh); +static int tap_dance_position_state_changed_listener(const zmk_event_t *eh); -ZMK_LISTENER(behavior_tap_dance, tap_dance_keycode_state_changed_listener); -ZMK_SUBSCRIPTION(behavior_tap_dance, zmk_keycode_state_changed); +ZMK_LISTENER(behavior_tap_dance, tap_dance_position_state_changed_listener); +ZMK_SUBSCRIPTION(behavior_tap_dance, zmk_position_state_changed); -static int tap_dance_keycode_state_changed_listener(const zmk_event_t *eh) { - struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh); +static int tap_dance_position_state_changed_listener(const zmk_event_t *eh) { + LOG_DBG("Position state changed"); + struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh); if (ev == NULL) { return ZMK_EV_EVENT_BUBBLE; } for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { struct active_tap_dance *tap_dance = &active_tap_dances[i]; + // LOG_DBG("TD %d, Position %d, Counter %d", i, tap_dance->position, tap_dance->counter); if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { continue; - } - - if (strcmp(tap_dance->config->behaviors->behavior_dev, "KEY_PRESS") == 0 && - HID_USAGE_ID(tap_dance->param1) == ev->keycode && - HID_USAGE_PAGE(tap_dance->param1) == ev->usage_page && - SELECT_MODS(tap_dance->param1) == ev->implicit_modifiers) { - // don't catch key down events generated by the sticky key behavior itself - continue; - } - - // If events were queued, the timer event may be queued late or not at all. - // Release the sticky key if the timer should've run out in the meantime. - if (tap_dance->release_at != 0 && ev->timestamp > tap_dance->release_at) { - stop_timer(tap_dance); - release_tap_dance_behavior(tap_dance, tap_dance->release_at); - continue; - } - if (ev->state) { // key down - if (tap_dance->modified_key_usage_page != 0 || tap_dance->modified_key_keycode != 0) { - // this sticky key is already in use for a keycode - continue; - } - if (tap_dance->timer_started) { - stop_timer(tap_dance); - } - tap_dance->modified_key_usage_page = ev->usage_page; - tap_dance->modified_key_keycode = ev->keycode; - } else { // key up - if (tap_dance->timer_started && - tap_dance->modified_key_usage_page == ev->usage_page && - tap_dance->modified_key_keycode == ev->keycode) { - stop_timer(tap_dance); + } else { + if (tap_dance->position != ev->position && tap_dance->timer_started) { + press_tap_dance_behavior(tap_dance, ev->timestamp); release_tap_dance_behavior(tap_dance, ev->timestamp); + continue; } } } From 8004ef847b4526b45ae5458c410d63c64b6224c6 Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Tue, 25 May 2021 22:33:26 -0700 Subject: [PATCH 18/21] TD Documentation --- docs/docs/behaviors/tap-dance.md | 56 ++++++++++++++++++++++++++++++++ docs/sidebars.js | 1 + 2 files changed, 57 insertions(+) create mode 100644 docs/docs/behaviors/tap-dance.md diff --git a/docs/docs/behaviors/tap-dance.md b/docs/docs/behaviors/tap-dance.md new file mode 100644 index 00000000..9b5112d3 --- /dev/null +++ b/docs/docs/behaviors/tap-dance.md @@ -0,0 +1,56 @@ +--- +title: Tap Dance Behavior +sidebar_label: Tap Dance +--- + +## Summary + +A tap dance key outputs a keycode or behavior corresponding to how many times it is pressed. +Tap dances are completely custom, so for every unique tap dance key, a new tap dance must be defined in your keymap's +`behaviors`. + +### Configuration + +#### `tapping-term-ms` + +Defines how much time in milliseconds after the tap dance is pressed before a keybind is registered. + +#### `bindings` + +A list of one or more keybinds. This list can include [any keycode in ZMK](../codes/) and keybinds for ZMK behaviors. + +:::warning +Tap dances currently do not support hold-taps. +::: + +#### Example Usage + +This example configures a tap dance that outputs the number of keypresses from 1-5: + +``` +#include +#include + +/ { + behaviors { + td: tap_dance { + compatible = "zmk,behavior-tap-dance"; + label = "TAP_TEST"; + #binding-cells = <0>; + tapping_term_ms = <1000>; + bindings = <&kp N1>, <&kp N2>, <&kp N3>, <&kp N4> , <&kp N5>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &td + >; + }; + }; +}; + +``` \ No newline at end of file diff --git a/docs/sidebars.js b/docs/sidebars.js index 4ae23c59..57e77397 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -25,6 +25,7 @@ module.exports = { "behaviors/mod-morph", "behaviors/sticky-key", "behaviors/sticky-layer", + "behaviors/tap-dance", "behaviors/reset", "behaviors/bluetooth", "behaviors/outputs", From 0813d8901c5ecce4aa27b53eeff866d9b32aca0a Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Wed, 26 May 2021 09:49:49 -0700 Subject: [PATCH 19/21] TDs allow for holds/hold-taps --- app/src/behaviors/behavior_tap_dance.c | 59 +++++++++++++++++++------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index 5455308b..2f02bdba 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -37,10 +37,12 @@ struct active_tap_dance { uint32_t position; uint32_t param1; uint32_t param2; + bool is_pressed; const struct behavior_tap_dance_config *config; // timer data bool timer_started; bool timer_cancelled; + bool timer_timeout; int64_t release_at; struct k_delayed_work release_timer; }; @@ -74,6 +76,15 @@ static struct active_tap_dance *find_tap_dance(uint32_t position) { return NULL; } +static int stop_timer(struct active_tap_dance *tap_dance) { + int timer_cancel_result = k_delayed_work_cancel(&tap_dance->release_timer); + if (timer_cancel_result == -EINPROGRESS) { + // too late to cancel, we'll let the timer handler clear up. + tap_dance->timer_cancelled = true; + } + return timer_cancel_result; +} + static void clear_tap_dance(struct active_tap_dance *tap_dance) { LOG_DBG("Clearing Tap Dance"); tap_dance->position = ZMK_BHV_TAP_DANCE_POSITION_FREE; @@ -119,9 +130,9 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, struct active_tap_dance *tap_dance; tap_dance = find_tap_dance(event.position); if (tap_dance != NULL) { + stop_timer(tap_dance); if (++tap_dance->counter >= cfg->behavior_count) { press_tap_dance_behavior(tap_dance, event.timestamp); - release_tap_dance_behavior(tap_dance, event.timestamp); } } else { tap_dance = store_tap_dance(event.position, cfg); @@ -131,6 +142,7 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } } + tap_dance->is_pressed = true; // No other key was pressed. Start the timer. tap_dance->release_at = event.timestamp + tap_dance->config->tapping_term_ms; // adjust timer in case this behavior was queued by a hold-tap @@ -148,7 +160,15 @@ static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, struct active_tap_dance *tap_dance = find_tap_dance(event.position); if (tap_dance == NULL) { LOG_ERR("ACTIVE TAP DANCE CLEARED TOO EARLY"); + return ZMK_BEHAVIOR_OPAQUE; } + tap_dance->is_pressed = false; + int32_t ms_left = tap_dance->release_at - k_uptime_get(); + LOG_DBG("ms_left equal to: %d", ms_left); + if (ms_left <= 0) { + release_tap_dance_behavior(tap_dance, event.timestamp); + } + return ZMK_BEHAVIOR_OPAQUE; } @@ -161,8 +181,13 @@ void behavior_tap_dance_timer_handler(struct k_work *item) { if (tap_dance->timer_cancelled) { tap_dance->timer_cancelled = false; } else { - press_tap_dance_behavior(tap_dance, tap_dance->release_at); - release_tap_dance_behavior(tap_dance, tap_dance->release_at); + if (!tap_dance->is_pressed) { + press_tap_dance_behavior(tap_dance, tap_dance->release_at); + release_tap_dance_behavior(tap_dance, tap_dance->release_at); + return; + } else { + press_tap_dance_behavior(tap_dance, tap_dance->release_at); + } } } @@ -179,23 +204,25 @@ ZMK_SUBSCRIPTION(behavior_tap_dance, zmk_position_state_changed); static int tap_dance_position_state_changed_listener(const zmk_event_t *eh) { LOG_DBG("Position state changed"); struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh); - if (ev == NULL) { - return ZMK_EV_EVENT_BUBBLE; - } - for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { - struct active_tap_dance *tap_dance = &active_tap_dances[i]; - // LOG_DBG("TD %d, Position %d, Counter %d", i, tap_dance->position, tap_dance->counter); - if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { - continue; - } else { - if (tap_dance->position != ev->position && tap_dance->timer_started) { - press_tap_dance_behavior(tap_dance, ev->timestamp); - release_tap_dance_behavior(tap_dance, ev->timestamp); + if (ev != NULL) { + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + struct active_tap_dance *tap_dance = &active_tap_dances[i]; + if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { continue; + } else { + if (tap_dance->position != ev->position && tap_dance->timer_started) { + stop_timer(tap_dance); + if (tap_dance->is_pressed) { + press_tap_dance_behavior(tap_dance, tap_dance->release_at); + release_tap_dance_behavior(tap_dance, tap_dance->release_at); + } + } } } + return ZMK_EV_EVENT_BUBBLE; + } else { + return 0; } - return ZMK_EV_EVENT_BUBBLE; } #define _TRANSFORM_ENTRY(idx, node) \ From e11ced596957fee5d14b8c5a9cff1ff8f7b5e0da Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Wed, 26 May 2021 09:50:58 -0700 Subject: [PATCH 20/21] Updated docs --- docs/docs/behaviors/tap-dance.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/docs/behaviors/tap-dance.md b/docs/docs/behaviors/tap-dance.md index 9b5112d3..c29adad9 100644 --- a/docs/docs/behaviors/tap-dance.md +++ b/docs/docs/behaviors/tap-dance.md @@ -18,11 +18,6 @@ Defines how much time in milliseconds after the tap dance is pressed before a ke #### `bindings` A list of one or more keybinds. This list can include [any keycode in ZMK](../codes/) and keybinds for ZMK behaviors. - -:::warning -Tap dances currently do not support hold-taps. -::: - #### Example Usage This example configures a tap dance that outputs the number of keypresses from 1-5: From 0064dc42ae5660277a0bc3464d87b380b2052811 Mon Sep 17 00:00:00 2001 From: kurtis-lew Date: Wed, 9 Jun 2021 19:24:07 -0700 Subject: [PATCH 21/21] Updated behavior_tap_dance.c Addressed comments by @okke-formsma and testing from @dxmh --- app/src/behaviors/behavior_tap_dance.c | 111 ++++++++++++------------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index 2f02bdba..5f54b99e 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -42,7 +42,7 @@ struct active_tap_dance { // timer data bool timer_started; bool timer_cancelled; - bool timer_timeout; + bool tap_dance_decided; int64_t release_at; struct k_delayed_work release_timer; }; @@ -60,8 +60,10 @@ static struct active_tap_dance *store_tap_dance(uint32_t position, tap_dance->position = position; tap_dance->config = config; tap_dance->release_at = 0; + tap_dance->is_pressed = true; tap_dance->timer_started = true; tap_dance->timer_cancelled = false; + tap_dance->tap_dance_decided = false; return tap_dance; } return NULL; @@ -88,36 +90,47 @@ static int stop_timer(struct active_tap_dance *tap_dance) { static void clear_tap_dance(struct active_tap_dance *tap_dance) { LOG_DBG("Clearing Tap Dance"); tap_dance->position = ZMK_BHV_TAP_DANCE_POSITION_FREE; + tap_dance->counter = 1; +} + +static void reset_timer(struct active_tap_dance *tap_dance, + struct zmk_behavior_binding_event event) { + LOG_DBG("Resetting Timer"); + // Start the timer. + tap_dance->release_at = event.timestamp + tap_dance->config->tapping_term_ms; + int32_t ms_left = tap_dance->release_at - k_uptime_get(); + LOG_DBG("ms_left equal to: %d", ms_left); + if (ms_left > 0) { + k_delayed_work_submit(&tap_dance->release_timer, K_MSEC(ms_left)); + } } static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, int64_t timestamp) { LOG_DBG("Press Tap Dance Behavior"); - struct zmk_behavior_binding binding = tap_dance->config->behaviors[(tap_dance->counter) - 1]; + struct zmk_behavior_binding binding = + (tap_dance->counter < tap_dance->config->behavior_count) + ? tap_dance->config->behaviors[(tap_dance->counter) - 1] + : tap_dance->config->behaviors[(tap_dance->config->behavior_count) - 1]; struct zmk_behavior_binding_event event = { .position = tap_dance->position, .timestamp = timestamp, }; - if (tap_dance->counter <= tap_dance->config->behavior_count) { - behavior_keymap_binding_pressed(&binding, event); - } else { - LOG_DBG("Counter exceeded number of keybinds"); - } + behavior_keymap_binding_pressed(&binding, event); return 0; } static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, int64_t timestamp) { LOG_DBG("Release Tap Dance Behavior"); - struct zmk_behavior_binding binding = tap_dance->config->behaviors[(tap_dance->counter) - 1]; + struct zmk_behavior_binding binding = + (tap_dance->counter < tap_dance->config->behavior_count) + ? tap_dance->config->behaviors[(tap_dance->counter) - 1] + : tap_dance->config->behaviors[(tap_dance->config->behavior_count) - 1]; struct zmk_behavior_binding_event event = { .position = tap_dance->position, .timestamp = timestamp, }; - if (tap_dance->counter <= tap_dance->config->behavior_count) { - behavior_keymap_binding_released(&binding, event); - } else { - LOG_DBG("Counter exceeded number of keybinds"); - } + behavior_keymap_binding_released(&binding, event); clear_tap_dance(tap_dance); return 0; } @@ -129,28 +142,19 @@ static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, const struct behavior_tap_dance_config *cfg = dev->config; struct active_tap_dance *tap_dance; tap_dance = find_tap_dance(event.position); - if (tap_dance != NULL) { - stop_timer(tap_dance); - if (++tap_dance->counter >= cfg->behavior_count) { - press_tap_dance_behavior(tap_dance, event.timestamp); - } - } else { + if (tap_dance == NULL) { tap_dance = store_tap_dance(event.position, cfg); - if (tap_dance == NULL) { - LOG_ERR("unable to store tap dance, did you press more than %d tap_dance?", - ZMK_BHV_TAP_DANCE_MAX_HELD); - return ZMK_BEHAVIOR_OPAQUE; - } + reset_timer(tap_dance, event); + return ZMK_BEHAVIOR_OPAQUE; } tap_dance->is_pressed = true; - // No other key was pressed. Start the timer. - tap_dance->release_at = event.timestamp + tap_dance->config->tapping_term_ms; - // adjust timer in case this behavior was queued by a hold-tap - int32_t ms_left = tap_dance->release_at - k_uptime_get(); - LOG_DBG("ms_left equal to: %d", ms_left); - if (ms_left > 0) { - k_delayed_work_submit(&tap_dance->release_timer, K_MSEC(ms_left)); + stop_timer(tap_dance); + if (++tap_dance->counter >= cfg->behavior_count) { + press_tap_dance_behavior(tap_dance, event.timestamp); + tap_dance->tap_dance_decided = true; + return ZMK_BEHAVIOR_OPAQUE; } + reset_timer(tap_dance, event); return ZMK_BEHAVIOR_OPAQUE; } @@ -163,12 +167,10 @@ static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } tap_dance->is_pressed = false; - int32_t ms_left = tap_dance->release_at - k_uptime_get(); - LOG_DBG("ms_left equal to: %d", ms_left); - if (ms_left <= 0) { + if (tap_dance->tap_dance_decided) { release_tap_dance_behavior(tap_dance, event.timestamp); + tap_dance->tap_dance_decided = false; } - return ZMK_BEHAVIOR_OPAQUE; } @@ -181,13 +183,14 @@ void behavior_tap_dance_timer_handler(struct k_work *item) { if (tap_dance->timer_cancelled) { tap_dance->timer_cancelled = false; } else { - if (!tap_dance->is_pressed) { + if (tap_dance->is_pressed) { press_tap_dance_behavior(tap_dance, tap_dance->release_at); - release_tap_dance_behavior(tap_dance, tap_dance->release_at); + tap_dance->tap_dance_decided = true; return; - } else { - press_tap_dance_behavior(tap_dance, tap_dance->release_at); } + press_tap_dance_behavior(tap_dance, tap_dance->release_at); + release_tap_dance_behavior(tap_dance, tap_dance->release_at); + return; } } @@ -204,25 +207,21 @@ ZMK_SUBSCRIPTION(behavior_tap_dance, zmk_position_state_changed); static int tap_dance_position_state_changed_listener(const zmk_event_t *eh) { LOG_DBG("Position state changed"); struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh); - if (ev != NULL) { - for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { - struct active_tap_dance *tap_dance = &active_tap_dances[i]; - if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { - continue; - } else { - if (tap_dance->position != ev->position && tap_dance->timer_started) { - stop_timer(tap_dance); - if (tap_dance->is_pressed) { - press_tap_dance_behavior(tap_dance, tap_dance->release_at); - release_tap_dance_behavior(tap_dance, tap_dance->release_at); - } - } - } - } - return ZMK_EV_EVENT_BUBBLE; - } else { + if (ev == NULL) { return 0; } + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + struct active_tap_dance *tap_dance = &active_tap_dances[i]; + if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { + continue; + } + if (tap_dance->position != ev->position && tap_dance->timer_started) { + stop_timer(tap_dance); + press_tap_dance_behavior(tap_dance, tap_dance->release_at); + release_tap_dance_behavior(tap_dance, tap_dance->release_at); + } + } + return ZMK_EV_EVENT_BUBBLE; } #define _TRANSFORM_ENTRY(idx, node) \