@@ 0,0 1,639 @@
+diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
+index 60c502fc..2361edb7 100644
+--- a/app/CMakeLists.txt
++++ b/app/CMakeLists.txt
+@@ -48,6 +48,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_ifdef(CONFIG_ZMK_BEHAVIOR_HOLD_TAP app PRIVATE src/behaviors/behavior_hold_tap.c)
+ target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_STICKY_KEY app PRIVATE src/behaviors/behavior_sticky_key.c)
+ target_sources(app PRIVATE src/behaviors/behavior_caps_word.c)
+diff --git a/app/Kconfig.behaviors b/app/Kconfig.behaviors
+index da9bcc41..724c5b17 100644
+--- a/app/Kconfig.behaviors
++++ b/app/Kconfig.behaviors
+@@ -134,3 +134,8 @@ config ZMK_BEHAVIOR_MACRO
+ bool
+ default y
+ depends on DT_HAS_ZMK_BEHAVIOR_MACRO_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_ONE_PARAM_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_TWO_PARAM_ENABLED
++
++config ZMK_BEHAVIOR_TURBO_KEY
++ bool
++ default y
++ depends on DT_HAS_ZMK_BEHAVIOR_TURBO_KEY_ENABLED || DT_HAS_ZMK_BEHAVIOR_TURBO_KEY_ONE_PARAM_ENABLED || DT_HAS_ZMK_BEHAVIOR_TURBO_KEY_TWO_PARAM_ENABLED
+diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi
+index 653b085d..cd8cfd72 100644
+--- a/app/dts/behaviors.dtsi
++++ b/app/dts/behaviors.dtsi
+@@ -28,3 +28,4 @@
+ #include <behaviors/soft_off.dtsi>
+ #include <behaviors/studio_unlock.dtsi>
+ #include <behaviors/mouse_keys.dtsi>
++#include <behaviors/turbo.dtsi>
+diff --git a/app/dts/behaviors/turbo.dtsi b/app/dts/behaviors/turbo.dtsi
+new file mode 100644
+index 00000000..6e74110c
+--- /dev/null
++++ b/app/dts/behaviors/turbo.dtsi
+@@ -0,0 +1,52 @@
++/*
++ * Copyright (c) 2025 The ZMK Contributors
++ *
++ * SPDX-License-Identifier: MIT
++ */
++
++#define TURBO_PLACEHOLDER 0
++
++#define ZMK_TURBO(name,...) \
++name: name { \
++ compatible = "zmk,behavior-turbo-key"; \
++ #binding-cells = <0>; \
++ __VA_ARGS__ \
++};
++
++#define ZMK_TURBO1(name,...) \
++name: name { \
++ compatible = "zmk,behavior-turbo-key-one-param"; \
++ #binding-cells = <1>; \
++ __VA_ARGS__ \
++};
++
++#define ZMK_TURBO2(name,...) \
++name: name { \
++ compatible = "zmk,behavior-turbo-key-two-param"; \
++ #binding-cells = <2>; \
++ __VA_ARGS__ \
++};
++
++/ {
++ behaviors {
++ turbo_param_1to1: turbo_param_1to1 {
++ compatible = "zmk,turbo-param-1to1";
++ #binding-cells = <0>;
++ };
++
++ turbo_param_1to2: turbo_param_1to2 {
++ compatible = "zmk,turbo-param-1to2";
++ #binding-cells = <0>;
++ };
++
++ turbo_param_2to1: turbo_param_2to1 {
++ compatible = "zmk,turbo-param-2to1";
++ #binding-cells = <0>;
++ };
++
++ turbo_param_2to2: turbo_param_2to2 {
++ compatible = "zmk,turbo-param-2to2";
++ #binding-cells = <0>;
++ };
++ };
++};
+diff --git a/app/dts/bindings/behaviors/turbo_base.yaml b/app/dts/bindings/behaviors/turbo_base.yaml
+new file mode 100644
+index 00000000..6470ce7e
+--- /dev/null
++++ b/app/dts/bindings/behaviors/turbo_base.yaml
+@@ -0,0 +1,16 @@
++# Copyright (c) 2025 The ZMK Contributors
++# SPDX-License-Identifier: MIT
++
++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/dts/bindings/behaviors/zmk,behavior-turbo-key-one-param.yaml b/app/dts/bindings/behaviors/zmk,behavior-turbo-key-one-param.yaml
+new file mode 100644
+index 00000000..3001ab55
+--- /dev/null
++++ b/app/dts/bindings/behaviors/zmk,behavior-turbo-key-one-param.yaml
+@@ -0,0 +1,8 @@
++# Copyright (c) 2025 The ZMK Contributors
++# SPDX-License-Identifier: MIT
++
++description: Turbo key behavior
++
++compatible: "zmk,behavior-turbo-key-one-param"
++
++include: [one_param.yaml, turbo_base.yaml]
+diff --git a/app/dts/bindings/behaviors/zmk,behavior-turbo-key-two-param.yaml b/app/dts/bindings/behaviors/zmk,behavior-turbo-key-two-param.yaml
+new file mode 100644
+index 00000000..15bbaae5
+--- /dev/null
++++ b/app/dts/bindings/behaviors/zmk,behavior-turbo-key-two-param.yaml
+@@ -0,0 +1,8 @@
++# Copyright (c) 2025 The ZMK Contributors
++# SPDX-License-Identifier: MIT
++
++description: Turbo key behavior
++
++compatible: "zmk,behavior-turbo-key-two-param"
++
++include: [two_param.yaml, turbo_base.yaml]
+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..8130ac02
+--- /dev/null
++++ b/app/dts/bindings/behaviors/zmk,behavior-turbo-key.yaml
+@@ -0,0 +1,8 @@
++# Copyright (c) 2022 The ZMK Contributors
++# SPDX-License-Identifier: MIT
++
++description: Turbo key behavior
++
++compatible: "zmk,behavior-turbo-key"
++
++include: [zero_param.yaml, turbo_base.yaml]
+diff --git a/app/dts/bindings/turbo/zmk,turbo-param-1to1.yaml b/app/dts/bindings/turbo/zmk,turbo-param-1to1.yaml
+new file mode 100644
+index 00000000..082a8ac6
+--- /dev/null
++++ b/app/dts/bindings/turbo/zmk,turbo-param-1to1.yaml
+@@ -0,0 +1,8 @@
++# Copyright (c) 2025 The ZMK Contributors
++# SPDX-License-Identifier: MIT
++
++description: Turbo Parameter One Substituted Into Next Binding's First Parameter
++
++compatible: "zmk,turbo-param-1to1"
++
++include: zero_param.yaml
+diff --git a/app/dts/bindings/turbo/zmk,turbo-param-1to2.yaml b/app/dts/bindings/turbo/zmk,turbo-param-1to2.yaml
+new file mode 100644
+index 00000000..fa601a75
+--- /dev/null
++++ b/app/dts/bindings/turbo/zmk,turbo-param-1to2.yaml
+@@ -0,0 +1,8 @@
++# Copyright (c) 2025 The ZMK Contributors
++# SPDX-License-Identifier: MIT
++
++description: Turbo Parameter One Substituted Into Next Binding's Second Parameter
++
++compatible: "zmk,turbo-param-1to2"
++
++include: zero_param.yaml
+diff --git a/app/dts/bindings/turbo/zmk,turbo-param-2to1.yaml b/app/dts/bindings/turbo/zmk,turbo-param-2to1.yaml
+new file mode 100644
+index 00000000..9fc4c5ac
+--- /dev/null
++++ b/app/dts/bindings/turbo/zmk,turbo-param-2to1.yaml
+@@ -0,0 +1,8 @@
++# Copyright (c) 2025 The ZMK Contributors
++# SPDX-License-Identifier: MIT
++
++description: Turbo Parameter Two Substituted Into Next Binding's First Parameter
++
++compatible: "zmk,turbo-param-2to1"
++
++include: zero_param.yaml
+diff --git a/app/dts/bindings/turbo/zmk,turbo-param-2to2.yaml b/app/dts/bindings/turbo/zmk,turbo-param-2to2.yaml
+new file mode 100644
+index 00000000..ceea8ed1
+--- /dev/null
++++ b/app/dts/bindings/turbo/zmk,turbo-param-2to2.yaml
+@@ -0,0 +1,8 @@
++# Copyright (c) 2025 The ZMK Contributors
++# SPDX-License-Identifier: MIT
++
++description: Turbo Parameter Two Substituted Into Next Binding's Second Parameter
++
++compatible: "zmk,turbo-param-2to2"
++
++include: zero_param.yaml
+diff --git a/app/src/behaviors/behavior_turbo_key.c b/app/src/behaviors/behavior_turbo_key.c
+new file mode 100644
+index 00000000..65813c21
+--- /dev/null
++++ b/app/src/behaviors/behavior_turbo_key.c
+@@ -0,0 +1,238 @@
++/*
++ * Copyright (c) 2022 The ZMK Contributors
++ *
++ * SPDX-License-Identifier: MIT
++ */
++
++#include <zephyr/device.h>
++#include <drivers/behavior.h>
++#include <zephyr/logging/log.h>
++
++#include <zmk/behavior.h>
++#include <zmk/behavior_queue.h>
++#include <zmk/keymap.h>
++
++LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
++
++struct behavior_turbo_data {
++ int32_t tap_ms;
++ int32_t wait_ms;
++ int32_t toggle_term_ms;
++
++ 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;
++
++ uint32_t binding_count;
++ struct zmk_behavior_binding binding;
++ struct zmk_behavior_binding new_binding;
++ const struct zmk_behavior_binding bindings[];
++};
++
++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.
++ data->timer_cancelled = true;
++ }
++ return timer_cancel_result;
++}
++
++static void clear_turbo(struct behavior_turbo_data *data) {
++ LOG_DBG("Turbo deactivated at position %d", data->position);
++ data->is_active = false;
++ stop_timer(data);
++}
++
++static void reset_timer(struct behavior_turbo_data *data, struct zmk_behavior_binding_event event) {
++ data->release_at = event.timestamp + data->wait_ms;
++ int32_t ms_left = data->release_at - k_uptime_get();
++ if (ms_left > 0) {
++ k_work_schedule(&data->release_timer, K_MSEC(ms_left));
++ LOG_DBG("Successfully reset turbo timer at position %d", data->position);
++ }
++}
++
++static void press_turbo_binding(struct zmk_behavior_binding_event *event,
++ const struct behavior_turbo_data *data) {
++ LOG_DBG("Pressing turbo binding %s, %d, %d", data->binding.behavior_dev, data->binding.param1,
++ data->binding.param2);
++ zmk_behavior_queue_add(event, data->binding, true, data->tap_ms);
++ zmk_behavior_queue_add(event, data->binding, false, 0);
++}
++
++static void behavior_turbo_timer_handler(struct k_work *item) {
++ struct k_work_delayable *d_work = k_work_delayable_from_work(item);
++
++ struct behavior_turbo_data *data =
++ CONTAINER_OF(d_work, struct behavior_turbo_data, release_timer);
++
++ if (!data->is_active || data->timer_cancelled) {
++ return;
++ }
++
++ LOG_DBG("Turbo timer reached.");
++ struct zmk_behavior_binding_event event = {.position = data->position,
++ .timestamp = k_uptime_get()};
++
++ press_turbo_binding(&event, data);
++ reset_timer(data, event);
++}
++
++#define P1TO1 DEVICE_DT_NAME(DT_INST(0, zmk_turbo_param_1to1))
++#define P1TO2 DEVICE_DT_NAME(DT_INST(0, zmk_turbo_param_1to2))
++#define P2TO1 DEVICE_DT_NAME(DT_INST(0, zmk_turbo_param_2to1))
++#define P2TO2 DEVICE_DT_NAME(DT_INST(0, zmk_turbo_param_2to2))
++
++#define ZM_IS_NODE_MATCH(a, b) (strcmp(a, b) == 0)
++
++#define IS_P1TO1(dev) ZM_IS_NODE_MATCH(dev, P1TO1)
++#define IS_P1TO2(dev) ZM_IS_NODE_MATCH(dev, P1TO2)
++#define IS_P2TO1(dev) ZM_IS_NODE_MATCH(dev, P2TO1)
++#define IS_P2TO2(dev) ZM_IS_NODE_MATCH(dev, P2TO2)
++
++static bool handle_control_binding(struct behavior_turbo_data *data,
++ struct zmk_behavior_binding *binding,
++ const struct zmk_behavior_binding new_binding) {
++ if (IS_P1TO1(new_binding.behavior_dev)) {
++ data->new_binding.param1 = binding->param1;
++ LOG_DBG("turbo param: 1to1: %d", binding->param1);
++ } else if (IS_P1TO2(new_binding.behavior_dev)) {
++ data->new_binding.param2 = binding->param1;
++ LOG_DBG("turbo param: 1to2");
++ } else if (IS_P2TO1(new_binding.behavior_dev)) {
++ data->new_binding.param1 = binding->param2;
++ LOG_DBG("turbo param: 2to1");
++ } else if (IS_P2TO2(new_binding.behavior_dev)) {
++ data->new_binding.param2 = binding->param2;
++ LOG_DBG("turbo param: 2to2");
++ } else {
++ return false;
++ }
++
++ return true;
++}
++
++static uint8_t get_binding_without_parameters_count(struct behavior_turbo_data *data) {
++ uint8_t bindings_without_parameters = 0;
++
++ for (int i = 0; i < data->binding_count; i++) {
++ struct zmk_behavior_binding binding = data->bindings[i];
++ if (!handle_control_binding(data, &binding, binding)) {
++ bindings_without_parameters++;
++ }
++ }
++
++ return bindings_without_parameters;
++}
++
++static void squash_params(struct behavior_turbo_data *data, struct zmk_behavior_binding *binding,
++ struct zmk_behavior_binding *new_bindings) {
++ uint8_t new_bindings_index = 0;
++ LOG_DBG("turbo bindings count is %d", data->binding_count);
++
++ for (int i = 0; i < data->binding_count; i++) {
++ bool is_control_binding = handle_control_binding(data, binding, data->bindings[i]);
++
++ if (!is_control_binding) {
++ data->new_binding.behavior_dev = data->bindings[i].behavior_dev;
++
++ if (!data->new_binding.param1) {
++ data->new_binding.param1 = data->bindings[i].param1;
++ }
++
++ if (!data->new_binding.param2) {
++ data->new_binding.param2 = data->bindings[i].param1;
++ }
++
++ new_bindings[new_bindings_index] = data->new_binding;
++ new_bindings_index++;
++ }
++
++ LOG_DBG("current turbo binding at index %d is %s, %d, %d", i,
++ data->new_binding.behavior_dev, data->new_binding.param1, data->new_binding.param2);
++ }
++}
++
++static int on_turbo_binding_pressed(struct zmk_behavior_binding *binding,
++ struct zmk_behavior_binding_event event) {
++ const struct device *dev = device_get_binding(binding->behavior_dev);
++ struct behavior_turbo_data *data = dev->data;
++
++ struct zmk_behavior_binding new_bindings[get_binding_without_parameters_count(data)];
++ squash_params(data, binding, new_bindings);
++
++ data->binding = new_bindings[0];
++
++ if (!data->is_active) {
++ data->is_active = true;
++
++ LOG_DBG("Started new turbo at position %d", event.position);
++
++ data->press_time = k_uptime_get();
++ data->position = event.position;
++
++ press_turbo_binding(&event, data);
++ reset_timer(data, event);
++ } else {
++ clear_turbo(data);
++ }
++
++ return ZMK_BEHAVIOR_OPAQUE;
++}
++
++static int on_turbo_binding_released(struct zmk_behavior_binding *binding,
++ struct zmk_behavior_binding_event event) {
++ const struct device *dev = device_get_binding(binding->behavior_dev);
++ struct behavior_turbo_data *data = dev->data;
++
++ if (data->is_active) {
++ data->is_pressed = false;
++ int32_t elapsedTime = k_uptime_get() - data->press_time;
++ LOG_DBG("turbo elapsed time: %d", elapsedTime);
++ if (elapsedTime > data->toggle_term_ms) {
++ clear_turbo(data);
++ }
++ }
++ return 0;
++}
++
++static int behavior_turbo_key_init(const struct device *dev) {
++ struct behavior_turbo_data *data = dev->data;
++ k_work_init_delayable(&data->release_timer, behavior_turbo_timer_handler);
++ return 0;
++};
++
++#define TRANSFORMED_BEHAVIORS(n) \
++ {LISTIFY(DT_PROP_LEN(n, bindings), ZMK_KEYMAP_EXTRACT_BINDING, (, ), n)}
++
++static const struct behavior_driver_api behavior_turbo_key_driver_api = {
++ .binding_pressed = on_turbo_binding_pressed,
++ .binding_released = on_turbo_binding_released,
++};
++
++#define TURBO_INST(n) \
++ static struct behavior_turbo_data behavior_turbo_data_##n = { \
++ .tap_ms = DT_PROP(n, tap_ms), \
++ .wait_ms = DT_PROP(n, wait_ms), \
++ .toggle_term_ms = DT_PROP(n, toggle_term_ms), \
++ .bindings = TRANSFORMED_BEHAVIORS(n), \
++ .binding_count = DT_PROP_LEN(n, bindings), \
++ .is_active = false, \
++ .is_pressed = false}; \
++ BEHAVIOR_DT_DEFINE(n, behavior_turbo_key_init, NULL, &behavior_turbo_data_##n, NULL, \
++ POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
++ &behavior_turbo_key_driver_api);
++
++DT_FOREACH_STATUS_OKAY(zmk_behavior_turbo_key, TURBO_INST)
++DT_FOREACH_STATUS_OKAY(zmk_behavior_turbo_key_one_param, TURBO_INST)
++DT_FOREACH_STATUS_OKAY(zmk_behavior_turbo_key_two_param, 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..e37d4f75
+--- /dev/null
++++ b/app/tests/turbo/behavior_keymap.dtsi
+@@ -0,0 +1,36 @@
++#include <dt-bindings/zmk/keys.h>
++#include <behaviors.dtsi>
++#include <dt-bindings/zmk/kscan_mock.h>
++
++/ {
++ 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 = "turbo2";
++ #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/keymaps/behaviors/turbo.md b/docs/docs/keymaps/behaviors/turbo.md
+new file mode 100644
+index 00000000..bf771482
+--- /dev/null
++++ b/docs/docs/keymaps/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 0a20a29e..4e58632e 100644
+--- a/docs/sidebars.js
++++ b/docs/sidebars.js
+@@ -70,6 +70,7 @@ module.exports = {
+ "keymaps/behaviors/mod-morph",
+ "keymaps/behaviors/macros",
+ "keymaps/behaviors/key-toggle",
++ "keymaps/behaviors/turbo",
+ "keymaps/behaviors/sticky-key",
+ "keymaps/behaviors/sticky-layer",
+ "keymaps/behaviors/tap-dance",