mirror of https://github.com/zmkfirmware/zmk.git
Dynamic macros
This commit is contained in:
parent
6b44d33db2
commit
e45bd2af62
|
|
@ -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)
|
||||
|
|
|
|||
11
app/Kconfig
11
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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define PLAY 0
|
||||
#define RECORD 1
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* 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>
|
||||
#include <zmk/events/keycode_state_changed.h>
|
||||
#include <dt-bindings/zmk/dynamic-macros.h>
|
||||
|
||||
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)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*behavior_queue_process_next/queue_process_next/p
|
||||
s/.*macro_binding/mac/p
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#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)
|
||||
>;
|
||||
};
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include <dt-bindings/zmk/dynamic-macros.h>
|
||||
|
||||
/ {
|
||||
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>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*behavior_queue_process_next/queue_process_next/p
|
||||
s/.*macro_binding/mac/p
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#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)
|
||||
>;
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*behavior_queue_process_next/queue_process_next/p
|
||||
s/.*macro_binding/mac/p
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#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)
|
||||
>;
|
||||
};
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include <dt-bindings/zmk/dynamic-macros.h>
|
||||
|
||||
/ {
|
||||
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>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*behavior_queue_process_next/queue_process_next/p
|
||||
s/.*macro_binding/mac/p
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#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)
|
||||
>;
|
||||
};
|
||||
|
|
@ -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.
|
||||
|
|
@ -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 <dt-bindings/zmk/dynamic-macros.h>
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
@ -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",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue