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/dt-bindings/zmk/hid_usage_pages.h b/app/include/dt-bindings/zmk/hid_usage_pages.h index f38f4fd3..830641f0 100644 --- a/app/include/dt-bindings/zmk/hid_usage_pages.h +++ b/app/include/dt-bindings/zmk/hid_usage_pages.h @@ -12,7 +12,7 @@ #define HID_USAGE(page, id) ((page << 16) | id) #define HID_USAGE_ID(usage) (usage & 0xFFFF) -#define HID_USAGE_PAGE(usage) (usage >> 16) +#define HID_USAGE_PAGE(usage) ((usage >> 16) & 0xFF) /* WARNING: DEPRECATED from dt-bindings/zmk/keys.h */ #define USAGE_KEYPAD (0x07) // WARNING: DEPRECATED (DO NOT USE) diff --git a/app/include/zmk/events/keycode_state_changed.h b/app/include/zmk/events/keycode_state_changed.h index 466bbd76..924eb223 100644 --- a/app/include/zmk/events/keycode_state_changed.h +++ b/app/include/zmk/events/keycode_state_changed.h @@ -23,7 +23,7 @@ ZMK_EVENT_DECLARE(zmk_keycode_state_changed); static inline struct zmk_keycode_state_changed_event * zmk_keycode_state_changed_from_encoded(uint32_t encoded, bool pressed, int64_t timestamp) { - uint16_t page = HID_USAGE_PAGE(encoded) & 0xFF; + uint16_t page = HID_USAGE_PAGE(encoded); uint16_t id = HID_USAGE_ID(encoded); uint8_t implicit_modifiers = 0x00; uint8_t explicit_modifiers = 0x00; diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index 5aa004c2..5043083c 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -169,17 +169,26 @@ 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); 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/behaviors/behavior_sticky_key.c b/app/src/behaviors/behavior_sticky_key.c index 40ca3f89..1c5c5b7b 100644 --- a/app/src/behaviors/behavior_sticky_key.c +++ b/app/src/behaviors/behavior_sticky_key.c @@ -195,7 +195,7 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) { if (strcmp(sticky_key->config->behavior.behavior_dev, "KEY_PRESS") == 0 && HID_USAGE_ID(sticky_key->param1) == ev->keycode && - (HID_USAGE_PAGE(sticky_key->param1) & 0xFF) == ev->usage_page && + HID_USAGE_PAGE(sticky_key->param1) == ev->usage_page && SELECT_MODS(sticky_key->param1) == ev->implicit_modifiers) { // don't catch key down events generated by the sticky key behavior itself continue; diff --git a/app/src/hid.c b/app/src/hid.c index 7ab080e5..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) { @@ -131,6 +149,45 @@ int zmk_hid_consumer_release(zmk_key_t code) { 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: + return zmk_hid_keyboard_press(code); + case HID_USAGE_CONSUMER: + return zmk_hid_consumer_press(code); + } + return -EINVAL; +} + +int zmk_hid_release(uint8_t usage_page, zmk_key_t code) { + switch (usage_page) { + case HID_USAGE_KEY: + return zmk_hid_keyboard_release(code); + case HID_USAGE_CONSUMER: + return zmk_hid_consumer_release(code); + } + return -EINVAL; +} + +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/src/hid_listener.c b/app/src/hid_listener.c index d582c169..1766f965 100644 --- a/app/src/hid_listener.c +++ b/app/src/hid_listener.c @@ -20,21 +20,10 @@ static int hid_listener_keycode_pressed(const struct zmk_keycode_state_changed * int err; LOG_DBG("usage_page 0x%02X keycode 0x%02X implicit_mods 0x%02X explicit_mods 0x%02X", ev->usage_page, ev->keycode, ev->implicit_modifiers, ev->explicit_modifiers); - switch (ev->usage_page) { - case HID_USAGE_KEY: - err = zmk_hid_keyboard_press(ev->keycode); - if (err) { - LOG_ERR("Unable to press keycode"); - return err; - } - break; - case HID_USAGE_CONSUMER: - err = zmk_hid_consumer_press(ev->keycode); - if (err) { - LOG_ERR("Unable to press keycode"); - return err; - } - break; + err = zmk_hid_press(ev->usage_page, ev->keycode); + if (err) { + LOG_DBG("Unable to press keycode"); + return err; } zmk_hid_register_mods(ev->explicit_modifiers); zmk_hid_implicit_modifiers_press(ev->implicit_modifiers); @@ -45,20 +34,10 @@ static int hid_listener_keycode_released(const struct zmk_keycode_state_changed int err; LOG_DBG("usage_page 0x%02X keycode 0x%02X implicit_mods 0x%02X explicit_mods 0x%02X", ev->usage_page, ev->keycode, ev->implicit_modifiers, ev->explicit_modifiers); - switch (ev->usage_page) { - case HID_USAGE_KEY: - err = zmk_hid_keyboard_release(ev->keycode); - if (err) { - LOG_ERR("Unable to release keycode"); - return err; - } - break; - case HID_USAGE_CONSUMER: - err = zmk_hid_consumer_release(ev->keycode); - if (err) { - LOG_ERR("Unable to release keycode"); - return err; - } + err = zmk_hid_release(ev->usage_page, ev->keycode); + if (err) { + LOG_DBG("Unable to release keycode"); + return err; } zmk_hid_unregister_mods(ev->explicit_modifiers); // There is a minor issue with this code. @@ -83,4 +62,4 @@ int hid_listener(const zmk_event_t *eh) { } ZMK_LISTENER(hid_listener, hid_listener); -ZMK_SUBSCRIPTION(hid_listener, zmk_keycode_state_changed); \ No newline at end of file +ZMK_SUBSCRIPTION(hid_listener, zmk_keycode_state_changed); 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