Skip to content

Commit d6a715b

Browse files
state: Fix mods not being independently cleared
The modifiers filters should ensure minimal interaction between them, but currently the Latch mod filters are overzealous and mess with the mods from other filters set to be cleared, resulting in some modifiers permanently set. Fixed by clearing mods properly with `OR` rather than direct setting of `state::clear_mods`. While we are at it, `state::set_mods` should be `OR`ed as well. This should not have any impact for now, but this is more future-proof. Fixes xkbcommon#583 Co-authored-by: Jules Bertholet <[email protected]> Co-authored-by: Pierre Le Marre <[email protected]>
1 parent dfa286b commit d6a715b

File tree

4 files changed

+101
-7
lines changed

4 files changed

+101
-7
lines changed

changes/api/583.bugfix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed modifiers not properly unset when multiple latches are used simultaneously.

src/state.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ xkb_filter_group_latch_func(struct xkb_state *state,
454454
static void
455455
xkb_filter_mod_set_new(struct xkb_state *state, struct xkb_filter *filter)
456456
{
457-
state->set_mods = filter->action.mods.mods.mask;
457+
state->set_mods |= filter->action.mods.mods.mask;
458458
}
459459

460460
static bool
@@ -476,7 +476,7 @@ xkb_filter_mod_set_func(struct xkb_state *state,
476476
return XKB_FILTER_CONSUME;
477477
}
478478

479-
state->clear_mods = filter->action.mods.mods.mask;
479+
state->clear_mods |= filter->action.mods.mods.mask;
480480
if (filter->action.mods.flags & ACTION_LOCK_CLEAR)
481481
state->components.locked_mods &= ~filter->action.mods.mods.mask;
482482

@@ -522,7 +522,7 @@ static void
522522
xkb_filter_mod_latch_new(struct xkb_state *state, struct xkb_filter *filter)
523523
{
524524
filter->priv = LATCH_KEY_DOWN;
525-
state->set_mods = filter->action.mods.mods.mask;
525+
state->set_mods |= filter->action.mods.mods.mask;
526526
}
527527

528528
static bool
@@ -553,7 +553,7 @@ xkb_filter_mod_latch_func(struct xkb_state *state,
553553
else {
554554
filter->action.type = ACTION_TYPE_MOD_SET;
555555
filter->func = xkb_filter_mod_set_func;
556-
state->set_mods = filter->action.mods.mods.mask;
556+
state->set_mods |= filter->action.mods.mods.mask;
557557
}
558558
filter->key = key;
559559
state->components.latched_mods &= ~filter->action.mods.mods.mask;
@@ -585,13 +585,13 @@ xkb_filter_mod_latch_func(struct xkb_state *state,
585585
state->components.latched_mods &=
586586
~filter->action.mods.mods.mask;
587587
else
588-
state->clear_mods = filter->action.mods.mods.mask;
588+
state->clear_mods |= filter->action.mods.mods.mask;
589589
state->components.locked_mods &= ~filter->action.mods.mods.mask;
590590
filter->func = NULL;
591591
}
592592
else {
593593
latch = LATCH_PENDING;
594-
state->clear_mods = filter->action.mods.mods.mask;
594+
state->clear_mods |= filter->action.mods.mods.mask;
595595
state->components.latched_mods |= filter->action.mods.mods.mask;
596596
/* XXX beep beep! */
597597
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
default partial alphanumeric_keys
2+
xkb_symbols "base" {
3+
name[Group1] = "Test issue #583";
4+
5+
key <AB01> { [ z, Z, y, Y, ezh, EZH, ydiaeresis, Ydiaeresis ], type[Group1] = "EIGHT_LEVEL" };
6+
key <AB02> { [ x, X ], type[Group1] = "PC_CONTROL_LEVEL2" };
7+
8+
key <RALT> { [ ISO_Level3_Latch, ISO_Level5_Latch ], type[Group1] = "PC_CONTROL_LEVEL2" };
9+
key <RCTL> { [ Control_R ], [LatchMods(modifiers=Control)] };
10+
key <LWIN> { [ ISO_Level3_Latch ], type[Group1] = "ONE_LEVEL" };
11+
key <RWIN> { [ ISO_Level5_Latch ], type[Group1] = "ONE_LEVEL" };
12+
13+
modifier_map None { <RALT> };
14+
modifier_map Mod3 { <RWIN> };
15+
modifier_map Mod5 { <LWIN> };
16+
};

test/keyseq.c

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,82 @@ test_explicit_actions(struct xkb_context *ctx)
374374
}
375375
}
376376

377+
static void
378+
test_simultaneous_modifier_clear(struct xkb_context *context)
379+
{
380+
struct xkb_keymap *keymap;
381+
382+
keymap = test_compile_rules(context, "evdev", "pc104",
383+
"simultaneous-mods-latches", "", "");
384+
assert(keymap);
385+
386+
/*
387+
* Github issue #583: simultaneous latches of *different* modifiers should
388+
* not affect each other when clearing their mods.
389+
*/
390+
391+
/* Original key sequence reported in the issue */
392+
assert(test_key_seq(keymap,
393+
KEY_LEFTCTRL, DOWN, XKB_KEY_Control_L , NEXT, /* Set Control */
394+
KEY_RIGHTALT, BOTH, XKB_KEY_ISO_Level5_Latch, NEXT, /* Latch Level5 */
395+
KEY_LEFTCTRL, UP , XKB_KEY_Control_L , NEXT, /* Unset Control */
396+
KEY_RIGHTALT, BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Latch Level3 */
397+
KEY_Z , BOTH, XKB_KEY_ydiaeresis , NEXT, /* Unlatch Level3, unlatch Level5 */
398+
KEY_Z , BOTH, XKB_KEY_z , NEXT,
399+
KEY_Z , BOTH, XKB_KEY_z , FINISH
400+
));
401+
402+
/* Alternative key sequence with only mod latches */
403+
assert(test_key_seq(keymap,
404+
KEY_RIGHTCTRL, BOTH, XKB_KEY_Control_R , NEXT, /* Latch Control */
405+
KEY_RIGHTALT, BOTH, XKB_KEY_ISO_Level5_Latch, NEXT, /* Latch Level5 */
406+
KEY_LEFTMETA, BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Latch Level3 */
407+
KEY_Z , BOTH, XKB_KEY_ydiaeresis , NEXT, /* Unlatch Control, Level3 and Level5 */
408+
KEY_Z , BOTH, XKB_KEY_z , NEXT,
409+
KEY_Z , BOTH, XKB_KEY_z , NEXT,
410+
KEY_X , BOTH, XKB_KEY_x , FINISH
411+
));
412+
413+
/* Alternative simplier key sequence */
414+
assert(test_key_seq(keymap,
415+
KEY_LEFTMETA, BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Latch Level3 */
416+
KEY_RIGHTMETA, BOTH, XKB_KEY_ISO_Level5_Latch, NEXT, /* Latch Level5 */
417+
KEY_Z , BOTH, XKB_KEY_ydiaeresis , NEXT, /* Unlatch Level3, unlatch Level5 */
418+
KEY_Z , BOTH, XKB_KEY_z , NEXT,
419+
KEY_Z , BOTH, XKB_KEY_z , FINISH
420+
));
421+
422+
/*
423+
* Test same modifier latch but on a different key
424+
*/
425+
426+
/* Level 3 */
427+
assert(test_key_seq(keymap,
428+
KEY_LEFTMETA, BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Latch Level3 */
429+
KEY_RIGHTALT, BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Lock Level3 via latch */
430+
KEY_Z , BOTH, XKB_KEY_y , NEXT, /* Locked Level3 */
431+
KEY_Z , BOTH, XKB_KEY_y , NEXT,
432+
KEY_RIGHTALT, BOTH, XKB_KEY_ISO_Level3_Latch, NEXT, /* Unlock Level3 via latch */
433+
KEY_Z , BOTH, XKB_KEY_z , NEXT,
434+
KEY_Z , BOTH, XKB_KEY_z , FINISH
435+
));
436+
437+
/* Level 5, via Control latch */
438+
assert(test_key_seq(keymap,
439+
KEY_RIGHTCTRL, BOTH, XKB_KEY_Control_R , NEXT, /* Latch Control */
440+
KEY_RIGHTALT, BOTH, XKB_KEY_ISO_Level5_Latch, NEXT, /* Lock Level5 via latch */
441+
KEY_RIGHTMETA, BOTH, XKB_KEY_ISO_Level5_Latch, NEXT, /* Latch Level5 */
442+
KEY_Z , BOTH, XKB_KEY_ezh , NEXT, /* Locked Level5 */
443+
KEY_Z , BOTH, XKB_KEY_ezh , NEXT,
444+
KEY_RIGHTMETA, BOTH, XKB_KEY_ISO_Level5_Latch, NEXT, /* Unlock Level5 via latch */
445+
KEY_Z , BOTH, XKB_KEY_z , NEXT,
446+
KEY_Z , BOTH, XKB_KEY_z , NEXT,
447+
KEY_X , BOTH, XKB_KEY_x , FINISH
448+
));
449+
450+
xkb_keymap_unref(keymap);
451+
}
452+
377453
int
378454
main(void)
379455
{
@@ -384,6 +460,7 @@ main(void)
384460

385461
assert(ctx);
386462

463+
test_simultaneous_modifier_clear(ctx);
387464
test_group_latch(ctx);
388465
test_explicit_actions(ctx);
389466

@@ -839,5 +916,5 @@ main(void)
839916

840917
xkb_keymap_unref(keymap);
841918
xkb_context_unref(ctx);
842-
return 0;
919+
return EXIT_SUCCESS;
843920
}

0 commit comments

Comments
 (0)