diff options
author | Peter Johanson <peter@peterjohanson.com> | 2021-11-29 04:26:22 +0000 |
---|---|---|
committer | Pete Johanson <peter@peterjohanson.com> | 2022-01-30 22:47:34 -0500 |
commit | 70bb7c93349344e0990f12282abfcd8d00ba7208 (patch) | |
tree | eeb47ee39ec4a887ef6d1edf809b88709e6269bb /app | |
parent | ac3c3170bd70f5186544495cb5ced7bfbf3f6764 (diff) |
feat(behaviors): `&key_repeat` behavior + tests.
* Add new `&key_repeat` behavior that captures and re-sends
the most recently triggered keycode.
Closes: #853
Diffstat (limited to 'app')
15 files changed, 240 insertions, 0 deletions
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 970c6c2..c5b74aa 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -48,6 +48,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c) 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_momentary_layer.c) target_sources(app PRIVATE src/behaviors/behavior_mod_morph.c) target_sources(app PRIVATE src/behaviors/behavior_outputs.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 5b5f72b..0648961 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -15,3 +15,4 @@ #include <behaviors/ext_power.dtsi> #include <behaviors/outputs.dtsi> #include <behaviors/caps_word.dtsi> +#include <behaviors/key_repeat.dtsi> diff --git a/app/dts/behaviors/key_repeat.dtsi b/app/dts/behaviors/key_repeat.dtsi new file mode 100644 index 0000000..aa8ffa0 --- /dev/null +++ b/app/dts/behaviors/key_repeat.dtsi @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include <dt-bindings/zmk/keys.h> + +/ { + behaviors { + /omit-if-no-ref/ key_repeat: behavior_key_repeat { + compatible = "zmk,behavior-key-repeat"; + label = "KEY_REPEAT"; + #binding-cells = <0>; + usage-pages = <HID_USAGE_KEY>; + }; + }; +}; + diff --git a/app/dts/bindings/behaviors/zmk,behavior-key-repeat.yaml b/app/dts/bindings/behaviors/zmk,behavior-key-repeat.yaml new file mode 100644 index 0000000..10b3aa0 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-key-repeat.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Key repeat behavior + +compatible: "zmk,behavior-key-repeat" + +include: zero_param.yaml + +properties: + usage-pages: + type: array + required: true diff --git a/app/src/behaviors/behavior_key_repeat.c b/app/src/behaviors/behavior_key_repeat.c new file mode 100644 index 0000000..b2e28a6 --- /dev/null +++ b/app/src/behaviors/behavior_key_repeat.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_key_repeat + +#include <device.h> +#include <drivers/behavior.h> +#include <logging/log.h> +#include <zmk/behavior.h> + +#include <zmk/event_manager.h> +#include <zmk/events/keycode_state_changed.h> + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +struct behavior_key_repeat_config { + uint8_t index; + uint8_t usage_pages_count; + uint16_t usage_pages[]; +}; + +struct behavior_key_repeat_data { + struct zmk_keycode_state_changed last_keycode_pressed; + struct zmk_keycode_state_changed current_keycode_pressed; +}; + +static int on_key_repeat_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + const struct device *dev = device_get_binding(binding->behavior_dev); + struct behavior_key_repeat_data *data = dev->data; + + if (data->last_keycode_pressed.usage_page == 0) { + return ZMK_BEHAVIOR_OPAQUE; + } + + memcpy(&data->current_keycode_pressed, &data->last_keycode_pressed, + sizeof(struct zmk_keycode_state_changed)); + data->current_keycode_pressed.timestamp = k_uptime_get(); + + ZMK_EVENT_RAISE(new_zmk_keycode_state_changed(data->current_keycode_pressed)); + + return ZMK_BEHAVIOR_OPAQUE; +} + +static int on_key_repeat_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + const struct device *dev = device_get_binding(binding->behavior_dev); + struct behavior_key_repeat_data *data = dev->data; + + if (data->current_keycode_pressed.usage_page == 0) { + return ZMK_BEHAVIOR_OPAQUE; + } + + data->current_keycode_pressed.timestamp = k_uptime_get(); + data->current_keycode_pressed.state = false; + + ZMK_EVENT_RAISE(new_zmk_keycode_state_changed(data->current_keycode_pressed)); + return ZMK_BEHAVIOR_OPAQUE; +} + +static const struct behavior_driver_api behavior_key_repeat_driver_api = { + .binding_pressed = on_key_repeat_binding_pressed, + .binding_released = on_key_repeat_binding_released, +}; + +static int key_repeat_keycode_state_changed_listener(const zmk_event_t *eh); + +ZMK_LISTENER(behavior_key_repeat, key_repeat_keycode_state_changed_listener); +ZMK_SUBSCRIPTION(behavior_key_repeat, zmk_keycode_state_changed); + +static const struct device *devs[DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT)]; + +static int key_repeat_keycode_state_changed_listener(const zmk_event_t *eh) { + struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh); + if (ev == NULL || !ev->state) { + return ZMK_EV_EVENT_BUBBLE; + } + + for (int i = 0; i < DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT); i++) { + const struct device *dev = devs[i]; + if (dev == NULL) { + continue; + } + + struct behavior_key_repeat_data *data = dev->data; + const struct behavior_key_repeat_config *config = dev->config; + + for (int u = 0; u < config->usage_pages_count; u++) { + if (config->usage_pages[u] == ev->usage_page) { + memcpy(&data->last_keycode_pressed, ev, sizeof(struct zmk_keycode_state_changed)); + break; + } + } + } + + return ZMK_EV_EVENT_BUBBLE; +} + +static int behavior_key_repeat_init(const struct device *dev) { + const struct behavior_key_repeat_config *config = dev->config; + devs[config->index] = dev; + return 0; +} + +#define KR_INST(n) \ + static struct behavior_key_repeat_data behavior_key_repeat_data_##n = {}; \ + static struct behavior_key_repeat_config behavior_key_repeat_config_##n = { \ + .index = n, \ + .usage_pages = DT_INST_PROP(n, usage_pages), \ + .usage_pages_count = DT_INST_PROP_LEN(n, usage_pages), \ + }; \ + DEVICE_DT_INST_DEFINE(n, behavior_key_repeat_init, device_pm_control_nop, \ + &behavior_key_repeat_data_##n, &behavior_key_repeat_config_##n, \ + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_key_repeat_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KR_INST) + +#endif diff --git a/app/tests/key-repeat/behavior_keymap.dtsi b/app/tests/key-repeat/behavior_keymap.dtsi new file mode 100644 index 0000000..93b6d06 --- /dev/null +++ b/app/tests/key-repeat/behavior_keymap.dtsi @@ -0,0 +1,17 @@ +#include <dt-bindings/zmk/keys.h> +#include <behaviors.dtsi> +#include <dt-bindings/zmk/kscan_mock.h> + +/ { + keymap { + compatible = "zmk,keymap"; + label = "Default keymap"; + + default_layer { + bindings = < + &key_repeat &kp A + &kp B &kp C_VOL_UP + >; + }; + }; +}; diff --git a/app/tests/key-repeat/ignore-other-usage-page-events/events.patterns b/app/tests/key-repeat/ignore-other-usage-page-events/events.patterns new file mode 100644 index 0000000..7947192 --- /dev/null +++ b/app/tests/key-repeat/ignore-other-usage-page-events/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*hid_implicit_modifiers_//p
\ No newline at end of file diff --git a/app/tests/key-repeat/ignore-other-usage-page-events/keycode_events.snapshot b/app/tests/key-repeat/ignore-other-usage-page-events/keycode_events.snapshot new file mode 100644 index 0000000..c06d94a --- /dev/null +++ b/app/tests/key-repeat/ignore-other-usage-page-events/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +press: Modifiers set to 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +release: Modifiers set to 0x00 +pressed: usage_page 0x0c keycode 0xe9 implicit_mods 0x00 explicit_mods 0x00 +press: Modifiers set to 0x00 +released: usage_page 0x0c keycode 0xe9 implicit_mods 0x00 explicit_mods 0x00 +release: Modifiers set to 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +press: Modifiers set to 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +release: Modifiers set to 0x00 diff --git a/app/tests/key-repeat/ignore-other-usage-page-events/native_posix.keymap b/app/tests/key-repeat/ignore-other-usage-page-events/native_posix.keymap new file mode 100644 index 0000000..b042e8e --- /dev/null +++ b/app/tests/key-repeat/ignore-other-usage-page-events/native_posix.keymap @@ -0,0 +1,15 @@ +#include <dt-bindings/zmk/keys.h> +#include <behaviors.dtsi> +#include <dt-bindings/zmk/kscan_mock.h> +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(1,1,10) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +};
\ No newline at end of file diff --git a/app/tests/key-repeat/press-and-release-after-key-usage/events.patterns b/app/tests/key-repeat/press-and-release-after-key-usage/events.patterns new file mode 100644 index 0000000..7947192 --- /dev/null +++ b/app/tests/key-repeat/press-and-release-after-key-usage/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*hid_implicit_modifiers_//p
\ No newline at end of file diff --git a/app/tests/key-repeat/press-and-release-after-key-usage/keycode_events.snapshot b/app/tests/key-repeat/press-and-release-after-key-usage/keycode_events.snapshot new file mode 100644 index 0000000..d568d37 --- /dev/null +++ b/app/tests/key-repeat/press-and-release-after-key-usage/keycode_events.snapshot @@ -0,0 +1,8 @@ +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +press: Modifiers set to 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +release: Modifiers set to 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +press: Modifiers set to 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +release: Modifiers set to 0x00 diff --git a/app/tests/key-repeat/press-and-release-after-key-usage/native_posix.keymap b/app/tests/key-repeat/press-and-release-after-key-usage/native_posix.keymap new file mode 100644 index 0000000..98c8f6f --- /dev/null +++ b/app/tests/key-repeat/press-and-release-after-key-usage/native_posix.keymap @@ -0,0 +1,13 @@ +#include <dt-bindings/zmk/keys.h> +#include <behaviors.dtsi> +#include <dt-bindings/zmk/kscan_mock.h> +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +};
\ No newline at end of file diff --git a/app/tests/key-repeat/send-nothing-if-no-keys-pressed-yet/events.patterns b/app/tests/key-repeat/send-nothing-if-no-keys-pressed-yet/events.patterns new file mode 100644 index 0000000..7947192 --- /dev/null +++ b/app/tests/key-repeat/send-nothing-if-no-keys-pressed-yet/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode_//p +s/.*hid_implicit_modifiers_//p
\ No newline at end of file diff --git a/app/tests/key-repeat/send-nothing-if-no-keys-pressed-yet/keycode_events.snapshot b/app/tests/key-repeat/send-nothing-if-no-keys-pressed-yet/keycode_events.snapshot new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/tests/key-repeat/send-nothing-if-no-keys-pressed-yet/keycode_events.snapshot diff --git a/app/tests/key-repeat/send-nothing-if-no-keys-pressed-yet/native_posix.keymap b/app/tests/key-repeat/send-nothing-if-no-keys-pressed-yet/native_posix.keymap new file mode 100644 index 0000000..9ff6446 --- /dev/null +++ b/app/tests/key-repeat/send-nothing-if-no-keys-pressed-yet/native_posix.keymap @@ -0,0 +1,11 @@ +#include <dt-bindings/zmk/keys.h> +#include <behaviors.dtsi> +#include <dt-bindings/zmk/kscan_mock.h> +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +};
\ No newline at end of file |