This commit is contained in:
Nick Conway 2024-08-18 01:23:50 +00:00 committed by GitHub
commit f9be73e234
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 1151 additions and 0 deletions

View file

@ -52,6 +52,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_LEADER_KEY app PRIVATE src/behaviors/behavior_leader_key.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)
@ -65,6 +66,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_MOUSE_KEY_PRESS app PRIVATE src/behaviors/behavior_mouse_key_press.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_STUDIO_UNLOCK app PRIVATE src/behaviors/behavior_studio_unlock.c)
target_sources(app PRIVATE src/combo.c)
target_sources_ifdef(CONFIG_ZMK_LEADER app PRIVATE src/leader.c)
target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c)
target_sources(app PRIVATE src/behavior_queue.c)
target_sources(app PRIVATE src/conditional_layer.c)

View file

@ -472,6 +472,19 @@ config ZMK_COMBO_MAX_KEYS_PER_COMBO
#Combo options
endmenu
menu "Leader Options"
config ZMK_LEADER_MAX_KEYS_PER_SEQUENCE
int "Maximum number of key presses in a leader sequence"
default 4
config ZMK_LEADER_MAX_SEQUENCES_PER_KEY
int "Maximum number of leader sequences that a key can belong to"
default 5
#Leader options
endmenu
menu "Behavior Options"
config ZMK_BEHAVIORS_QUEUE_SIZE
@ -488,6 +501,18 @@ config ZMK_MACRO_DEFAULT_TAP_MS
int "Default time to wait (in milliseconds) between the press and release events of a tapped behavior in macros"
default 30
DT_COMPAT_ZMK_LEADER := zmk,leader-sequences
config ZMK_LEADER
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_LEADER))
DT_COMPAT_ZMK_BEHAVIOR_LEADER_KEY := zmk,behavior-leader-key
config ZMK_BEHAVIOR_LEADER_KEY
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_LEADER_KEY))
endmenu
menu "Advanced"

View file

@ -22,3 +22,4 @@
#include <behaviors/mouse_key_press.dtsi>
#include <behaviors/soft_off.dtsi>
#include <behaviors/studio_unlock.dtsi>
#include <behaviors/leader_key.dtsi>

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
/ {
behaviors {
/omit-if-no-ref/ leader: leader_key {
compatible = "zmk,behavior-leader-key";
label = "LEADER";
#binding-cells = <0>;
};
};
};

View file

@ -0,0 +1,15 @@
# Copyright (c) 2022 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Leader key behavior
compatible: "zmk,behavior-leader-key"
include: zero_param.yaml
properties:
timerless:
type: boolean
timeout-ms:
type: int
default: 200

View file

@ -0,0 +1,22 @@
# Copyright (c) 2022, The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Leader sequence container
compatible: "zmk,leader-sequences"
child-binding:
description: "A leader sequence"
properties:
bindings:
type: phandle-array
required: true
key-positions:
type: array
required: true
layers:
type: array
default: [-1]
immediate-trigger:
type: boolean

10
app/include/zmk/leader.h Normal file
View file

@ -0,0 +1,10 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
void zmk_leader_activate(int32_t timeout, bool timeout_on_activation, uint32_t position);
void zmk_leader_deactivate();

View file

@ -23,3 +23,8 @@
* Gets the virtual key position to use for the combo with the given index.
*/
#define ZMK_VIRTUAL_KEY_POSITION_COMBO(index) (ZMK_KEYMAP_LEN + ZMK_KEYMAP_SENSORS_LEN + (index))
/**
* Gets the virtual key position to use for the leader sequence with the given index.
*/
#define ZMK_VIRTUAL_KEY_POSITION_LEADER(index) (ZMK_KEYMAP_LEN + ZMK_KEYMAP_SENSORS_LEN + (index))

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_leader_key
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
#include <zmk/hid.h>
#include <zmk/event_manager.h>
#include <zmk/events/keycode_state_changed.h>
#include <zmk/behavior.h>
#include <zmk/leader.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct behavior_leader_key_config {
int32_t timeout_ms;
bool timerless;
};
static int behavior_leader_key_init(const struct device *dev) { return 0; }
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct behavior_leader_key_config *cfg = dev->config;
zmk_leader_activate(cfg->timeout_ms, cfg->timerless, event.position);
return ZMK_BEHAVIOR_OPAQUE;
}
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
return 0;
}
static const struct behavior_driver_api behavior_leader_key_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
};
#define LEAD_INST(n) \
static struct behavior_leader_key_config behavior_leader_key_config_##n = { \
.timerless = DT_INST_PROP(n, timerless), .timeout_ms = DT_INST_PROP(n, timeout_ms)}; \
BEHAVIOR_DT_INST_DEFINE(n, behavior_leader_key_init, NULL, NULL, \
&behavior_leader_key_config_##n, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_leader_key_driver_api);
DT_INST_FOREACH_STATUS_OKAY(LEAD_INST)

368
app/src/leader.c Normal file
View file

@ -0,0 +1,368 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_leader_sequences
#include <zephyr/device.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/dlist.h>
#include <zephyr/kernel.h>
#include <drivers/behavior.h>
#include <zmk/behavior.h>
#include <zmk/event_manager.h>
#include <zmk/events/position_state_changed.h>
#include <zmk/hid.h>
#include <zmk/matrix.h>
#include <zmk/keymap.h>
#include <zmk/virtual_key_position.h>
#include <zmk/leader.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static bool leader_status;
static int32_t press_count;
static int32_t release_count;
static int32_t timeout_ms;
static int32_t active_leader_position;
static int8_t layer;
static bool first_release;
static struct k_work_delayable release_timer;
static int64_t release_at;
// static bool timer_started;
static bool timer_cancelled;
static bool timerless;
struct leader_seq_cfg {
int32_t key_positions[CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE];
int32_t key_position_len;
bool immediate_trigger;
bool is_pressed;
// the virtual key position is a key position outside the range used by the keyboard.
// it is necessary so hold-taps can uniquely identify a behavior.
int32_t virtual_key_position;
struct zmk_behavior_binding behavior;
int32_t layers_len;
int8_t layers[];
};
// leader_pressed_keys is filled with an event when a key is pressed.
// The keys are removed from this array when they are released.
// Once this array is empty, the behavior is released.
static const struct zmk_position_state_changed
*leader_pressed_keys[CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE] = {NULL};
static uint32_t current_sequence[CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE] = {-1};
// the set of candidate leader based on the currently leader_pressed_keys
static int num_candidates;
static struct leader_seq_cfg *sequence_candidates[CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY];
static int num_comp_candidates;
static struct leader_seq_cfg *completed_sequence_candidates[CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY];
// a lookup dict that maps a key position to all sequences on that position
static struct leader_seq_cfg *sequence_lookup[ZMK_KEYMAP_LEN][CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY] = {
NULL};
// Store the leader key pointer in the leader array, one pointer for each key position
// The leader are sorted shortest-first, then by virtual-key-position.
static int intitialiaze_leader_sequences(struct leader_seq_cfg *seq) {
for (int i = 0; i < seq->key_position_len; i++) {
int32_t position = seq->key_positions[i];
if (position >= ZMK_KEYMAP_LEN) {
LOG_ERR("Unable to initialize leader, key position %d does not exist", position);
return -EINVAL;
}
struct leader_seq_cfg *new_seq = seq;
bool set = false;
for (int j = 0; j < CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY; j++) {
struct leader_seq_cfg *sequence_at_j = sequence_lookup[position][j];
if (sequence_at_j == NULL) {
sequence_lookup[position][j] = new_seq;
set = true;
break;
}
if (sequence_at_j->key_position_len < new_seq->key_position_len ||
(sequence_at_j->key_position_len == new_seq->key_position_len &&
sequence_at_j->virtual_key_position < new_seq->virtual_key_position)) {
continue;
}
// put new_seq in this spot, move all other leader up.
sequence_lookup[position][j] = new_seq;
new_seq = sequence_at_j;
}
if (!set) {
LOG_ERR(
"Too many leader for key position %d, CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY %d.",
position, CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY);
return -ENOMEM;
}
}
return 0;
}
static bool sequence_active_on_layer(struct leader_seq_cfg *sequence) {
if (sequence->layers[0] == -1) {
// -1 in the first layer position is global layer scope
return true;
}
for (int j = 0; j < sequence->layers_len; j++) {
if (sequence->layers[j] == layer) {
return true;
}
}
return false;
}
static bool has_current_sequence(struct leader_seq_cfg *sequence, int count) {
for (int i = 0; i < count; i++) {
if (sequence->key_positions[i] != current_sequence[i]) {
return false;
}
}
return true;
}
static bool is_in_current_sequence(int32_t position) {
for (int i = 0; i < CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE; i++) {
if (position == current_sequence[i]) {
return true;
}
}
return false;
}
static bool is_duplicate(struct leader_seq_cfg *seq) {
for (int i = 0; i < CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE; i++) {
if (sequence_candidates[i] == seq) {
return true;
}
}
return false;
}
static bool release_key_in_sequence(int32_t position) {
for (int i = 0; i < release_count; i++) {
if (leader_pressed_keys[i] && position == leader_pressed_keys[i]->position) {
leader_pressed_keys[i] = NULL;
return true;
}
}
return false;
}
static bool all_keys_released() {
for (int i = 0; i < press_count; i++) {
if (NULL != leader_pressed_keys[i]) {
return false;
}
}
return true;
}
static void clear_candidates() {
for (int i = 0; i < CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY; i++) {
sequence_candidates[i] = NULL;
completed_sequence_candidates[i] = NULL;
}
}
static void leader_find_candidates(int32_t position, int count) {
clear_candidates();
num_candidates = 0;
num_comp_candidates = 0;
for (int i = 0; i < CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY; i++) {
struct leader_seq_cfg *sequence = sequence_lookup[position][i];
if (sequence == NULL) {
continue;
}
if (sequence_active_on_layer(sequence) && sequence->key_positions[count] == position &&
has_current_sequence(sequence, count) && !is_duplicate(sequence)) {
sequence_candidates[num_candidates] = sequence;
num_candidates++;
if (sequence->key_position_len == count + 1) {
completed_sequence_candidates[num_comp_candidates] = sequence;
num_comp_candidates++;
}
}
}
}
const struct zmk_listener zmk_listener_leader;
static inline int press_leader_behavior(struct leader_seq_cfg *sequence, int32_t timestamp) {
struct zmk_behavior_binding_event event = {
.position = sequence->virtual_key_position,
.timestamp = timestamp,
};
sequence->is_pressed = true;
return behavior_keymap_binding_pressed(&sequence->behavior, event);
}
static inline int release_leader_behavior(struct leader_seq_cfg *sequence, int32_t timestamp) {
struct zmk_behavior_binding_event event = {
.position = sequence->virtual_key_position,
.timestamp = timestamp,
};
sequence->is_pressed = false;
return behavior_keymap_binding_released(&sequence->behavior, event);
}
static int stop_timer() {
int timer_cancel_result = k_work_cancel_delayable(&release_timer);
if (timer_cancel_result == -EINPROGRESS) {
// too late to cancel, we'll let the timer handler clear up.
timer_cancelled = true;
}
return timer_cancel_result;
}
static void reset_timer(int32_t timestamp) {
release_at = timestamp + timeout_ms;
int32_t ms_left = release_at - k_uptime_get();
if (ms_left > 0) {
k_work_schedule(&release_timer, K_MSEC(ms_left));
LOG_DBG("Successfully reset leader timer");
}
}
void zmk_leader_activate(int32_t timeout, bool _timerless, uint32_t position) {
LOG_DBG("leader key activated");
leader_status = true;
press_count = 0;
release_count = 0;
timeout_ms = timeout;
active_leader_position = position;
layer = zmk_keymap_highest_layer_active();
first_release = false;
timerless = _timerless;
if (!timerless) {
reset_timer(k_uptime_get());
}
for (int i = 0; i < CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE; i++) {
leader_pressed_keys[i] = NULL;
}
};
void zmk_leader_deactivate() {
LOG_DBG("leader key deactivated");
leader_status = false;
clear_candidates();
};
void behavior_leader_key_timer_handler(struct k_work *item) {
if (!leader_status) {
return;
}
if (timer_cancelled) {
return;
}
LOG_DBG("Leader deactivated due to timeout");
for (int i = 0; i < num_comp_candidates; i++) {
if (!completed_sequence_candidates[i]->is_pressed) {
press_leader_behavior(completed_sequence_candidates[i], k_uptime_get());
release_leader_behavior(completed_sequence_candidates[i], k_uptime_get());
}
}
zmk_leader_deactivate();
}
static int position_state_changed_listener(const zmk_event_t *ev) {
struct zmk_position_state_changed *data = as_zmk_position_state_changed(ev);
if (data == NULL) {
return 0;
}
if (!leader_status && !data->state && !all_keys_released()) {
if (release_key_in_sequence(data->position)) {
return ZMK_EV_EVENT_HANDLED;
}
return 0;
}
if (leader_status) {
if (data->state) { // keydown
leader_find_candidates(data->position, press_count);
LOG_DBG("leader cands: %d comp: %d", num_candidates, num_comp_candidates);
stop_timer();
current_sequence[press_count] = data->position;
leader_pressed_keys[press_count] = data;
press_count++;
for (int i = 0; i < num_comp_candidates; i++) {
struct leader_seq_cfg *seq = completed_sequence_candidates[i];
if (seq->immediate_trigger || (num_candidates == 1 && num_comp_candidates == 1)) {
press_leader_behavior(seq, data->timestamp);
}
}
} else { // keyup
if (data->position == active_leader_position && !first_release) {
first_release = true;
return 0;
}
if (!is_in_current_sequence(data->position)) {
return 0;
}
if (num_candidates == 0) {
zmk_leader_deactivate();
return ZMK_EV_EVENT_HANDLED;
}
release_count++;
release_key_in_sequence(data->position);
for (int i = 0; i < num_comp_candidates; i++) {
struct leader_seq_cfg *seq = completed_sequence_candidates[i];
if (seq->is_pressed && all_keys_released()) {
release_leader_behavior(seq, data->timestamp);
num_comp_candidates--;
}
if (num_candidates == 1 && num_comp_candidates == 0) {
zmk_leader_deactivate();
}
}
if (!timerless || num_comp_candidates < num_candidates) {
reset_timer(data->timestamp);
}
}
return ZMK_EV_EVENT_HANDLED;
}
return 0;
}
ZMK_LISTENER(leader, position_state_changed_listener);
ZMK_SUBSCRIPTION(leader, zmk_position_state_changed);
#define LEADER_INST(n) \
static struct leader_seq_cfg sequence_config_##n = { \
.virtual_key_position = ZMK_VIRTUAL_KEY_POSITION_LEADER(__COUNTER__), \
.immediate_trigger = DT_PROP(n, immediate_trigger), \
.is_pressed = false, \
.key_positions = DT_PROP(n, key_positions), \
.key_position_len = DT_PROP_LEN(n, key_positions), \
.behavior = ZMK_KEYMAP_EXTRACT_BINDING(0, n), \
.layers = DT_PROP(n, layers), \
.layers_len = DT_PROP_LEN(n, layers), \
};
#define INTITIALIAZE_LEADER_SEQUENCES(n) intitialiaze_leader_sequences(&sequence_config_##n);
DT_INST_FOREACH_CHILD(0, LEADER_INST)
static int leader_init(void) {
k_work_init_delayable(&release_timer, behavior_leader_key_timer_handler);
DT_INST_FOREACH_CHILD(0, INTITIALIAZE_LEADER_SEQUENCES);
return 0;
}
SYS_INIT(leader_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
#endif

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,10 @@
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,2000)
>;
};

View file

@ -0,0 +1,41 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
&leader {
timeout-ms = <200>;
};
/ {
leader-sequences {
compatible = "zmk,leader-sequences";
leader_seq_one {
key-positions = <0>;
bindings = <&kp A>;
};
leader_seq_two {
key-positions = <1>;
bindings = <&kp B>;
};
leader_seq_three {
key-positions = <3>;
bindings = <&kp N1>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp N1 &kp N2
&kp N3 &leader
>;
};
};
};

View file

@ -0,0 +1,33 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
leader-sequences {
compatible = "zmk,leader-sequences";
leader_seq_one {
key-positions = <0>;
bindings = <&kp A>;
layers = <2>;
};
leader_seq_two {
key-positions = <1>;
bindings = <&kp B>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp N1 &kp N2
&kp N3 &leader
>;
};
};
};

View file

@ -0,0 +1,41 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
&leader {
timeout-ms = <200>;
};
/ {
leader-sequences {
compatible = "zmk,leader-sequences";
leader_seq_one {
key-positions = <0>;
bindings = <&kp A>;
};
leader_seq_two {
key-positions = <1>;
bindings = <&kp B>;
};
leader_seq_three {
key-positions = <0 1>;
bindings = <&kp C>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp N1 &kp N2
&kp N3 &leader
>;
};
};
};

View file

@ -0,0 +1,42 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
&leader {
timeout-ms = <200>;
};
/ {
leader-sequences {
compatible = "zmk,leader-sequences";
leader_seq_one {
key-positions = <0>;
bindings = <&kp A>;
immediate-trigger;
};
leader_seq_two {
key-positions = <1>;
bindings = <&kp B>;
};
leader_seq_three {
key-positions = <0 1>;
bindings = <&kp C>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp N1 &kp N2
&kp N3 &leader
>;
};
};
};

View file

@ -0,0 +1,37 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
&leader {
timeout-ms = <200>;
timerless;
};
/ {
leader-sequences {
compatible = "zmk,leader-sequences";
leader_seq_one {
key-positions = <0>;
bindings = <&kp A>;
};
leader_seq_two {
key-positions = <0 0>;
bindings = <&kp B>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp N1 &kp N2
&kp N3 &leader
>;
};
};
};

View file

@ -0,0 +1,36 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
&leader {
timeout-ms = <200>;
timerless;
};
/ {
leader-sequences {
compatible = "zmk,leader-sequences";
leader_seq_one {
key-positions = <0 1 2>;
bindings = <&kp A>;
};
leader_seq_two {
key-positions = <3 3>;
bindings = <&kp B>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp N1 &kp N2
&kp N3 &leader
>;
};
};
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x1E implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x1E implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,10 @@
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,12 @@
#include "../behavior_keymap_three.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,2 @@
leader: leader key activated
leader: leader key deactivated

View file

@ -0,0 +1,10 @@
#include "../behavior_keymap_layers.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,2 @@
leader: leader key activated
leader: leader key deactivated

View file

@ -0,0 +1,10 @@
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,6 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,12 @@
#include "../behavior_keymap_overlap_immediate.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,10 @@
#include "../behavior_keymap_overlap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,10 @@
#include "../behavior_keymap_overlap_timerless.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,12 @@
#include "../behavior_keymap_overlap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,12 @@
#include "../behavior_keymap_overlap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(0,0,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,14 @@
#include "../behavior_keymap_three.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,2,10)
ZMK_MOCK_RELEASE(0,2,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,14 @@
#include "../behavior_keymap_three.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_PRESS(0,2,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(0,2,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,14 @@
#include "../behavior_keymap_three.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_PRESS(0,2,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(0,2,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,4 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,12 @@
#include "../behavior_keymap_overlap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,2 @@
leader: leader key activated
leader: leader key deactivated

View file

@ -0,0 +1,8 @@
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,2000)
>;
};

View file

@ -0,0 +1,2 @@
s/.*hid_listener_keycode_//p
s/.*zmk_leader.*:/leader:/p

View file

@ -0,0 +1,8 @@
leader: leader key activated
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated
leader: leader key activated
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
leader: leader key deactivated

View file

@ -0,0 +1,14 @@
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,2000)
>;
};

View file

@ -0,0 +1,42 @@
---
title: Leader Key Behavior
sidebar_label: Leader Key
---
## Summary
The leader key behavior when triggered will capture all following key presses and trigger a [Leader Sequence](../features/leader.md) if pressed.
### Behavior Binding
- Reference: `&leader`
Example:
```
&leader
```
### Configuration
#### `timeout-ms`
Defines the amount of time to wait to trigger a completed leader sequence. Defaults to 200ms.
To change the timeout term, you can update the existing behavior:
```
&leader {
timeout-ms = <500>;
};
/ {
keymap {
...
};
};
```
#### `timerless`
By default, the leader key will have a timeout, and will not wait for a sequence to be completed or another key to be pressed. Specify `timerless` if you want a timeout.

View file

@ -0,0 +1,40 @@
---
title: Leader Configuration
sidebar_label: Leader
---
See the [Leader feature page](../features/leader.md) for more details and examples.
See [Configuration Overview](index.md) for instructions on how to change these settings.
## Kconfig
Definition file: [zmk/app/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/Kconfig)
| Config | Type | Description | Default |
| ----------------------------------------- | ---- | ----------------------------------------------------------------- | ------- |
| `CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE` | int | Maximum number of leader sequences that use the same key position | 4 |
| `CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY` | int | Maximum number of keys to press to activate a leader sequence | 5 |
`CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE` sets the maximum length of a leader sequence.
If `CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY` is 5, you can have 5 separate leader sequences that start with position `0`, 5 leader sequences that start with position `1`, and so on.
## Devicetree
Applies to: `compatible = "zmk,leader-sequences"`
Definition file: [zmk/app/dts/bindings/zmk,leader-sequences.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/zmk%2Cleader-sequences.yaml)
The `zmk,leader-sequences` node itself has no properties. It should have one child node per leader sequence.
Each child node can have the following properties:
| Property | Type | Description | Default |
| ------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `bindings` | phandle-array | A [behavior](../features/keymaps.md#behaviors) to run when the leader sequence is triggered | |
| `key-positions` | array | A list of key position indices for the keys which should trigger the leader sequence | |
| `immediate-trigger` | bool | Triggers the leader sequence when all keys are pressed instead of waiting for the timeout (only applicable when one leader sequence overlaps another) | false |
| `layers` | array | A list of layers on which the leader sequence may be triggered. `-1` allows all layers. | `<-1>` |
The `key-positions` array must not be longer than the `CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE` setting, which defaults to 4. If you want a leader sequence that triggers when pressing 5 keys, then you must change the setting to 5.

View file

@ -0,0 +1,39 @@
---
title: Leader Sequences
---
## Summary
Leader sequences are a way to have a sequence of key presses to output a different key. For example, you can hit the leader key followed by Q, then W to output escape.
### Configuration
Leader sequences configured in your `.keymap` file, but are separate from the `keymap` node found there, since they are processed before the normal keymap. They are specified like this:
```
/ {
leader_sequences {
compatible = "zmk,leader-sequences";
seq_esc {
key-positions = <0 1>;
bindings = <&kp ESC>;
};
};
};
```
- The `compatible` property should always be `"zmk,leader-sequences"` for leader sequences.
- `key-positions` is an array of key positions. See the info section below about how to figure out the positions on your board.
- `layers = <0 1...>` will allow limiting a sequence to specific layers. This is an _optional_ parameter, when omitted it defaults to global scope.
- `bindings` is the behavior that is activated when all the key positions are pressed.
- (advanced) you can specify `immediate-trigger` if you want the sequence to be triggered as soon as all key positions are pressed. The default is to wait for the leader key's timeout to trigger the sequence if it overlaps another.
:::info
Key positions are numbered like the keys in your keymap, starting at 0. So, if the first key in your keymap is `Q`, this key is in position `0`. The next key (possibly `W`) will have position 1, etcetera.
:::
### Advanced usage
See [leader configuration](/docs/config/leader) for advanced configuration options.

View file

@ -33,6 +33,7 @@ ZMK is currently missing some features found in other popular firmware. This tab
| One Shot Keys | ✅ | ✅ | ✅ |
| [Combo Keys](features/combos.md) | ✅ | | ✅ |
| [Macros](behaviors/macros.md) | ✅ | ✅ | ✅ |
| [Leader Key](behaviors/leader-key.md) | ✅ | | ✅ |
| Mouse Keys | 🚧 | ✅ | ✅ |
| Low Active Power Usage | ✅ | | |
| Low Power Sleep States | ✅ | ✅ | |

View file

@ -35,6 +35,7 @@ module.exports = {
"features/bluetooth",
"features/split-keyboards",
"features/combos",
"features/leader",
"features/conditional-layers",
"features/debouncing",
"features/displays",
@ -62,6 +63,7 @@ module.exports = {
"behaviors/mod-tap",
"behaviors/mod-morph",
"behaviors/macros",
"behaviors/leader-key",
"behaviors/key-toggle",
"behaviors/sticky-key",
"behaviors/sticky-layer",