Pressing &key_repeat after using a mod-morph key yields the wrong result. This only affects some mod-morph combinations.
For example:
Consider the keymap with a mod-morph key with . and : (=Shift+.) and a mod-morph key with , and ; (= Shift+,) and a &key_repeat key and a &kp LSHIFT key.
- Pressing
. then &key_repeat produces .. as expected
- Holding
LSHIFT, pressing ., releasing LSHIFT, then &key_repeat produces :: as expected.
- However, holding
LSHIFT, pressing ,, releasing LSHIFT, then &key_repeat produces ;: (semicolon colon), but it should be ;; (two semicolons).
Affected versions ZMK v3.0 and ed56d81 (current main as of writing)
Below is a test case, to be placed in path/to/zmk/app/tests/key-repeat/mod-morph-compatibility, which can be run with
cd path/to/zmk/app
./run-test.sh tests/key-repeat/mod-morph-compatibility
Note: The test will fail because of additional comments and empty files in the snapshot file but these should be the only errors.
File native_posix_64.keymap (ZMK v3.0)
File native_sim.keymap (ZMK v4.0)
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
behaviors {
kp_DotColon: kp_DotColon {
compatible = "zmk,behavior-mod-morph";
#binding-cells = <0>;
bindings = <&kp DOT>, <&kp COLON>;
mods = <(MOD_LSFT|MOD_RSFT)>;
};
kp_CommaSemicolon: kp_CommaSemicolon {
compatible = "zmk,behavior-mod-morph";
#binding-cells = <0>;
bindings = <&kp COMMA>, <&kp SEMICOLON>;
mods = <(MOD_LSFT|MOD_RSFT)>;
};
};
keymap {
compatible = "zmk,keymap";
default_layer {
bindings = <
&key_repeat &kp LSHIFT
&kp_DotColon &kp_CommaSemicolon
>;
};
};
};
&kscan {
events = <
// dot repeat
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
// comma repeat
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
// colon repeat
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
// semicolon repeat
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};
File keycode_events.snapshot.
- this file contains additional comments for clarity. Sadly these let the test always fail.
- Be aware: I'm not entirely sure what the expected output should be but I guess that
&key_repeat should produce the same "press" and "release" events as the previous press.
// DOT = 0x37
pressed: usage_page 0x07 keycode 0x37 implicit_mods 0x00 explicit_mods 0x00
press: Modifiers set to 0x00
released: usage_page 0x07 keycode 0x37 implicit_mods 0x00 explicit_mods 0x00
release: Modifiers set to 0x00
pressed: usage_page 0x07 keycode 0x37 implicit_mods 0x00 explicit_mods 0x00
press: Modifiers set to 0x00
released: usage_page 0x07 keycode 0x37 implicit_mods 0x00 explicit_mods 0x00
release: Modifiers set to 0x00
// COMMA = 0x36
pressed: usage_page 0x07 keycode 0x36 implicit_mods 0x00 explicit_mods 0x00
press: Modifiers set to 0x00
released: usage_page 0x07 keycode 0x36 implicit_mods 0x00 explicit_mods 0x00
release: Modifiers set to 0x00
pressed: usage_page 0x07 keycode 0x36 implicit_mods 0x00 explicit_mods 0x00
press: Modifiers set to 0x00
released: usage_page 0x07 keycode 0x36 implicit_mods 0x00 explicit_mods 0x00
release: Modifiers set to 0x00
// COLON = (0x33 + implicit shift modifier)
// LEFT_SHIFT = 0xE1
pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
press: Modifiers set to 0x02
pressed: usage_page 0x07 keycode 0x33 implicit_mods 0x02 explicit_mods 0x00
press: Modifiers set to 0x02
released: usage_page 0x07 keycode 0x33 implicit_mods 0x02 explicit_mods 0x00
release: Modifiers set to 0x00
released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
release: Modifiers set to 0x00
pressed: usage_page 0x07 keycode 0x33 implicit_mods 0x02 explicit_mods 0x00
press: Modifiers set to 0x02
released: usage_page 0x07 keycode 0x33 implicit_mods 0x02 explicit_mods 0x00
release: Modifiers set to 0x00
// SEMICOLON = 0x33
// LEFT_SHIFT = 0xE1
pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
press: Modifiers set to 0x02
pressed: usage_page 0x07 keycode 0x33 implicit_mods 0x00 explicit_mods 0x00
press: Modifiers set to 0x00
released: usage_page 0x07 keycode 0x33 implicit_mods 0x00 explicit_mods 0x00
release: Modifiers set to 0x00
released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
release: Modifiers set to 0x00
// same press/released events as between the previous Shift press and release event
pressed: usage_page 0x07 keycode 0x33 implicit_mods 0x00 explicit_mods 0x00
press: Modifiers set to 0x00
released: usage_page 0x07 keycode 0x33 implicit_mods 0x00 explicit_mods 0x00
release: Modifiers set to 0x00
File events.patterns
s/.*hid_listener_keycode_//p
s/.*hid_implicit_modifiers_//p
Pressing
&key_repeatafter using amod-morphkey yields the wrong result. This only affects somemod-morphcombinations.For example:
Consider the keymap with a mod-morph key with
.and:(=Shift+.) and a mod-morph key with,and;(=Shift+,) and a&key_repeatkey and a&kp LSHIFTkey..then&key_repeatproduces..as expectedLSHIFT, pressing., releasingLSHIFT, then&key_repeatproduces::as expected.LSHIFT, pressing,, releasingLSHIFT, then&key_repeatproduces;:(semicolon colon), but it should be;;(two semicolons).Affected versions ZMK v3.0 and ed56d81 (current main as of writing)
Below is a test case, to be placed in
path/to/zmk/app/tests/key-repeat/mod-morph-compatibility, which can be run withNote: The test will fail because of additional comments and empty files in the snapshot file but these should be the only errors.
File
native_posix_64.keymap(ZMK v3.0)File
native_sim.keymap(ZMK v4.0)File
keycode_events.snapshot.&key_repeatshould produce the same "press" and "release" events as the previous press.File
events.patterns