Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(behaviors): Leader key #1380

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
25 changes: 25 additions & 0 deletions app/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand Down
1 change: 1 addition & 0 deletions app/dts/behaviors.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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>
15 changes: 15 additions & 0 deletions app/dts/behaviors/leader_key.dtsi
Original file line number Diff line number Diff line change
@@ -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>;
};
};
};
15 changes: 15 additions & 0 deletions app/dts/bindings/behaviors/zmk,behavior-leader-key.yaml
Original file line number Diff line number Diff line change
@@ -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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs documenting as well. Also see the other reviewers question.

type: int
default: 200
22 changes: 22 additions & 0 deletions app/dts/bindings/zmk,leader-sequences.yaml
Original file line number Diff line number Diff line change
@@ -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 changes: 10 additions & 0 deletions app/include/zmk/leader.h
Original file line number Diff line number Diff line change
@@ -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();
5 changes: 5 additions & 0 deletions app/include/zmk/virtual_key_position.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
54 changes: 54 additions & 0 deletions app/src/behaviors/behavior_leader_key.c
Original file line number Diff line number Diff line change
@@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you press this again while it's active, should the behavior be to deactivate to interrupt it?

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)
Loading
Loading