Skip to content

Commit

Permalink
Break latches upon setting/releasing a lock with latchToLock/`clear…
Browse files Browse the repository at this point in the history
…Locks`

Signed-off-by: Jules Bertholet <[email protected]>
  • Loading branch information
Jules-Bertholet committed Dec 23, 2024
1 parent 63db755 commit dc4277e
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 11 deletions.
1 change: 1 addition & 0 deletions changes/api/+locks-break-latches.breaking.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Changed modifier and group latches so that setting or releasing a lock now breaks them.
(This includes setting a lock via `latchToLock`, or clearing it via `clearLocks`.)
This enables implementing a key that, for example, latches `Shift`
when pressed once, but locks `Caps` when pressed twice in succession.
37 changes: 28 additions & 9 deletions src/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,11 @@ xkb_filter_group_set_func(struct xkb_state *state,

state->components.base_group = filter->priv;

if (filter->action.group.flags & ACTION_LOCK_CLEAR)
if (filter->action.group.flags & ACTION_LOCK_CLEAR && state->components.locked_group) {
state->components.locked_group = 0;
// break all mod latches
state->components.latched_mods = 0;
}

filter->func = NULL;
return XKB_FILTER_CONTINUE;
Expand Down Expand Up @@ -405,6 +408,8 @@ xkb_filter_group_latch_func(struct xkb_state *state,
filter->action.type = ACTION_TYPE_GROUP_LOCK;
filter->func = xkb_filter_group_lock_func;
xkb_filter_group_lock_new(state, filter);
// break all mod latches
state->components.latched_mods = 0;
}
else {
/* Degrade to plain set */
Expand Down Expand Up @@ -441,8 +446,8 @@ xkb_filter_group_latch_func(struct xkb_state *state,
}
else if (direction == XKB_KEY_UP && key == filter->key) {
/* Our key got released. If we've set it to clear locks, and we
* currently have a group locked, then release it and
* don't actually latch. Else we've actually hit the latching
* currently have a group locked, then release it, break all mod latches,
* and don't actually latch. Else we've actually hit the latching
* stage, so set PENDING and move our group from base to
* latched. */
if (latch == NO_LATCH ||
Expand All @@ -452,8 +457,11 @@ xkb_filter_group_latch_func(struct xkb_state *state,
state->components.latched_group -= priv.group_delta;
else
state->components.base_group -= priv.group_delta;
if (filter->action.group.flags & ACTION_LOCK_CLEAR)
if (filter->action.group.flags & ACTION_LOCK_CLEAR) {
state->components.locked_group = 0;
// break all mod latches
state->components.latched_mods = 0;
}
filter->func = NULL;
}
else {
Expand Down Expand Up @@ -497,8 +505,11 @@ xkb_filter_mod_set_func(struct xkb_state *state,
}

state->clear_mods = filter->action.mods.mods.mask;
if (filter->action.mods.flags & ACTION_LOCK_CLEAR)
if (filter->action.mods.flags & ACTION_LOCK_CLEAR && state->components.locked_mods & filter->action.mods.mods.mask) {
state->components.locked_mods &= ~filter->action.mods.mods.mask;
// break all mod latches
state->components.latched_mods = 0;
}

filter->func = NULL;
return XKB_FILTER_CONTINUE;
Expand Down Expand Up @@ -570,6 +581,8 @@ xkb_filter_mod_latch_func(struct xkb_state *state,
filter->action.type = ACTION_TYPE_MOD_LOCK;
filter->func = xkb_filter_mod_lock_func;
state->components.locked_mods |= filter->action.mods.mods.mask;
// break all mod latches
state->components.latched_mods = 0;
}
else {
filter->action.type = ACTION_TYPE_MOD_SET;
Expand Down Expand Up @@ -642,9 +655,10 @@ xkb_filter_mod_latch_func(struct xkb_state *state,
}
else if (direction == XKB_KEY_UP && key == filter->key) {
/* Our key got released. If we've set it to clear locks, and we
* currently have the same modifiers locked, then release them and
* don't actually latch. Else we've actually hit the latching
* stage, so set PENDING and move our modifier from base to
* currently have the same modifiers locked, then release them,
* break all mod latches, and don't actually latch.
* Else we've actually hit the latching stage,
* so set PENDING and move our modifier from base to
* latched. */
if (latch == NO_LATCH ||
((filter->action.mods.flags & ACTION_LOCK_CLEAR) &&
Expand All @@ -657,7 +671,12 @@ xkb_filter_mod_latch_func(struct xkb_state *state,
~filter->action.mods.mods.mask;
else
state->clear_mods = filter->action.mods.mods.mask;
state->components.locked_mods &= ~filter->action.mods.mods.mask;
if (filter->action.mods.flags & ACTION_LOCK_CLEAR &&
state->components.locked_mods & filter->action.mods.mods.mask) {
state->components.locked_mods &= ~filter->action.mods.mods.mask;
// break all mod latches
state->components.latched_mods = 0;
}
filter->func = NULL;
}
else {
Expand Down
8 changes: 6 additions & 2 deletions test/data/symbols/latch
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ xkb_symbols "base" {

key <AD01> { [ q, Q ], type[Group1] = "ALPHABETIC" };

key <AC01> { [ a, A, ISO_Level5_Latch, NoSymbol ], type[Group1] = "FOUR_LEVEL_SEMIALPHABETIC" };
key <AC01> { [ a, A, ISO_Level5_Latch, minus ], type[Group1] = "FOUR_LEVEL_SEMIALPHABETIC" };

key <CAPS> { [ Caps_Lock ], type[Group1] = "ONE_LEVEL" };

Expand All @@ -28,7 +28,11 @@ xkb_symbols "base" {
type[Group1] = "ONE_LEVEL"
};

key <RCTL> { [ ISO_Level3_Latch ], type[Group1] = "ONE_LEVEL" };
key <RCTL> {
symbols[Group1] = [ ISO_Level3_Latch ],
actions[Group1] = [ LatchMods(modifiers=LevelThree,latchToLock,clearLocks) ],
type[Group1] = "ONE_LEVEL"
};

key <RTSH> {
symbols[Group1] = [ Shift_R, Shift_R, Shift_R, Shift_R, Shift_R ],
Expand Down
22 changes: 22 additions & 0 deletions test/keyseq.c
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,28 @@ test_latch_mod_cancel(struct xkb_context *context)
NEXT, KEY_Q , BOTH, XKB_KEY_Q , // Unlatch Lock, unlatch LevelFive
NEXT, KEY_Q , BOTH, XKB_KEY_q ,

// `latchToLock` locks and `clearLocks` unlocks break existing latches

NEXT, KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , // Latch Shift
NEXT, KEY_RIGHTCTRL , BOTH, XKB_KEY_ISO_Level3_Latch, // Latch LevelThree
NEXT, KEY_A , BOTH, XKB_KEY_minus , // Unlatch Shift, unlatch LevelThree

NEXT, KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , // Latch Shift
NEXT, KEY_RIGHTCTRL , BOTH, XKB_KEY_ISO_Level3_Latch, // Latch LevelThree
NEXT, KEY_RIGHTCTRL , BOTH, XKB_KEY_ISO_Level3_Latch, // Lock LevelThree, unlatch Shift
NEXT, KEY_A , BOTH, XKB_KEY_ISO_Level5_Latch, // Latch LevelFive
NEXT, KEY_Q , BOTH, XKB_KEY_q , // Unlatch LevelFive
NEXT, KEY_A , BOTH, XKB_KEY_ISO_Level5_Latch, // Latch LevelFive
NEXT, KEY_Q , BOTH, XKB_KEY_q , // Unlatch LevelFive
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_RIGHTCTRL , BOTH, XKB_KEY_ISO_Level3_Latch, // Unlock LevelThree, unlatch Shift
NEXT, KEY_A , BOTH, XKB_KEY_a ,



FINISH));

xkb_keymap_unref(keymap);
Expand Down

0 comments on commit dc4277e

Please sign in to comment.