From 4dbf309b30c12f6ba873b3998a3380fe7033904b Mon Sep 17 00:00:00 2001 From: Okke Formsma Date: Tue, 16 Mar 2021 21:38:36 +0100 Subject: [PATCH] feat(behavior): Add key toggle --- app/CMakeLists.txt | 1 + app/dts/behaviors.dtsi | 1 + app/dts/behaviors/key_toggle.dtsi | 15 ++++++ .../behaviors/zmk,behavior-key-toggle.yaml | 8 +++ app/include/zmk/hid.h | 5 ++ app/src/behaviors/behavior_key_toggle.c | 50 +++++++++++++++++++ app/src/hid.c | 39 ++++++++++++++- app/tests/keytoggle/behavior_keymap.dtsi | 17 +++++++ .../kt-press-release/events.patterns | 1 + .../kt-press-release/keycode_events.snapshot | 2 + .../kt-press-release/native_posix.keymap | 10 ++++ 11 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 app/dts/behaviors/key_toggle.dtsi create mode 100644 app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml create mode 100644 app/src/behaviors/behavior_key_toggle.c create mode 100644 app/tests/keytoggle/behavior_keymap.dtsi create mode 100644 app/tests/keytoggle/kt-press-release/events.patterns create mode 100644 app/tests/keytoggle/kt-press-release/keycode_events.snapshot create mode 100644 app/tests/keytoggle/kt-press-release/native_posix.keymap diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e283487f..ad0d97a8 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -43,6 +43,7 @@ target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed target_sources_ifdef(CONFIG_USB app PRIVATE src/events/usb_conn_state_changed.c) if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) + target_sources(app PRIVATE src/behaviors/behavior_key_toggle.c) target_sources(app PRIVATE src/behaviors/behavior_reset.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 4333ceea..0aa281bd 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/app/dts/behaviors/key_toggle.dtsi b/app/dts/behaviors/key_toggle.dtsi new file mode 100644 index 00000000..304bd03c --- /dev/null +++ b/app/dts/behaviors/key_toggle.dtsi @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + behaviors { + /omit-if-no-ref/ kt: behavior_key_toggle { + compatible = "zmk,behavior-key-toggle"; + label = "KEY_TOGGLE"; + #binding-cells = <1>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml b/app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml new file mode 100644 index 00000000..01bdae1e --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Key toggle behavior + +compatible: "zmk,behavior-key-toggle" + +include: one_param.yaml diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index 458e9b96..5043083c 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -169,6 +169,8 @@ struct zmk_hid_consumer_report { zmk_mod_flags_t zmk_hid_get_explicit_mods(); int zmk_hid_register_mod(zmk_mod_t modifier); int zmk_hid_unregister_mod(zmk_mod_t modifier); +bool zmk_hid_mod_is_pressed(zmk_mod_t modifier); + int zmk_hid_register_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_unregister_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers); @@ -177,13 +179,16 @@ int zmk_hid_implicit_modifiers_release(); int zmk_hid_keyboard_press(zmk_key_t key); int zmk_hid_keyboard_release(zmk_key_t key); void zmk_hid_keyboard_clear(); +bool zmk_hid_keyboard_is_pressed(); int zmk_hid_consumer_press(zmk_key_t key); int zmk_hid_consumer_release(zmk_key_t key); void zmk_hid_consumer_clear(); +bool zmk_hid_consumer_is_pressed(zmk_key_t key); int zmk_hid_press(uint8_t usage_page, zmk_key_t code); int zmk_hid_release(uint8_t usage_page, zmk_key_t code); +bool zmk_hid_is_pressed(uint8_t usage_page, zmk_key_t code); struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(); struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(); diff --git a/app/src/behaviors/behavior_key_toggle.c b/app/src/behaviors/behavior_key_toggle.c new file mode 100644 index 00000000..1bd23c43 --- /dev/null +++ b/app/src/behaviors/behavior_key_toggle.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_key_toggle + +#include +#include +#include + +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +static int behavior_key_toggle_init(const struct device *dev) { return 0; }; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + bool pressed = + zmk_hid_is_pressed(HID_USAGE_PAGE(binding->param1), HID_USAGE_ID(binding->param1)); + return ZMK_EVENT_RAISE( + zmk_keycode_state_changed_from_encoded(binding->param1, !pressed, event.timestamp)); +} + +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_key_toggle_driver_api = { + .binding_pressed = on_keymap_binding_pressed, + .binding_released = on_keymap_binding_released, +}; + +#define KP_INST(n) \ + DEVICE_AND_API_INIT(behavior_key_toggle_##n, DT_INST_LABEL(n), behavior_key_toggle_init, NULL, \ + NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_key_toggle_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/src/hid.c b/app/src/hid.c index e5e7f728..39c2c880 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: MIT */ +#include "zmk/keys.h" #include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -51,6 +52,11 @@ int zmk_hid_unregister_mod(zmk_mod_t modifier) { return 0; } +bool zmk_hid_mod_is_pressed(zmk_mod_t modifier) { + zmk_mod_flags_t mod_flag = 1 << modifier; + return (zmk_hid_get_explicit_mods() & mod_flag) == mod_flag; +} + int zmk_hid_register_mods(zmk_mod_flags_t modifiers) { for (zmk_mod_t i = 0; i < 8; i++) { if (modifiers & (1 << i)) { @@ -117,6 +123,18 @@ int zmk_hid_keyboard_release(zmk_key_t code) { return 0; }; +bool zmk_hid_keyboard_is_pressed(zmk_key_t code) { + if (code >= HID_USAGE_KEY_KEYBOARD_LEFTCONTROL && code <= HID_USAGE_KEY_KEYBOARD_RIGHT_GUI) { + return zmk_hid_mod_is_pressed(code - HID_USAGE_KEY_KEYBOARD_LEFTCONTROL); + } + for (int idx = 0; idx < ZMK_HID_KEYBOARD_NKRO_SIZE; idx++) { + if (keyboard_report.body.keys[idx] == code) { + return true; + } + } + return false; +} + void zmk_hid_keyboard_clear() { memset(&keyboard_report.body, 0, sizeof(keyboard_report.body)); } int zmk_hid_consumer_press(zmk_key_t code) { @@ -129,6 +147,17 @@ int zmk_hid_consumer_release(zmk_key_t code) { return 0; }; +void zmk_hid_consumer_clear() { memset(&consumer_report.body, 0, sizeof(consumer_report.body)); } + +bool zmk_hid_consumer_is_pressed(zmk_key_t key) { + for (int idx = 0; idx < ZMK_HID_CONSUMER_NKRO_SIZE; idx++) { + if (consumer_report.body.keys[idx] == key) { + return true; + } + } + return false; +} + int zmk_hid_press(uint8_t usage_page, zmk_key_t code) { switch (usage_page) { case HID_USAGE_KEY: @@ -149,7 +178,15 @@ int zmk_hid_release(uint8_t usage_page, zmk_key_t code) { return -EINVAL; } -void zmk_hid_consumer_clear() { memset(&consumer_report.body, 0, sizeof(consumer_report.body)); } +bool zmk_hid_is_pressed(uint8_t usage_page, zmk_key_t code) { + switch (usage_page) { + case HID_USAGE_KEY: + return zmk_hid_keyboard_is_pressed(code); + case HID_USAGE_CONSUMER: + return zmk_hid_consumer_is_pressed(code); + } + return false; +} struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() { return &keyboard_report; diff --git a/app/tests/keytoggle/behavior_keymap.dtsi b/app/tests/keytoggle/behavior_keymap.dtsi new file mode 100644 index 00000000..32712a8d --- /dev/null +++ b/app/tests/keytoggle/behavior_keymap.dtsi @@ -0,0 +1,17 @@ +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kt B &none + &none &none + >; + }; + }; +}; diff --git a/app/tests/keytoggle/kt-press-release/events.patterns b/app/tests/keytoggle/kt-press-release/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/keytoggle/kt-press-release/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/keytoggle/kt-press-release/keycode_events.snapshot b/app/tests/keytoggle/kt-press-release/keycode_events.snapshot new file mode 100644 index 00000000..259501ba --- /dev/null +++ b/app/tests/keytoggle/kt-press-release/keycode_events.snapshot @@ -0,0 +1,2 @@ +pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/keytoggle/kt-press-release/native_posix.keymap b/app/tests/keytoggle/kt-press-release/native_posix.keymap new file mode 100644 index 00000000..644caa26 --- /dev/null +++ b/app/tests/keytoggle/kt-press-release/native_posix.keymap @@ -0,0 +1,10 @@ +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; \ No newline at end of file