Stop shortcut repeat when modifiers change

915709c54aa4a1c7f98913d0e9bb431c8eab1dd4
Alexis Sellier committed ago 1 parent 10145e82
swm.c +13 -5
3903 3903
    wl_event_source_timer_update(
3904 3904
        group->key_repeat_source, group->wlr_group->keyboard.repeat_info.delay
3905 3905
    );
3906 3906
}
3907 3907
3908 +
/* Clear the active shortcut and disarm its repeat timer. */
3909 +
static void key_repeat_stop(keyboard_group_t *group) {
3910 +
    group->nsyms          = 0;
3911 +
    group->repeat_keycode = 0;
3912 +
    wl_event_source_timer_update(group->key_repeat_source, 0);
3913 +
}
3914 +
3908 3915
/* Return whether this event supersedes the active shortcut repeat. */
3909 3916
static bool key_repeat_should_stop(
3910 3917
    keyboard_group_t *group, const struct wlr_keyboard_key_event *event
3911 3918
) {
3912 3919
    if (!group->nsyms)
3949 3956
        for (i = 0; i < nsyms; i++)
3950 3957
            handled |= key_binding(mods, syms[i], 0);
3951 3958
    }
3952 3959
    if ((handled & 2) && group->wlr_group->keyboard.repeat_info.delay > 0)
3953 3960
        key_repeat_begin(group, syms, nsyms, mods, event->keycode);
3954 -
    else if (key_repeat_should_stop(group, event)) {
3955 -
        group->nsyms          = 0;
3956 -
        group->repeat_keycode = 0;
3957 -
        wl_event_source_timer_update(group->key_repeat_source, 0);
3958 -
    }
3961 +
    else if (key_repeat_should_stop(group, event))
3962 +
        key_repeat_stop(group);
3959 3963
    if (!key_event_should_forward(group, event->keycode, event->state, handled))
3960 3964
        return;
3961 3965
3962 3966
    wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
3963 3967
    /* Send unhandled keys to the input method or focused application. */
3974 3978
void key_press_modifiers(struct wl_listener *listener, void *data) {
3975 3979
    /* A modifier such as Shift or Alt changed. */
3976 3980
    keyboard_group_t                         *group = wl_container_of(listener, group, modifiers);
3977 3981
    struct wlr_input_method_keyboard_grab_v2 *grab;
3978 3982
3983 +
    if (group->nsyms && CLEANMASK(group->mods) !=
3984 +
                            CLEANMASK(wlr_keyboard_get_modifiers(&group->wlr_group->keyboard)))
3985 +
        key_repeat_stop(group);
3986 +
3979 3987
    wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
3980 3988
    /* Send modifiers to the input method or focused application. */
3981 3989
    if ((grab = input_method_grab(group))) {
3982 3990
        wlr_input_method_keyboard_grab_v2_set_keyboard(grab, &group->wlr_group->keyboard);
3983 3991
        wlr_input_method_keyboard_grab_v2_send_modifiers(
test/clients.py +48 -0
905 905
            lock.destroy()
906 906
        connection.roundtrips()
907 907
    connection.close()
908 908
909 909
910 +
def shortcut_repeat() -> None:
911 +
    """Stop a held focus shortcut when its modifier is released."""
912 +
913 +
    connection = Connection(WlCompositor, WlShm, WlSeat, XdgWmBase, ZwpVirtualKeyboardManagerV1)
914 +
    seat = connection.get(WlSeat)
915 +
    virtual = connection.get(ZwpVirtualKeyboardManagerV1).create_virtual_keyboard(seat)
916 +
    keymap, indices = keymap_text()
917 +
    with tempfile.TemporaryFile(dir=os.environ.get("SWM_TEST_DIR")) as stream:
918 +
        stream.write(keymap + b"\0")
919 +
        stream.flush()
920 +
        virtual.keymap(WlKeyboard.keymap_format.xkb_v1, stream.fileno(), len(keymap) + 1)
921 +
    connection.roundtrips()
922 +
    keyboard = seat.get_keyboard()
923 +
    focused = []
924 +
    repeat = []
925 +
    keyboard.dispatcher["enter"] = lambda proxy, serial, surface, keys: focused.append(surface)
926 +
    keyboard.dispatcher["repeat_info"] = lambda proxy, rate, delay: repeat.append((rate, delay))
927 +
    first = Window(connection, "repeat-first", 0xFF224466)
928 +
    second = Window(connection, "repeat-second", 0xFF336699)
929 +
    connection.roundtrips(4)
930 +
    configured = int((Path(os.environ["SWM_TEST_BUILD"]) / "modkey").read_text())
931 +
    modifier, code = ("alt", 56) if configured == 8 else ("logo", 125)
932 +
    virtual.key(0, code, 1)
933 +
    virtual.modifiers(1 << indices[modifier], 0, 0, 0)
934 +
    virtual.key(1, 36, 1)
935 +
    connection.roundtrips(2)
936 +
    count = len(focused)
937 +
    rate, delay = repeat[-1]
938 +
    time.sleep(delay / 1000 + 2 / rate)
939 +
    connection.roundtrips(3)
940 +
    if len(focused) <= count:
941 +
        raise Failure("held focus shortcut did not repeat")
942 +
    virtual.key(2, code, 0)
943 +
    virtual.modifiers(0, 0, 0, 0)
944 +
    connection.roundtrips(2)
945 +
    count = len(focused)
946 +
    time.sleep(3 / rate)
947 +
    connection.roundtrips(3)
948 +
    if len(focused) != count:
949 +
        raise Failure("focus shortcut kept repeating after modifier release")
950 +
    virtual.key(3, 36, 0)
951 +
    first.destroy()
952 +
    second.destroy()
953 +
    virtual.destroy()
954 +
    connection.close()
955 +
956 +
910 957
def input_method_keys() -> None:
911 958
    """Check keyboard grabs and input-method key reinjection."""
912 959
913 960
    connection = Connection(
914 961
        WlCompositor, WlShm, WlSeat, XdgWmBase,
1120 1167
    popup_surface.destroy()
1121 1168
    text.destroy()
1122 1169
    method.destroy()
1123 1170
    connection.close()
1124 1171
    input_method_keys()
1172 +
    shortcut_repeat()
1125 1173
1126 1174
1127 1175
def x11(arguments: list[str]) -> None:
1128 1176
    """Exercise managed, configured, fullscreen, and unmanaged X11 windows."""
1129 1177