Skip to content

Commit 975462c

Browse files
Break latches upon changing a lock with latchToLock/clearLocks
Signed-off-by: Jules Bertholet <[email protected]>
1 parent e0f5a89 commit 975462c

File tree

4 files changed

+132
-48
lines changed

4 files changed

+132
-48
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
Changed modifier and group latches so that setting or releasing a lock now breaks them.
2+
(This includes setting a lock via `latchToLock`, or clearing it via `clearLocks`.)
23
This enables implementing a key that, for example, latches `Shift`
34
when pressed once, but locks `Caps` when pressed twice in succession.

src/state.c

Lines changed: 73 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,57 @@ enum xkb_filter_result {
221221
else \
222222
(state_)->components.component_ += (filter_)->action.group.group
223223

224+
enum xkb_key_latch_state {
225+
NO_LATCH = 0,
226+
LATCH_KEY_DOWN,
227+
LATCH_PENDING,
228+
_KEY_LATCH_STATE_NUM_ENTRIES
229+
};
230+
231+
#define MAX_XKB_KEY_LATCH_STATE_LOG2 2
232+
#if (_KEY_LATCH_STATE_NUM_ENTRIES > (1 << MAX_XKB_KEY_LATCH_STATE_LOG2)) || \
233+
(-XKB_MAX_GROUPS) < (INT32_MIN >> MAX_XKB_KEY_LATCH_STATE_LOG2) || \
234+
XKB_MAX_GROUPS > (INT32_MAX >> MAX_XKB_KEY_LATCH_STATE_LOG2)
235+
#error "Cannot represent priv field of the group latch filter"
236+
#endif
237+
238+
/* Hold the latch state *and* the group delta */
239+
union group_latch_priv {
240+
uint32_t priv;
241+
struct {
242+
/* The type is really: enum xkb_key_latch_state, but it is problematic
243+
* on Windows, because it is interpreted as signed and leads to wrong
244+
* negative values. */
245+
unsigned int latch:MAX_XKB_KEY_LATCH_STATE_LOG2;
246+
int32_t group_delta:(32 - MAX_XKB_KEY_LATCH_STATE_LOG2);
247+
};
248+
};
249+
250+
static void
251+
xkb_break_all_latches(struct xkb_state *state) {
252+
struct xkb_filter *filter;
253+
254+
darray_foreach(filter, state->filters) {
255+
if (filter->action.type == ACTION_TYPE_MOD_LATCH) {
256+
if (filter->priv == LATCH_PENDING) {
257+
filter->func = NULL;
258+
} else {
259+
filter->priv = NO_LATCH;
260+
}
261+
} else if (filter->action.type == ACTION_TYPE_GROUP_LATCH) {
262+
union group_latch_priv priv = {.priv = filter->priv};
263+
if (priv.latch == LATCH_PENDING) {
264+
filter->func = NULL;
265+
} else {
266+
priv.latch = NO_LATCH;
267+
filter->priv = priv.priv;
268+
}
269+
}
270+
}
271+
state->components.latched_mods = 0;
272+
state->components.latched_group = 0;
273+
}
274+
224275
static void
225276
xkb_filter_group_set_new(struct xkb_state *state, struct xkb_filter *filter)
226277
{
@@ -249,8 +300,10 @@ xkb_filter_group_set_func(struct xkb_state *state,
249300

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

252-
if (filter->action.group.flags & ACTION_LOCK_CLEAR)
303+
if (filter->action.group.flags & ACTION_LOCK_CLEAR && state->components.locked_group) {
253304
state->components.locked_group = 0;
305+
xkb_break_all_latches(state);
306+
}
254307

255308
filter->func = NULL;
256309
return XKB_FILTER_CONTINUE;
@@ -304,32 +357,6 @@ xkb_action_breaks_latch(const union xkb_action *action)
304357
}
305358
}
306359

307-
enum xkb_key_latch_state {
308-
NO_LATCH = 0,
309-
LATCH_KEY_DOWN,
310-
LATCH_PENDING,
311-
_KEY_LATCH_STATE_NUM_ENTRIES
312-
};
313-
314-
#define MAX_XKB_KEY_LATCH_STATE_LOG2 2
315-
#if (_KEY_LATCH_STATE_NUM_ENTRIES > (1 << MAX_XKB_KEY_LATCH_STATE_LOG2)) || \
316-
(-XKB_MAX_GROUPS) < (INT32_MIN >> MAX_XKB_KEY_LATCH_STATE_LOG2) || \
317-
XKB_MAX_GROUPS > (INT32_MAX >> MAX_XKB_KEY_LATCH_STATE_LOG2)
318-
#error "Cannot represent priv field of the group latch filter"
319-
#endif
320-
321-
/* Hold the latch state *and* the group delta */
322-
union group_latch_priv {
323-
uint32_t priv;
324-
struct {
325-
/* The type is really: enum xkb_key_latch_state, but it is problematic
326-
* on Windows, because it is interpreted as signed and leads to wrong
327-
* negative values. */
328-
unsigned int latch:MAX_XKB_KEY_LATCH_STATE_LOG2;
329-
int32_t group_delta:(32 - MAX_XKB_KEY_LATCH_STATE_LOG2);
330-
};
331-
};
332-
333360
static void
334361
xkb_filter_group_latch_new(struct xkb_state *state, struct xkb_filter *filter)
335362
{
@@ -369,10 +396,10 @@ xkb_filter_group_latch_func(struct xkb_state *state,
369396
if (filter->action.group.flags & ACTION_LATCH_TO_LOCK &&
370397
filter->action.group.group != 0) {
371398
/* Promote to lock */
399+
xkb_break_all_latches(state); // Must be run *before* setting filter->func
372400
filter->action.type = ACTION_TYPE_GROUP_LOCK;
373401
filter->func = xkb_filter_group_lock_func;
374402
xkb_filter_group_lock_new(state, filter);
375-
state->components.latched_group -= priv.group_delta;
376403
filter->key = key;
377404
/* XXX beep beep! */
378405
return XKB_FILTER_CONSUME;
@@ -407,8 +434,8 @@ xkb_filter_group_latch_func(struct xkb_state *state,
407434
}
408435
else if (direction == XKB_KEY_UP && key == filter->key) {
409436
/* Our key got released. If we've set it to clear locks, and we
410-
* currently have a group locked, then release it and
411-
* don't actually latch. Else we've actually hit the latching
437+
* currently have a group locked, then release it, break all latches,
438+
* and don't actually latch. Else we've actually hit the latching
412439
* stage, so set PENDING and move our group from base to
413440
* latched. */
414441
if (latch == NO_LATCH ||
@@ -418,8 +445,10 @@ xkb_filter_group_latch_func(struct xkb_state *state,
418445
state->components.latched_group -= priv.group_delta;
419446
else
420447
state->components.base_group -= priv.group_delta;
421-
if (filter->action.group.flags & ACTION_LOCK_CLEAR)
448+
if (filter->action.group.flags & ACTION_LOCK_CLEAR && state->components.locked_group) {
422449
state->components.locked_group = 0;
450+
xkb_break_all_latches(state);
451+
}
423452
filter->func = NULL;
424453
}
425454
/* We may already have reached the latch state if pressing the
@@ -465,8 +494,10 @@ xkb_filter_mod_set_func(struct xkb_state *state,
465494
}
466495

467496
state->clear_mods |= filter->action.mods.mods.mask;
468-
if (filter->action.mods.flags & ACTION_LOCK_CLEAR)
497+
if (filter->action.mods.flags & ACTION_LOCK_CLEAR && state->components.locked_mods & filter->action.mods.mods.mask) {
469498
state->components.locked_mods &= ~filter->action.mods.mods.mask;
499+
xkb_break_all_latches(state);
500+
}
470501

471502
filter->func = NULL;
472503
return XKB_FILTER_CONTINUE;
@@ -538,6 +569,7 @@ xkb_filter_mod_latch_func(struct xkb_state *state,
538569
filter->action.type = ACTION_TYPE_MOD_LOCK;
539570
filter->func = xkb_filter_mod_lock_func;
540571
state->components.locked_mods |= filter->action.mods.mods.mask;
572+
xkb_break_all_latches(state);
541573
}
542574
else {
543575
filter->action.type = ACTION_TYPE_MOD_SET;
@@ -610,9 +642,10 @@ xkb_filter_mod_latch_func(struct xkb_state *state,
610642
}
611643
else if (direction == XKB_KEY_UP && key == filter->key) {
612644
/* Our key got released. If we've set it to clear locks, and we
613-
* currently have the same modifiers locked, then release them and
614-
* don't actually latch. Else we've actually hit the latching
615-
* stage, so set PENDING and move our modifier from base to
645+
* currently have the same modifiers locked, then release them,
646+
* break all latches, and don't actually latch.
647+
* Else we've actually hit the latching stage,
648+
* so set PENDING and move our modifier from base to
616649
* latched. */
617650
if (latch == NO_LATCH ||
618651
((filter->action.mods.flags & ACTION_LOCK_CLEAR) &&
@@ -625,7 +658,11 @@ xkb_filter_mod_latch_func(struct xkb_state *state,
625658
~filter->action.mods.mods.mask;
626659
else
627660
state->clear_mods |= filter->action.mods.mods.mask;
628-
state->components.locked_mods &= ~filter->action.mods.mods.mask;
661+
if (filter->action.mods.flags & ACTION_LOCK_CLEAR &&
662+
state->components.locked_mods & filter->action.mods.mods.mask) {
663+
state->components.locked_mods &= ~filter->action.mods.mods.mask;
664+
xkb_break_all_latches(state);
665+
}
629666
filter->func = NULL;
630667
}
631668
else {

test/data/symbols/latch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ xkb_symbols "modifiers" {
88

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

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

1313
key <LFSH> {
1414
symbols[Group1] = [ Shift_L ],

test/keyseq.c

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -750,8 +750,8 @@ test_mod_latch(struct xkb_context *context)
750750

751751
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Latch Shift */
752752
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Latch Level3 */
753-
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Lock Shift */
754-
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT, /* Unlatch Level3 */
753+
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Lock Shift, unlatch Level3 */
754+
KEY_1 , BOTH, XKB_KEY_exclam , NEXT,
755755
KEY_1 , BOTH, XKB_KEY_exclam , NEXT,
756756
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Unlock Shift */
757757
KEY_1 , BOTH, XKB_KEY_1 , NEXT,
@@ -770,12 +770,12 @@ test_mod_latch(struct xkb_context *context)
770770

771771
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Latch Shift */
772772
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Latch Level3 */
773-
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Lock Shift */
774-
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Lock Level3 */
775-
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT,
776-
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT,
777-
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Unlock Level3 */
773+
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Lock Shift, unlatch Level3 */
774+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Latch Level3 */
775+
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT, /* Unatch Level3 */
778776
KEY_1 , BOTH, XKB_KEY_exclam , NEXT,
777+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Latch Level3 */
778+
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT,
779779
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Unlock Shift */
780780
KEY_1 , BOTH, XKB_KEY_1 , FINISH
781781
));
@@ -860,17 +860,17 @@ test_mod_latch(struct xkb_context *context)
860860
KEY_RIGHTALT , DOWN, XKB_KEY_ISO_Level3_Latch, NEXT, /* Set Level3 */
861861
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Latch Shift */
862862
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Lock Shift */
863-
KEY_RIGHTALT , UP , XKB_KEY_ISO_Level3_Latch, NEXT, /* Latch Level3 */
864-
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT, /* Unlatch Level3 */
863+
KEY_RIGHTALT , UP , XKB_KEY_ISO_Level3_Latch, NEXT,
864+
KEY_1 , BOTH, XKB_KEY_exclam , NEXT,
865865
KEY_1 , BOTH, XKB_KEY_exclam , NEXT,
866866
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Unlock Shift */
867867
KEY_1 , BOTH, XKB_KEY_1 , NEXT,
868868

869869
KEY_RIGHTALT , DOWN, XKB_KEY_ISO_Level3_Latch, NEXT, /* Set Level3 */
870870
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Latch Shift */
871871
KEY_RIGHTALT , UP , XKB_KEY_ISO_Level3_Latch, NEXT, /* Latch Level3 */
872-
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Lock Shift */
873-
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT, /* Unlatch level 3*/
872+
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Lock Shift, unlatch Level3 */
873+
KEY_1 , BOTH, XKB_KEY_exclam , NEXT,
874874
KEY_1 , BOTH, XKB_KEY_exclam , NEXT,
875875
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, /* Unlock Shift */
876876
KEY_1 , BOTH, XKB_KEY_1 , FINISH
@@ -1124,6 +1124,52 @@ test_latch_mod_cancel(struct xkb_context *context)
11241124
KEY_Q , BOTH, XKB_KEY_q , FINISH
11251125
));
11261126

1127+
// `latchToLock` locks and `clearLocks` unlocks break existing latches...
1128+
assert(test_key_seq(keymap,
1129+
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, // Latch Shift
1130+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, // Latch LevelThree
1131+
KEY_A , BOTH, XKB_KEY_minus , NEXT, // Unlatch Shift, unlatch LevelThree
1132+
1133+
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, // Latch Shift
1134+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, // Latch LevelThree
1135+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, // Lock LevelThree, unlatch Shift
1136+
KEY_A , BOTH, XKB_KEY_ISO_Level5_Latch, NEXT, // Latch LevelFive
1137+
KEY_Q , BOTH, XKB_KEY_q , NEXT, // Unlatch LevelFive
1138+
KEY_A , BOTH, XKB_KEY_ISO_Level5_Latch, NEXT, // Latch LevelFive
1139+
KEY_Q , BOTH, XKB_KEY_q , NEXT, // Unlatch LevelFive
1140+
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, // Latch Shift
1141+
KEY_Q , BOTH, XKB_KEY_Q , NEXT, // Unlatch Shift
1142+
KEY_Q , BOTH, XKB_KEY_q , NEXT,
1143+
KEY_LEFTSHIFT , BOTH, XKB_KEY_Shift_L , NEXT, // Latch Shift
1144+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, // Unlock LevelThree, unlatch Shift
1145+
KEY_A , BOTH, XKB_KEY_a , FINISH
1146+
));
1147+
1148+
// ... but a latch key still sets while being held down
1149+
assert(test_key_seq(keymap,
1150+
KEY_F3 , DOWN, XKB_KEY_Shift_L , NEXT, // Set Shift
1151+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, // Latch LevelThree
1152+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, // Lock LevelThree
1153+
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT,
1154+
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT,
1155+
KEY_F3 , UP , XKB_KEY_Caps_Lock , NEXT, // Unset Shift
1156+
KEY_1 , BOTH, XKB_KEY_onesuperior , NEXT,
1157+
KEY_1 , BOTH, XKB_KEY_onesuperior , NEXT,
1158+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, // Unlock LevelThree
1159+
KEY_1 , BOTH, XKB_KEY_1 , NEXT,
1160+
1161+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, // Latch LevelThree
1162+
KEY_F3 , DOWN, XKB_KEY_Shift_L , NEXT, // Set Shift
1163+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, // Lock LevelThree
1164+
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT,
1165+
KEY_1 , BOTH, XKB_KEY_exclamdown , NEXT,
1166+
KEY_F3 , UP , XKB_KEY_Caps_Lock , NEXT, // Unset Shift
1167+
KEY_1 , BOTH, XKB_KEY_onesuperior , NEXT,
1168+
KEY_1 , BOTH, XKB_KEY_onesuperior , NEXT,
1169+
KEY_RIGHTALT , BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, // Unlock LevelThree
1170+
KEY_1 , BOTH, XKB_KEY_1 , FINISH
1171+
));
1172+
11271173
xkb_keymap_unref(keymap);
11281174
}
11291175

0 commit comments

Comments
 (0)