From d4e656b4a27a3a50adcddafcc01dbe17ae5ad57a Mon Sep 17 00:00:00 2001 From: down Date: Sun, 4 Aug 2024 17:35:05 +0700 Subject: [PATCH] feat(behaviors): add non-overlap behavior --- app/CMakeLists.txt | 1 + app/dts/behaviors.dtsi | 1 + app/dts/behaviors/non_overlap.dtsi | 18 ++ .../behaviors/zmk,behavior-non-overlap.yaml | 17 ++ app/src/behaviors/behavior_non_overlap.c | 278 ++++++++++++++++++ app/tests/non-overlap/basic/events.patterns | 1 + .../non-overlap/basic/keycode_events.snapshot | 6 + .../non-overlap/basic/native_posix_64.keymap | 19 ++ app/tests/non-overlap/behavior_keymap.dtsi | 21 ++ .../keep-active-size/events.patterns | 1 + .../keep-active-size/keycode_events.snapshot | 10 + .../keep-active-size/native_posix_64.keymap | 37 +++ .../non-overlap/linked-list/events.patterns | 1 + .../linked-list/keycode_events.snapshot | 48 +++ .../linked-list/native_posix_64.keymap | 65 ++++ .../non-overlap/no-active/events.patterns | 1 + .../no-active/keycode_events.snapshot | 4 + .../no-active/native_posix_64.keymap | 25 ++ .../release-backward/events.patterns | 1 + .../release-backward/keycode_events.snapshot | 14 + .../release-backward/native_posix_64.keymap | 37 +++ .../release-random/events.patterns | 1 + .../release-random/keycode_events.snapshot | 230 +++++++++++++++ .../release-random/native_posix_64.keymap | 236 +++++++++++++++ app/tests/non-overlap/release/events.patterns | 1 + .../release/keycode_events.snapshot | 4 + .../release/native_posix_64.keymap | 20 ++ app/tests/non-overlap/tap/events.patterns | 1 + .../non-overlap/tap/keycode_events.snapshot | 2 + .../non-overlap/tap/native_posix_64.keymap | 14 + app/tests/non-overlap/wasd/events.patterns | 1 + .../non-overlap/wasd/keycode_events.snapshot | 8 + .../non-overlap/wasd/native_posix_64.keymap | 31 ++ docs/docs/behaviors/index.mdx | 1 + docs/docs/behaviors/non-overlap.mdx | 101 +++++++ docs/sidebars.js | 1 + 36 files changed, 1258 insertions(+) create mode 100644 app/dts/behaviors/non_overlap.dtsi create mode 100644 app/dts/bindings/behaviors/zmk,behavior-non-overlap.yaml create mode 100644 app/src/behaviors/behavior_non_overlap.c create mode 100644 app/tests/non-overlap/basic/events.patterns create mode 100644 app/tests/non-overlap/basic/keycode_events.snapshot create mode 100644 app/tests/non-overlap/basic/native_posix_64.keymap create mode 100644 app/tests/non-overlap/behavior_keymap.dtsi create mode 100644 app/tests/non-overlap/keep-active-size/events.patterns create mode 100644 app/tests/non-overlap/keep-active-size/keycode_events.snapshot create mode 100644 app/tests/non-overlap/keep-active-size/native_posix_64.keymap create mode 100644 app/tests/non-overlap/linked-list/events.patterns create mode 100644 app/tests/non-overlap/linked-list/keycode_events.snapshot create mode 100644 app/tests/non-overlap/linked-list/native_posix_64.keymap create mode 100644 app/tests/non-overlap/no-active/events.patterns create mode 100644 app/tests/non-overlap/no-active/keycode_events.snapshot create mode 100644 app/tests/non-overlap/no-active/native_posix_64.keymap create mode 100644 app/tests/non-overlap/release-backward/events.patterns create mode 100644 app/tests/non-overlap/release-backward/keycode_events.snapshot create mode 100644 app/tests/non-overlap/release-backward/native_posix_64.keymap create mode 100644 app/tests/non-overlap/release-random/events.patterns create mode 100644 app/tests/non-overlap/release-random/keycode_events.snapshot create mode 100644 app/tests/non-overlap/release-random/native_posix_64.keymap create mode 100644 app/tests/non-overlap/release/events.patterns create mode 100644 app/tests/non-overlap/release/keycode_events.snapshot create mode 100644 app/tests/non-overlap/release/native_posix_64.keymap create mode 100644 app/tests/non-overlap/tap/events.patterns create mode 100644 app/tests/non-overlap/tap/keycode_events.snapshot create mode 100644 app/tests/non-overlap/tap/native_posix_64.keymap create mode 100644 app/tests/non-overlap/wasd/events.patterns create mode 100644 app/tests/non-overlap/wasd/keycode_events.snapshot create mode 100644 app/tests/non-overlap/wasd/native_posix_64.keymap create mode 100644 docs/docs/behaviors/non-overlap.mdx diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index ab2e1502ac4..d9662ca7a1f 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -59,6 +59,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_to_layer.c) target_sources(app PRIVATE src/behaviors/behavior_transparent.c) target_sources(app PRIVATE src/behaviors/behavior_none.c) + target_sources(app PRIVATE src/behaviors/behavior_non_overlap.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE app PRIVATE src/behaviors/behavior_sensor_rotate.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR app PRIVATE src/behaviors/behavior_sensor_rotate_var.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON app PRIVATE src/behaviors/behavior_sensor_rotate_common.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index fde75271891..9eb5618d25c 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -21,3 +21,4 @@ #include #include #include +#include diff --git a/app/dts/behaviors/non_overlap.dtsi b/app/dts/behaviors/non_overlap.dtsi new file mode 100644 index 00000000000..733cfd67b58 --- /dev/null +++ b/app/dts/behaviors/non_overlap.dtsi @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + behaviors { + /omit-if-no-ref/ nkp: non_overlap_key_press { + compatible = "zmk,behavior-non-overlap"; + #binding-cells = <1>; + bindings = <&kp>; + display-name = "Non-overlap"; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-non-overlap.yaml b/app/dts/bindings/behaviors/zmk,behavior-non-overlap.yaml new file mode 100644 index 00000000000..b6255c23def --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-non-overlap.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Non-overlap behavior + +compatible: "zmk,behavior-non-overlap" + +include: one_param.yaml + +properties: + bindings: + type: phandles + required: true + keep-active-size: + type: int + no-active: + type: boolean diff --git a/app/src/behaviors/behavior_non_overlap.c b/app/src/behaviors/behavior_non_overlap.c new file mode 100644 index 00000000000..1694fbcbe14 --- /dev/null +++ b/app/src/behaviors/behavior_non_overlap.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_non_overlap + +#include +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +struct active_non_overlap { + struct active_non_overlap *previous; + struct active_non_overlap *next; + bool is_pressed; + uint32_t position; + struct zmk_behavior_binding binding; +}; + +struct behavior_non_overlap_config { + const char *behavior_dev; +}; + +struct behavior_non_overlap_data { + struct active_non_overlap *head; + struct active_non_overlap *tail; + struct active_non_overlap *actives; + const size_t keep_active_size; +}; + +/* + * Non-overlap key presses are kept in a static array of `struct active_non_overlap`. + * The array size defaults to 10 if `keep-active-size` is not specified. + * The array size limits the number of key presses that non-overlap behavior can remember. + * Each instance of non-overlap behavior has its own array. + * + * Non-overlap behavior must preserve the order of key presses. + * Linked list is implemented to allow efficient queue operations. + * When the number of key presses reaches its limit, + * previous key presses will be deleted to accommodate new key presses in a FIFO manner. + */ + +static inline struct active_non_overlap *find_empty_slot(struct behavior_non_overlap_data *data) { + const size_t keep_active_size = data->keep_active_size; + struct active_non_overlap *actives = data->actives; + + for (int i = 0; i < keep_active_size; i++) { + struct active_non_overlap *active = &actives[i]; + if (!active->is_pressed) { + return active; + } + } + + return NULL; +} + +static inline bool matches_params(const struct zmk_behavior_binding *this, + const struct zmk_behavior_binding *that) { + return this->param1 == that->param1 && this->param2 == that->param2; +} + +static inline bool matches_active(struct zmk_behavior_binding *binding, uint32_t position, + const struct active_non_overlap *active) { + return position == active->position && matches_params(binding, &active->binding); +} + +static inline struct active_non_overlap *find_active(struct zmk_behavior_binding *binding, + uint32_t position, + struct active_non_overlap *tail) { + for (struct active_non_overlap *active = tail; active != NULL; active = active->previous) { + if (matches_active(binding, position, active)) { + return active; + } + } + + return NULL; +} + +static inline void release_binding(struct active_non_overlap *active, int64_t timestamp) { + struct zmk_behavior_binding_event event = { + .position = active->position, + .timestamp = timestamp, + }; + + behavior_keymap_binding_released(&active->binding, event); +} + +static inline void press_binding(struct active_non_overlap *active, int64_t timestamp) { + struct zmk_behavior_binding_event event = { + .position = active->position, + .timestamp = timestamp, + }; + + behavior_keymap_binding_pressed(&active->binding, event); +} + +static int behavior_non_overlap_init(const struct device *dev) { return 0; }; + +static int on_non_overlap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position = %d, param1 = 0x%02X.", event.position, binding->param1); + + const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev); + struct behavior_non_overlap_data *data = dev->data; + struct active_non_overlap *least_recent_active = data->head; + struct active_non_overlap *most_recent_active = data->tail; + struct active_non_overlap *new_active; + + if (most_recent_active == NULL) { + LOG_DBG("First active."); + new_active = data->actives; + + data->head = new_active; + } else { + LOG_DBG("New active. Release the previous active."); + release_binding(most_recent_active, event.timestamp); + + new_active = find_empty_slot(data); + if (new_active == NULL) { + new_active = least_recent_active; + most_recent_active->next = least_recent_active; + + data->head = least_recent_active->next; + data->head->previous = NULL; + } + + most_recent_active->next = new_active; + } + + if (new_active == NULL) { + LOG_ERR("New active is not available for some reason."); + return ZMK_BEHAVIOR_OPAQUE; + } + + new_active->is_pressed = true; + new_active->position = event.position; + new_active->binding.param1 = binding->param1; + new_active->binding.param2 = binding->param2; + press_binding(new_active, event.timestamp); + + if (new_active != most_recent_active) { + new_active->previous = most_recent_active; + } + new_active->next = NULL; + + data->tail = new_active; + + return ZMK_BEHAVIOR_OPAQUE; +} + +static int on_non_overlap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position = %d, param1 = 0x%02X.", event.position, binding->param1); + + const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev); + struct behavior_non_overlap_data *data = dev->data; + struct active_non_overlap *least_recent_active = data->head; + struct active_non_overlap *most_recent_active = data->tail; + struct active_non_overlap *active = find_active(binding, event.position, most_recent_active); + const int64_t timestamp = event.timestamp; + + if (active == NULL) { + LOG_DBG("No existing active. Nothing to do here."); + return ZMK_BEHAVIOR_OPAQUE; + } + + active->is_pressed = false; + + if (active == most_recent_active) { + LOG_DBG("This is the most recent active. Release it."); + release_binding(most_recent_active, timestamp); + + most_recent_active = most_recent_active->previous; + if (most_recent_active != NULL) { + LOG_DBG("Previous active exists. Re-press it."); + press_binding(most_recent_active, timestamp); + + most_recent_active->next = NULL; + } + + data->tail = most_recent_active; + return ZMK_BEHAVIOR_OPAQUE; + } + + active->next->previous = active->previous; + + if (active == least_recent_active) { + data->head = least_recent_active->next; + } else { + active->previous->next = active->next; + } + + LOG_DBG("Matched active deleted."); + return ZMK_BEHAVIOR_OPAQUE; +} + +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static int non_overlap_parameter_metadata(const struct device *non_overlap, + struct behavior_parameter_metadata *param_metadata) { + const struct behavior_non_overlap_config *cfg = non_overlap->config; + struct behavior_parameter_metadata child_metadata; + + int err = behavior_get_parameter_metadata(zmk_behavior_get_binding(cfg->behavior_dev), + &child_metadata); + if (err < 0) { + LOG_WRN("Failed to get the non-overlap behavior parameter: %d", err); + return err; + } + + for (int s = 0; s < child_metadata.sets_len; s++) { + const struct behavior_parameter_metadata_set *set = &child_metadata.sets[s]; + + if (set->param2_values_len > 0) { + return -ENOTSUP; + } + } + + *param_metadata = child_metadata; + + return 0; +} + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static struct behavior_driver_api behavior_non_overlap_driver_api = { + .binding_pressed = on_non_overlap_binding_pressed, + .binding_released = on_non_overlap_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = non_overlap_parameter_metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) +}; + +#define _SET_NULL(i, inst) \ + { \ + .previous = NULL, \ + .next = NULL, \ + .is_pressed = false, \ + .position = 0, \ + .binding = \ + { \ + .behavior_dev = behavior_non_overlap_config_##inst.behavior_dev, \ + .param1 = 0, \ + .param2 = 0, \ + }, \ + } + +#define _KEEP_ACTIVE_SIZE(inst) \ + COND_CODE_1(DT_INST_PROP(inst, no_active), (1), (DT_INST_PROP_OR(inst, keep_active_size, 10))) + +#define _SET_ACTIVES(inst) {LISTIFY(_KEEP_ACTIVE_SIZE(inst), _SET_NULL, (, ), inst)} + +#define KP_INST(inst) \ + static const struct behavior_non_overlap_config behavior_non_overlap_config_##inst = { \ + .behavior_dev = DEVICE_DT_NAME(DT_INST_PHANDLE_BY_IDX(inst, bindings, 0)), \ + }; \ + static struct active_non_overlap actives_##inst[] = _SET_ACTIVES(inst); \ + static struct behavior_non_overlap_data behavior_non_overlap_data_##inst = { \ + .head = NULL, \ + .tail = NULL, \ + .actives = actives_##inst, \ + .keep_active_size = _KEEP_ACTIVE_SIZE(inst), \ + }; \ + BEHAVIOR_DT_INST_DEFINE( \ + inst, behavior_non_overlap_init, NULL, &behavior_non_overlap_data_##inst, \ + &behavior_non_overlap_config_##inst, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_non_overlap_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/tests/non-overlap/basic/events.patterns b/app/tests/non-overlap/basic/events.patterns new file mode 100644 index 00000000000..b1342af4d97 --- /dev/null +++ b/app/tests/non-overlap/basic/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/non-overlap/basic/keycode_events.snapshot b/app/tests/non-overlap/basic/keycode_events.snapshot new file mode 100644 index 00000000000..12b8c8051b5 --- /dev/null +++ b/app/tests/non-overlap/basic/keycode_events.snapshot @@ -0,0 +1,6 @@ +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 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/non-overlap/basic/native_posix_64.keymap b/app/tests/non-overlap/basic/native_posix_64.keymap new file mode 100644 index 00000000000..360e3d20547 --- /dev/null +++ b/app/tests/non-overlap/basic/native_posix_64.keymap @@ -0,0 +1,19 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +/* Basic non-overlap test. */ + +&kscan { + events = < + // Press &nkp A + ZMK_MOCK_PRESS(0,0,10) + // Tap &nkp D + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + // &kp A is re-pressed after releasing &nkp D + // &kp A remains pressed until &nkp A is released + ZMK_MOCK_RELEASE(0,0,10) + >; +}; diff --git a/app/tests/non-overlap/behavior_keymap.dtsi b/app/tests/non-overlap/behavior_keymap.dtsi new file mode 100644 index 00000000000..f5e9b54dbdf --- /dev/null +++ b/app/tests/non-overlap/behavior_keymap.dtsi @@ -0,0 +1,21 @@ +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &nkp A &kp W + &nkp D &mo 1>; + }; + + lower_layer { + bindings = < + &kp LEFT_SHIFT &kp LEFT_CONTROL + &kp Y &kp Z>; + }; + }; +}; diff --git a/app/tests/non-overlap/keep-active-size/events.patterns b/app/tests/non-overlap/keep-active-size/events.patterns new file mode 100644 index 00000000000..b1342af4d97 --- /dev/null +++ b/app/tests/non-overlap/keep-active-size/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/non-overlap/keep-active-size/keycode_events.snapshot b/app/tests/non-overlap/keep-active-size/keycode_events.snapshot new file mode 100644 index 00000000000..3efe6c76c89 --- /dev/null +++ b/app/tests/non-overlap/keep-active-size/keycode_events.snapshot @@ -0,0 +1,10 @@ +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/non-overlap/keep-active-size/native_posix_64.keymap b/app/tests/non-overlap/keep-active-size/native_posix_64.keymap new file mode 100644 index 00000000000..31f25c9c9a3 --- /dev/null +++ b/app/tests/non-overlap/keep-active-size/native_posix_64.keymap @@ -0,0 +1,37 @@ +#include +#include +#include + +/* + * Press in sequence WASD, then release backward DSAW. + * Only keep track of the few most recent key presses. + */ + +&nkp { + keep-active-size = <2>; +}; + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &nkp A &nkp W + &nkp D &nkp S>; + }; + }; +}; + +&kscan { + events = < + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(0,1,10) // W + >; +}; diff --git a/app/tests/non-overlap/linked-list/events.patterns b/app/tests/non-overlap/linked-list/events.patterns new file mode 100644 index 00000000000..b1342af4d97 --- /dev/null +++ b/app/tests/non-overlap/linked-list/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/non-overlap/linked-list/keycode_events.snapshot b/app/tests/non-overlap/linked-list/keycode_events.snapshot new file mode 100644 index 00000000000..369ff71b9b0 --- /dev/null +++ b/app/tests/non-overlap/linked-list/keycode_events.snapshot @@ -0,0 +1,48 @@ +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +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 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/non-overlap/linked-list/native_posix_64.keymap b/app/tests/non-overlap/linked-list/native_posix_64.keymap new file mode 100644 index 00000000000..942ff779742 --- /dev/null +++ b/app/tests/non-overlap/linked-list/native_posix_64.keymap @@ -0,0 +1,65 @@ +#include +#include +#include + +/* + * Queue operations should work correctly within a limited space linked list. + * + * This test barely validates that the linked list is implemented correctly. + * Further work should provide a way to generate and evaluate all possible cases. + */ + +&nkp { + keep-active-size = <3>; +}; + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &nkp A &nkp W + &nkp D &nkp S>; + }; + }; +}; + +&kscan { + events = < + ZMK_MOCK_PRESS(0,1,10) // W W__ These 3 characters represent how + ZMK_MOCK_PRESS(0,0,10) // A WA_ the array should look like. + ZMK_MOCK_RELEASE(0,0,10) // A W__ _ represents an empty space. + ZMK_MOCK_PRESS(0,0,10) // A WA_ 130 These numbers show the order of + ZMK_MOCK_PRESS(1,1,10) // S WAS 123 each key press. + ZMK_MOCK_RELEASE(1,1,10) // S WA_ 130 0 means empty. + ZMK_MOCK_PRESS(1,1,10) // S WAS 123 1 is head. 2 is mid. 3 is tail. + ZMK_MOCK_PRESS(1,0,10) // D DAS 312 Limit reached. First in first out. + ZMK_MOCK_RELEASE(1,0,10) // D _AS 013 Test release tail. + ZMK_MOCK_PRESS(1,0,10) // D DAS 312 + ZMK_MOCK_RELEASE(1,1,10) // S DA_ 310 Test release mid. + ZMK_MOCK_PRESS(1,1,10) // S DAS 213 + ZMK_MOCK_RELEASE(0,0,10) // A D_S 103 Test release head. + ZMK_MOCK_PRESS(0,0,10) // A DAS 132 + ZMK_MOCK_RELEASE(1,1,10) // S DA_ 130 Test release mid + ZMK_MOCK_RELEASE(0,0,10) // A D__ 100 and then tail. + ZMK_MOCK_PRESS(1,1,10) // S DS_ 130 + ZMK_MOCK_PRESS(0,0,10) // A DSA 123 + ZMK_MOCK_RELEASE(1,0,10) // D _SA 013 Test release head + ZMK_MOCK_RELEASE(1,1,10) // S __A 001 twice. + ZMK_MOCK_PRESS(1,1,10) // S S_A 301 + ZMK_MOCK_PRESS(1,0,10) // D SDA 231 + ZMK_MOCK_RELEASE(1,1,10) // S _DA 031 Test release mid + ZMK_MOCK_RELEASE(0,0,10) // A _D_ 010 and then head. + ZMK_MOCK_PRESS(0,0,10) // A AD_ 310 + ZMK_MOCK_PRESS(1,1,10) // S ADS 213 + ZMK_MOCK_RELEASE(1,0,10) // D A_S 103 Test release head + ZMK_MOCK_RELEASE(1,1,10) // S A__ 100 and then tail. + ZMK_MOCK_PRESS(1,1,10) // S AS_ + ZMK_MOCK_PRESS(1,0,10) // D ASD + ZMK_MOCK_RELEASE(1,0,10) // D AS_ Test release all tails. + ZMK_MOCK_RELEASE(1,1,10) // S A__ + ZMK_MOCK_RELEASE(0,0,10) // A ___ + ZMK_MOCK_RELEASE(0,1,10) // W ___ + >; +}; diff --git a/app/tests/non-overlap/no-active/events.patterns b/app/tests/non-overlap/no-active/events.patterns new file mode 100644 index 00000000000..b1342af4d97 --- /dev/null +++ b/app/tests/non-overlap/no-active/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/non-overlap/no-active/keycode_events.snapshot b/app/tests/non-overlap/no-active/keycode_events.snapshot new file mode 100644 index 00000000000..4f6b2fece0b --- /dev/null +++ b/app/tests/non-overlap/no-active/keycode_events.snapshot @@ -0,0 +1,4 @@ +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 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/non-overlap/no-active/native_posix_64.keymap b/app/tests/non-overlap/no-active/native_posix_64.keymap new file mode 100644 index 00000000000..912b55b8e12 --- /dev/null +++ b/app/tests/non-overlap/no-active/native_posix_64.keymap @@ -0,0 +1,25 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +/* + * Keep no record of previous key presses. + * Previous non-overlap key presses will not be re-pressed. + */ + +&nkp { + no-active; +}; + +&kscan { + events = < + // Press &nkp A + ZMK_MOCK_PRESS(0,0,10) + // Tap &nkp D + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + // &kp A is not re-pressed + ZMK_MOCK_RELEASE(0,0,10) + >; +}; diff --git a/app/tests/non-overlap/release-backward/events.patterns b/app/tests/non-overlap/release-backward/events.patterns new file mode 100644 index 00000000000..b1342af4d97 --- /dev/null +++ b/app/tests/non-overlap/release-backward/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/non-overlap/release-backward/keycode_events.snapshot b/app/tests/non-overlap/release-backward/keycode_events.snapshot new file mode 100644 index 00000000000..98044671a2c --- /dev/null +++ b/app/tests/non-overlap/release-backward/keycode_events.snapshot @@ -0,0 +1,14 @@ +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +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 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/non-overlap/release-backward/native_posix_64.keymap b/app/tests/non-overlap/release-backward/native_posix_64.keymap new file mode 100644 index 00000000000..bafbbda779d --- /dev/null +++ b/app/tests/non-overlap/release-backward/native_posix_64.keymap @@ -0,0 +1,37 @@ +#include +#include +#include + +/* + * Press and release in sequence WASD, then DSAW. + * Non-overlap keys that remain pressed should be re-pressed in the correct order. + */ + +&nkp { + keep-active-size = <4>; +}; + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &nkp A &nkp W + &nkp D &nkp S>; + }; + }; +}; + +&kscan { + events = < + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(0,1,10) // W + >; +}; diff --git a/app/tests/non-overlap/release-random/events.patterns b/app/tests/non-overlap/release-random/events.patterns new file mode 100644 index 00000000000..b1342af4d97 --- /dev/null +++ b/app/tests/non-overlap/release-random/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/non-overlap/release-random/keycode_events.snapshot b/app/tests/non-overlap/release-random/keycode_events.snapshot new file mode 100644 index 00000000000..394114cd752 --- /dev/null +++ b/app/tests/non-overlap/release-random/keycode_events.snapshot @@ -0,0 +1,230 @@ +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +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 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +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 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +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 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +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 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +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 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +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 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +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 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x16 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/non-overlap/release-random/native_posix_64.keymap b/app/tests/non-overlap/release-random/native_posix_64.keymap new file mode 100644 index 00000000000..858d3aad051 --- /dev/null +++ b/app/tests/non-overlap/release-random/native_posix_64.keymap @@ -0,0 +1,236 @@ +#include +#include +#include + +/* + * Press in sequence WASD. Release in every possible way. + * Non-overlap keys that remain pressed should be re-pressed in the correct order. + */ + +&nkp { + keep-active-size = <4>; +}; + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &nkp A &nkp W + &nkp D &nkp S>; + }; + }; +}; + +&kscan { + events = < + // Release DSWA. Re-press SA. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(0,0,10) // A + // Release DASW. Re-press SW. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,1,10) // W + // Release DAWS. Re-press S. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,1,10) // S + // Release DWSA. Re-press SA. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,0,10) // A + // Release DWAS. Re-press S. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,1,10) // S + // Release SDAW. Re-press AW. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(0,1,10) // W + // Release SDWA. Re-press A. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(0,0,10) // A + // Release SADW. Re-press W. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + // Release SAWD. No re-press. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,0,10) // D + // Release SWDA. Re-press A. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + // Release SWAD. No re-press. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,0,10) // D + // Release ADSW. Re-press SW. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,1,10) // W + // Release ADWS. Re-press S. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,1,10) // S + // Release ASDW. Re-press W. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + // Release ASWD. No re-press. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,0,10) // D + // Release AWDS. Re-press S. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + // Release AWSD. No re-press. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(1,0,10) // D + // Release WDSA. Re-press SA. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,0,10) // A + // Release WDAS. Re-press S. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,1,10) // S + // Release WSDA. Re-press A. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(0,0,10) // A + // Release WSAD. No re-press. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,0,10) // D + // Release WADS. Re-press S. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,0,10) // D + ZMK_MOCK_RELEASE(1,1,10) // S + // Release WASD. No re-press. + ZMK_MOCK_PRESS(0,1,10) // W + ZMK_MOCK_PRESS(0,0,10) // A + ZMK_MOCK_PRESS(1,1,10) // S + ZMK_MOCK_PRESS(1,0,10) // D + ZMK_MOCK_RELEASE(0,1,10) // W + ZMK_MOCK_RELEASE(0,0,10) // A + ZMK_MOCK_RELEASE(1,1,10) // S + ZMK_MOCK_RELEASE(1,0,10) // D + >; +}; diff --git a/app/tests/non-overlap/release/events.patterns b/app/tests/non-overlap/release/events.patterns new file mode 100644 index 00000000000..b1342af4d97 --- /dev/null +++ b/app/tests/non-overlap/release/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/non-overlap/release/keycode_events.snapshot b/app/tests/non-overlap/release/keycode_events.snapshot new file mode 100644 index 00000000000..4f6b2fece0b --- /dev/null +++ b/app/tests/non-overlap/release/keycode_events.snapshot @@ -0,0 +1,4 @@ +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 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/non-overlap/release/native_posix_64.keymap b/app/tests/non-overlap/release/native_posix_64.keymap new file mode 100644 index 00000000000..eb24eccb6a4 --- /dev/null +++ b/app/tests/non-overlap/release/native_posix_64.keymap @@ -0,0 +1,20 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +/* Release &nkp A before releasing &nkp D */ + +&kscan { + events = < + // Press &nkp A + ZMK_MOCK_PRESS(0,0,10) + // Press &nkp D + ZMK_MOCK_PRESS(1,0,10) + // Release &nkp A + ZMK_MOCK_RELEASE(0,0,10) + // Release &nkp D + ZMK_MOCK_RELEASE(1,0,10) + // &kp A is not re-pressed after releasing &nkp D + >; +}; diff --git a/app/tests/non-overlap/tap/events.patterns b/app/tests/non-overlap/tap/events.patterns new file mode 100644 index 00000000000..b1342af4d97 --- /dev/null +++ b/app/tests/non-overlap/tap/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/non-overlap/tap/keycode_events.snapshot b/app/tests/non-overlap/tap/keycode_events.snapshot new file mode 100644 index 00000000000..b555343e6a4 --- /dev/null +++ b/app/tests/non-overlap/tap/keycode_events.snapshot @@ -0,0 +1,2 @@ +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/non-overlap/tap/native_posix_64.keymap b/app/tests/non-overlap/tap/native_posix_64.keymap new file mode 100644 index 00000000000..c83d84312b1 --- /dev/null +++ b/app/tests/non-overlap/tap/native_posix_64.keymap @@ -0,0 +1,14 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +/* Non-overlap key press should behave like a normal key press. */ + +&kscan { + events = < + // Tap &nkp A + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; diff --git a/app/tests/non-overlap/wasd/events.patterns b/app/tests/non-overlap/wasd/events.patterns new file mode 100644 index 00000000000..b1342af4d97 --- /dev/null +++ b/app/tests/non-overlap/wasd/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p diff --git a/app/tests/non-overlap/wasd/keycode_events.snapshot b/app/tests/non-overlap/wasd/keycode_events.snapshot new file mode 100644 index 00000000000..e95340d9916 --- /dev/null +++ b/app/tests/non-overlap/wasd/keycode_events.snapshot @@ -0,0 +1,8 @@ +pressed: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 +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 0x07 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1A implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/non-overlap/wasd/native_posix_64.keymap b/app/tests/non-overlap/wasd/native_posix_64.keymap new file mode 100644 index 00000000000..5e0bc6b75e1 --- /dev/null +++ b/app/tests/non-overlap/wasd/native_posix_64.keymap @@ -0,0 +1,31 @@ +#include +#include +#include + +/* Non-overlap keys and normal keys do not interfere with one another. */ + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &nkp A &kp W + &nkp D &kp S>; + }; + }; +}; + +&kscan { + events = < + // Hold &kp W + ZMK_MOCK_PRESS(0,1,10) + // Basic non-overlap test + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_RELEASE(0,0,10) + // Release &kp W + ZMK_MOCK_RELEASE(0,1,10) + >; +}; diff --git a/docs/docs/behaviors/index.mdx b/docs/docs/behaviors/index.mdx index bdacc209ad7..887f4a0f349 100644 --- a/docs/docs/behaviors/index.mdx +++ b/docs/docs/behaviors/index.mdx @@ -20,6 +20,7 @@ Below is a summary of pre-defined behavior bindings and user-definable behaviors | `&gresc` | [Grave Escape](mod-morph.md#behavior-binding) | Sends Grave Accent `` ` `` keycode if shift or GUI is held, sends Escape keycode otherwise | | `&caps_word` | [Caps Word](caps-word.md) | Behaves similar to caps lock, but automatically deactivates when any key not in a continue list is pressed, or if the caps word key is pressed again | | `&key_repeat` | [Key Repeat](key-repeat.md) | Sends again whatever keycode was last sent | +| `&nkp` | [Non-overlap](non-overlap.mdx) | Allows only one key to be pressed at a time | ## Miscellaneous Behaviors diff --git a/docs/docs/behaviors/non-overlap.mdx b/docs/docs/behaviors/non-overlap.mdx new file mode 100644 index 00000000000..0d12a2f1491 --- /dev/null +++ b/docs/docs/behaviors/non-overlap.mdx @@ -0,0 +1,101 @@ +--- +title: Non-overlap Behavior +sidebar_label: Non-overlap +--- + +## Summary + +This behavior allows only one key to be pressed at a time. + +### Behavior Binding + +The default non-overlap `&nkp` uses [key press](key-press.md) binding `&kp`. +Any [keycode](../codes/index.mdx) from the usage page should work. + +The following keymap prevents `A` and `D` from overlapping each other. + +```dts +/ { + keymap { + default_layer { + bindings = < + &kp Q &kp W &kp E + &nkp A &kp S &nkp D>; + }; + }; +}; +``` + +When `&nkp A` is being pressed, pressing `&nkp D` will release `&kp A` (the binding of `&nkp A`). +After `&nkp D` is released, `&kp A` will be pressed again. + +### Configuration + +#### `keep-active-size` + +Non-overlap behavior tries to re-press any remaining active keys +in the exact order they were pressed. +`keep-active-size` limits the number of active keys this behavior can handle. +The default is 10. + +In this example, when you press and release keys in sequence ASDF-FDSA, only F is re-pressed +as the behavior only keeps track of the two most recent key presses, D and F. + +```dts +&nkp { + keep-active-size = <2>; +}; + +/ { + keymap { + default_layer { + bindings = < + &nkp A &nkp S + &nkp D &nkp F>; + }; + }; +}; +``` + +#### `no-active` + +When `no-active` is enabled, the behavior will not track pressed keys and no re-press will be made. +`no-active` is disabled by default. + +```dts +&nkp { + no-active; +}; + +/ { + keymap { + ... + }; +}; +``` + +You can also set `keep-active-size = <1>` to achieve the same effect. + +### Multiple instances + +The following example creates a new non-overlap group that works separately from the default `&nkp`. + +```dts +/ { + behaviors { + nkp2: non_overlap_key_press_2 { + compatible = "zmk,behavior-non-overlap"; + #binding-cells = <1>; + bindings = <&kp>; + }; + }; + + keymap { + default_layer { + bindings = < + &kp Q &nkp2 W &kp E + &nkp A &nkp2 S &nkp D>; + }; + }; +}; +``` diff --git a/docs/sidebars.js b/docs/sidebars.js index 1233ccf1b42..97c69047e97 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -67,6 +67,7 @@ module.exports = { "behaviors/tap-dance", "behaviors/caps-word", "behaviors/key-repeat", + "behaviors/non-overlap", "behaviors/sensor-rotate", "behaviors/mouse-emulation", "behaviors/reset",