From 948f7a59af991db3b5690b91bc3acce457927bcf Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Wed, 9 Oct 2024 08:34:27 +0200 Subject: [PATCH] symbols: Skip interprets only for groups with explicit actions Previously setting explicit actions for a group in symbols files made the parser skip compatibility interpretations for the corresponding *whole* key, so the other groups with *no* explicit actions could result broken on some levels. In the following example, `` would have an action on group 2, because it is explicit, but none on group 1 because interpretation are also skipped there as a side effect: ```c key { symbols[1]= [ ISO_Level3_Shift ], symbols[2]= [ ISO_Level3_Shift ], actions[2]= [ SetMods(modifiers=LevelThree) ] }; ``` Fixed by skipping interpretations *only* for groups with explicit actions. We still set `key->explicit |= EXPLICIT_INTERP` if at least one group has explicit actions. In such case, when dumping a keymap, we will write explicit actions for *all* groups, in order to ensure that X11 and previous versions of libxkbcommon can parse the keymap as intended. One side effect is that no interpretation will be run on this key anymore, so we may have to set some extra fields explicitly: repeat, virtualMods. Thus the previous example would be bumped as: ```c key { repeat= No, symbols[1]= [ ISO_Level3_Shift ], actions[1]= [ SetMods(modifiers=LevelThree,clearLocks) ], symbols[2]= [ ISO_Level3_Shift ], actions[2]= [ SetMods(modifiers=LevelThree) ] }; ``` --- .../511.explicit-interp-per-group.bugfix.md | 6 + meson.build | 12 +- src/keymap.h | 23 +- src/x11/keymap.c | 7 +- src/xkbcomp/keymap-dump.c | 25 +- src/xkbcomp/keymap.c | 8 +- src/xkbcomp/symbols.c | 14 +- test/compose.c | 2 +- test/data/keymaps/explicit-actions.xkb | 368 ++++++++++++++++++ test/keyseq.c | 81 +++- test/stringcomp.c | 41 +- 11 files changed, 558 insertions(+), 29 deletions(-) create mode 100644 changes/api/511.explicit-interp-per-group.bugfix.md create mode 100644 test/data/keymaps/explicit-actions.xkb diff --git a/changes/api/511.explicit-interp-per-group.bugfix.md b/changes/api/511.explicit-interp-per-group.bugfix.md new file mode 100644 index 000000000..21bb7502d --- /dev/null +++ b/changes/api/511.explicit-interp-per-group.bugfix.md @@ -0,0 +1,6 @@ +Previously, setting *explicit actions* for a group in symbols files made the parser +skip compatibility interpretations for *all* the groups in the corresponding key, +resulting in possibly broken groups with *no* explicit actions or missing key fields. + +Fixed by skipping interpretations only for groups with explicit actions when parsing +a keymap and setting relevant fields explicitly when serializing a keymap to a string. diff --git a/meson.build b/meson.build index 82588831d..33aea6f6c 100644 --- a/meson.build +++ b/meson.build @@ -738,7 +738,13 @@ test( ) test( 'stringcomp', - executable('test-stringcomp', 'test/stringcomp.c', dependencies: test_dep), + executable( + 'test-stringcomp', + 'test/stringcomp.c', + 'test/utils-text.c', + 'test/utils-text.h', + dependencies: test_dep + ), env: test_env, ) test( @@ -787,10 +793,10 @@ test( executable( 'test-compose', 'test/compose.c', - 'test/utils-text.c', - 'test/utils-text.h', 'test/compose-iter.c', 'test/compose-iter.h', + 'test/utils-text.c', + 'test/utils-text.h', 'src/compose/dump.c', 'src/compose/dump.h', 'src/compose/escape.h', diff --git a/src/keymap.h b/src/keymap.h index f7ea5bdf1..7ded5864d 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -324,11 +324,28 @@ struct xkb_level { } u; }; +/** + * Group in a key + */ struct xkb_group { - bool explicit_type; - /* Points to a type in keymap->types. */ + /** + * Flag that indicates whether a group has explicit actions. In case it has, + * compatibility interpretations will not be used on it. + * See also EXPLICIT_INTERP flag at key level. + */ + bool explicit_actions:1; + /** + * Flag that indicates whether a group has an explicit key type. In case it + * has, type detection will not be used on it. + */ + bool explicit_type:1; + /** + * Key type of the group. Points to an entry in keymap->types. + */ const struct xkb_key_type *type; - /* Use XkbKeyNumLevels for the number of levels. */ + /** + * Array of group levels. Use `XkbKeyNumLevels` for the number of levels. + */ struct xkb_level *levels; }; diff --git a/src/x11/keymap.c b/src/x11/keymap.c index 0c327305c..1beddedfb 100644 --- a/src/x11/keymap.c +++ b/src/x11/keymap.c @@ -602,8 +602,13 @@ get_explicits(struct xkb_keymap *keymap, xcb_connection_t *conn, if ((wire->explicit & XCB_XKB_EXPLICIT_KEY_TYPE_4) && key->num_groups > 3) key->groups[3].explicit_type = true; - if (wire->explicit & XCB_XKB_EXPLICIT_INTERPRET) + if (wire->explicit & XCB_XKB_EXPLICIT_INTERPRET) { key->explicit |= EXPLICIT_INTERP; + /* Make all key groups have explicit actions too */ + for (xkb_layout_index_t l = 0; l < key->num_groups; l++) { + key->groups[l].explicit_actions = true; + } + } if (wire->explicit & XCB_XKB_EXPLICIT_AUTO_REPEAT) key->explicit |= EXPLICIT_REPEAT; if (wire->explicit & XCB_XKB_EXPLICIT_V_MOD_MAP) diff --git a/src/xkbcomp/keymap-dump.c b/src/xkbcomp/keymap-dump.c index b7828bff6..428ad5627 100644 --- a/src/xkbcomp/keymap-dump.c +++ b/src/xkbcomp/keymap-dump.c @@ -531,7 +531,20 @@ write_key(struct xkb_keymap *keymap, struct buf *buf, } } - if (key->explicit & EXPLICIT_REPEAT) { + /* + * NOTE: we use key->explicit and not key->group[i].explicit_actions, in + * order to have X11 and the previous versions of libxkbcommon (without this + * group property) parse the keymap as intended, by setting explicitly for + * this key all actions in all groups. + * + * One side effect is that no interpretation will be run on this key anymore, + * so we may have to set some extra fields explicitly: repeat, virtualMods. + */ + show_actions = (key->explicit & EXPLICIT_INTERP); + + /* If we show actions, interprets are not going to be used to set this + * field, so make it explicit. */ + if ((key->explicit & EXPLICIT_REPEAT) || show_actions) { if (key->repeats) write_buf(buf, "\n\t\trepeat= Yes,"); else @@ -539,7 +552,9 @@ write_key(struct xkb_keymap *keymap, struct buf *buf, simple = false; } - if (key->vmodmap && (key->explicit & EXPLICIT_VMODMAP)) + /* If we show actions, interprets are not going to be used to set this + * field, so make it explicit. */ + if (key->vmodmap && ((key->explicit & EXPLICIT_VMODMAP) || show_actions)) write_buf(buf, "\n\t\tvirtualMods= %s,", ModMaskText(keymap->ctx, &keymap->mods, key->vmodmap)); @@ -557,8 +572,6 @@ write_key(struct xkb_keymap *keymap, struct buf *buf, break; } - show_actions = (key->explicit & EXPLICIT_INTERP); - if (key->num_groups > 1 || show_actions) simple = false; @@ -584,8 +597,8 @@ write_key(struct xkb_keymap *keymap, struct buf *buf, if (level != 0) write_buf(buf, ", "); write_action(keymap, buf, - &key->groups[group].levels[level].action, - NULL, NULL); + &key->groups[group].levels[level].action, + NULL, NULL); } write_buf(buf, " ]"); } diff --git a/src/xkbcomp/keymap.c b/src/xkbcomp/keymap.c index 0aaed1f84..a0e442828 100644 --- a/src/xkbcomp/keymap.c +++ b/src/xkbcomp/keymap.c @@ -133,11 +133,11 @@ ApplyInterpsToKey(struct xkb_keymap *keymap, struct xkb_key *key) xkb_layout_index_t group; xkb_level_index_t level; - /* If we've been told not to bind interps to this key, then don't. */ - if (key->explicit & EXPLICIT_INTERP) - return true; - for (group = 0; group < key->num_groups; group++) { + /* Skip any interpretation for this group if it has explicit actions */ + if (key->groups[group].explicit_actions) + continue; + for (level = 0; level < XkbKeyNumLevels(key, group); level++) { const struct xkb_sym_interpret *interp; diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c index b06921a9e..d4b147593 100644 --- a/src/xkbcomp/symbols.c +++ b/src/xkbcomp/symbols.c @@ -1494,8 +1494,13 @@ CopySymbolsDefToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info, } /* Copy levels. */ - darray_enumerate(i, groupi, keyi->groups) + darray_enumerate(i, groupi, keyi->groups) { darray_steal(groupi->levels, &key->groups[i].levels, NULL); + if (groupi->defined & GROUP_FIELD_ACTS) { + key->groups[i].explicit_actions = true; + key->explicit |= EXPLICIT_INTERP; + } + } key->out_of_range_group_number = keyi->out_of_range_group_number; key->out_of_range_group_action = keyi->out_of_range_group_action; @@ -1510,13 +1515,6 @@ CopySymbolsDefToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info, key->explicit |= EXPLICIT_REPEAT; } - darray_foreach(groupi, keyi->groups) { - if (groupi->defined & GROUP_FIELD_ACTS) { - key->explicit |= EXPLICIT_INTERP; - break; - } - } - return true; } diff --git a/test/compose.c b/test/compose.c index 2e0caf553..7b2696c15 100644 --- a/test/compose.c +++ b/test/compose.c @@ -34,8 +34,8 @@ #include "src/compose/parser.h" #include "src/compose/escape.h" #include "src/compose/dump.h" -#include "test/utils-text.h" #include "test/compose-iter.h" +#include "test/utils-text.h" static const char * compose_status_string(enum xkb_compose_status status) diff --git a/test/data/keymaps/explicit-actions.xkb b/test/data/keymaps/explicit-actions.xkb new file mode 100644 index 000000000..8d1e74efb --- /dev/null +++ b/test/data/keymaps/explicit-actions.xkb @@ -0,0 +1,368 @@ +xkb_keymap { +xkb_keycodes "(unnamed)" { + minimum = 8; + maximum = 255; + = 9; + = 10; + = 11; + = 12; + = 13; + = 14; + = 15; + = 16; + = 17; + = 18; + = 19; + = 20; + = 21; + = 22; + = 23; + = 24; + = 25; + = 26; + = 27; + = 28; + = 29; + = 30; + = 31; + = 32; + = 33; + = 34; + = 35; + = 36; + = 37; + = 38; + = 39; + = 40; + = 41; + = 42; + = 43; + = 44; + = 45; + = 46; + = 47; + = 48; + = 49; + = 50; + = 51; + = 52; + = 53; + = 54; + = 55; + = 56; + = 57; + = 58; + = 59; + = 60; + = 61; + = 62; + = 63; + = 64; + = 65; + = 66; + = 67; + = 68; + = 69; + = 70; + = 71; + = 72; + = 73; + = 74; + = 75; + = 76; + = 77; + = 78; + = 79; + = 80; + = 81; + = 82; + = 83; + = 84; + = 85; + = 86; + = 87; + = 88; + = 89; + = 90; + = 91; + = 92; + = 94; + = 95; + = 96; + = 97; + = 105; + = 108; + = 133; + = 134; + = 135; + = 203; + = 204; + = 205; + = 206; + = 207; +}; + +xkb_types "(unnamed)" { + virtual_modifiers NumLock,Alt,LevelThree,ScrollLock,LevelFive,Super,AltGr,Meta,Hyper; + + type "ONE_LEVEL" { + modifiers= none; + level_name[1]= "Any"; + }; + type "TWO_LEVEL" { + modifiers= Shift; + map[Shift]= 2; + level_name[1]= "Base"; + level_name[2]= "Shift"; + }; + type "ALPHABETIC" { + modifiers= Shift+Lock; + map[Shift]= 2; + map[Lock]= 2; + level_name[1]= "Base"; + level_name[2]= "Caps"; + }; + type "FOUR_LEVEL" { + modifiers= Shift+LevelThree; + map[Shift]= 2; + map[LevelThree]= 3; + map[Shift+LevelThree]= 4; + level_name[1]= "Base"; + level_name[2]= "Shift"; + level_name[3]= "AltGr"; + level_name[4]= "Shift AltGr"; + }; + type "EIGHT_LEVEL_ALPHABETIC_LEVEL_FIVE_LOCK" { + modifiers= Shift+Lock+NumLock+LevelThree+LevelFive; + map[Shift]= 2; + map[LevelThree]= 3; + map[Shift+LevelThree]= 4; + map[LevelFive]= 5; + map[Shift+LevelFive]= 6; + preserve[Shift+LevelFive]= Shift; + map[LevelThree+LevelFive]= 7; + map[Shift+LevelThree+LevelFive]= 8; + map[NumLock]= 5; + map[Shift+NumLock]= 6; + preserve[Shift+NumLock]= Shift; + map[NumLock+LevelThree]= 7; + map[Shift+NumLock+LevelThree]= 8; + map[Shift+NumLock+LevelFive]= 2; + map[NumLock+LevelThree+LevelFive]= 3; + map[Shift+NumLock+LevelThree+LevelFive]= 4; + map[Lock]= 2; + map[Lock+LevelThree]= 3; + map[Shift+Lock+LevelThree]= 4; + map[Lock+LevelFive]= 5; + map[Shift+Lock+LevelFive]= 6; + map[Lock+LevelThree+LevelFive]= 7; + map[Shift+Lock+LevelThree+LevelFive]= 8; + map[Lock+NumLock]= 5; + map[Shift+Lock+NumLock]= 6; + map[Lock+NumLock+LevelThree]= 7; + map[Shift+Lock+NumLock+LevelThree]= 8; + map[Lock+NumLock+LevelFive]= 2; + map[Lock+NumLock+LevelThree+LevelFive]= 4; + map[Shift+Lock+NumLock+LevelThree+LevelFive]= 3; + level_name[1]= "Base"; + level_name[2]= "Shift"; + level_name[3]= "Alt Base"; + level_name[4]= "Shift Alt"; + level_name[5]= "X"; + level_name[6]= "X Shift"; + level_name[7]= "X Alt Base"; + level_name[8]= "X Shift Alt"; + }; + type "FOUR_LEVEL_SEMIALPHABETIC" { + modifiers= Shift+Lock+LevelThree; + map[Shift]= 2; + map[Lock]= 2; + map[LevelThree]= 3; + map[Shift+LevelThree]= 4; + map[Lock+LevelThree]= 3; + preserve[Lock+LevelThree]= Lock; + map[Shift+Lock+LevelThree]= 4; + preserve[Shift+Lock+LevelThree]= Lock; + level_name[1]= "Base"; + level_name[2]= "Shift"; + level_name[3]= "AltGr"; + level_name[4]= "Shift AltGr"; + }; +}; + +xkb_compatibility "(unnamed)" { + virtual_modifiers NumLock,Alt,LevelThree,ScrollLock,LevelFive,Super,AltGr,Meta,Hyper; + + interpret.useModMapMods= AnyLevel; + interpret.repeat= False; + interpret ISO_Level2_Latch+Exactly(Shift) { + useModMapMods=level1; + action= LatchMods(modifiers=Shift,clearLocks,latchToLock); + }; + interpret Shift_Lock+AnyOf(Shift+Lock) { + action= LockMods(modifiers=Shift); + }; + interpret Num_Lock+AnyOf(all) { + virtualModifier= NumLock; + action= LockMods(modifiers=NumLock); + }; + interpret ISO_Level3_Shift+AnyOf(all) { + virtualModifier= LevelThree; + useModMapMods=level1; + action= SetMods(modifiers=LevelThree,clearLocks); + }; + interpret ISO_Level3_Latch+AnyOf(all) { + virtualModifier= LevelThree; + useModMapMods=level1; + action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock); + }; + interpret ISO_Level3_Lock+AnyOf(all) { + virtualModifier= LevelThree; + useModMapMods=level1; + action= LockMods(modifiers=LevelThree); + }; + interpret Alt_L+AnyOf(all) { + virtualModifier= Alt; + action= SetMods(modifiers=modMapMods,clearLocks); + }; + interpret Alt_R+AnyOf(all) { + virtualModifier= Alt; + action= SetMods(modifiers=modMapMods,clearLocks); + }; + interpret ISO_Level5_Shift+AnyOf(all) { + virtualModifier= LevelFive; + useModMapMods=level1; + action= SetMods(modifiers=LevelFive,clearLocks); + }; + interpret Mode_switch+AnyOfOrNone(all) { + virtualModifier= AltGr; + useModMapMods=level1; + action= SetGroup(group=+1); + }; + interpret ISO_Level3_Shift+AnyOfOrNone(all) { + action= SetMods(modifiers=LevelThree,clearLocks); + }; + interpret ISO_Next_Group+AnyOfOrNone(all) { + virtualModifier= AltGr; + useModMapMods=level1; + action= LockGroup(group=+1); + }; + interpret ISO_Prev_Group+AnyOfOrNone(all) { + virtualModifier= AltGr; + useModMapMods=level1; + action= LockGroup(group=-1); + }; + interpret Alt_L+AnyOfOrNone(all) { + action= SetMods(modifiers=Alt,clearLocks); + }; + interpret Alt_R+AnyOfOrNone(all) { + action= SetMods(modifiers=Alt,clearLocks); + }; + interpret Shift_L+AnyOfOrNone(all) { + action= SetMods(modifiers=Shift,clearLocks); + }; + interpret Shift_R+AnyOfOrNone(all) { + action= SetMods(modifiers=Shift,clearLocks); + }; + interpret ISO_Level5_Shift+AnyOfOrNone(all) { + action= SetMods(modifiers=LevelFive,clearLocks); + }; + interpret ISO_Level5_Latch+AnyOfOrNone(all) { + action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock); + }; + interpret ISO_Level5_Lock+AnyOfOrNone(all) { + action= LockMods(modifiers=LevelFive); + }; + interpret Any+AnyOf(all) { + action= SetMods(modifiers=modMapMods,clearLocks); + }; +}; + +xkb_symbols "(unnamed)" { + name[Group1]="English (US)"; + name[Group2]="Czech"; + name[Group3]="German (Neo 2)"; + + key { + //// repeat=Yes is set with the default *interpretation*, but + //// using explicit actions makes the key keep the initial value, + //// i.e. repeat=No. + //repeat= Yes, + symbols[Group1]= [ t, T ], + //actions[Group1]= [ NoAction(), NoAction() ], + symbols[Group2]= [ Cyrillic_ie, Cyrillic_IE ], + actions[Group2]= [ NoAction(), NoAction() ], + symbols[Group3]= [ w, W ]//, + //actions[Group3]= [ NoAction(), NoAction() ] + }; + key { + type[Group3]= "EIGHT_LEVEL_ALPHABETIC_LEVEL_FIVE_LOCK", + symbols[Group1]= [ y, Y ], + symbols[Group2]= [ z, Z, leftarrow, yen ], + symbols[Group3]= [ k, K, exclam, Greek_kappa, exclamdown, NoSymbol, multiply, NoSymbol ] + }; + key { + type[Group3]= "TWO_LEVEL", + symbols[Group1]= [ Shift_L ], + symbols[Group2]= [ Shift_L ], + symbols[Group3]= [ Shift_L, Caps_Lock ] + }; + //// This should set the key’s EXPLICIT_INTERP explicit flag and + //// the group 2 explicit_actions, but it should still allow to + //// interpret the keysyms in groups 1 and 3. + key { + type[Group1]= "ONE_LEVEL", + type[Group2]= "TWO_LEVEL", + type[Group3]= "TWO_LEVEL", + //repeat= No, + symbols[Group1]= [ Shift_L ], + //actions[Group1]= [ SetMods(modifiers=Shift,clearLocks) ], + symbols[Group2]= [ ISO_Level3_Shift, Multi_key ], + actions[Group2]= [ SetMods(modifiers=LevelThree), NoAction() ], + symbols[Group3]= [ ISO_Level5_Shift, ISO_Level3_Shift ]//, + //actions[Group3]= [ SetMods(modifiers=LevelFive,clearLocks), SetMods(modifiers=LevelThree,clearLocks) ] + }; + key { + type= "ONE_LEVEL", + //// The following fields are added, because they are set via + //// compatibility interpretations, but using explicit actions + //// disable them. + //repeat= No, + //virtualMods= LevelThree, + symbols[Group1]= [ ISO_Level3_Shift ], + //actions[Group1]= [ SetMods(modifiers=LevelThree,clearLocks) ], + symbols[Group2]= [ ISO_Level3_Shift ], + actions[Group2]= [ SetMods(modifiers=LevelThree) ], + symbols[Group3]= [ ISO_Level3_Shift ]//, + //actions[Group3]= [ SetMods(modifiers=LevelThree,clearLocks) ] + }; + key { + symbols[Group1]= [ less, greater, bar, brokenbar ], + symbols[Group2]= [ backslash, bar, slash, NoSymbol ], + symbols[Group3]= [ ISO_Level5_Shift ] + }; + key { + type[Group1]= "TWO_LEVEL", + type[Group2]= "ONE_LEVEL", + type[Group3]= "ONE_LEVEL", + symbols[Group1]= [ Alt_R, Meta_R ], + symbols[Group2]= [ ISO_Level3_Shift ], + symbols[Group3]= [ ISO_Level5_Shift ] + }; + key { [ ISO_Next_Group, Menu ] }; + key { + type= "ONE_LEVEL", + symbols[Group1]= [ ISO_Level5_Shift ], + symbols[Group2]= [ ISO_Level5_Shift ], + symbols[Group3]= [ ISO_Level5_Shift ] + }; + key { [ NoSymbol, Alt_L ] }; + key { [ NoSymbol, Meta_L ] }; + key { [ NoSymbol, Super_L ] }; + modifier_map Shift { , }; + modifier_map Mod1 { , , }; + modifier_map Mod3 { }; + modifier_map Mod5 { }; +}; + +}; diff --git a/test/keyseq.c b/test/keyseq.c index 17d133565..29c80d07b 100644 --- a/test/keyseq.c +++ b/test/keyseq.c @@ -25,6 +25,7 @@ #include "evdev-scancodes.h" #include "test.h" +#include "keymap.h" static void test_group_latch(struct xkb_context *ctx) @@ -287,17 +288,95 @@ test_group_latch(struct xkb_context *ctx) #undef test_no_latch_to_lock } +struct key_properties { + const char *name; + bool repeats; + xkb_mod_mask_t vmodmap; +}; + +static void +test_explicit_actions(struct xkb_context *ctx) +{ + struct xkb_keymap *original = + test_compile_file(ctx, "keymaps/explicit-actions.xkb"); + assert(original); + + /* Reload keymap from its dump */ + char *dump = xkb_keymap_get_as_string(original, + XKB_KEYMAP_USE_ORIGINAL_FORMAT); + assert(dump); + struct xkb_keymap *roundtrip = test_compile_string(ctx, dump); + free(dump); + + struct xkb_keymap *keymaps[] = { original, roundtrip }; + + for (unsigned k = 0; k < ARRAY_SIZE(keymaps); k++) { + assert(keymaps[k]); + + /* + * : Groups 1 & 3 have no explicit actions while group 2 does. + * We expect that groups 1 & 3 will have the corresponding interpret run + * to set their actions. + * + * has explicit actions on group 2; dumping the keymap forces + * explicit actions as well as the essential virtualMods=LevelThree field. + * + * has explicit actions on group 2; dumping the keymap forces + * explicit actions as well as repeat=Yes. + */ + const struct key_properties keys[] = { + { .name = "LALT", .repeats = false, .vmodmap = 0 }, + { .name = "LVL3", .repeats = false, .vmodmap = 1u << 10 }, + { .name = "AD05", .repeats = true , .vmodmap = 0 }, + /* No explicit actions, check defaults */ + { .name = "AD06", .repeats = true , .vmodmap = 0 }, + }; + for (unsigned i = 0; i < ARRAY_SIZE(keys); i++) { + xkb_keycode_t kc = xkb_keymap_key_by_name(keymaps[k], keys[i].name); + assert(kc != XKB_KEYCODE_INVALID); + assert(keys[i].repeats == xkb_keymap_key_repeats(keymaps[k], kc)); + assert(keys[i].vmodmap == keymaps[k]->keys[kc].vmodmap); + } + assert(test_key_seq( + keymaps[k], + KEY_Y, BOTH, XKB_KEY_y, NEXT, + KEY_LEFTALT, DOWN, XKB_KEY_Shift_L, NEXT, + KEY_Y, BOTH, XKB_KEY_Y, NEXT, + KEY_LEFTALT, UP, XKB_KEY_Shift_L, NEXT, + KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT, + KEY_Y, BOTH, XKB_KEY_z, NEXT, + KEY_LEFTALT, DOWN, XKB_KEY_ISO_Level3_Shift, NEXT, + KEY_Y, BOTH, XKB_KEY_leftarrow, NEXT, + KEY_LEFTALT, UP, XKB_KEY_ISO_Level3_Shift, NEXT, + KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT, + KEY_Y, BOTH, XKB_KEY_k, NEXT, + KEY_LEFTALT, DOWN, XKB_KEY_ISO_Level5_Shift, NEXT, + KEY_Y, BOTH, XKB_KEY_exclamdown, NEXT, + KEY_LEFTALT, UP, XKB_KEY_ISO_Level5_Shift, NEXT, + KEY_LEFTSHIFT, DOWN, XKB_KEY_Shift_L, NEXT, + KEY_LEFTALT, DOWN, XKB_KEY_ISO_Level3_Shift, NEXT, + KEY_Y, BOTH, XKB_KEY_Greek_kappa, NEXT, + KEY_LEFTALT, UP, XKB_KEY_ISO_Level3_Shift, NEXT, + KEY_LEFTSHIFT, UP, XKB_KEY_Caps_Lock, NEXT, + KEY_Y, BOTH, XKB_KEY_k, FINISH + )); + + xkb_keymap_unref(keymaps[k]); + } +} + int main(void) { test_init(); - struct xkb_context *ctx = test_get_context(0); + struct xkb_context *ctx = test_get_context(CONTEXT_NO_FLAG); struct xkb_keymap *keymap; assert(ctx); test_group_latch(ctx); + test_explicit_actions(ctx); keymap = test_compile_rules(ctx, "evdev", "evdev", "us,il,ru,de", ",,phonetic,neo", diff --git a/test/stringcomp.c b/test/stringcomp.c index 470a56cc6..97cc20031 100644 --- a/test/stringcomp.c +++ b/test/stringcomp.c @@ -28,15 +28,50 @@ #include #include "test.h" +#include "test/utils-text.h" #define DATA_PATH "keymaps/stringcomp.data" +static void +test_explicit_actions(struct xkb_context *ctx) +{ + struct xkb_keymap *keymap; + + char *original = test_read_file("keymaps/explicit-actions.xkb"); + assert(original); + char *tmp = uncomment(original, strlen(original), "//"); + assert(tmp); + char *expected = strip_lines(tmp, strlen(tmp), "//"); + free(tmp); + assert(expected); + char *got = NULL; + + /* Try original */ + keymap = test_compile_string(ctx, original); + free(original); + assert(keymap); + got = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_USE_ORIGINAL_FORMAT); + xkb_keymap_unref(keymap); + assert_streq_not_null("Check output from original", expected, got); + free(got); + + /* Try round-trip */ + keymap = test_compile_string(ctx, expected); + assert(keymap); + got = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_USE_ORIGINAL_FORMAT); + xkb_keymap_unref(keymap); + assert_streq_not_null("Check roundtrip", expected, got); + free(got); + + free(expected); +} + int main(int argc, char *argv[]) { test_init(); - struct xkb_context *ctx = test_get_context(0); + struct xkb_context *ctx = test_get_context(CONTEXT_NO_FLAG); struct xkb_keymap *keymap; char *original, *dump, *dump2; @@ -103,7 +138,9 @@ main(int argc, char *argv[]) free(dump); free(dump2); + test_explicit_actions(ctx); + xkb_context_unref(ctx); - return 0; + return EXIT_SUCCESS; }