Skip to content

&key_repeat does not repeat some mod-morph keys #3368

Description

@bertram7771edgcomb

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions