From e45bd2af6260846f049290007d4db863f4e502ea Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Fri, 2 Jun 2023 20:53:59 -0400 Subject: [PATCH 1/9] Dynamic macros --- app/CMakeLists.txt | 1 + app/Kconfig | 11 + .../behaviors/zmk,behavior-dynamic-macro.yaml | 16 ++ app/include/dt-bindings/zmk/dynamic-macros.h | 8 + app/src/behaviors/behavior_dynamic_macro.c | 206 ++++++++++++++++++ .../dynamic-macros/basic/events.patterns | 3 + .../basic/keycode_events.snapshot | 28 +++ .../basic/native_posix_64.keymap | 27 +++ app/tests/dynamic-macros/behavior_keymap.dtsi | 60 +++++ .../dynamic-macros/call-macro/events.patterns | 3 + .../call-macro/keycode_events.snapshot | 40 ++++ .../call-macro/native_posix_64.keymap | 27 +++ .../dynamic-macros/interrupt/events.patterns | 3 + .../interrupt/keycode_events.snapshot | 28 +++ .../interrupt/native_posix_64.keymap | 27 +++ .../recorded-time/behavior_keymap.dtsi | 59 +++++ .../recorded-time/events.patterns | 3 + .../recorded-time/keycode_events.snapshot | 28 +++ .../recorded-time/native_posix_64.keymap | 27 +++ docs/docs/intro.md | 54 +++++ docs/docs/keymaps/behaviors/dynamic-macros.md | 72 ++++++ docs/sidebars.js | 88 +++----- 22 files changed, 766 insertions(+), 53 deletions(-) create mode 100644 app/dts/bindings/behaviors/zmk,behavior-dynamic-macro.yaml create mode 100644 app/include/dt-bindings/zmk/dynamic-macros.h create mode 100644 app/src/behaviors/behavior_dynamic_macro.c create mode 100644 app/tests/dynamic-macros/basic/events.patterns create mode 100644 app/tests/dynamic-macros/basic/keycode_events.snapshot create mode 100644 app/tests/dynamic-macros/basic/native_posix_64.keymap create mode 100644 app/tests/dynamic-macros/behavior_keymap.dtsi create mode 100644 app/tests/dynamic-macros/call-macro/events.patterns create mode 100644 app/tests/dynamic-macros/call-macro/keycode_events.snapshot create mode 100644 app/tests/dynamic-macros/call-macro/native_posix_64.keymap create mode 100644 app/tests/dynamic-macros/interrupt/events.patterns create mode 100644 app/tests/dynamic-macros/interrupt/keycode_events.snapshot create mode 100644 app/tests/dynamic-macros/interrupt/native_posix_64.keymap create mode 100644 app/tests/dynamic-macros/recorded-time/behavior_keymap.dtsi create mode 100644 app/tests/dynamic-macros/recorded-time/events.patterns create mode 100644 app/tests/dynamic-macros/recorded-time/keycode_events.snapshot create mode 100644 app/tests/dynamic-macros/recorded-time/native_posix_64.keymap create mode 100644 docs/docs/intro.md create mode 100644 docs/docs/keymaps/behaviors/dynamic-macros.md diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 60c502fcd..e3c00bad4 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -53,6 +53,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_caps_word.c) target_sources(app PRIVATE src/behaviors/behavior_key_repeat.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_MACRO app PRIVATE src/behaviors/behavior_macro.c) + target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_DYNAMIC_MACRO app PRIVATE src/behaviors/behavior_dynamic_macro.c) target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c) target_sources(app PRIVATE src/behaviors/behavior_mod_morph.c) target_sources(app PRIVATE src/behaviors/behavior_outputs.c) diff --git a/app/Kconfig b/app/Kconfig index 6b4e3509a..a31bc9c55 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -464,6 +464,11 @@ config ZMK_BEHAVIORS_QUEUE_SIZE int "Maximum number of behaviors to allow queueing from a macro or other complex behavior" default 64 +config ZMK_DYNAMIC_MACRO_MAX_ACTIONS + int "Maximum number of keystrokes to be recorded in a dynamic macro" + default 64 + +DT_COMPAT_ZMK_BEHAVIOR_KEY_TOGGLE := zmk,behavior-key-toggle rsource "Kconfig.behaviors" config ZMK_MACRO_DEFAULT_WAIT_MS @@ -692,6 +697,12 @@ choice CBPRINTF_IMPLEMENTATION endchoice +DT_COMPAT_ZMK_BEHAVIOR_DYNAMIC_MACRO := zmk,behavior-dynamic-macro + +config ZMK_BEHAVIOR_DYNAMIC_MACRO + bool + default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_DYNAMIC_MACRO)) + module = ZMK module-str = zmk source "subsys/logging/Kconfig.template.log_config" diff --git a/app/dts/bindings/behaviors/zmk,behavior-dynamic-macro.yaml b/app/dts/bindings/behaviors/zmk,behavior-dynamic-macro.yaml new file mode 100644 index 000000000..48db2f2c1 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-dynamic-macro.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Dynamic Macro Behavior + +compatible: "zmk,behavior-dynamic-macro" + +include: one_param.yaml + +properties: + wait-ms: + type: int + default: -1 + description: The default time to wait (in milliseconds) before triggering the next behavior in the macro bindings list. + no-output: + type: boolean diff --git a/app/include/dt-bindings/zmk/dynamic-macros.h b/app/include/dt-bindings/zmk/dynamic-macros.h new file mode 100644 index 000000000..29f154893 --- /dev/null +++ b/app/include/dt-bindings/zmk/dynamic-macros.h @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define PLAY 0 +#define RECORD 1 diff --git a/app/src/behaviors/behavior_dynamic_macro.c b/app/src/behaviors/behavior_dynamic_macro.c new file mode 100644 index 000000000..5a04b09c4 --- /dev/null +++ b/app/src/behaviors/behavior_dynamic_macro.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#define DT_DRV_COMPAT zmk_behavior_dynamic_macro + +#define HID_KEY_USAGE_PAGE 0x70000 + +int8_t total_recorded_actions = 0; + +struct behavior_dynamic_macro_bind { + uint32_t wait_ms; + bool pressed; + struct zmk_behavior_binding binding; +}; + +struct behavior_dynamic_macro_state { + bool recording; + uint32_t lastEventTime; + uint32_t count; + struct behavior_dynamic_macro_bind bindings[CONFIG_ZMK_DYNAMIC_MACRO_MAX_ACTIONS]; +}; + +struct behavior_dynamic_macro_config { + uint32_t wait_ms; + bool no_output; +}; + +#define ZMK_BHV_RECORDING_MACRO_MAX 10 + +struct recording_macro { + uint32_t count; + uint32_t position; + bool recording; + const struct behavior_dynamic_macro_config *config; + struct behavior_dynamic_macro_state *state; +}; + +struct recording_macro recording_macros[ZMK_BHV_RECORDING_MACRO_MAX] = {}; + +static struct recording_macro *find_recording_macro(uint32_t position) { + for (int i = 0; i < ZMK_BHV_RECORDING_MACRO_MAX; i++) { + if (recording_macros[i].position == position && recording_macros[i].recording) { + return &recording_macros[i]; + } + } + return NULL; +} + +static int new_recording_macro(uint32_t position, + const struct behavior_dynamic_macro_config *config, + struct behavior_dynamic_macro_state *state, + struct recording_macro **macro) { + for (int i = 0; i < ZMK_BHV_RECORDING_MACRO_MAX; i++) { + struct recording_macro *const ref_macro = &recording_macros[i]; + if (!ref_macro->recording) { + ref_macro->recording = true; + ref_macro->count = 0; + ref_macro->position = position; + ref_macro->config = config; + ref_macro->state = state; + *macro = ref_macro; + return 0; + } + } + return -ENOMEM; +} + +static void queue_dynamic_macro(uint32_t position, uint32_t time, + struct behavior_dynamic_macro_state *state) { + LOG_DBG("Iterating dynamic macro bindings - count: %d", state->count); + for (int i = 0; i < state->count; i++) { + uint32_t wait_ms = time; + if (time == -1) { + wait_ms = state->bindings[i].wait_ms; + } + zmk_behavior_queue_add(position, state->bindings[i].binding, state->bindings[i].pressed, + wait_ms); + } +} + +static int on_dynamic_macro_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_dynamic_macro_config *cfg = dev->config; + struct behavior_dynamic_macro_state *state = dev->data; + + if (binding->param1 == PLAY) { + if (state->recording) { + LOG_ERR("Macro is currently recording, can't play"); + } else { + LOG_DBG("Playing Dynamic Macro"); + queue_dynamic_macro(event.position, cfg->wait_ms, state); + } + } else if (binding->param1 == RECORD) { + state->recording = !state->recording; + LOG_DBG("Recording Status: %d", state->recording); + if (state->recording) { + struct recording_macro *macro; + macro = find_recording_macro(event.position); + if (new_recording_macro(event.position, cfg, state, ¯o) == -ENOMEM) { + LOG_ERR("Unable to record new macro. Insufficient space in recording_macros[]"); + return ZMK_BEHAVIOR_OPAQUE; + } + LOG_DBG("Recording new macro: %d", event.position); + if (macro) { + total_recorded_actions -= macro->count; + } + macro->count = 0; + } else { + struct recording_macro *macro; + macro = find_recording_macro(event.position); + macro->recording = false; + macro->state->count = macro->count; + } + } + + return ZMK_BEHAVIOR_OPAQUE; +} + +static int on_dynamic_macro_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + return ZMK_BEHAVIOR_OPAQUE; +} + +static int behavior_dynamic_macro_init(const struct device *dev) { return 0; }; + +static const struct behavior_driver_api behavior_dynamic_macro_driver_api = { + .binding_pressed = on_dynamic_macro_binding_pressed, + .binding_released = on_dynamic_macro_binding_released, +}; + +static int dynamic_macro_keycode_state_changed_listener(const zmk_event_t *eh); + +ZMK_LISTENER(behavior_dynamic_macro, dynamic_macro_keycode_state_changed_listener); +ZMK_SUBSCRIPTION(behavior_dynamic_macro, zmk_keycode_state_changed); + +static int dynamic_macro_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_RECORDING_MACRO_MAX; i++) { + struct recording_macro *macro = &recording_macros[i]; + if (macro->recording && total_recorded_actions < CONFIG_ZMK_DYNAMIC_MACRO_MAX_ACTIONS) { + uint32_t eventTime = k_uptime_get(); + uint32_t elapsedTime = eventTime - macro->state->lastEventTime; + macro->state->lastEventTime = eventTime; + if (ev->state) { + macro->state->bindings[macro->count].pressed = true; + } else { + macro->state->bindings[macro->count].pressed = false; + } + macro->state->bindings[macro->count].binding.behavior_dev = "KEY_PRESS"; + macro->state->bindings[macro->count].binding.param1 = HID_KEY_USAGE_PAGE + ev->keycode; + macro->state->bindings[macro->count].binding.param2 = 0; + + if (macro->count > 0) { + macro->state->bindings[macro->count - 1].wait_ms = elapsedTime; + } + + macro->count++; + total_recorded_actions++; + + if (macro->config->no_output) { + return ZMK_EV_EVENT_HANDLED; + } + return ZMK_EV_EVENT_BUBBLE; + } else if (total_recorded_actions >= CONFIG_ZMK_DYNAMIC_MACRO_MAX_ACTIONS) { + LOG_ERR( + "Action not recorded, not enough space, CONFIG_ZMK_DYNAMIC_MACRO_MAX_ACTIONS %d", + CONFIG_ZMK_DYNAMIC_MACRO_MAX_ACTIONS); + if (macro->config->no_output) { + return ZMK_EV_EVENT_HANDLED; + } + return ZMK_EV_EVENT_BUBBLE; + } + } + return ZMK_EV_EVENT_BUBBLE; +} + +#define DYNAMIC_MACRO_INST(n) \ + static struct behavior_dynamic_macro_state behavior_dynamic_macro_state_##n = { \ + .recording = false, .count = 0}; \ + static struct behavior_dynamic_macro_config behavior_dynamic_macro_config_##n = { \ + .wait_ms = DT_INST_PROP_OR(n, wait_ms, -1), .no_output = DT_INST_PROP(n, no_output)}; \ + DEVICE_DT_INST_DEFINE(n, behavior_dynamic_macro_init, NULL, &behavior_dynamic_macro_state_##n, \ + &behavior_dynamic_macro_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_dynamic_macro_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(DYNAMIC_MACRO_INST) diff --git a/app/tests/dynamic-macros/basic/events.patterns b/app/tests/dynamic-macros/basic/events.patterns new file mode 100644 index 000000000..e399dfe6c --- /dev/null +++ b/app/tests/dynamic-macros/basic/events.patterns @@ -0,0 +1,3 @@ +s/.*hid_listener_keycode/kp/p +s/.*behavior_queue_process_next/queue_process_next/p +s/.*macro_binding/mac/p diff --git a/app/tests/dynamic-macros/basic/keycode_events.snapshot b/app/tests/dynamic-macros/basic/keycode_events.snapshot new file mode 100644 index 000000000..2be6ee25d --- /dev/null +++ b/app/tests/dynamic-macros/basic/keycode_events.snapshot @@ -0,0 +1,28 @@ +mac_pressed: Recording Status: 1 +mac_pressed: Recording new macro: 0 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +mac_pressed: Recording Status: 0 +mac_pressed: Playing Dynamic Macro +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms diff --git a/app/tests/dynamic-macros/basic/native_posix_64.keymap b/app/tests/dynamic-macros/basic/native_posix_64.keymap new file mode 100644 index 000000000..43ed36d02 --- /dev/null +++ b/app/tests/dynamic-macros/basic/native_posix_64.keymap @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,100) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,100) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,100) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,100) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,100) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,100) + >; +}; diff --git a/app/tests/dynamic-macros/behavior_keymap.dtsi b/app/tests/dynamic-macros/behavior_keymap.dtsi new file mode 100644 index 000000000..3d163aea0 --- /dev/null +++ b/app/tests/dynamic-macros/behavior_keymap.dtsi @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +/ { + macros { + dm1: dm1 { + compatible = "zmk,behavior-dynamic-macro"; + label = "dynamic-macro-1"; + wait-ms = <10>; + #binding-cells = <1>; + }; + dm2: dm2 { + compatible = "zmk,behavior-dynamic-macro"; + label = "dynamic-macro-2"; + wait-ms = <10>; + #binding-cells = <1>; + }; + rm1: rm1 { + compatible = "zmk,behavior-macro"; + label = "macro-1"; + wait-ms = <10>; + tap-ms = <10>; + #binding-cells = <0>; + bindings = <&kp A &kp A &kp A>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &dm1 RECORD &dm1 PLAY + &kp A &tog 1>; + }; + + extra_layer { + bindings = < + &kp N1 &rm1 + &tog 2 &trans>; + + }; + + extra_layer_2 { + bindings = < + &dm2 RECORD &dm2 PLAY + &trans &kp N1>; + }; + + }; +}; diff --git a/app/tests/dynamic-macros/call-macro/events.patterns b/app/tests/dynamic-macros/call-macro/events.patterns new file mode 100644 index 000000000..e399dfe6c --- /dev/null +++ b/app/tests/dynamic-macros/call-macro/events.patterns @@ -0,0 +1,3 @@ +s/.*hid_listener_keycode/kp/p +s/.*behavior_queue_process_next/queue_process_next/p +s/.*macro_binding/mac/p diff --git a/app/tests/dynamic-macros/call-macro/keycode_events.snapshot b/app/tests/dynamic-macros/call-macro/keycode_events.snapshot new file mode 100644 index 000000000..fb128a85b --- /dev/null +++ b/app/tests/dynamic-macros/call-macro/keycode_events.snapshot @@ -0,0 +1,40 @@ +mac_pressed: Recording Status: 1 +mac_pressed: Recording new macro: 0 +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +mac_pressed: Recording Status: 0 +mac_pressed: Playing Dynamic Macro +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms diff --git a/app/tests/dynamic-macros/call-macro/native_posix_64.keymap b/app/tests/dynamic-macros/call-macro/native_posix_64.keymap new file mode 100644 index 000000000..2ddb6fad5 --- /dev/null +++ b/app/tests/dynamic-macros/call-macro/native_posix_64.keymap @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,100) + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(1,1,100) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,100) + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(1,1,100) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,100) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,100) + >; +}; diff --git a/app/tests/dynamic-macros/interrupt/events.patterns b/app/tests/dynamic-macros/interrupt/events.patterns new file mode 100644 index 000000000..e399dfe6c --- /dev/null +++ b/app/tests/dynamic-macros/interrupt/events.patterns @@ -0,0 +1,3 @@ +s/.*hid_listener_keycode/kp/p +s/.*behavior_queue_process_next/queue_process_next/p +s/.*macro_binding/mac/p diff --git a/app/tests/dynamic-macros/interrupt/keycode_events.snapshot b/app/tests/dynamic-macros/interrupt/keycode_events.snapshot new file mode 100644 index 000000000..2be6ee25d --- /dev/null +++ b/app/tests/dynamic-macros/interrupt/keycode_events.snapshot @@ -0,0 +1,28 @@ +mac_pressed: Recording Status: 1 +mac_pressed: Recording new macro: 0 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +mac_pressed: Recording Status: 0 +mac_pressed: Playing Dynamic Macro +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 10ms diff --git a/app/tests/dynamic-macros/interrupt/native_posix_64.keymap b/app/tests/dynamic-macros/interrupt/native_posix_64.keymap new file mode 100644 index 000000000..303e60ced --- /dev/null +++ b/app/tests/dynamic-macros/interrupt/native_posix_64.keymap @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,100) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,100) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,100) + ZMK_MOCK_RELEASE(0,0,100) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,100) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,100) + >; +}; diff --git a/app/tests/dynamic-macros/recorded-time/behavior_keymap.dtsi b/app/tests/dynamic-macros/recorded-time/behavior_keymap.dtsi new file mode 100644 index 000000000..bdd6bbde6 --- /dev/null +++ b/app/tests/dynamic-macros/recorded-time/behavior_keymap.dtsi @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +/ { + macros { + dm1: dm1 { + compatible = "zmk,behavior-dynamic-macro"; + label = "dynamic-macro-1"; + #binding-cells = <1>; + }; + dm2: dm2 { + compatible = "zmk,behavior-dynamic-macro"; + label = "dynamic-macro-2"; + wait-ms = <10>; + #binding-cells = <1>; + }; + rm1: rm1 { + compatible = "zmk,behavior-macro"; + label = "macro-1"; + wait-ms = <10>; + tap-ms = <10>; + #binding-cells = <0>; + bindings = <&kp A &kp A &kp A>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &dm1 RECORD &dm1 PLAY + &kp A &tog 1>; + }; + + extra_layer { + bindings = < + &kp N1 &rm1 + &tog 2 &trans>; + + }; + + extra_layer_2 { + bindings = < + &dm2 RECORD &dm2 PLAY + &trans &kp N1>; + }; + + }; +}; diff --git a/app/tests/dynamic-macros/recorded-time/events.patterns b/app/tests/dynamic-macros/recorded-time/events.patterns new file mode 100644 index 000000000..e399dfe6c --- /dev/null +++ b/app/tests/dynamic-macros/recorded-time/events.patterns @@ -0,0 +1,3 @@ +s/.*hid_listener_keycode/kp/p +s/.*behavior_queue_process_next/queue_process_next/p +s/.*macro_binding/mac/p diff --git a/app/tests/dynamic-macros/recorded-time/keycode_events.snapshot b/app/tests/dynamic-macros/recorded-time/keycode_events.snapshot new file mode 100644 index 000000000..e8d346357 --- /dev/null +++ b/app/tests/dynamic-macros/recorded-time/keycode_events.snapshot @@ -0,0 +1,28 @@ +mac_pressed: Recording Status: 1 +mac_pressed: Recording new macro: 0 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +mac_pressed: Recording Status: 0 +mac_pressed: Playing Dynamic Macro +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 11ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 1001ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 11ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 1001ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 11ms +queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +queue_process_next: Processing next queued behavior in 0ms diff --git a/app/tests/dynamic-macros/recorded-time/native_posix_64.keymap b/app/tests/dynamic-macros/recorded-time/native_posix_64.keymap new file mode 100644 index 000000000..2c527b664 --- /dev/null +++ b/app/tests/dynamic-macros/recorded-time/native_posix_64.keymap @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include "behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,100) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,1000) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,1000) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,1000) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,100) + ZMK_MOCK_PRESS(0,1,10000) + ZMK_MOCK_RELEASE(0,1,100) + >; +}; diff --git a/docs/docs/intro.md b/docs/docs/intro.md new file mode 100644 index 000000000..09469bcbb --- /dev/null +++ b/docs/docs/intro.md @@ -0,0 +1,54 @@ +--- +title: Introduction to ZMK +sidebar_label: Introduction +slug: / +--- + +ZMK Firmware is an open source (MIT) keyboard +firmware built on the [Zephyrâ„¢ Project](https://zephyrproject.org/) Real Time Operating System (RTOS). ZMK's goal is to provide a modern, wireless, and powerful firmware free of licensing issues. + +## Features + +ZMK is currently missing some features found in other popular firmware. This table compares the features supported by ZMK, BlueMicro and QMK: + +| Legend: | ✅ Supported | 🚧 Under Development | 💡 Planned | +| :------ | :----------- | :------------------- | :--------- | + +| **Feature** | ZMK | BlueMicro | QMK | +| ---------------------------------------------------------------------------------------------------------------------------------- | :-: | :-------: | :-: | +| Low Latency BLE Support | ✅ | ✅ | | +| Multi-Device BLE Support | ✅ | | | +| [USB Connectivity](behaviors/outputs.md) | ✅ | ✅ | ✅ | +| User Configuration Repositories | ✅ | | | +| Split Keyboard Support | ✅ | ✅ | ✅ | +| [Keymaps and Layers](behaviors/layers.md) | ✅ | ✅ | ✅ | +| [Hold-Tap](behaviors/hold-tap.mdx) (which includes [Mod-Tap](behaviors/mod-tap.md) and [Layer-Tap](behaviors/layers.md#layer-tap)) | ✅ | ✅ | ✅ | +| [Tap-Dance](behaviors/tap-dance.mdx) | ✅ | ✅[^2] | ✅ | +| [Keyboard Codes](codes/index.mdx#keyboard) | ✅ | ✅ | ✅ | +| [Media](codes/index.mdx#media-controls) & [Consumer](codes/index.mdx#consumer-controls) Codes | ✅ | ✅ | ✅ | +| [Encoders](features/encoders.md) | ✅ | ✅ | ✅ | +| [Display Support](features/displays.md)[^1] | 🚧 | 🚧 | ✅ | +| [RGB Underglow](features/underglow.md) | ✅ | ✅ | ✅ | +| [Backlight](features/backlight.mdx) | ✅ | ✅ | ✅ | +| One Shot Keys | ✅ | ✅ | ✅ | +| [Combo Keys](features/combos.md) | ✅ | | ✅ | +| [Macros](behaviors/macros.md) | ✅ | ✅ | ✅ | +| [Dynamic Macros](behaviors/dynamic-macros.md) | ✅ | | ✅ | +| Mouse Keys | 🚧 | ✅ | ✅ | +| Low Active Power Usage | ✅ | | | +| Low Power Sleep States | ✅ | ✅ | | +| [Low Power Mode (VCC Shutoff)](behaviors/power.md) | ✅ | ✅ | | +| Battery Reporting | ✅ | ✅ | | +| Shell over BLE | 💡 | | | +| Realtime Keymap Updating | 💡 | | ✅ | +| AVR/8 Bit | | | ✅ | +| [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/3.5.0/boards/index.html) | ✅ | | | + +[^2]: Tap-Dances are limited to single and double-tap on BlueMicro +[^1]: OLEDs are currently proof of concept in ZMK. + +## Code of Conduct + +Please note that this project is released with a +[Contributor Code of Conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/). +By participating in this project you agree to abide by its terms. diff --git a/docs/docs/keymaps/behaviors/dynamic-macros.md b/docs/docs/keymaps/behaviors/dynamic-macros.md new file mode 100644 index 000000000..2bde5cea3 --- /dev/null +++ b/docs/docs/keymaps/behaviors/dynamic-macros.md @@ -0,0 +1,72 @@ +--- +title: Dynamic Macro Behavior +sidebar_label: Dynamic Macros +--- + +## Summary + +The dynamic macro behavior allows creating macros and replaying them by recording key presses. While recording a macro, you can also play another macro. + +:::note +Dynamic macros are cleared on reboot. +::: + +:::warning +Dynamic macros are memory intensive, and may cause the firmware to crash. It is recommended to use only 1 dynamic macro in your keymap and re-record it if it needs changed. The maximum amount of actions to be recorded can be set with `CONFIG_ZMK_DYNAMIC_MACRO_MAX_ACTIONS` (default 64). +::: + +## Dynamic Macro Action Defines + +Dynamic macro action defines are provided through the [`dt-bindings/zmk/dynamic-macros.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/dynamic-macros.h) header, +which is added at the top of the keymap file: + +``` +#include +``` + +This will allow you to reference the actions defined in this header such as `PLAY`. + +Here is a table describing the action for each define: + +| Define | Action | +| -------- | --------------------------- | +| `PLAY` | Play back a recorded macro | +| `RECORD` | Toggle recording of a macro | + +## Macro Definition + +A dynamic macro definition looks like: + +``` +/ { + macros { + dyn-macro: dyn-macro { + label = "ZM_dynamic-macro"; + compatible = "zmk,behavior-dynamic-macro"; + #binding-cells = <1>; + }; + }; +}; +``` + +The macro can then be bound in your keymap by referencing it by the label `dyn-macro` followed by PLAY or RECORD, e.g.: + +``` + / { + keymap { + &dyn-macro PLAY &dyn-macro RECORD + ... + }; +}; +``` + +## Configuration + +### Wait Time + +The wait time setting controls how long of a delay is introduced between behaviors. By default, a macro will play back at the speed it +was recorded, but it can be overwritten by assigning a value to the `wait-ms` property of the macro, e.g. `wait-ms = <20>;`. + +### No Output + +By default, keystrokes will still be sent to the host while a dynamic macro is recording. Setting `no-output` will change this and will not send keystrokes to the host while recording. diff --git a/docs/sidebars.js b/docs/sidebars.js index 3d55192b0..62d70bb83 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -54,59 +54,41 @@ module.exports = { }, collapsed: true, items: [ - { - type: "category", - label: "Behaviors", - link: { - type: "doc", - id: "keymaps/behaviors/index", - }, - collapsed: true, - items: [ - "keymaps/behaviors/key-press", - "keymaps/behaviors/layers", - "keymaps/behaviors/misc", - "keymaps/behaviors/hold-tap", - "keymaps/behaviors/mod-morph", - "keymaps/behaviors/macros", - "keymaps/behaviors/key-toggle", - "keymaps/behaviors/sticky-key", - "keymaps/behaviors/sticky-layer", - "keymaps/behaviors/tap-dance", - "keymaps/behaviors/caps-word", - "keymaps/behaviors/key-repeat", - "keymaps/behaviors/sensor-rotate", - "keymaps/behaviors/mouse-emulation", - "keymaps/behaviors/reset", - "keymaps/behaviors/bluetooth", - "keymaps/behaviors/outputs", - "keymaps/behaviors/underglow", - "keymaps/behaviors/backlight", - "keymaps/behaviors/power", - "keymaps/behaviors/soft-off", - "keymaps/behaviors/studio-unlock", - ], - }, - "keymaps/modifiers", - "keymaps/combos", - "keymaps/conditional-layers", - "keymaps/list-of-keycodes", - { - type: "category", - label: "Input Processors", - link: { - type: "doc", - id: "keymaps/input-processors/index", - }, - collapsed: true, - items: [ - "keymaps/input-processors/usage", - "keymaps/input-processors/scaler", - "keymaps/input-processors/transformer", - "keymaps/input-processors/code-mapper", - "keymaps/input-processors/temp-layer", - ], - }, + "behaviors/key-press", + "behaviors/layers", + "behaviors/misc", + "behaviors/hold-tap", + "behaviors/mod-tap", + "behaviors/mod-morph", + "behaviors/dynamic-macros", + "behaviors/macros", + "behaviors/key-toggle", + "behaviors/sticky-key", + "behaviors/sticky-layer", + "behaviors/tap-dance", + "behaviors/caps-word", + "behaviors/key-repeat", + "behaviors/sensor-rotate", + "behaviors/mouse-emulation", + "behaviors/reset", + "behaviors/bluetooth", + "behaviors/outputs", + "behaviors/underglow", + "behaviors/backlight", + "behaviors/power", + "behaviors/soft-off", + ], + }, + { + Codes: [ + "codes/index", + "codes/keyboard-keypad", + "codes/modifiers", + "codes/editing", + "codes/media", + "codes/applications", + "codes/input-assist", + "codes/power", ], }, { From a6b601ddea1d14628ab1ffa1e190ff706af1f3f5 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Sun, 4 Jun 2023 05:12:21 -0400 Subject: [PATCH 2/9] Fix total actions not updating --- app/src/behaviors/behavior_dynamic_macro.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/behaviors/behavior_dynamic_macro.c b/app/src/behaviors/behavior_dynamic_macro.c index 5a04b09c4..435fe335a 100644 --- a/app/src/behaviors/behavior_dynamic_macro.c +++ b/app/src/behaviors/behavior_dynamic_macro.c @@ -60,6 +60,15 @@ static struct recording_macro *find_recording_macro(uint32_t position) { return NULL; } +static struct recording_macro *find_macro_at_position(uint32_t position) { + for (int i = 0; i < ZMK_BHV_RECORDING_MACRO_MAX; i++) { + if (recording_macros[i].position == position) { + return &recording_macros[i]; + } + } + return NULL; +} + static int new_recording_macro(uint32_t position, const struct behavior_dynamic_macro_config *config, struct behavior_dynamic_macro_state *state, @@ -116,8 +125,11 @@ static int on_dynamic_macro_binding_pressed(struct zmk_behavior_binding *binding return ZMK_BEHAVIOR_OPAQUE; } LOG_DBG("Recording new macro: %d", event.position); - if (macro) { - total_recorded_actions -= macro->count; + + struct recording_macro *old_macro; + old_macro = find_macro_at_position(event.position); + if (old_macro) { + total_recorded_actions -= old_macro->state->count; } macro->count = 0; } else { From d4c3c8e1a3fe0bf24ed44d06dcef2f16eb942772 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Tue, 16 Apr 2024 18:18:03 -0400 Subject: [PATCH 3/9] Fix for 3.5 --- app/src/behaviors/behavior_dynamic_macro.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/behaviors/behavior_dynamic_macro.c b/app/src/behaviors/behavior_dynamic_macro.c index 435fe335a..7c16aa9ab 100644 --- a/app/src/behaviors/behavior_dynamic_macro.c +++ b/app/src/behaviors/behavior_dynamic_macro.c @@ -210,9 +210,9 @@ static int dynamic_macro_keycode_state_changed_listener(const zmk_event_t *eh) { .recording = false, .count = 0}; \ static struct behavior_dynamic_macro_config behavior_dynamic_macro_config_##n = { \ .wait_ms = DT_INST_PROP_OR(n, wait_ms, -1), .no_output = DT_INST_PROP(n, no_output)}; \ - DEVICE_DT_INST_DEFINE(n, behavior_dynamic_macro_init, NULL, &behavior_dynamic_macro_state_##n, \ - &behavior_dynamic_macro_config_##n, APPLICATION, \ - CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + BEHAVIOR_DT_INST_DEFINE(n, behavior_dynamic_macro_init, NULL, \ + &behavior_dynamic_macro_state_##n, &behavior_dynamic_macro_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ &behavior_dynamic_macro_driver_api); DT_INST_FOREACH_STATUS_OKAY(DYNAMIC_MACRO_INST) From 1f5877615610fd1d307d18bfd87c7c47987d08bc Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Tue, 16 Apr 2024 18:23:40 -0400 Subject: [PATCH 4/9] Run pre-commit checks --- app/src/behaviors/behavior_dynamic_macro.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/behaviors/behavior_dynamic_macro.c b/app/src/behaviors/behavior_dynamic_macro.c index 7c16aa9ab..5bd039d67 100644 --- a/app/src/behaviors/behavior_dynamic_macro.c +++ b/app/src/behaviors/behavior_dynamic_macro.c @@ -211,8 +211,8 @@ static int dynamic_macro_keycode_state_changed_listener(const zmk_event_t *eh) { static struct behavior_dynamic_macro_config behavior_dynamic_macro_config_##n = { \ .wait_ms = DT_INST_PROP_OR(n, wait_ms, -1), .no_output = DT_INST_PROP(n, no_output)}; \ BEHAVIOR_DT_INST_DEFINE(n, behavior_dynamic_macro_init, NULL, \ - &behavior_dynamic_macro_state_##n, &behavior_dynamic_macro_config_##n, \ - POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ - &behavior_dynamic_macro_driver_api); + &behavior_dynamic_macro_state_##n, &behavior_dynamic_macro_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_dynamic_macro_driver_api); DT_INST_FOREACH_STATUS_OKAY(DYNAMIC_MACRO_INST) From e7363239427269211bfa927b999b0d7a6509417f Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Sat, 14 Jun 2025 23:57:54 -0400 Subject: [PATCH 5/9] rebase dynamic macros onto main --- app/src/behaviors/behavior_dynamic_macro.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/behaviors/behavior_dynamic_macro.c b/app/src/behaviors/behavior_dynamic_macro.c index 5bd039d67..f68694982 100644 --- a/app/src/behaviors/behavior_dynamic_macro.c +++ b/app/src/behaviors/behavior_dynamic_macro.c @@ -88,15 +88,17 @@ static int new_recording_macro(uint32_t position, return -ENOMEM; } -static void queue_dynamic_macro(uint32_t position, uint32_t time, +static void queue_dynamic_macro(struct zmk_behavior_binding_event event, uint32_t time, struct behavior_dynamic_macro_state *state) { LOG_DBG("Iterating dynamic macro bindings - count: %d", state->count); for (int i = 0; i < state->count; i++) { uint32_t wait_ms = time; + if (time == -1) { wait_ms = state->bindings[i].wait_ms; } - zmk_behavior_queue_add(position, state->bindings[i].binding, state->bindings[i].pressed, + + zmk_behavior_queue_add(&event, state->bindings[i].binding, state->bindings[i].pressed, wait_ms); } } @@ -112,7 +114,7 @@ static int on_dynamic_macro_binding_pressed(struct zmk_behavior_binding *binding LOG_ERR("Macro is currently recording, can't play"); } else { LOG_DBG("Playing Dynamic Macro"); - queue_dynamic_macro(event.position, cfg->wait_ms, state); + queue_dynamic_macro(event, cfg->wait_ms, state); } } else if (binding->param1 == RECORD) { state->recording = !state->recording; @@ -172,12 +174,14 @@ static int dynamic_macro_keycode_state_changed_listener(const zmk_event_t *eh) { uint32_t eventTime = k_uptime_get(); uint32_t elapsedTime = eventTime - macro->state->lastEventTime; macro->state->lastEventTime = eventTime; + if (ev->state) { macro->state->bindings[macro->count].pressed = true; } else { macro->state->bindings[macro->count].pressed = false; } - macro->state->bindings[macro->count].binding.behavior_dev = "KEY_PRESS"; + + macro->state->bindings[macro->count].binding.behavior_dev = "key_press"; macro->state->bindings[macro->count].binding.param1 = HID_KEY_USAGE_PAGE + ev->keycode; macro->state->bindings[macro->count].binding.param2 = 0; From 07ca1319457f8aa230cb97a8e57d8a89b01420d2 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Sun, 15 Jun 2025 16:33:20 -0400 Subject: [PATCH 6/9] satisfy pre-commit checks --- docs/docs/intro.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/intro.md b/docs/docs/intro.md index 09469bcbb..bef1c7864 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -45,6 +45,7 @@ ZMK is currently missing some features found in other popular firmware. This tab | [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/3.5.0/boards/index.html) | ✅ | | | [^2]: Tap-Dances are limited to single and double-tap on BlueMicro + [^1]: OLEDs are currently proof of concept in ZMK. ## Code of Conduct From 5e52271908fdd5a84ef02a2c38544f285d9cc8c7 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Tue, 17 Jun 2025 10:03:45 -0400 Subject: [PATCH 7/9] fix(dynamic-macros): update test snapshot wording to reflect behavior change --- .../basic/keycode_events.snapshot | 12 +++++----- .../call-macro/keycode_events.snapshot | 24 +++++++++---------- .../interrupt/keycode_events.snapshot | 12 +++++----- .../recorded-time/keycode_events.snapshot | 12 +++++----- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/tests/dynamic-macros/basic/keycode_events.snapshot b/app/tests/dynamic-macros/basic/keycode_events.snapshot index 2be6ee25d..d5403c7d3 100644 --- a/app/tests/dynamic-macros/basic/keycode_events.snapshot +++ b/app/tests/dynamic-macros/basic/keycode_events.snapshot @@ -8,21 +8,21 @@ kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 mac_pressed: Recording Status: 0 mac_pressed: Playing Dynamic Macro -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms diff --git a/app/tests/dynamic-macros/call-macro/keycode_events.snapshot b/app/tests/dynamic-macros/call-macro/keycode_events.snapshot index fb128a85b..cc79923fd 100644 --- a/app/tests/dynamic-macros/call-macro/keycode_events.snapshot +++ b/app/tests/dynamic-macros/call-macro/keycode_events.snapshot @@ -1,40 +1,40 @@ mac_pressed: Recording Status: 1 mac_pressed: Recording new macro: 0 -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms mac_pressed: Recording Status: 0 mac_pressed: Playing Dynamic Macro -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms diff --git a/app/tests/dynamic-macros/interrupt/keycode_events.snapshot b/app/tests/dynamic-macros/interrupt/keycode_events.snapshot index 2be6ee25d..d5403c7d3 100644 --- a/app/tests/dynamic-macros/interrupt/keycode_events.snapshot +++ b/app/tests/dynamic-macros/interrupt/keycode_events.snapshot @@ -8,21 +8,21 @@ kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 mac_pressed: Recording Status: 0 mac_pressed: Playing Dynamic Macro -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 10ms diff --git a/app/tests/dynamic-macros/recorded-time/keycode_events.snapshot b/app/tests/dynamic-macros/recorded-time/keycode_events.snapshot index e8d346357..70b8ed8d0 100644 --- a/app/tests/dynamic-macros/recorded-time/keycode_events.snapshot +++ b/app/tests/dynamic-macros/recorded-time/keycode_events.snapshot @@ -8,21 +8,21 @@ kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 mac_pressed: Recording Status: 0 mac_pressed: Playing Dynamic Macro -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 11ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 1001ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 11ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 1001ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 11ms -queue_process_next: Invoking KEY_PRESS: 0x70004 0x00 +queue_process_next: Invoking key_press: 0x70004 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 queue_process_next: Processing next queued behavior in 0ms From 5b01d7a89e868041b2313d310b1a88277fd8a389 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Tue, 17 Jun 2025 10:14:00 -0400 Subject: [PATCH 8/9] docs(dynamic-macros): fix and update docs structure --- docs/docs/intro.md | 55 ------------ docs/docs/intro.mdx | 1 + docs/docs/keymaps/behaviors/dynamic-macros.md | 7 +- docs/sidebars.js | 89 +++++++++++-------- 4 files changed, 58 insertions(+), 94 deletions(-) delete mode 100644 docs/docs/intro.md diff --git a/docs/docs/intro.md b/docs/docs/intro.md deleted file mode 100644 index bef1c7864..000000000 --- a/docs/docs/intro.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Introduction to ZMK -sidebar_label: Introduction -slug: / ---- - -ZMK Firmware is an open source (MIT) keyboard -firmware built on the [Zephyrâ„¢ Project](https://zephyrproject.org/) Real Time Operating System (RTOS). ZMK's goal is to provide a modern, wireless, and powerful firmware free of licensing issues. - -## Features - -ZMK is currently missing some features found in other popular firmware. This table compares the features supported by ZMK, BlueMicro and QMK: - -| Legend: | ✅ Supported | 🚧 Under Development | 💡 Planned | -| :------ | :----------- | :------------------- | :--------- | - -| **Feature** | ZMK | BlueMicro | QMK | -| ---------------------------------------------------------------------------------------------------------------------------------- | :-: | :-------: | :-: | -| Low Latency BLE Support | ✅ | ✅ | | -| Multi-Device BLE Support | ✅ | | | -| [USB Connectivity](behaviors/outputs.md) | ✅ | ✅ | ✅ | -| User Configuration Repositories | ✅ | | | -| Split Keyboard Support | ✅ | ✅ | ✅ | -| [Keymaps and Layers](behaviors/layers.md) | ✅ | ✅ | ✅ | -| [Hold-Tap](behaviors/hold-tap.mdx) (which includes [Mod-Tap](behaviors/mod-tap.md) and [Layer-Tap](behaviors/layers.md#layer-tap)) | ✅ | ✅ | ✅ | -| [Tap-Dance](behaviors/tap-dance.mdx) | ✅ | ✅[^2] | ✅ | -| [Keyboard Codes](codes/index.mdx#keyboard) | ✅ | ✅ | ✅ | -| [Media](codes/index.mdx#media-controls) & [Consumer](codes/index.mdx#consumer-controls) Codes | ✅ | ✅ | ✅ | -| [Encoders](features/encoders.md) | ✅ | ✅ | ✅ | -| [Display Support](features/displays.md)[^1] | 🚧 | 🚧 | ✅ | -| [RGB Underglow](features/underglow.md) | ✅ | ✅ | ✅ | -| [Backlight](features/backlight.mdx) | ✅ | ✅ | ✅ | -| One Shot Keys | ✅ | ✅ | ✅ | -| [Combo Keys](features/combos.md) | ✅ | | ✅ | -| [Macros](behaviors/macros.md) | ✅ | ✅ | ✅ | -| [Dynamic Macros](behaviors/dynamic-macros.md) | ✅ | | ✅ | -| Mouse Keys | 🚧 | ✅ | ✅ | -| Low Active Power Usage | ✅ | | | -| Low Power Sleep States | ✅ | ✅ | | -| [Low Power Mode (VCC Shutoff)](behaviors/power.md) | ✅ | ✅ | | -| Battery Reporting | ✅ | ✅ | | -| Shell over BLE | 💡 | | | -| Realtime Keymap Updating | 💡 | | ✅ | -| AVR/8 Bit | | | ✅ | -| [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/3.5.0/boards/index.html) | ✅ | | | - -[^2]: Tap-Dances are limited to single and double-tap on BlueMicro - -[^1]: OLEDs are currently proof of concept in ZMK. - -## Code of Conduct - -Please note that this project is released with a -[Contributor Code of Conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/). -By participating in this project you agree to abide by its terms. diff --git a/docs/docs/intro.mdx b/docs/docs/intro.mdx index 714d2bf36..00783bcd9 100644 --- a/docs/docs/intro.mdx +++ b/docs/docs/intro.mdx @@ -63,6 +63,7 @@ Below table lists major features/capabilities currently supported in ZMK, as wel | [Sticky (One Shot) Keys](keymaps/behaviors/sticky-key.md) | ✅ | | [Combos](keymaps/combos.md) | ✅ | | [Macros](keymaps/behaviors/macros.md) | ✅ | +| [Dynamic Macros](keymaps/behaviors/dynamic-macros.md) | ✅ | | [Mouse Keys](keymaps/behaviors/mouse-emulation.md) | ✅ | | [Realtime Keymap Updating](features/studio.md) | 🚧 | diff --git a/docs/docs/keymaps/behaviors/dynamic-macros.md b/docs/docs/keymaps/behaviors/dynamic-macros.md index 2bde5cea3..95b29ee87 100644 --- a/docs/docs/keymaps/behaviors/dynamic-macros.md +++ b/docs/docs/keymaps/behaviors/dynamic-macros.md @@ -40,8 +40,7 @@ A dynamic macro definition looks like: ``` / { macros { - dyn-macro: dyn-macro { - label = "ZM_dynamic-macro"; + dm: dm { compatible = "zmk,behavior-dynamic-macro"; #binding-cells = <1>; }; @@ -49,12 +48,12 @@ A dynamic macro definition looks like: }; ``` -The macro can then be bound in your keymap by referencing it by the label `dyn-macro` followed by PLAY or RECORD, e.g.: +The macro can then be bound in your keymap by referencing it by the label `dm` followed by PLAY or RECORD, e.g.: ``` / { keymap { - &dyn-macro PLAY &dyn-macro RECORD + &dm PLAY &dm RECORD ... }; }; diff --git a/docs/sidebars.js b/docs/sidebars.js index 62d70bb83..c0994ef21 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -54,41 +54,60 @@ module.exports = { }, collapsed: true, items: [ - "behaviors/key-press", - "behaviors/layers", - "behaviors/misc", - "behaviors/hold-tap", - "behaviors/mod-tap", - "behaviors/mod-morph", - "behaviors/dynamic-macros", - "behaviors/macros", - "behaviors/key-toggle", - "behaviors/sticky-key", - "behaviors/sticky-layer", - "behaviors/tap-dance", - "behaviors/caps-word", - "behaviors/key-repeat", - "behaviors/sensor-rotate", - "behaviors/mouse-emulation", - "behaviors/reset", - "behaviors/bluetooth", - "behaviors/outputs", - "behaviors/underglow", - "behaviors/backlight", - "behaviors/power", - "behaviors/soft-off", - ], - }, - { - Codes: [ - "codes/index", - "codes/keyboard-keypad", - "codes/modifiers", - "codes/editing", - "codes/media", - "codes/applications", - "codes/input-assist", - "codes/power", + { + type: "category", + label: "Behaviors", + link: { + type: "doc", + id: "keymaps/behaviors/index", + }, + collapsed: true, + items: [ + "keymaps/behaviors/key-press", + "keymaps/behaviors/layers", + "keymaps/behaviors/misc", + "keymaps/behaviors/hold-tap", + "keymaps/behaviors/mod-morph", + "keymaps/behaviors/macros", + "keymaps/behaviors/dynamic-macros", + "keymaps/behaviors/key-toggle", + "keymaps/behaviors/sticky-key", + "keymaps/behaviors/sticky-layer", + "keymaps/behaviors/tap-dance", + "keymaps/behaviors/caps-word", + "keymaps/behaviors/key-repeat", + "keymaps/behaviors/sensor-rotate", + "keymaps/behaviors/mouse-emulation", + "keymaps/behaviors/reset", + "keymaps/behaviors/bluetooth", + "keymaps/behaviors/outputs", + "keymaps/behaviors/underglow", + "keymaps/behaviors/backlight", + "keymaps/behaviors/power", + "keymaps/behaviors/soft-off", + "keymaps/behaviors/studio-unlock", + ], + }, + "keymaps/modifiers", + "keymaps/combos", + "keymaps/conditional-layers", + "keymaps/list-of-keycodes", + { + type: "category", + label: "Input Processors", + link: { + type: "doc", + id: "keymaps/input-processors/index", + }, + collapsed: true, + items: [ + "keymaps/input-processors/usage", + "keymaps/input-processors/scaler", + "keymaps/input-processors/transformer", + "keymaps/input-processors/code-mapper", + "keymaps/input-processors/temp-layer", + ], + }, ], }, { From a1b1d7cb221b227e35a093d860efe5e89be090cd Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Tue, 17 Jun 2025 12:14:22 -0400 Subject: [PATCH 9/9] feat(dynamic-macros): update kconfig files --- app/Kconfig | 6 ------ app/Kconfig.behaviors | 5 +++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/Kconfig b/app/Kconfig index a31bc9c55..404a31551 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -697,12 +697,6 @@ choice CBPRINTF_IMPLEMENTATION endchoice -DT_COMPAT_ZMK_BEHAVIOR_DYNAMIC_MACRO := zmk,behavior-dynamic-macro - -config ZMK_BEHAVIOR_DYNAMIC_MACRO - bool - default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_DYNAMIC_MACRO)) - module = ZMK module-str = zmk source "subsys/logging/Kconfig.template.log_config" diff --git a/app/Kconfig.behaviors b/app/Kconfig.behaviors index da9bcc41a..c8110e023 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_DYNAMIC_MACRO + bool + default y + depends on DT_HAS_ZMK_BEHAVIOR_DYNAMIC_MACRO_ENABLED