From 595fc42196966120be550eeb2da74d40d6632b35 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Fri, 2 Jun 2023 20:58:38 -0400 Subject: [PATCH 1/4] Turbo key behavior --- app/CMakeLists.txt | 1 + app/Kconfig | 6 + .../behaviors/zmk,behavior-turbo-key.yaml | 22 ++ app/src/behaviors/behavior_turbo_key.c | 193 ++++++++++++++++++ app/tests/turbo/basic/events.patterns | 1 + app/tests/turbo/basic/keycode_events.snapshot | 8 + app/tests/turbo/basic/native_posix_64.keymap | 8 + app/tests/turbo/behavior_keymap.dtsi | 36 ++++ app/tests/turbo/toggle/events.patterns | 1 + .../turbo/toggle/keycode_events.snapshot | 8 + app/tests/turbo/toggle/native_posix_64.keymap | 10 + docs/docs/behaviors/turbo.md | 51 +++++ docs/sidebars.js | 1 + 13 files changed, 346 insertions(+) create mode 100644 app/dts/bindings/behaviors/zmk,behavior-turbo-key.yaml create mode 100644 app/src/behaviors/behavior_turbo_key.c create mode 100644 app/tests/turbo/basic/events.patterns create mode 100644 app/tests/turbo/basic/keycode_events.snapshot create mode 100644 app/tests/turbo/basic/native_posix_64.keymap create mode 100644 app/tests/turbo/behavior_keymap.dtsi create mode 100644 app/tests/turbo/toggle/events.patterns create mode 100644 app/tests/turbo/toggle/keycode_events.snapshot create mode 100644 app/tests/turbo/toggle/native_posix_64.keymap create mode 100644 docs/docs/behaviors/turbo.md diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index a647e883..caa0d6ad 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -39,6 +39,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/hid.c) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c) + target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_TURBO_KEY app PRIVATE src/behaviors/behavior_turbo_key.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c) target_sources(app PRIVATE src/behaviors/behavior_caps_word.c) diff --git a/app/Kconfig b/app/Kconfig index d1b6682f..c2bff36e 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -365,6 +365,12 @@ config ZMK_MACRO_DEFAULT_TAP_MS int "Default time to wait (in milliseconds) between the press and release events of a tapped behavior in macros" default 30 +DT_COMPAT_ZMK_BEHAVIOR_TURBO_KEY := zmk,behavior-turbo-key + +config ZMK_BEHAVIOR_TURBO_KEY + bool + default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_TURBO_KEY)) + endmenu menu "Advanced" diff --git a/app/dts/bindings/behaviors/zmk,behavior-turbo-key.yaml b/app/dts/bindings/behaviors/zmk,behavior-turbo-key.yaml new file mode 100644 index 00000000..2f718b59 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-turbo-key.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Turbo key behavior + +compatible: "zmk,behavior-turbo-key" + +include: zero_param.yaml + +properties: + bindings: + type: phandle-array + required: true + wait-ms: + type: int + default: 200 + tap-ms: + type: int + default: 5 + toggle-term-ms: + type: int + default: -1 diff --git a/app/src/behaviors/behavior_turbo_key.c b/app/src/behaviors/behavior_turbo_key.c new file mode 100644 index 00000000..cd4c2435 --- /dev/null +++ b/app/src/behaviors/behavior_turbo_key.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_turbo_key + +#include +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +struct behavior_turbo_config { + int tap_ms; + int wait_ms; + int toggle_term_ms; + const struct zmk_behavior_binding binding; +}; + +#define ZMK_BHV_TURBO_MAX_ACTIVE 5 + +struct active_turbo { + const struct behavior_turbo_config *config; + uint32_t position; + bool is_active; + bool is_pressed; + + int32_t press_time; + + // Timer Data + bool timer_started; + bool timer_cancelled; + bool turbo_decided; + int64_t release_at; + struct k_work_delayable release_timer; +}; + +struct active_turbo active_turbos[ZMK_BHV_TURBO_MAX_ACTIVE] = {}; + +static struct active_turbo *find_active_turbo(uint32_t position) { + for (int i = 0; i < ZMK_BHV_TURBO_MAX_ACTIVE; i++) { + if (active_turbos[i].is_active) { + return &active_turbos[i]; + } + } + return NULL; +} + +static int new_turbo(uint32_t position, const struct behavior_turbo_config *config, + struct active_turbo **turbo) { + for (int i = 0; i < ZMK_BHV_TURBO_MAX_ACTIVE; i++) { + struct active_turbo *const ref_turbo = &active_turbos[i]; + if (!ref_turbo->is_active) { + ref_turbo->is_active = true; + ref_turbo->position = position; + ref_turbo->config = config; + ref_turbo->is_pressed = true; + ref_turbo->press_time = k_uptime_get(); + ref_turbo->release_at = 0; + ref_turbo->timer_started = true; + ref_turbo->timer_cancelled = false; + *turbo = ref_turbo; + return 0; + } + } + return -ENOMEM; +}; + +static int stop_timer(struct active_turbo *turbo) { + int timer_cancel_result = k_work_cancel_delayable(&turbo->release_timer); + if (timer_cancel_result == -EINPROGRESS) { + // too late to cancel, we'll let the timer handler clear up. + turbo->timer_cancelled = true; + } + return timer_cancel_result; +} + +static void clear_turbo(struct active_turbo *turbo) { + LOG_DBG("Turbo deactivated"); + turbo->is_active = false; + stop_timer(turbo); +} + +static void reset_timer(struct active_turbo *turbo, struct zmk_behavior_binding_event event) { + turbo->release_at = event.timestamp + turbo->config->wait_ms; + int32_t ms_left = turbo->release_at - k_uptime_get(); + if (ms_left > 0) { + k_work_schedule(&turbo->release_timer, K_MSEC(ms_left)); + LOG_DBG("Successfully reset turbo timer at position %d", turbo->position); + } +} + +static void behavior_turbo_timer_handler(struct k_work *item) { + struct active_turbo *turbo = CONTAINER_OF(item, struct active_turbo, release_timer); + if (!turbo->is_active) { + return; + } + if (turbo->timer_cancelled) { + return; + } + LOG_DBG("Turbo timer reached."); + struct zmk_behavior_binding_event event = {.position = turbo->position, + .timestamp = k_uptime_get()}; + zmk_behavior_queue_add(event.position, turbo->config->binding, true, turbo->config->tap_ms); + zmk_behavior_queue_add(event.position, turbo->config->binding, false, 0); + reset_timer(turbo, event); +} + +static int behavior_turbo_key_init(const struct device *dev) { + static bool init_first_run = true; + if (init_first_run) { + for (int i = 0; i < ZMK_BHV_TURBO_MAX_ACTIVE; i++) { + k_work_init_delayable(&active_turbos[i].release_timer, behavior_turbo_timer_handler); + clear_turbo(&active_turbos[i]); + } + } + init_first_run = false; + return 0; +}; + +static int on_keymap_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_turbo_config *cfg = dev->config; + + struct active_turbo *turbo; + turbo = find_active_turbo(event.position); + if (turbo == NULL) { + if (new_turbo(event.position, cfg, &turbo) == -ENOMEM) { + LOG_ERR("Unable to create new turbo. Insufficient space in active_turbos[]."); + return ZMK_BEHAVIOR_OPAQUE; + } + LOG_DBG("%d created new turbo", event.position); + zmk_behavior_queue_add(event.position, turbo->config->binding, true, turbo->config->tap_ms); + zmk_behavior_queue_add(event.position, turbo->config->binding, false, 0); + reset_timer(turbo, event); + } else { + clear_turbo(turbo); + } + return ZMK_BEHAVIOR_OPAQUE; +} + +static int on_keymap_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_turbo_config *cfg = dev->config; + + struct active_turbo *turbo; + turbo = find_active_turbo(event.position); + if (turbo != NULL) { + turbo->is_pressed = false; + int32_t elapsedTime = k_uptime_get() - turbo->press_time; + LOG_DBG("turbo elapsed time: %d", elapsedTime); + if (elapsedTime > cfg->toggle_term_ms) { + clear_turbo(turbo); + } + } + return 0; +} + +static const struct behavior_driver_api behavior_turbo_key_driver_api = { + .binding_pressed = on_keymap_binding_pressed, + .binding_released = on_keymap_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 TURBO_INST(n) \ + static struct behavior_turbo_config behavior_turbo_config_##n = { \ + .tap_ms = DT_INST_PROP(n, tap_ms), \ + .wait_ms = DT_INST_PROP(n, wait_ms), \ + .toggle_term_ms = DT_INST_PROP(n, toggle_term_ms), \ + .binding = _TRANSFORM_ENTRY(0, n)}; \ + DEVICE_DT_INST_DEFINE(n, behavior_turbo_key_init, NULL, NULL, &behavior_turbo_config_##n, \ + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_turbo_key_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(TURBO_INST) diff --git a/app/tests/turbo/basic/events.patterns b/app/tests/turbo/basic/events.patterns new file mode 100644 index 00000000..b1342af4 --- /dev/null +++ b/app/tests/turbo/basic/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/turbo/basic/keycode_events.snapshot b/app/tests/turbo/basic/keycode_events.snapshot new file mode 100644 index 00000000..d0767ca4 --- /dev/null +++ b/app/tests/turbo/basic/keycode_events.snapshot @@ -0,0 +1,8 @@ +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/turbo/basic/native_posix_64.keymap b/app/tests/turbo/basic/native_posix_64.keymap new file mode 100644 index 00000000..ba4dd794 --- /dev/null +++ b/app/tests/turbo/basic/native_posix_64.keymap @@ -0,0 +1,8 @@ +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,1000) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; diff --git a/app/tests/turbo/behavior_keymap.dtsi b/app/tests/turbo/behavior_keymap.dtsi new file mode 100644 index 00000000..b7ad1f91 --- /dev/null +++ b/app/tests/turbo/behavior_keymap.dtsi @@ -0,0 +1,36 @@ +#include +#include +#include + +/ { + behaviors { + turbo: turbo { + compatible = "zmk,behavior-turbo-key"; + label = "turbo"; + #binding-cells = <0>; + tap-ms = <5>; + wait-ms = <300>; + bindings = <&kp C>; + }; + t2: turbo2 { + compatible = "zmk,behavior-turbo-key"; + label = "turbo"; + #binding-cells = <0>; + tap-ms = <5>; + wait-ms = <300>; + toggle-term-ms = <50>; + bindings = <&kp C>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &turbo &t2 + &kp D &kp Q>; + }; + }; +}; diff --git a/app/tests/turbo/toggle/events.patterns b/app/tests/turbo/toggle/events.patterns new file mode 100644 index 00000000..b1342af4 --- /dev/null +++ b/app/tests/turbo/toggle/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/turbo/toggle/keycode_events.snapshot b/app/tests/turbo/toggle/keycode_events.snapshot new file mode 100644 index 00000000..d0767ca4 --- /dev/null +++ b/app/tests/turbo/toggle/keycode_events.snapshot @@ -0,0 +1,8 @@ +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/turbo/toggle/native_posix_64.keymap b/app/tests/turbo/toggle/native_posix_64.keymap new file mode 100644 index 00000000..c6745c8e --- /dev/null +++ b/app/tests/turbo/toggle/native_posix_64.keymap @@ -0,0 +1,10 @@ +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,1000) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; diff --git a/docs/docs/behaviors/turbo.md b/docs/docs/behaviors/turbo.md new file mode 100644 index 00000000..bf771482 --- /dev/null +++ b/docs/docs/behaviors/turbo.md @@ -0,0 +1,51 @@ +--- +title: Turbo Behavior +sidebar_label: Turbo Key +--- + +## Summary + +The turbo behavior will repeatedly trigger a behavior after a specified amount of time. + +### Configuration + +An example of how to implement a turbo key to output `A` every 5 seconds: + +``` +/ { + behaviors { + turbo_A: turbo_A { + compatible = "zmk,behavior-turbo-key"; + label = "TURBO_A"; + #binding-cells = <0>; + bindings = <&kp A>; + wait-ms = <5000>; + }; + }; +}; +``` + +### Behavior Binding + +- Reference: `&turbo_A` +- Parameter: None + +Example: + +``` +&turbo_A +``` + +### Advanced Configuration + +#### `wait-ms` + +Defines how often the behavior will trigger. Defaults to 200ms. + +#### `tap-ms` + +Defines how long the behavior will be held for each press. Defaults to 5ms. + +#### `toggle-term-ms` + +Releasing the turbo key within `toggle-term-ms` will toggle the repeating, removing the need to hold down the key. diff --git a/docs/sidebars.js b/docs/sidebars.js index 43f17b41..4ee1d6d3 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -30,6 +30,7 @@ module.exports = { "behaviors/mod-morph", "behaviors/macros", "behaviors/key-toggle", + "behaviors/turbo", "behaviors/sticky-key", "behaviors/sticky-layer", "behaviors/tap-dance", From 4cb986a306297e9e27e79753fc804aff69c8e8c8 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Sun, 4 Jun 2023 02:00:00 -0400 Subject: [PATCH 2/4] Refactor turbo key --- app/src/behaviors/behavior_turbo_key.c | 134 +++++++++---------------- app/tests/turbo/behavior_keymap.dtsi | 2 +- 2 files changed, 50 insertions(+), 86 deletions(-) diff --git a/app/src/behaviors/behavior_turbo_key.c b/app/src/behaviors/behavior_turbo_key.c index cd4c2435..96238032 100644 --- a/app/src/behaviors/behavior_turbo_key.c +++ b/app/src/behaviors/behavior_turbo_key.c @@ -25,16 +25,17 @@ struct behavior_turbo_config { const struct zmk_behavior_binding binding; }; -#define ZMK_BHV_TURBO_MAX_ACTIVE 5 - -struct active_turbo { - const struct behavior_turbo_config *config; +struct behavior_turbo_data { uint32_t position; bool is_active; bool is_pressed; int32_t press_time; + int tap_ms; + int wait_ms; + struct zmk_behavior_binding binding; + // Timer Data bool timer_started; bool timer_cancelled; @@ -43,107 +44,67 @@ struct active_turbo { struct k_work_delayable release_timer; }; -struct active_turbo active_turbos[ZMK_BHV_TURBO_MAX_ACTIVE] = {}; +static int behavior_turbo_key_init(const struct device *dev) { return 0; }; -static struct active_turbo *find_active_turbo(uint32_t position) { - for (int i = 0; i < ZMK_BHV_TURBO_MAX_ACTIVE; i++) { - if (active_turbos[i].is_active) { - return &active_turbos[i]; - } - } - return NULL; -} - -static int new_turbo(uint32_t position, const struct behavior_turbo_config *config, - struct active_turbo **turbo) { - for (int i = 0; i < ZMK_BHV_TURBO_MAX_ACTIVE; i++) { - struct active_turbo *const ref_turbo = &active_turbos[i]; - if (!ref_turbo->is_active) { - ref_turbo->is_active = true; - ref_turbo->position = position; - ref_turbo->config = config; - ref_turbo->is_pressed = true; - ref_turbo->press_time = k_uptime_get(); - ref_turbo->release_at = 0; - ref_turbo->timer_started = true; - ref_turbo->timer_cancelled = false; - *turbo = ref_turbo; - return 0; - } - } - return -ENOMEM; -}; - -static int stop_timer(struct active_turbo *turbo) { - int timer_cancel_result = k_work_cancel_delayable(&turbo->release_timer); +static int stop_timer(struct behavior_turbo_data *data) { + int timer_cancel_result = k_work_cancel_delayable(&data->release_timer); if (timer_cancel_result == -EINPROGRESS) { // too late to cancel, we'll let the timer handler clear up. - turbo->timer_cancelled = true; + data->timer_cancelled = true; } return timer_cancel_result; } -static void clear_turbo(struct active_turbo *turbo) { +static void clear_turbo(struct behavior_turbo_data *data) { LOG_DBG("Turbo deactivated"); - turbo->is_active = false; - stop_timer(turbo); + data->is_active = false; + stop_timer(data); } -static void reset_timer(struct active_turbo *turbo, struct zmk_behavior_binding_event event) { - turbo->release_at = event.timestamp + turbo->config->wait_ms; - int32_t ms_left = turbo->release_at - k_uptime_get(); +static void reset_timer(struct behavior_turbo_data *data, struct zmk_behavior_binding_event event) { + data->release_at = event.timestamp + data->wait_ms; + LOG_DBG("Resetting turbo timer: %d + %d = %d", event.timestamp, data->wait_ms, + data->release_at); + int32_t ms_left = data->release_at - k_uptime_get(); if (ms_left > 0) { - k_work_schedule(&turbo->release_timer, K_MSEC(ms_left)); - LOG_DBG("Successfully reset turbo timer at position %d", turbo->position); + k_work_schedule(&data->release_timer, K_MSEC(ms_left)); + LOG_DBG("Successfully reset turbo timer at position %d", data->position); } } static void behavior_turbo_timer_handler(struct k_work *item) { - struct active_turbo *turbo = CONTAINER_OF(item, struct active_turbo, release_timer); - if (!turbo->is_active) { + struct behavior_turbo_data *data = + CONTAINER_OF(item, struct behavior_turbo_data, release_timer); + if (!data->is_active) { return; } - if (turbo->timer_cancelled) { + if (data->timer_cancelled) { return; } LOG_DBG("Turbo timer reached."); - struct zmk_behavior_binding_event event = {.position = turbo->position, + struct zmk_behavior_binding_event event = {.position = data->position, .timestamp = k_uptime_get()}; - zmk_behavior_queue_add(event.position, turbo->config->binding, true, turbo->config->tap_ms); - zmk_behavior_queue_add(event.position, turbo->config->binding, false, 0); - reset_timer(turbo, event); + zmk_behavior_queue_add(event.position, data->binding, true, data->tap_ms); + zmk_behavior_queue_add(event.position, data->binding, false, 0); + reset_timer(data, event); } -static int behavior_turbo_key_init(const struct device *dev) { - static bool init_first_run = true; - if (init_first_run) { - for (int i = 0; i < ZMK_BHV_TURBO_MAX_ACTIVE; i++) { - k_work_init_delayable(&active_turbos[i].release_timer, behavior_turbo_timer_handler); - clear_turbo(&active_turbos[i]); - } - } - init_first_run = false; - return 0; -}; - static int on_keymap_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_turbo_config *cfg = dev->config; + struct behavior_turbo_data *data = dev->data; - struct active_turbo *turbo; - turbo = find_active_turbo(event.position); - if (turbo == NULL) { - if (new_turbo(event.position, cfg, &turbo) == -ENOMEM) { - LOG_ERR("Unable to create new turbo. Insufficient space in active_turbos[]."); - return ZMK_BEHAVIOR_OPAQUE; - } - LOG_DBG("%d created new turbo", event.position); - zmk_behavior_queue_add(event.position, turbo->config->binding, true, turbo->config->tap_ms); - zmk_behavior_queue_add(event.position, turbo->config->binding, false, 0); - reset_timer(turbo, event); + if (!data->is_active) { + data->is_active = true; + + LOG_DBG("%d started new turbo", event.position); + k_work_init_delayable(&data->release_timer, behavior_turbo_timer_handler); + zmk_behavior_queue_add(event.position, cfg->binding, true, cfg->tap_ms); + zmk_behavior_queue_add(event.position, cfg->binding, false, 0); + reset_timer(data, event); } else { - clear_turbo(turbo); + clear_turbo(data); } return ZMK_BEHAVIOR_OPAQUE; } @@ -152,15 +113,14 @@ static int on_keymap_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_turbo_config *cfg = dev->config; + struct behavior_turbo_data *data = dev->data; - struct active_turbo *turbo; - turbo = find_active_turbo(event.position); - if (turbo != NULL) { - turbo->is_pressed = false; - int32_t elapsedTime = k_uptime_get() - turbo->press_time; + if (&data->is_active == false) { + data->is_pressed = false; + int32_t elapsedTime = k_uptime_get() - data->press_time; LOG_DBG("turbo elapsed time: %d", elapsedTime); if (elapsedTime > cfg->toggle_term_ms) { - clear_turbo(turbo); + clear_turbo(data); } } return 0; @@ -186,8 +146,12 @@ static const struct behavior_driver_api behavior_turbo_key_driver_api = { .wait_ms = DT_INST_PROP(n, wait_ms), \ .toggle_term_ms = DT_INST_PROP(n, toggle_term_ms), \ .binding = _TRANSFORM_ENTRY(0, n)}; \ - DEVICE_DT_INST_DEFINE(n, behavior_turbo_key_init, NULL, NULL, &behavior_turbo_config_##n, \ - APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ - &behavior_turbo_key_driver_api); + static struct behavior_turbo_data behavior_turbo_data_##n = { \ + .tap_ms = DT_INST_PROP(n, tap_ms), \ + .wait_ms = DT_INST_PROP(n, wait_ms), \ + .binding = _TRANSFORM_ENTRY(0, n)}; \ + DEVICE_DT_INST_DEFINE(n, behavior_turbo_key_init, NULL, &behavior_turbo_data_##n, \ + &behavior_turbo_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_turbo_key_driver_api); DT_INST_FOREACH_STATUS_OKAY(TURBO_INST) diff --git a/app/tests/turbo/behavior_keymap.dtsi b/app/tests/turbo/behavior_keymap.dtsi index b7ad1f91..e37d4f75 100644 --- a/app/tests/turbo/behavior_keymap.dtsi +++ b/app/tests/turbo/behavior_keymap.dtsi @@ -14,7 +14,7 @@ }; t2: turbo2 { compatible = "zmk,behavior-turbo-key"; - label = "turbo"; + label = "turbo2"; #binding-cells = <0>; tap-ms = <5>; wait-ms = <300>; From 0308d161f65b845168e143621593f839d80832d7 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Sun, 4 Jun 2023 02:04:51 -0400 Subject: [PATCH 3/4] Fix turbo key warnings --- app/src/behaviors/behavior_turbo_key.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/behaviors/behavior_turbo_key.c b/app/src/behaviors/behavior_turbo_key.c index 96238032..eb9e8425 100644 --- a/app/src/behaviors/behavior_turbo_key.c +++ b/app/src/behaviors/behavior_turbo_key.c @@ -63,8 +63,6 @@ static void clear_turbo(struct behavior_turbo_data *data) { static void reset_timer(struct behavior_turbo_data *data, struct zmk_behavior_binding_event event) { data->release_at = event.timestamp + data->wait_ms; - LOG_DBG("Resetting turbo timer: %d + %d = %d", event.timestamp, data->wait_ms, - data->release_at); int32_t ms_left = data->release_at - k_uptime_get(); if (ms_left > 0) { k_work_schedule(&data->release_timer, K_MSEC(ms_left)); @@ -115,7 +113,7 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding, const struct behavior_turbo_config *cfg = dev->config; struct behavior_turbo_data *data = dev->data; - if (&data->is_active == false) { + if (data->is_active) { data->is_pressed = false; int32_t elapsedTime = k_uptime_get() - data->press_time; LOG_DBG("turbo elapsed time: %d", elapsedTime); From d38b4d383f0a2ab6fb280f741d82c18b549016e0 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Tue, 29 Aug 2023 10:57:43 -0400 Subject: [PATCH 4/4] feat(behaviors): Turbo Key - fix timing --- app/src/behaviors/behavior_turbo_key.c | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/behaviors/behavior_turbo_key.c b/app/src/behaviors/behavior_turbo_key.c index eb9e8425..dff50e2d 100644 --- a/app/src/behaviors/behavior_turbo_key.c +++ b/app/src/behaviors/behavior_turbo_key.c @@ -97,6 +97,7 @@ static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, data->is_active = true; LOG_DBG("%d started new turbo", event.position); + data->press_time = k_uptime_get(); k_work_init_delayable(&data->release_timer, behavior_turbo_timer_handler); zmk_behavior_queue_add(event.position, cfg->binding, true, cfg->tap_ms); zmk_behavior_queue_add(event.position, cfg->binding, false, 0);