From ef2e6e9156806760d9e91c0d635cd809058c3ee7 Mon Sep 17 00:00:00 2001 From: Robert U <978080+urob@users.noreply.github.com> Date: Fri, 14 Oct 2022 21:40:28 -0400 Subject: [PATCH] feat(behaviors): Add mod-morph `keep-mods` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update docs for mod-morph * Add unit tests for mod-morph * Add keep-mods to DT binding Co-authored-by: Martin Aumùˆller Co-authored-by: Cem Aksoylar --- .../behaviors/zmk,behavior-mod-morph.yaml | 3 ++ app/include/zmk/hid.h | 2 + app/src/behaviors/behavior_mod_morph.c | 9 +++- app/src/hid.c | 24 ++++++++-- .../mod-morph/1-no-morph/events.patterns | 8 ++++ .../1-no-morph/keycode_events.snapshot | 5 ++ .../1-no-morph/native_posix_64.keymap | 11 +++++ .../mod-morph/2a-masked-morph/events.patterns | 8 ++++ .../2a-masked-morph/keycode_events.snapshot | 12 +++++ .../2a-masked-morph/native_posix_64.keymap | 13 +++++ .../events.patterns | 8 ++++ .../keycode_events.snapshot | 12 +++++ .../native_posix_64.keymap | 37 +++++++++++++++ .../events.patterns | 8 ++++ .../keycode_events.snapshot | 18 +++++++ .../native_posix_64.keymap | 15 ++++++ .../events.patterns | 8 ++++ .../keycode_events.snapshot | 12 +++++ .../native_posix_64.keymap | 45 ++++++++++++++++++ .../events.patterns | 8 ++++ .../keycode_events.snapshot | 12 +++++ .../native_posix_64.keymap | 47 +++++++++++++++++++ .../3-unmasked-morph/events.patterns | 8 ++++ .../3-unmasked-morph/keycode_events.snapshot | 12 +++++ .../3-unmasked-morph/native_posix_64.keymap | 37 +++++++++++++++ app/tests/mod-morph/behavior_keymap.dtsi | 23 +++++++++ .../keycode_events.snapshot | 2 +- docs/docs/behaviors/mod-morph.md | 29 +++++++++--- 28 files changed, 425 insertions(+), 11 deletions(-) create mode 100644 app/tests/mod-morph/1-no-morph/events.patterns create mode 100644 app/tests/mod-morph/1-no-morph/keycode_events.snapshot create mode 100644 app/tests/mod-morph/1-no-morph/native_posix_64.keymap create mode 100644 app/tests/mod-morph/2a-masked-morph/events.patterns create mode 100644 app/tests/mod-morph/2a-masked-morph/keycode_events.snapshot create mode 100644 app/tests/mod-morph/2a-masked-morph/native_posix_64.keymap create mode 100644 app/tests/mod-morph/2b-masked-morph-implicit-overwrite/events.patterns create mode 100644 app/tests/mod-morph/2b-masked-morph-implicit-overwrite/keycode_events.snapshot create mode 100644 app/tests/mod-morph/2b-masked-morph-implicit-overwrite/native_posix_64.keymap create mode 100644 app/tests/mod-morph/2c-masked-morph-and-explicit-mods/events.patterns create mode 100644 app/tests/mod-morph/2c-masked-morph-and-explicit-mods/keycode_events.snapshot create mode 100644 app/tests/mod-morph/2c-masked-morph-and-explicit-mods/native_posix_64.keymap create mode 100644 app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/events.patterns create mode 100644 app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/keycode_events.snapshot create mode 100644 app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/native_posix_64.keymap create mode 100644 app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/events.patterns create mode 100644 app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/keycode_events.snapshot create mode 100644 app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/native_posix_64.keymap create mode 100644 app/tests/mod-morph/3-unmasked-morph/events.patterns create mode 100644 app/tests/mod-morph/3-unmasked-morph/keycode_events.snapshot create mode 100644 app/tests/mod-morph/3-unmasked-morph/native_posix_64.keymap create mode 100644 app/tests/mod-morph/behavior_keymap.dtsi diff --git a/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml b/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml index ed40f936..20235d04 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml @@ -14,3 +14,6 @@ properties: mods: type: int required: true + keep-mods: + type: int + required: false diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index 902b76d1..0f9cfe2e 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -135,6 +135,8 @@ 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_masked_modifiers_set(zmk_mod_flags_t masked_modifiers); +int zmk_hid_masked_modifiers_clear(); int zmk_hid_keyboard_press(zmk_key_t key); int zmk_hid_keyboard_release(zmk_key_t key); diff --git a/app/src/behaviors/behavior_mod_morph.c b/app/src/behaviors/behavior_mod_morph.c index a40bd365..effbe4ac 100644 --- a/app/src/behaviors/behavior_mod_morph.c +++ b/app/src/behaviors/behavior_mod_morph.c @@ -27,6 +27,7 @@ struct behavior_mod_morph_config { struct zmk_behavior_binding normal_binding; struct zmk_behavior_binding morph_binding; zmk_mod_flags_t mods; + zmk_mod_flags_t masked_mods; }; struct behavior_mod_morph_data { @@ -45,6 +46,7 @@ static int on_mod_morph_binding_pressed(struct zmk_behavior_binding *binding, } if (zmk_hid_get_explicit_mods() & cfg->mods) { + zmk_hid_masked_modifiers_set(cfg->masked_mods); data->pressed_binding = (struct zmk_behavior_binding *)&cfg->morph_binding; } else { data->pressed_binding = (struct zmk_behavior_binding *)&cfg->normal_binding; @@ -64,7 +66,10 @@ static int on_mod_morph_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding *pressed_binding = data->pressed_binding; data->pressed_binding = NULL; - return behavior_keymap_binding_released(pressed_binding, event); + int err; + err = behavior_keymap_binding_released(pressed_binding, event); + zmk_hid_masked_modifiers_clear(); + return err; } static const struct behavior_driver_api behavior_mod_morph_driver_api = { @@ -88,6 +93,8 @@ static int behavior_mod_morph_init(const struct device *dev) { return 0; } .normal_binding = _TRANSFORM_ENTRY(0, n), \ .morph_binding = _TRANSFORM_ENTRY(1, n), \ .mods = DT_INST_PROP(n, mods), \ + .masked_mods = COND_CODE_0(DT_INST_NODE_HAS_PROP(n, keep_mods), (DT_INST_PROP(n, mods)), \ + (DT_INST_PROP(n, mods) & ~DT_INST_PROP(n, keep_mods))), \ }; \ static struct behavior_mod_morph_data behavior_mod_morph_data_##n = {}; \ DEVICE_DT_INST_DEFINE(n, behavior_mod_morph_init, NULL, &behavior_mod_morph_data_##n, \ diff --git a/app/src/hid.c b/app/src/hid.c index c3462dde..b66a910d 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -20,10 +20,12 @@ static struct zmk_hid_consumer_report consumer_report = {.report_id = 2, .body = // Only release the modifier if the count is 0. static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0}; static zmk_mod_flags_t explicit_modifiers = 0; +static zmk_mod_flags_t implicit_modifiers = 0; +static zmk_mod_flags_t masked_modifiers = 0; #define SET_MODIFIERS(mods) \ { \ - keyboard_report.body.modifiers = mods; \ + keyboard_report.body.modifiers = (mods & ~masked_modifiers) | implicit_modifiers; \ LOG_DBG("Modifiers set to 0x%02X", keyboard_report.body.modifiers); \ } @@ -158,13 +160,29 @@ static inline int check_keyboard_usage(zmk_key_t usage) { } \ } -int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers) { +int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t new_implicit_modifiers) { + implicit_modifiers = new_implicit_modifiers; zmk_mod_flags_t current = GET_MODIFIERS; - SET_MODIFIERS(explicit_modifiers | implicit_modifiers); + SET_MODIFIERS(explicit_modifiers); return current == GET_MODIFIERS ? 0 : 1; } int zmk_hid_implicit_modifiers_release() { + implicit_modifiers = 0; + zmk_mod_flags_t current = GET_MODIFIERS; + SET_MODIFIERS(explicit_modifiers); + return current == GET_MODIFIERS ? 0 : 1; +} + +int zmk_hid_masked_modifiers_set(zmk_mod_flags_t new_masked_modifiers) { + masked_modifiers = new_masked_modifiers; + zmk_mod_flags_t current = GET_MODIFIERS; + SET_MODIFIERS(explicit_modifiers); + return current == GET_MODIFIERS ? 0 : 1; +} + +int zmk_hid_masked_modifiers_clear() { + masked_modifiers = 0; zmk_mod_flags_t current = GET_MODIFIERS; SET_MODIFIERS(explicit_modifiers); return current == GET_MODIFIERS ? 0 : 1; diff --git a/app/tests/mod-morph/1-no-morph/events.patterns b/app/tests/mod-morph/1-no-morph/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/1-no-morph/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/1-no-morph/keycode_events.snapshot b/app/tests/mod-morph/1-no-morph/keycode_events.snapshot new file mode 100644 index 00000000..3a2d70fe --- /dev/null +++ b/app/tests/mod-morph/1-no-morph/keycode_events.snapshot @@ -0,0 +1,5 @@ +pressed: keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x00 +released: keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x00 +unmask mods: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/1-no-morph/native_posix_64.keymap b/app/tests/mod-morph/1-no-morph/native_posix_64.keymap new file mode 100644 index 00000000..fb939de5 --- /dev/null +++ b/app/tests/mod-morph/1-no-morph/native_posix_64.keymap @@ -0,0 +1,11 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; diff --git a/app/tests/mod-morph/2a-masked-morph/events.patterns b/app/tests/mod-morph/2a-masked-morph/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/2a-masked-morph/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/2a-masked-morph/keycode_events.snapshot b/app/tests/mod-morph/2a-masked-morph/keycode_events.snapshot new file mode 100644 index 00000000..dcf2aae6 --- /dev/null +++ b/app/tests/mod-morph/2a-masked-morph/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x02 +reg implicit: Modifiers set to 0x02 +mask mods: Modifiers set to 0x00 +pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x00 +released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x00 +unmask mods: Modifiers set to 0x02 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/2a-masked-morph/native_posix_64.keymap b/app/tests/mod-morph/2a-masked-morph/native_posix_64.keymap new file mode 100644 index 00000000..9ad50202 --- /dev/null +++ b/app/tests/mod-morph/2a-masked-morph/native_posix_64.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; diff --git a/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/events.patterns b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/keycode_events.snapshot b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/keycode_events.snapshot new file mode 100644 index 00000000..ce85f25d --- /dev/null +++ b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x02 +reg implicit: Modifiers set to 0x02 +mask mods: Modifiers set to 0x00 +pressed: keycode 0x05 implicit_mods 0x02 explicit_mods 0x00 +reg implicit: Modifiers set to 0x02 +released: keycode 0x05 implicit_mods 0x02 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x00 +unmask mods: Modifiers set to 0x02 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/native_posix_64.keymap b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/native_posix_64.keymap new file mode 100644 index 00000000..74de8588 --- /dev/null +++ b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/native_posix_64.keymap @@ -0,0 +1,37 @@ +#include +#include +#include + +/ { + behaviors { + mod_morph: mod_morph { + compatible = "zmk,behavior-mod-morph"; + label = "MOD_MORPH_TEST"; + #binding-cells = <0>; + bindings = <&kp A>, <&kp LS(B)>; // implict mod overwrite + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp LEFT_ALT &mod_morph + &kp LEFT_SHIFT &kp RIGHT_SHIFT + >; + }; + }; +}; + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; + diff --git a/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/events.patterns b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/keycode_events.snapshot b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/keycode_events.snapshot new file mode 100644 index 00000000..561f88a9 --- /dev/null +++ b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/keycode_events.snapshot @@ -0,0 +1,18 @@ +pressed: keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x04 +reg implicit: Modifiers set to 0x04 +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x06 +reg implicit: Modifiers set to 0x06 +mask mods: Modifiers set to 0x04 +pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x04 +released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x04 +unmask mods: Modifiers set to 0x06 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x04 +unreg implicit: Modifiers set to 0x04 +released: keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/native_posix_64.keymap b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/native_posix_64.keymap new file mode 100644 index 00000000..d5406e19 --- /dev/null +++ b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/native_posix_64.keymap @@ -0,0 +1,15 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; diff --git a/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/events.patterns b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/keycode_events.snapshot b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/keycode_events.snapshot new file mode 100644 index 00000000..dcf2aae6 --- /dev/null +++ b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x02 +reg implicit: Modifiers set to 0x02 +mask mods: Modifiers set to 0x00 +pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x00 +released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x00 +unmask mods: Modifiers set to 0x02 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/native_posix_64.keymap b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/native_posix_64.keymap new file mode 100644 index 00000000..7071e8cd --- /dev/null +++ b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/native_posix_64.keymap @@ -0,0 +1,45 @@ +#include +#include +#include + +&kscan { + events = < + /* Shift + tap &mod_morph --> expect B (but get Shift + B) */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; + +/ { + behaviors { + mod_morph: mod_morph { + compatible = "zmk,behavior-mod-morph"; + label = "MOD_MORPH_TEST"; + #binding-cells = <0>; + bindings = <&kp A>, << 1 B>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp LEFT_SHIFT &mod_morph + &kp C &none + >; + }; + + second_layer { + bindings = < + &trans &trans + &kp D &trans + >; + }; + }; +}; diff --git a/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/events.patterns b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/keycode_events.snapshot b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/keycode_events.snapshot new file mode 100644 index 00000000..ba70ee98 --- /dev/null +++ b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x02 +reg implicit: Modifiers set to 0x02 +mask mods: Modifiers set to 0x00 +pressed: keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x00 +released: keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x00 +unmask mods: Modifiers set to 0x02 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/native_posix_64.keymap b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/native_posix_64.keymap new file mode 100644 index 00000000..96c2f270 --- /dev/null +++ b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/native_posix_64.keymap @@ -0,0 +1,47 @@ +#include +#include +#include + +&kscan { + events = < + /* Shift + hold &mod_morph --> expect and get D (no shift) */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(0,1,200) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; + +/ { + behaviors { + mod_morph: mod_morph { + compatible = "zmk,behavior-mod-morph"; + label = "MOD_MORPH_TEST"; + #binding-cells = <0>; + bindings = <&kp A>, << 1 B>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp LEFT_SHIFT &mod_morph + &kp C &none + >; + }; + + second_layer { + bindings = < + &trans &trans + &kp D &trans + >; + }; + }; +}; diff --git a/app/tests/mod-morph/3-unmasked-morph/events.patterns b/app/tests/mod-morph/3-unmasked-morph/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/3-unmasked-morph/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/3-unmasked-morph/keycode_events.snapshot b/app/tests/mod-morph/3-unmasked-morph/keycode_events.snapshot new file mode 100644 index 00000000..424242d5 --- /dev/null +++ b/app/tests/mod-morph/3-unmasked-morph/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x02 +reg implicit: Modifiers set to 0x02 +mask mods: Modifiers set to 0x02 +pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x02 +released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x02 +unmask mods: Modifiers set to 0x02 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/3-unmasked-morph/native_posix_64.keymap b/app/tests/mod-morph/3-unmasked-morph/native_posix_64.keymap new file mode 100644 index 00000000..a82d3ea7 --- /dev/null +++ b/app/tests/mod-morph/3-unmasked-morph/native_posix_64.keymap @@ -0,0 +1,37 @@ +#include +#include +#include + +/ { + behaviors { + mod_morph: mod_morph { + compatible = "zmk,behavior-mod-morph"; + label = "MOD_MORPH_TEST"; + #binding-cells = <0>; + bindings = <&kp A>, <&kp B>; + mods = <(MOD_LSFT|MOD_RSFT)>; + keep-mods = <(MOD_LSFT|MOD_RSFT)>; // no masking + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp LEFT_ALT &mod_morph + &kp LEFT_SHIFT &kp RIGHT_SHIFT + >; + }; + }; +}; + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; diff --git a/app/tests/mod-morph/behavior_keymap.dtsi b/app/tests/mod-morph/behavior_keymap.dtsi new file mode 100644 index 00000000..09720d8d --- /dev/null +++ b/app/tests/mod-morph/behavior_keymap.dtsi @@ -0,0 +1,23 @@ +/ { + behaviors { + mod_morph: mod_morph { + compatible = "zmk,behavior-mod-morph"; + label = "MOD_MORPH_TEST"; + #binding-cells = <0>; + bindings = <&kp A>, <&kp B>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp LEFT_ALT &mod_morph + &kp LEFT_SHIFT &kp RIGHT_SHIFT + >; + }; + }; +}; diff --git a/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot index 43edb961..45719679 100644 --- a/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot +++ b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot @@ -7,7 +7,7 @@ mods: Modifiers set to 0x03 released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00 unreg: Modifier 0 count: 0 unreg: Modifier 0 released -unreg: Modifiers set to 0x00 +unreg: Modifiers set to 0x02 mods: Modifiers set to 0x00 released: usage_page 0x07 keycode 0x05 implicit_mods 0x02 explicit_mods 0x00 mods: Modifiers set to 0x00 diff --git a/docs/docs/behaviors/mod-morph.md b/docs/docs/behaviors/mod-morph.md index 2606aaf8..33d18e33 100644 --- a/docs/docs/behaviors/mod-morph.md +++ b/docs/docs/behaviors/mod-morph.md @@ -14,8 +14,6 @@ The Mod-Morph behavior sends a different keypress, depending on whether a specif The Mod-Morph behavior acts as one of two keycodes, depending on if the required modifier is being held during the keypress. -When the modifier is being held it is sent along with the morphed keycode. This can cause problems when the morphed keycode and modifier have an existing relationship (such as `shift-delete` or `ctrl-v` on many operating systems). - ### Configuration An example of how to implement the mod-morph "Grave Escape": @@ -31,10 +29,6 @@ An example of how to implement the mod-morph "Grave Escape": mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>; }; }; - - keymap { - ... - }; }; ``` @@ -71,3 +65,26 @@ Example: ``` mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>; ``` + +### Advanced configuration + +`keep-mods` + +When a modifier specified in `mods` is being held, it won't be sent along with the morphed keycode unless it is also specified in `keep-mods`. By default `keep-mods` equals `0`, which means no modifier specified in `mods` will be sent along with the morphed keycode. + +For example, the following configuration morphs `LEFT_SHIFT` + `BACKSPACE` into `DELETE`, and morphs `RIGHT_SHIFT` + `BACKSPACE` into `RIGHT_SHIFT` + `DELETE`. + +``` +/ { + behaviors { + bspc_del: backspace_delete { + compatible = "zmk,behavior-mod-morph"; + label = "BACKSPACE_DELETE"; + #binding-cells = <0>; + bindings = <&kp BACKSPACE>, <&kp DELETE>; + mods = <(MOD_LSFT|MOD_RSFT)>; + keep-mods = <(MOD_RSFT)>; + }; + }; +}; +```