From 5409b36245887c43ebbb1f9eb683735cd7f97c5c Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Sat, 13 May 2023 18:20:18 +0200 Subject: [PATCH 1/2] Implement ":all" modifier for rules & include --- bench/rules.c | 3 ++- src/keymap-priv.c | 1 + src/keymap.h | 2 ++ src/xkbcomp/rules.c | 7 ++++- src/xkbcomp/rules.h | 3 ++- src/xkbcomp/symbols.c | 54 +++++++++++++++++++++++++++----------- src/xkbcomp/xkbcomp.c | 2 +- test/rules-file-includes.c | 20 +++++++++----- test/rules-file.c | 21 ++++++++++----- tools/compile-keymap.c | 2 +- 10 files changed, 82 insertions(+), 33 deletions(-) diff --git a/bench/rules.c b/bench/rules.c index 1306dc4b2..c3724226b 100644 --- a/bench/rules.c +++ b/bench/rules.c @@ -53,7 +53,8 @@ main(int argc, char *argv[]) for (i = 0; i < BENCHMARK_ITERATIONS; i++) { struct xkb_component_names kccgst; - assert(xkb_components_from_rules(ctx, &rmlvo, &kccgst)); + // [FIXME] num_explicit_groups + assert(xkb_components_from_rules(ctx, &rmlvo, &kccgst, NULL)); free(kccgst.keycodes); free(kccgst.types); free(kccgst.compat); diff --git a/src/keymap-priv.c b/src/keymap-priv.c index 8fdaf5b6f..97ddc71af 100644 --- a/src/keymap-priv.c +++ b/src/keymap-priv.c @@ -68,6 +68,7 @@ xkb_keymap_new(struct xkb_context *ctx, keymap->format = format; keymap->flags = flags; + keymap->num_explicit_groups = 0; update_builtin_keymap_fields(keymap); diff --git a/src/keymap.h b/src/keymap.h index f7ea5bdf1..9b954eb4b 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -389,6 +389,8 @@ struct xkb_keymap { /* Number of groups in the key with the most groups. */ xkb_layout_index_t num_groups; + /* Number of groups that were explicitly declared at section level. */ + xkb_layout_index_t num_explicit_groups; /* Not all groups must have names. */ xkb_layout_index_t num_group_names; xkb_atom_t *group_names; diff --git a/src/xkbcomp/rules.c b/src/xkbcomp/rules.c index 8029682aa..1c31dda0e 100644 --- a/src/xkbcomp/rules.c +++ b/src/xkbcomp/rules.c @@ -1104,7 +1104,8 @@ read_rules_file(struct xkb_context *ctx, bool xkb_components_from_rules(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo, - struct xkb_component_names *out) + struct xkb_component_names *out, + xkb_layout_index_t *layout_count) { bool ret = false; FILE *file; @@ -1118,6 +1119,10 @@ xkb_components_from_rules(struct xkb_context *ctx, goto err_out; matcher = matcher_new(ctx, rmlvo); + // Set the number of explicit layouts + if (layout_count != NULL) { + *layout_count = matcher->rmlvo.layouts.size; + } ret = read_rules_file(ctx, matcher, 0, file, path); if (!ret || diff --git a/src/xkbcomp/rules.h b/src/xkbcomp/rules.h index 5381b1562..e12b4c97a 100644 --- a/src/xkbcomp/rules.h +++ b/src/xkbcomp/rules.h @@ -27,6 +27,7 @@ bool xkb_components_from_rules(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo, - struct xkb_component_names *out); + struct xkb_component_names *out, + xkb_layout_index_t *layout_count); #endif diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c index f99052939..16a90df84 100644 --- a/src/xkbcomp/symbols.c +++ b/src/xkbcomp/symbols.c @@ -60,6 +60,7 @@ #include "vmod.h" #include "include.h" #include "keysym.h" +#include "utils.h" enum key_repeat { KEY_REPEAT_UNDEFINED = 0, @@ -565,7 +566,10 @@ HandleIncludeSymbols(SymbolsInfo *info, IncludeStmt *include) for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) { SymbolsInfo next_incl; XkbFile *file; + xkb_layout_index_t min_layout; + xkb_layout_index_t max_layout; + // Parse the file file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_SYMBOLS); if (!file) { info->errorCount += 10; @@ -573,27 +577,45 @@ HandleIncludeSymbols(SymbolsInfo *info, IncludeStmt *include) return false; } - InitSymbolsInfo(&next_incl, info->keymap, info->actions, - &included.mods); if (stmt->modifier) { - next_incl.explicit_group = atoi(stmt->modifier) - 1; - if (next_incl.explicit_group >= XKB_MAX_GROUPS) { - log_err(info->ctx, - "Cannot set explicit group to %d - must be between 1..%d; " - "Ignoring group number\n", - next_incl.explicit_group + 1, XKB_MAX_GROUPS); - next_incl.explicit_group = info->explicit_group; + // Try to interpret the modifier as a layout index + min_layout = atoi(stmt->modifier) - 1; + if (min_layout >= XKB_MAX_GROUPS) { + // Could not parse as a layout index, could it be “all”? + if (istreq(stmt->modifier, "all")) { + min_layout = 0; + // [FIXME] check this + max_layout = info->keymap->num_explicit_groups + ? info->keymap->num_explicit_groups - 1 + : 0; + } else { + log_err(info->ctx, + "Cannot set explicit group to %d - must be between 1..%d; " + "Ignoring group number\n", + next_incl.explicit_group + 1, XKB_MAX_GROUPS); + min_layout = max_layout = info->explicit_group; + } + } else { + // Explicit group + max_layout = min_layout; } - } - else { - next_incl.explicit_group = info->explicit_group; + } else { + // No modifier + min_layout = max_layout = info->explicit_group; } - HandleSymbolsFile(&next_incl, file, MERGE_OVERRIDE); - - MergeIncludedSymbols(&included, &next_incl, stmt->merge); + // Loop over the layouts to include + // NOTE: the check "min_layout <= layout" is necessary to handle + // min_layout == XKB_LAYOUT_INVALID + for (xkb_layout_index_t layout=min_layout; + min_layout <= layout && layout <= max_layout; layout++) { + InitSymbolsInfo(&next_incl, info->keymap, info->actions, &included.mods); + next_incl.explicit_group = layout; + HandleSymbolsFile(&next_incl, file, MERGE_OVERRIDE); + MergeIncludedSymbols(&included, &next_incl, stmt->merge); + ClearSymbolsInfo(&next_incl); + } - ClearSymbolsInfo(&next_incl); FreeXkbFile(file); } diff --git a/src/xkbcomp/xkbcomp.c b/src/xkbcomp/xkbcomp.c index 48547c978..216f03186 100644 --- a/src/xkbcomp/xkbcomp.c +++ b/src/xkbcomp/xkbcomp.c @@ -65,7 +65,7 @@ text_v1_keymap_new_from_names(struct xkb_keymap *keymap, rmlvo->rules, rmlvo->model, rmlvo->layout, rmlvo->variant, rmlvo->options); - ok = xkb_components_from_rules(keymap->ctx, rmlvo, &kccgst); + ok = xkb_components_from_rules(keymap->ctx, rmlvo, &kccgst, &keymap->num_explicit_groups); if (!ok) { log_err(keymap->ctx, "Couldn't look up rules '%s', model '%s', layout '%s', " diff --git a/test/rules-file-includes.c b/test/rules-file-includes.c index 006449c77..45705bd1d 100644 --- a/test/rules-file-includes.c +++ b/test/rules-file-includes.c @@ -44,6 +44,7 @@ struct test_data { const char *types; const char *compat; const char *symbols; + const xkb_layout_index_t groups; /* Or set this if xkb_components_from_rules() should fail. */ bool should_fail; @@ -57,6 +58,7 @@ test_rules(struct xkb_context *ctx, struct test_data *data) data->rules, data->model, data->layout, data->variant, data->options }; struct xkb_component_names kccgst; + xkb_layout_index_t groups; fprintf(stderr, "\n\nChecking : %s\t%s\t%s\t%s\t%s\n", data->rules, data->model, data->layout, data->variant, data->options); @@ -64,21 +66,22 @@ test_rules(struct xkb_context *ctx, struct test_data *data) if (data->should_fail) fprintf(stderr, "Expecting: FAILURE\n"); else - fprintf(stderr, "Expecting: %s\t%s\t%s\t%s\n", - data->keycodes, data->types, data->compat, data->symbols); + fprintf(stderr, "Expecting: %s\t%s\t%s\t%s\t%u\n", + data->keycodes, data->types, data->compat, data->symbols, data->groups); - if (!xkb_components_from_rules(ctx, &rmlvo, &kccgst)) { + if (!xkb_components_from_rules(ctx, &rmlvo, &kccgst, &groups)) { fprintf(stderr, "Received : FAILURE\n"); return data->should_fail; } - fprintf(stderr, "Received : %s\t%s\t%s\t%s\n", - kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols); + fprintf(stderr, "Received : %s\t%s\t%s\t%s\t%u\n", + kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols, groups); passed = streq(kccgst.keycodes, data->keycodes) && streq(kccgst.types, data->types) && streq(kccgst.compat, data->compat) && - streq(kccgst.symbols, data->symbols); + streq(kccgst.symbols, data->symbols) && + groups == data->groups; free(kccgst.keycodes); free(kccgst.types); @@ -105,6 +108,7 @@ main(int argc, char *argv[]) .keycodes = "my_keycodes", .types = "default_types", .compat = "default_compat", .symbols = "my_symbols", + .groups = 1, }; assert(test_rules(ctx, &test1)); @@ -115,6 +119,7 @@ main(int argc, char *argv[]) .keycodes = "my_keycodes", .types = "default_types", .compat = "default_compat", .symbols = "my_symbols", + .groups = 1, }; assert(test_rules(ctx, &test2)); @@ -134,6 +139,7 @@ main(int argc, char *argv[]) .keycodes = "my_keycodes", .types = "default_types", .compat = "default_compat", .symbols = "default_symbols", + .groups = 1, }; assert(test_rules(ctx, &test4)); @@ -146,6 +152,7 @@ main(int argc, char *argv[]) .keycodes = "my_keycodes", .types = "default_types", .compat = "default_compat+substring+group(bla)|some:compat", .symbols = "my_symbols+extra_variant+altwin(menu)", + .groups = 1, }; assert(test_rules(ctx, &test5)); @@ -156,6 +163,7 @@ main(int argc, char *argv[]) .keycodes = "my_keycodes", .types = "default_types", .compat = "default_compat", .symbols = "my_symbols", + .groups = 1, }; assert(test_rules(ctx, &test6)); diff --git a/test/rules-file.c b/test/rules-file.c index d217ba960..981afe777 100644 --- a/test/rules-file.c +++ b/test/rules-file.c @@ -42,6 +42,7 @@ struct test_data { const char *types; const char *compat; const char *symbols; + const xkb_layout_index_t groups; /* Or set this if xkb_components_from_rules() should fail. */ bool should_fail; @@ -55,6 +56,7 @@ test_rules(struct xkb_context *ctx, struct test_data *data) data->rules, data->model, data->layout, data->variant, data->options }; struct xkb_component_names kccgst; + xkb_layout_index_t groups; fprintf(stderr, "\n\nChecking : %s\t%s\t%s\t%s\t%s\n", data->rules, data->model, data->layout, data->variant, data->options); @@ -62,21 +64,22 @@ test_rules(struct xkb_context *ctx, struct test_data *data) if (data->should_fail) fprintf(stderr, "Expecting: FAILURE\n"); else - fprintf(stderr, "Expecting: %s\t%s\t%s\t%s\n", - data->keycodes, data->types, data->compat, data->symbols); + fprintf(stderr, "Expecting: %s\t%s\t%s\t%s\t%u\n", + data->keycodes, data->types, data->compat, data->symbols, data->groups); - if (!xkb_components_from_rules(ctx, &rmlvo, &kccgst)) { + if (!xkb_components_from_rules(ctx, &rmlvo, &kccgst, &groups)) { fprintf(stderr, "Received : FAILURE\n"); return data->should_fail; } - fprintf(stderr, "Received : %s\t%s\t%s\t%s\n", - kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols); + fprintf(stderr, "Received : %s\t%s\t%s\t%s\t%u\n", + kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols, groups); passed = streq(kccgst.keycodes, data->keycodes) && streq(kccgst.types, data->types) && streq(kccgst.compat, data->compat) && - streq(kccgst.symbols, data->symbols); + streq(kccgst.symbols, data->symbols) && + groups == data->groups; free(kccgst.keycodes); free(kccgst.types); @@ -103,6 +106,7 @@ main(int argc, char *argv[]) .keycodes = "my_keycodes", .types = "my_types", .compat = "my_compat|some:compat", .symbols = "my_symbols+extra_variant", + .groups = 1, }; assert(test_rules(ctx, &test1)); @@ -113,6 +117,7 @@ main(int argc, char *argv[]) .keycodes = "default_keycodes", .types = "default_types", .compat = "default_compat", .symbols = "default_symbols", + .groups = 1, }; assert(test_rules(ctx, &test2)); @@ -123,6 +128,7 @@ main(int argc, char *argv[]) .keycodes = "something(pc104)", .types = "default_types", .compat = "default_compat", .symbols = "default_symbols", + .groups = 1, }; assert(test_rules(ctx, &test3)); @@ -133,6 +139,7 @@ main(int argc, char *argv[]) .keycodes = "default_keycodes", .types = "default_types", .compat = "default_compat", .symbols = "my_symbols+(bar)", + .groups = 1, }; assert(test_rules(ctx, &test4)); @@ -155,6 +162,7 @@ main(int argc, char *argv[]) .keycodes = "default_keycodes", .types = "default_types", .compat = "default_compat", .symbols = "default_symbols+extra:1+extra:2+extra:3+extra:4", + .groups = 4, }; assert(test_rules(ctx, &test6)); @@ -167,6 +175,7 @@ main(int argc, char *argv[]) .keycodes = "my_keycodes", .types = "my_types", .compat = "my_compat+some:compat+group(bla)", .symbols = "my_symbols+extra_variant+compose(foo)+keypad(bar)+altwin(menu)", + .groups = 1, }; assert(test_rules(ctx, &test7)); diff --git a/tools/compile-keymap.c b/tools/compile-keymap.c index d0877a683..e3c42ce66 100644 --- a/tools/compile-keymap.c +++ b/tools/compile-keymap.c @@ -212,7 +212,7 @@ print_kccgst(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo) #if ENABLE_PRIVATE_APIS struct xkb_component_names kccgst; - if (!xkb_components_from_rules(ctx, rmlvo, &kccgst)) + if (!xkb_components_from_rules(ctx, rmlvo, &kccgst, NULL)) return false; printf("xkb_keymap {\n" From fef19d07cfdc0f739658461609beba306f0b6026 Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Sat, 13 May 2023 18:20:18 +0200 Subject: [PATCH 2/2] Add tests --- test/data/rules/modifiers | 19 ++++++++ test/rulescomp.c | 94 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 test/data/rules/modifiers diff --git a/test/data/rules/modifiers b/test/data/rules/modifiers new file mode 100644 index 000000000..68592f89b --- /dev/null +++ b/test/data/rules/modifiers @@ -0,0 +1,19 @@ +! include %S/evdev + +! option = symbols + // Override + my_option_O0 = +group(alt_space_toggle) + my_option_O1 = +group(alt_space_toggle):1 + my_option_O13 = +group(alt_space_toggle):1+group(alt_space_toggle):3 + my_option_Ox = +group(alt_space_toggle):4294967295 // invalid layout index + my_option_Oall = +group(alt_space_toggle):all + // Augment + my_option_A0 = |group(alt_space_toggle) + my_option_A1 = |group(alt_space_toggle):1 + my_option_A13 = |group(alt_space_toggle):1|group(alt_space_toggle):3 + my_option_Ax = |group(alt_space_toggle):4294967295 // invalid layout index + my_option_Aall = |group(alt_space_toggle):all + +! layout option = symbols +* my_option_LOall = +group(alt_space_toggle):all +* my_option_LAall = |group(alt_space_toggle):all diff --git a/test/rulescomp.c b/test/rulescomp.c index eddf30664..a8133f0f9 100644 --- a/test/rulescomp.c +++ b/test/rulescomp.c @@ -187,6 +187,100 @@ main(int argc, char *argv[]) assert(test_rmlvo_env(ctx, "evdev", "", "cz", "bksl", "", KEY_A, BOTH, XKB_KEY_a, FINISH)); + + /* Include modifiers */ + struct modifiers_data { + const char *layout; + const char *options; + xkb_keysym_t space_keysym1; + xkb_keysym_t space_keysym2; + xkb_keysym_t space_keysym3; + xkb_keysym_t space_keysym4; + xkb_keysym_t grave_keysym1; + xkb_keysym_t grave_keysym2; + xkb_keysym_t grave_keysym3; + xkb_keysym_t grave_keysym4; + }; + + struct modifiers_data test_modifiers[] = { + {"us,de,in", "", + XKB_KEY_space, XKB_KEY_space, XKB_KEY_space, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_grave, XKB_KEY_grave, XKB_KEY_grave}, + // Override ////////////////////////////////////////////////// + // Set 1st layout, 2nd layout inherits 1st layout + {"us,de,in", "my_option_O0", + XKB_KEY_space, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_dead_circumflex, KS("U094a"), KS("U094a")}, + // Set 1st layout, 2nd layout inherits 1st layout + {"us,de,in", "my_option_O1", + XKB_KEY_space, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_dead_circumflex, KS("U094a"), KS("U094a")}, + // Set 1st layout and 3rd layouts, 2nd layout inherits 1st layout + {"us,de,in", "my_option_O13", + XKB_KEY_space, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, + XKB_KEY_grave, XKB_KEY_dead_circumflex, KS("U094a"), XKB_KEY_grave}, + // Invalid layout index defaults to 1, set 1st layout, 2nd layout inherits 1st layout + {"us,de,in", "my_option_Ox", + XKB_KEY_space, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_dead_circumflex, KS("U094a"), KS("U094a")}, + // Set all layouts + {"us,de,in", "my_option_Oall", + XKB_KEY_space, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, + XKB_KEY_grave, XKB_KEY_dead_circumflex, KS("U094a"), XKB_KEY_grave}, + // No match: there more than 1 layout + {"us,de,in", "my_option_LOall", + XKB_KEY_space, XKB_KEY_space, XKB_KEY_space, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_grave, XKB_KEY_grave, XKB_KEY_grave}, + // Augment /////////////////////////////////////////////////// + // In all the following, 3rd layout can not be updated with augment mode. + {"us,de,in", "my_option_A0", + XKB_KEY_space, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_dead_circumflex, KS("U094a"), KS("U094a")}, + {"us,de,in", "my_option_A1", + XKB_KEY_space, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_dead_circumflex, KS("U094a"), KS("U094a")}, + {"us,de,in", "my_option_A13", + XKB_KEY_space, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_dead_circumflex, KS("U094a"), KS("U094a")}, + {"us,de,in", "my_option_Ax", + XKB_KEY_space, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_dead_circumflex, KS("U094a"), KS("U094a")}, + {"us,de,in", "my_option_Aall", + XKB_KEY_space, XKB_KEY_ISO_Next_Group, XKB_KEY_ISO_Next_Group, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_dead_circumflex, KS("U094a"), KS("U094a")}, + {"us,de,in", "my_option_LAall", // No match: there more than 1 layout + XKB_KEY_space, XKB_KEY_space, XKB_KEY_space, XKB_KEY_space, + XKB_KEY_grave, XKB_KEY_grave, XKB_KEY_grave, XKB_KEY_grave}, + {NULL, XKB_KEY_NoSymbol} + }; + + for (struct modifiers_data *md = test_modifiers; md->options; md++) { + assert(test_rmlvo( + // NOTE: 2nd layout “de” does not define so it will inherit + // it form first layout. On the other hand, 3rd layout “in” + // does define it, so it is not impacted by changes on + // 1st layout. + ctx, "modifiers", "", md->layout, "", md->options, + // Base layout + KEY_SPACE, BOTH, md->space_keysym1, NEXT, + KEY_GRAVE, BOTH, md->grave_keysym1, NEXT, + // Try to switch layout + KEY_LEFTALT, DOWN, XKB_KEY_Alt_L, NEXT, + KEY_SPACE, BOTH, md->space_keysym2, NEXT, + KEY_LEFTALT, UP, XKB_KEY_Alt_L, NEXT, + KEY_GRAVE, BOTH, md->grave_keysym2, NEXT, + // Try to switch layout + KEY_LEFTALT, DOWN, XKB_KEY_Alt_L, NEXT, + KEY_SPACE, BOTH, md->space_keysym3, NEXT, + KEY_LEFTALT, UP, XKB_KEY_Alt_L, NEXT, + KEY_GRAVE, BOTH, md->grave_keysym3, NEXT, + // Try to switch layout + KEY_LEFTALT, DOWN, XKB_KEY_Alt_L, NEXT, + KEY_SPACE, BOTH, md->space_keysym4, NEXT, + KEY_LEFTALT, UP, XKB_KEY_Alt_L, NEXT, + KEY_GRAVE, BOTH, md->grave_keysym4, FINISH)); + } + xkb_context_unref(ctx); ctx = test_get_context(0);