feat(leader-key): refactor for child nodes, multiple instances

This commit is contained in:
Nick Conway 2025-06-15 22:36:55 -04:00
parent 535be545a6
commit e96904956e
No known key found for this signature in database
GPG Key ID: AA850592E4C1D453
13 changed files with 328 additions and 475 deletions

View File

@ -54,7 +54,6 @@ 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/leader.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)

View File

@ -138,4 +138,4 @@ config ZMK_BEHAVIOR_MACRO
config ZMK_BEHAVIOR_LEADER_KEY
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_LEADER_KEY_ENABLED || DT_HAS_ZMK_BEHAVIOR_LEADER_SEQUENCES_ENABLED
depends on DT_HAS_ZMK_BEHAVIOR_LEADER_KEY_ENABLED

View File

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

View File

@ -1,15 +0,0 @@
/*
* 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

@ -11,3 +11,16 @@ properties:
timeout-ms:
type: int
default: -1
child-binding:
description: "A leader sequence"
properties:
bindings:
type: phandle-array
required: true
key-positions:
type: array
required: true
immediate-trigger:
type: boolean

View File

@ -1,22 +0,0 @@
# 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

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The ZMK Contributors
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
@ -13,29 +13,314 @@
#include <zmk/hid.h>
#include <zmk/event_manager.h>
#include <zmk/events/keycode_state_changed.h>
#include <zmk/events/position_state_changed.h>
#include <zmk/keymap.h>
#include <zmk/behavior.h>
#include <zmk/leader.h>
#include <zmk/matrix.h>
#include <zmk/virtual_key_position.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct behavior_leader_key_config {
int32_t timeout_ms;
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 bool first_release;
static struct k_work_delayable release_timer;
static int64_t release_at;
// static bool timer_started;
static bool timer_cancelled;
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
static uint8_t source;
#endif
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;
};
static int behavior_leader_key_init(const struct device *dev) { return 0; }
struct behavior_leader_key_config {
int32_t timeout_ms;
struct leader_seq_cfg *sequences;
size_t sequences_len;
};
// 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];
const static struct behavior_leader_key_config *active_leader_cfg;
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 (leader_pressed_keys[i] != NULL) {
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 < active_leader_cfg->sequences_len; i++) {
struct leader_seq_cfg *sequence = &(active_leader_cfg->sequences[i]);
if (sequence == NULL) {
continue;
}
if (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,
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = source,
#endif
.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,
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = source,
#endif
.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");
}
}
static void activate_leader_key(const struct behavior_leader_key_config *cfg, uint32_t position) {
LOG_DBG("leader key activated");
leader_status = true;
press_count = 0;
release_count = 0;
timeout_ms = cfg->timeout_ms;
active_leader_position = position;
first_release = false;
active_leader_cfg = cfg;
if (timeout_ms > 0) {
reset_timer(k_uptime_get());
}
for (int i = 0; i < CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE; i++) {
leader_pressed_keys[i] = NULL;
}
};
static void zmk_leader_deactivate() {
LOG_DBG("leader key deactivated");
leader_status = false;
clear_candidates();
};
static 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++) {
LOG_DBG("leader i is %d", 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 (timeout_ms > 0 || num_comp_candidates < num_candidates) {
reset_timer(data->timestamp);
}
}
return ZMK_EV_EVENT_HANDLED;
}
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, event.position);
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
source = event.source;
#endif
activate_leader_key(cfg, 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;
return ZMK_BEHAVIOR_OPAQUE;
}
static const struct behavior_driver_api behavior_leader_key_driver_api = {
@ -43,9 +328,30 @@ static const struct behavior_driver_api behavior_leader_key_driver_api = {
.binding_released = on_keymap_binding_released,
};
static int behavior_leader_key_init(const struct device *dev) {
k_work_init_delayable(&release_timer, behavior_leader_key_timer_handler);
return 0;
}
ZMK_LISTENER(leader, position_state_changed_listener);
ZMK_SUBSCRIPTION(leader, zmk_position_state_changed);
#define SEQUENCE_ITEM(i, n, prop) DT_PROP_BY_IDX(n, prop, i)
#define PROP_SEQUENCES(n, prop) \
{ \
.virtual_key_position = ZMK_VIRTUAL_KEY_POSITION_LEADER(__COUNTER__), \
.is_pressed = false, \
.key_position_len = DT_PROP_LEN(n, prop), \
.key_positions = {LISTIFY(DT_PROP_LEN(n, prop), SEQUENCE_ITEM, (, ), n, prop)}, \
.behavior = ZMK_KEYMAP_EXTRACT_BINDING(0, n), \
}
#define LEAD_INST(n) \
static struct leader_seq_cfg leader_sequences_##n[] = { \
DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(n, PROP_SEQUENCES, (, ), key_positions)}; \
static struct behavior_leader_key_config behavior_leader_key_config_##n = { \
.timeout_ms = DT_INST_PROP(n, timeout_ms)}; \
.sequences = leader_sequences_##n, .sequences_len = ARRAY_SIZE(leader_sequences_##n)}; \
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);

View File

@ -1,370 +0,0 @@
/*
* 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;
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, 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;
if (timeout_ms > 0) {
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 (timeout_ms > 0 || 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

@ -1,33 +0,0 @@
#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

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

View File

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

View File

@ -1,10 +0,0 @@
#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)
>;
};