state: fix unbound virtual modifier bug
Recent xkeyboard-config introduced the following line in symbols/level3:
vmods = LevelThree,
However, the XKM format which xkbcomp produces for the X server can't
handle explicit virtual modifiers such as this:
https://bugs.freedesktop.org/show_bug.cgi?id=4927
So by doing the following, for example:
setxkbmap -layout de (or another 3-level layouts)
xkbcomp $DISPLAY out.xkb
xkbcomp out.xkb $DISPLAY
The modifier is lost and can't be used for switching to Level3 (see the
included test).
We, however, are affected worse by this bug when we load the out.xkb
keymap. First, the FOUR_LEVEL_ALPHABETIC key type has these entries:
map[None] = Level1;
map[Shift] = Level2;
map[Lock] = Level2;
map[LevelThree] = Level3;
[...]
Now, because the LevelThree virtual modifier is not bound to anything,
the effective mask of the "map[LevelThree]" entry is just 0. So when
the modifier state is empty (initial state), this entry is chosen, and
we get Level3, instead of failing to match any entry and getting the
default Level1.
The difference in behavior from the xserver stems from this commit:
acdad6058d
Which removed the entry->active field. Without bugs, this would be
correct; however, it seems in this case we should just follow the
server's behavior.
The server sets the entry->active field like so in XKBMisc.c:
/* entry is active if vmods are bound */
entry->active = (mask != 0);
The xkblib spec explains this field, but does not specify how to
initialize it. This commit does the same as above but more directly.
Signed-off-by: Ran Benita <ran234@gmail.com>
master
parent
0ad8bf573f
commit
089c3a1811
11
src/state.c
11
src/state.c
|
@ -122,9 +122,18 @@ get_entry_for_key_state(struct xkb_state *state, const struct xkb_key *key,
|
|||
xkb_mod_mask_t active_mods = state->components.mods & type->mods.mask;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < type->num_entries; i++)
|
||||
for (i = 0; i < type->num_entries; i++) {
|
||||
/*
|
||||
* If the virtual modifiers are not bound to anything, we're
|
||||
* supposed to skip the entry (xserver does this with cached
|
||||
* entry->active field).
|
||||
*/
|
||||
if (!type->map[i].mods.mask)
|
||||
continue;
|
||||
|
||||
if (type->map[i].mods.mask == active_mods)
|
||||
return &type->map[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -420,7 +420,6 @@ main(void)
|
|||
KEY_H, BOTH, XKB_KEY_h, FINISH));
|
||||
|
||||
xkb_keymap_unref(keymap);
|
||||
assert(ctx);
|
||||
keymap = test_compile_rules(ctx, "evdev", "", "us,il,ru", "",
|
||||
"grp:switch,grp:lswitch,grp:menu_toggle");
|
||||
assert(keymap);
|
||||
|
@ -463,6 +462,16 @@ main(void)
|
|||
KEY_RIGHTALT, UP, XKB_KEY_ISO_Level3_Shift, NEXT,
|
||||
KEY_H, BOTH, XKB_KEY_h, FINISH));
|
||||
|
||||
xkb_keymap_unref(keymap);
|
||||
keymap = test_compile_file(ctx, "keymaps/unbound-vmod.xkb");
|
||||
assert(keymap);
|
||||
|
||||
assert(test_key_seq(keymap,
|
||||
KEY_H, BOTH, XKB_KEY_h, NEXT,
|
||||
KEY_Z, BOTH, XKB_KEY_y, NEXT,
|
||||
KEY_MINUS, BOTH, XKB_KEY_ssharp, NEXT,
|
||||
KEY_Z, BOTH, XKB_KEY_y, FINISH));
|
||||
|
||||
xkb_keymap_unref(keymap);
|
||||
xkb_context_unref(ctx);
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue