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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions