diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 4b61fc72174..935bed301b4 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -44,6 +44,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(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) @@ -54,6 +55,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_none.c) target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c) target_sources(app PRIVATE src/combo.c) + target_sources_ifdef(CONFIG_ZMK_LEADER app PRIVATE src/leader.c) target_sources(app PRIVATE src/behavior_queue.c) target_sources(app PRIVATE src/conditional_layer.c) target_sources(app PRIVATE src/endpoints.c) diff --git a/app/Kconfig b/app/Kconfig index f89d3279d1c..02a729a5eee 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -331,6 +331,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 @@ -343,6 +356,18 @@ config ZMK_BEHAVIOR_KEY_TOGGLE bool default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_KEY_TOGGLE)) +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" diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index b3502cbbc13..c133959e313 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -18,4 +18,5 @@ #include #include #include -#include \ No newline at end of file +#include +#include diff --git a/app/dts/behaviors/leader_key.dtsi b/app/dts/behaviors/leader_key.dtsi new file mode 100644 index 00000000000..3a1beaae3da --- /dev/null +++ b/app/dts/behaviors/leader_key.dtsi @@ -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>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-leader-key.yaml b/app/dts/bindings/behaviors/zmk,behavior-leader-key.yaml new file mode 100644 index 00000000000..9647044d42d --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-leader-key.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Leader key behavior + +compatible: "zmk,behavior-leader-key" + +include: zero_param.yaml + +properties: + timeout-ms: + type: int + default: -1 diff --git a/app/dts/bindings/zmk,leader-sequences.yaml b/app/dts/bindings/zmk,leader-sequences.yaml new file mode 100644 index 00000000000..f26a8b90b61 --- /dev/null +++ b/app/dts/bindings/zmk,leader-sequences.yaml @@ -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 diff --git a/app/include/zmk/leader.h b/app/include/zmk/leader.h new file mode 100644 index 00000000000..191dc7f7804 --- /dev/null +++ b/app/include/zmk/leader.h @@ -0,0 +1,11 @@ +/* + * 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(); +bool zmk_leader_get_status(); diff --git a/app/src/behaviors/behavior_leader_key.c b/app/src/behaviors/behavior_leader_key.c new file mode 100644 index 00000000000..e2970bdd0ff --- /dev/null +++ b/app/src/behaviors/behavior_leader_key.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_leader_key + +#include +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +struct behavior_leader_key_config { + int32_t timeout_ms; +}; + +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, 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 = { \ + .timeout_ms = DT_INST_PROP(n, timeout_ms)}; \ + DEVICE_DT_INST_DEFINE(n, behavior_leader_key_init, NULL, NULL, \ + &behavior_leader_key_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_leader_key_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(LEAD_INST) diff --git a/app/src/leader.c b/app/src/leader.c new file mode 100644 index 00000000000..d849170faca --- /dev/null +++ b/app/src/leader.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_leader_sequences + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +bool leader_status; +int32_t count; +int32_t timeout_ms; +int32_t active_leader_position; +struct k_work_delayable release_timer; +int64_t release_at; +bool timer_started; +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[]; +}; + +// set of keys pressed +uint32_t current_sequence[CONFIG_ZMK_LEADER_MAX_KEYS_PER_SEQUENCE] = {-1}; +// the set of candidate leader based on the currently pressed_keys +int num_candidates; +struct leader_seq_cfg *sequence_candidates[CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY]; +int num_comp_candidates; +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 +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, uint8_t layer) { + 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) { + for (int i = 0; i < count; i++) { + if (sequence->key_positions[i] != current_sequence[i]) { + return false; + } + } + return true; +} + +static int clear_candidates() { + for (int i = 0; i < CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY; i++) { + sequence_candidates[i] = NULL; + completed_sequence_candidates[i] = NULL; + } + return CONFIG_ZMK_LEADER_MAX_SEQUENCES_PER_KEY; +} + +static void leader_find_candidates(int32_t position) { + clear_candidates(); + num_candidates = 0; + num_comp_candidates = 0; + int number_of_leader_seq_candidates = 0; + uint8_t highest_active_layer = zmk_keymap_highest_layer_active(); + 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) { + return number_of_leader_seq_candidates; + } + if (sequence_active_on_layer(sequence, highest_active_layer) && + sequence->key_positions[count] == position && has_current_sequence(sequence)) { + sequence_candidates[number_of_leader_seq_candidates] = sequence; + num_candidates++; + if (sequence->key_position_len == count + 1) { + completed_sequence_candidates[number_of_leader_seq_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; + count = 0; + timeout_ms = timeout; + active_leader_position = position; + reset_timer(k_uptime_get()); +}; + +void zmk_leader_deactivate() { + LOG_DBG("leader key deactivated"); + leader_status = false; + clear_candidates(); +}; + +void behavior_leader_key_timer_handler(struct k_work *item) { + LOG_DBG("Leader timeout has been reached"); + if (!leader_status) { + return; + } + if (timer_cancelled) { + return; + } + LOG_DBG("Leader deactivated due to timeout"); + for (int i = 0; i < num_comp_candidates; i++) { + LOG_DBG("LEADER PRESSING DUE TO TIMEOUT"); + 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->position != active_leader_position) { + leader_find_candidates(data->position); + + if (num_candidates == 0) { + zmk_leader_deactivate(); + return 0; + } + + if (data->state) { // keydown + stop_timer(); + current_sequence[count] = data->position; + for (int i = 0; i < num_comp_candidates; i++) { + if (completed_sequence_candidates[i]->immediate_trigger || + (num_candidates == 1 && num_comp_candidates == 1)) { + LOG_DBG("LEADER PRESSING DUE TO CANDIDATES"); + press_leader_behavior(completed_sequence_candidates[i], data->timestamp); + } + } + } else { // keyup + for (int i = 0; i < num_comp_candidates; i++) { + if (completed_sequence_candidates[i]->is_pressed) { + release_leader_behavior(completed_sequence_candidates[i], data->timestamp); + } + } + if (num_candidates == 1) { + zmk_leader_deactivate(); + } + count++; + 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_KEYMAP_LEN + __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), \ + }; + +DT_INST_FOREACH_CHILD(0, LEADER_INST) + +#define INTITIALIAZE_LEADER_SEQUENCES(n) intitialiaze_leader_sequences(&sequence_config_##n); + +static int leader_init() { + 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); diff --git a/app/tests/leader/basic/events.patterns b/app/tests/leader/basic/events.patterns new file mode 100644 index 00000000000..e73813454e0 --- /dev/null +++ b/app/tests/leader/basic/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*zmk_leader.*:/leader:/p diff --git a/app/tests/leader/basic/keycode_events.snapshot b/app/tests/leader/basic/keycode_events.snapshot new file mode 100644 index 00000000000..9ab32b1a876 --- /dev/null +++ b/app/tests/leader/basic/keycode_events.snapshot @@ -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 diff --git a/app/tests/leader/basic/native_posix_64.keymap b/app/tests/leader/basic/native_posix_64.keymap new file mode 100644 index 00000000000..adab64b845a --- /dev/null +++ b/app/tests/leader/basic/native_posix_64.keymap @@ -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) + >; +}; diff --git a/app/tests/leader/behavior_keymap.dtsi b/app/tests/leader/behavior_keymap.dtsi new file mode 100644 index 00000000000..54a8b84e33d --- /dev/null +++ b/app/tests/leader/behavior_keymap.dtsi @@ -0,0 +1,36 @@ +#include +#include +#include + +&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>; + }; + + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp N1 &kp N2 + &kp N3 &leader + >; + }; + }; +}; diff --git a/app/tests/leader/behavior_keymap_layers.dtsi b/app/tests/leader/behavior_keymap_layers.dtsi new file mode 100644 index 00000000000..3fd3b370c0e --- /dev/null +++ b/app/tests/leader/behavior_keymap_layers.dtsi @@ -0,0 +1,33 @@ +#include +#include +#include + +/ { + 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 + >; + }; + }; +}; diff --git a/app/tests/leader/behavior_keymap_overlap.dtsi b/app/tests/leader/behavior_keymap_overlap.dtsi new file mode 100644 index 00000000000..8263a66b829 --- /dev/null +++ b/app/tests/leader/behavior_keymap_overlap.dtsi @@ -0,0 +1,41 @@ +#include +#include +#include + +&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 + >; + }; + }; +}; diff --git a/app/tests/leader/behavior_keymap_overlap_immediate.dtsi b/app/tests/leader/behavior_keymap_overlap_immediate.dtsi new file mode 100644 index 00000000000..a90208d8e4c --- /dev/null +++ b/app/tests/leader/behavior_keymap_overlap_immediate.dtsi @@ -0,0 +1,42 @@ +#include +#include +#include + +&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 + >; + }; + }; +}; diff --git a/app/tests/leader/none-layer/events.patterns b/app/tests/leader/none-layer/events.patterns new file mode 100644 index 00000000000..e73813454e0 --- /dev/null +++ b/app/tests/leader/none-layer/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*zmk_leader.*:/leader:/p diff --git a/app/tests/leader/none-layer/keycode_events.snapshot b/app/tests/leader/none-layer/keycode_events.snapshot new file mode 100644 index 00000000000..51f75fceaf6 --- /dev/null +++ b/app/tests/leader/none-layer/keycode_events.snapshot @@ -0,0 +1,4 @@ +leader: leader key activated +leader: leader key deactivated +pressed: usage_page 0x07 keycode 0x1E implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1E implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/leader/none-layer/native_posix_64.keymap b/app/tests/leader/none-layer/native_posix_64.keymap new file mode 100644 index 00000000000..0c8f350e4d3 --- /dev/null +++ b/app/tests/leader/none-layer/native_posix_64.keymap @@ -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) + >; +}; diff --git a/app/tests/leader/none/events.patterns b/app/tests/leader/none/events.patterns new file mode 100644 index 00000000000..e73813454e0 --- /dev/null +++ b/app/tests/leader/none/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*zmk_leader.*:/leader:/p diff --git a/app/tests/leader/none/keycode_events.snapshot b/app/tests/leader/none/keycode_events.snapshot new file mode 100644 index 00000000000..9689ffd284f --- /dev/null +++ b/app/tests/leader/none/keycode_events.snapshot @@ -0,0 +1,4 @@ +leader: leader key activated +leader: leader key deactivated +pressed: usage_page 0x07 keycode 0x20 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x20 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/leader/none/native_posix_64.keymap b/app/tests/leader/none/native_posix_64.keymap new file mode 100644 index 00000000000..95a659d7295 --- /dev/null +++ b/app/tests/leader/none/native_posix_64.keymap @@ -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) + >; +}; diff --git a/app/tests/leader/overlapping-immediate/events.patterns b/app/tests/leader/overlapping-immediate/events.patterns new file mode 100644 index 00000000000..e73813454e0 --- /dev/null +++ b/app/tests/leader/overlapping-immediate/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*zmk_leader.*:/leader:/p diff --git a/app/tests/leader/overlapping-immediate/keycode_events.snapshot b/app/tests/leader/overlapping-immediate/keycode_events.snapshot new file mode 100644 index 00000000000..73f87e7c320 --- /dev/null +++ b/app/tests/leader/overlapping-immediate/keycode_events.snapshot @@ -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 diff --git a/app/tests/leader/overlapping-immediate/native_posix_64.keymap b/app/tests/leader/overlapping-immediate/native_posix_64.keymap new file mode 100644 index 00000000000..f71551a80d8 --- /dev/null +++ b/app/tests/leader/overlapping-immediate/native_posix_64.keymap @@ -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) + >; +}; diff --git a/app/tests/leader/overlapping-timeout/events.patterns b/app/tests/leader/overlapping-timeout/events.patterns new file mode 100644 index 00000000000..e73813454e0 --- /dev/null +++ b/app/tests/leader/overlapping-timeout/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*zmk_leader.*:/leader:/p diff --git a/app/tests/leader/overlapping-timeout/keycode_events.snapshot b/app/tests/leader/overlapping-timeout/keycode_events.snapshot new file mode 100644 index 00000000000..9ab32b1a876 --- /dev/null +++ b/app/tests/leader/overlapping-timeout/keycode_events.snapshot @@ -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 diff --git a/app/tests/leader/overlapping-timeout/native_posix_64.keymap b/app/tests/leader/overlapping-timeout/native_posix_64.keymap new file mode 100644 index 00000000000..6c2788afe9d --- /dev/null +++ b/app/tests/leader/overlapping-timeout/native_posix_64.keymap @@ -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) + >; +}; diff --git a/app/tests/leader/overlapping/events.patterns b/app/tests/leader/overlapping/events.patterns new file mode 100644 index 00000000000..e73813454e0 --- /dev/null +++ b/app/tests/leader/overlapping/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*zmk_leader.*:/leader:/p diff --git a/app/tests/leader/overlapping/keycode_events.snapshot b/app/tests/leader/overlapping/keycode_events.snapshot new file mode 100644 index 00000000000..f73a0180d59 --- /dev/null +++ b/app/tests/leader/overlapping/keycode_events.snapshot @@ -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 diff --git a/app/tests/leader/overlapping/native_posix_64.keymap b/app/tests/leader/overlapping/native_posix_64.keymap new file mode 100644 index 00000000000..56f318f1f58 --- /dev/null +++ b/app/tests/leader/overlapping/native_posix_64.keymap @@ -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) + >; +}; diff --git a/app/tests/leader/timeout/events.patterns b/app/tests/leader/timeout/events.patterns new file mode 100644 index 00000000000..e73813454e0 --- /dev/null +++ b/app/tests/leader/timeout/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*zmk_leader.*:/leader:/p diff --git a/app/tests/leader/timeout/keycode_events.snapshot b/app/tests/leader/timeout/keycode_events.snapshot new file mode 100644 index 00000000000..c476ce63ba4 --- /dev/null +++ b/app/tests/leader/timeout/keycode_events.snapshot @@ -0,0 +1,2 @@ +leader: leader key activated +leader: leader key deactivated diff --git a/app/tests/leader/timeout/native_posix_64.keymap b/app/tests/leader/timeout/native_posix_64.keymap new file mode 100644 index 00000000000..4a2eea9ae2c --- /dev/null +++ b/app/tests/leader/timeout/native_posix_64.keymap @@ -0,0 +1,8 @@ +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(1,1,2000) + >; +}; diff --git a/app/tests/leader/two-seq/events.patterns b/app/tests/leader/two-seq/events.patterns new file mode 100644 index 00000000000..e73813454e0 --- /dev/null +++ b/app/tests/leader/two-seq/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*zmk_leader.*:/leader:/p diff --git a/app/tests/leader/two-seq/keycode_events.snapshot b/app/tests/leader/two-seq/keycode_events.snapshot new file mode 100644 index 00000000000..4e5ad45caa9 --- /dev/null +++ b/app/tests/leader/two-seq/keycode_events.snapshot @@ -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 diff --git a/app/tests/leader/two-seq/native_posix_64.keymap b/app/tests/leader/two-seq/native_posix_64.keymap new file mode 100644 index 00000000000..42264f335d0 --- /dev/null +++ b/app/tests/leader/two-seq/native_posix_64.keymap @@ -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) + >; +};