diff --git a/changes/api/+latches-sometimes-break-other-latches.breaking.md b/changes/api/+latches-sometimes-break-other-latches.breaking.md new file mode 100644 index 00000000..ca362884 --- /dev/null +++ b/changes/api/+latches-sometimes-break-other-latches.breaking.md @@ -0,0 +1,3 @@ +Changed latching behavior so that latching a modifier or group breaks existing modifier latches, +but only if the type of the key responsible for the latter latch +has the modifier of the pre-existing latch in its modifiers list. diff --git a/src/state.c b/src/state.c index 094126ae..9fe99ee7 100644 --- a/src/state.c +++ b/src/state.c @@ -173,7 +173,7 @@ xkb_state_key_get_layout(struct xkb_state *state, xkb_keycode_t kc) return XkbWrapGroupIntoRange(state->components.group, key->num_groups, key->out_of_range_group_action, - key->out_of_range_group_number); +key->out_of_range_group_number); } static unsigned int @@ -312,8 +312,9 @@ xkb_filter_group_lock_func(struct xkb_state *state, return XKB_FILTER_CONTINUE; } +/* Mod latches have additional break conditions not handled by this function */ static bool -xkb_action_breaks_latch(const union xkb_action *action, bool is_group_latch) +xkb_action_breaks_latch(const union xkb_action *action) { switch (action->type) { case ACTION_TYPE_NONE: @@ -568,11 +569,31 @@ xkb_filter_mod_latch_func(struct xkb_state *state, } else if (xkb_action_breaks_latch(&(actions[k]))) { /* XXX: This may be totally broken, we might need to break the - * latch in the next run after this press? */ + * latch in the next run after this press? */ state->components.latched_mods &= ~filter->action.mods.mods.mask; filter->func = NULL; return XKB_FILTER_CONTINUE; } + else if (actions->type == ACTION_TYPE_GROUP_LATCH || + actions->type == ACTION_TYPE_MOD_LATCH) { + /* We break latches only for mods that are part of the type's mod mask. */ + xkb_layout_index_t group = xkb_state_xkb_key_get_layout(state, key); + const struct xkb_key_type *type = key->groups[group].type; + xkb_mod_mask_t type_mod_mask = type->mods.mask; + + xkb_mod_mask_t filter_mod_mask = filter->action.mods.mods.mask; + xkb_mod_mask_t mods_to_unlatch_mask = filter_mod_mask & type_mod_mask; + xkb_mod_mask_t mods_to_keep_mask = filter_mod_mask & ~type_mod_mask; + + state->components.latched_mods &= ~mods_to_unlatch_mask; + if (mods_to_keep_mask == 0) { + filter->func = NULL; + } else { + filter->action.mods.mods.mask = mods_to_keep_mask; + } + + return XKB_FILTER_CONTINUE; + } } } else if (direction == XKB_KEY_UP && key == filter->key) { diff --git a/test/data/symbols/latch b/test/data/symbols/latch new file mode 100644 index 00000000..fc7702e2 --- /dev/null +++ b/test/data/symbols/latch @@ -0,0 +1,31 @@ +default partial alphanumeric_keys +xkb_symbols "base" { + name[Group1] = "Test latching behavior"; + + key { [ 1, exclam, NoSymbol, NoSymbol, plus ], type[Group1]="CTRL+ALT"}; + + key { [ q, Q ], type[Group1] = "ALPHABETIC" }; + + key { [ Caps_Lock ] }; + + key { + symbols[Group1] = [ Shift_L, Caps_Lock ], + actions[Group1] = [ LatchMods(modifiers=Shift), LockMods(modifiers=Lock) ] + }; + + key { + symbols[Group1] = [ Control_L ], + actions[Group1] = [ LatchMods(modifiers=Control) ] + }; + + key { + symbols[Group1] = [ Alt_L ], + actions[Group1] = [ LatchMods(modifiers=Alt) ] + }; + + key { + symbols[Group1] = [ Shift_R, Shift_R, Shift_R, Shift_R, Shift_R ], + actions[Group1] = [ LatchMods(modifiers=Lock), LatchMods(modifiers=Lock), LatchMods(modifiers=Lock), LatchMods(modifiers=Lock), LatchMods(modifiers=Lock) ], + type[Group1]="CTRL+ALT" + }; +}; diff --git a/test/keyseq.c b/test/keyseq.c index 29c80d07..3ef9e25c 100644 --- a/test/keyseq.c +++ b/test/keyseq.c @@ -365,6 +365,79 @@ test_explicit_actions(struct xkb_context *ctx) } } +static void +test_latch_mod_cancel(struct xkb_context *context) +{ + struct xkb_keymap *keymap; + + keymap = test_compile_rules(context, "evdev", "evdev", "latch", "", ""); + assert(keymap); + + assert(test_key_seq(keymap, + KEY_Q, BOTH, XKB_KEY_q, + NEXT, KEY_1, BOTH, XKB_KEY_1, + + // Basic latch/unlatch + + NEXT, KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L, // Latch Shift + NEXT, KEY_Q , BOTH, XKB_KEY_Q , // Unlatch Shift + NEXT, KEY_Q , BOTH, XKB_KEY_q , + + NEXT, KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L, // Latch Shift + NEXT, KEY_1 , BOTH, XKB_KEY_exclam , // Unlatch Shift + NEXT, KEY_1 , BOTH, XKB_KEY_1 , + + // Lock/unlock cancels latch + + NEXT, KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , // Latch Shift + NEXT, KEY_LEFTSHIFT , BOTH, XKB_KEY_Caps_Lock, // Lock Caps, unlatch Shift + NEXT, KEY_Q , BOTH, XKB_KEY_Q , + NEXT, KEY_1 , BOTH, XKB_KEY_1 , + NEXT, KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , // Latch Shift + NEXT, KEY_1 , BOTH, XKB_KEY_exclam , // Unlatch Shift + NEXT, KEY_1 , BOTH, XKB_KEY_1 , + NEXT, KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , // Latch Shift + NEXT, KEY_Q , BOTH, XKB_KEY_q , // Unlatch Shift + NEXT, KEY_Q , BOTH, XKB_KEY_Q , + NEXT, KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , // Latch Shift + NEXT, KEY_LEFTSHIFT , BOTH, XKB_KEY_Caps_Lock, // Unlock Caps, unlatch Shift + NEXT, KEY_Q , BOTH, XKB_KEY_q , + + // Double latch/unlatch + + NEXT, KEY_LEFTCTRL , BOTH, XKB_KEY_Control_L, // Latch Control + NEXT, KEY_LEFTALT , BOTH, XKB_KEY_Alt_L , // Latch Alt + NEXT, KEY_1 , BOTH, XKB_KEY_plus , // Unlatch Control, Unlatch Alt + + NEXT, KEY_RIGHTSHIFT, BOTH, XKB_KEY_Shift_R , // Latch Lock + NEXT, KEY_LEFTCTRL , BOTH, XKB_KEY_Control_L, // Latch Control + NEXT, KEY_LEFTALT , BOTH, XKB_KEY_Alt_L , // Latch Alt + NEXT, KEY_1 , BOTH, XKB_KEY_plus , // Unlatch Control, Unlatch Lock, Unlatch Alt + NEXT, KEY_Q , BOTH, XKB_KEY_q , + + NEXT, KEY_LEFTALT , BOTH, XKB_KEY_Alt_L , // Latch Alt + NEXT, KEY_RIGHTSHIFT, BOTH, XKB_KEY_Shift_R , // Latch Lock, unlatch Alt + NEXT, KEY_LEFTCTRL , BOTH, XKB_KEY_Control_L, // Latch Control + NEXT, KEY_1 , BOTH, XKB_KEY_1 , // Unlatch Control, Unlatch Lock + NEXT, KEY_Q , BOTH, XKB_KEY_q , + + NEXT, KEY_LEFTALT , BOTH, XKB_KEY_Alt_L , // Latch Alt + NEXT, KEY_LEFTCTRL , BOTH, XKB_KEY_Control_L, // Latch Control + NEXT, KEY_RIGHTSHIFT, BOTH, XKB_KEY_Shift_R , // Latch Lock, Unlatch Control, Unlatch Alt + NEXT, KEY_1 , BOTH, XKB_KEY_1 , // Unlatch Lock + NEXT, KEY_Q , BOTH, XKB_KEY_q , + + NEXT, KEY_LEFTALT , BOTH, XKB_KEY_Alt_L , // Latch Alt + NEXT, KEY_LEFTCTRL , BOTH, XKB_KEY_Control_L, // Latch Control + NEXT, KEY_RIGHTSHIFT, BOTH, XKB_KEY_Shift_R , // Latch Lock, Unlatch Control, Unlatch Alt + NEXT, KEY_Q , BOTH, XKB_KEY_Q , // Unlatch Lock + NEXT, KEY_Q , BOTH, XKB_KEY_q , + + FINISH)); + + xkb_keymap_unref(keymap); +} + int main(void) { @@ -377,6 +450,7 @@ main(void) test_group_latch(ctx); test_explicit_actions(ctx); + test_latch_mod_cancel(ctx); keymap = test_compile_rules(ctx, "evdev", "evdev", "us,il,ru,de", ",,phonetic,neo",