rulescomp.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. /*
  2. * Copyright © 2009 Dan Nicholson
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "config.h"
  6. #include "test-config.h"
  7. #include <assert.h>
  8. #include <stdlib.h>
  9. #include "xkbcommon/xkbcommon.h"
  10. #include "evdev-scancodes.h"
  11. #include "src/keymap.h"
  12. #include "src/keysym.h"
  13. #include "test.h"
  14. #include "utils.h"
  15. static void
  16. test_components_names_from_rules(struct xkb_context *ctx) {
  17. static const struct xkb_rule_names tests[] = {
  18. {
  19. .rules = "evdev",
  20. .model = "pc105",
  21. .layout = "us",
  22. .variant = NULL,
  23. .options = NULL
  24. },
  25. {
  26. .rules = "evdev",
  27. .model = "pc105",
  28. .layout = "us",
  29. .variant = NULL,
  30. .options = "" /* empty string */
  31. },
  32. {
  33. .rules = "evdev",
  34. .model = "pc105",
  35. .layout = "us",
  36. .variant = NULL,
  37. .options = "," /* dummy list */
  38. },
  39. };
  40. for (size_t t = 0; t < ARRAY_SIZE(tests); t++) {
  41. fprintf(stderr, "------\n*** %s: #%zu ***\n", __func__, t);
  42. struct xkb_rule_names rmlvo = {0};
  43. assert(xkb_components_names_from_rules(ctx, &tests[t], &rmlvo, NULL));
  44. assert_streq_not_null("rules", tests[t].rules, rmlvo.rules);
  45. assert_streq_not_null("model", tests[t].model, rmlvo.model);
  46. assert_streq_not_null("layout", tests[t].layout, rmlvo.layout);
  47. assert_streq("variant", tests[t].variant, rmlvo.variant);
  48. assert_streq("options", tests[t].options, rmlvo.options);
  49. }
  50. }
  51. /* xkb_rule_names API */
  52. static int
  53. test_rmlvo_va(struct xkb_context *context, enum xkb_keymap_format format,
  54. const char *rules, const char *model, const char *layout,
  55. const char *variant, const char *options, va_list ap)
  56. {
  57. struct xkb_keymap * const keymap =
  58. test_compile_rules(context, format, rules, model, layout, variant, options);
  59. if (!keymap)
  60. return 0;
  61. fprintf(stderr, "Compiled '%s' '%s' '%s' '%s' '%s'\n",
  62. strnull(rules), strnull(model), strnull(layout),
  63. strnull(variant), strnull(options));
  64. const int ret = test_key_seq_va(keymap, NULL, NULL, ap);
  65. xkb_keymap_unref(keymap);
  66. return ret;
  67. }
  68. /* xkb_rmlvo_builder API */
  69. static int
  70. test_rmlvo_builder_va(struct xkb_context *context, enum xkb_keymap_format format,
  71. const char *rules, const char *model, const char *layout,
  72. const char *variant, const char *options, va_list ap)
  73. {
  74. struct xkb_keymap * const keymap =
  75. test_compile_rmlvo(context, format, rules, model, layout, variant, options);
  76. if (!keymap)
  77. return 0;
  78. fprintf(stderr, "Compiled '%s' '%s' '%s' '%s' '%s'\n",
  79. strnull(rules), strnull(model), strnull(layout),
  80. strnull(variant), strnull(options));
  81. const int ret = test_key_seq_va(keymap, NULL, NULL, ap);
  82. xkb_keymap_unref(keymap);
  83. return ret;
  84. }
  85. static int
  86. test_rmlvo(struct xkb_context *context, enum xkb_keymap_format format,
  87. const char *rules, const char *model, const char *layout,
  88. const char *variant, const char *options, ...)
  89. {
  90. va_list ap;
  91. int ret;
  92. va_start(ap, options);
  93. ret = test_rmlvo_va(context, format, rules, model, layout, variant, options, ap);
  94. va_end(ap);
  95. va_start(ap, options);
  96. ret &= test_rmlvo_builder_va(context, format,
  97. rules, model, layout, variant, options, ap);
  98. va_end(ap);
  99. return ret;
  100. }
  101. static int
  102. test_rmlvo_env(struct xkb_context *ctx, enum xkb_keymap_format format,
  103. const char *rules, const char *model, const char *layout,
  104. const char *variant, const char *options, ...)
  105. {
  106. va_list ap;
  107. int ret;
  108. va_start (ap, options);
  109. if (!isempty(rules))
  110. setenv("XKB_DEFAULT_RULES", rules, 1);
  111. else
  112. unsetenv("XKB_DEFAULT_RULES");
  113. if (!isempty(model))
  114. setenv("XKB_DEFAULT_MODEL", model, 1);
  115. else
  116. unsetenv("XKB_DEFAULT_MODEL");
  117. if (!isempty(layout))
  118. setenv("XKB_DEFAULT_LAYOUT", layout, 1);
  119. else
  120. unsetenv("XKB_DEFAULT_LAYOUT");
  121. if (!isempty(variant))
  122. setenv("XKB_DEFAULT_VARIANT", variant, 1);
  123. else
  124. unsetenv("XKB_DEFAULT_VARIANT");
  125. if (!isempty(options))
  126. setenv("XKB_DEFAULT_OPTIONS", options, 1);
  127. else
  128. unsetenv("XKB_DEFAULT_OPTIONS");
  129. ret = test_rmlvo_va(ctx, format, NULL, NULL, NULL, NULL, NULL, ap);
  130. va_end(ap);
  131. return ret;
  132. }
  133. /* Test more than 4 groups */
  134. static void
  135. test_extended_groups(struct xkb_context *ctx)
  136. {
  137. struct {
  138. enum xkb_keymap_format format;
  139. xkb_layout_index_t num_layouts;
  140. const char *layouts;
  141. } tests[] = {
  142. /* v1: 4 groups */
  143. {
  144. .format = XKB_KEYMAP_FORMAT_TEXT_V1,
  145. .layouts = "cz,us,ca,de",
  146. .num_layouts = XKB_MAX_GROUPS_X11
  147. },
  148. /* v1: 5 groups, discard 1 group */
  149. {
  150. .format = XKB_KEYMAP_FORMAT_TEXT_V1,
  151. .layouts = "cz,us,ca,de,in",
  152. .num_layouts = XKB_MAX_GROUPS_X11
  153. },
  154. /* v2: 5 groups */
  155. {
  156. .format = XKB_KEYMAP_FORMAT_TEXT_V2,
  157. .layouts = "cz,us,ca,de,in",
  158. .num_layouts = XKB_MAX_GROUPS_X11 + 1
  159. },
  160. /* v2: 32 groups */
  161. {
  162. .format = XKB_KEYMAP_FORMAT_TEXT_V2,
  163. .layouts = "cz,us,ca,de,in,cz,us,ca,de,in,cz,us,ca,de,in,"
  164. "cz,us,ca,de,in,cz,us,ca,de,in,cz,us,ca,de,in,"
  165. "cz,us",
  166. .num_layouts = XKB_MAX_GROUPS
  167. },
  168. /* v2: 33 groups, discard 1 group */
  169. {
  170. .format = XKB_KEYMAP_FORMAT_TEXT_V2,
  171. .layouts = "cz,us,ca,de,in,cz,us,ca,de,in,cz,us,ca,de,in,"
  172. "cz,us,ca,de,in,cz,us,ca,de,in,cz,us,ca,de,in,"
  173. "cz,us,ca",
  174. .num_layouts = XKB_MAX_GROUPS
  175. },
  176. };
  177. for (size_t k = 0; k < ARRAY_SIZE(tests); k++) {
  178. fprintf(stderr, "------\n*** %s: #%zu ***\n", __func__, k);
  179. struct xkb_keymap *keymap =
  180. test_compile_rules(ctx, tests[k].format, "evdev-modern",
  181. "pc105", tests[k].layouts, NULL, NULL);
  182. assert(keymap);
  183. assert(xkb_keymap_num_layouts(keymap) == tests[k].num_layouts);
  184. xkb_keymap_unref(keymap);
  185. }
  186. #define U(cp) (XKB_KEYSYM_UNICODE_OFFSET + (cp))
  187. assert(test_rmlvo_env(ctx, XKB_KEYMAP_FORMAT_TEXT_V2, "evdev-modern",
  188. "", "cz,us,ca,de,in,ru,il", ",,,,,phonetic,",
  189. "grp:menu_toggle",
  190. KEY_2, BOTH, XKB_KEY_ecaron, NEXT,
  191. KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
  192. KEY_Y, BOTH, XKB_KEY_y, NEXT,
  193. KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
  194. KEY_102ND, BOTH, XKB_KEY_guillemetleft, NEXT,
  195. KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
  196. KEY_Y, BOTH, XKB_KEY_z, NEXT,
  197. KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
  198. KEY_Y, BOTH, U(0x092c), NEXT,
  199. KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
  200. KEY_Y, BOTH, XKB_KEY_Cyrillic_ze, NEXT,
  201. KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
  202. KEY_Y, BOTH, XKB_KEY_hebrew_tet, FINISH));
  203. #undef U
  204. }
  205. int
  206. main(int argc, char *argv[])
  207. {
  208. test_init();
  209. struct xkb_context *ctx = test_get_context(CONTEXT_ALLOW_ENVIRONMENT_NAMES);
  210. assert(ctx);
  211. /* Reject invalid flags */
  212. assert(!xkb_rmlvo_builder_new(ctx, NULL, NULL, -1));
  213. assert(!xkb_rmlvo_builder_new(ctx, NULL, NULL, 0xffff));
  214. static const struct xkb_rule_names rmlvo = { NULL };
  215. assert(!xkb_keymap_new_from_names2(ctx, &rmlvo,
  216. XKB_KEYMAP_FORMAT_TEXT_V1, -1));
  217. assert(!xkb_keymap_new_from_names2(ctx, &rmlvo,
  218. XKB_KEYMAP_FORMAT_TEXT_V1, 5453));
  219. test_components_names_from_rules(ctx);
  220. /* Test “Last” group constant as an array index */
  221. struct xkb_keymap *keymap =
  222. test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev-modern",
  223. "pc105", "last-group", NULL, NULL);
  224. assert(keymap);
  225. assert(xkb_keymap_num_layouts(keymap) == 1);
  226. const xkb_keysym_t *syms = NULL;
  227. int nsyms = xkb_keymap_key_get_syms_by_level(
  228. keymap, KEY_Q + EVDEV_OFFSET, 0, 0, &syms
  229. );
  230. assert(nsyms == 1);
  231. assert(XKB_KEY_a == syms[0]); /* `Last` works: there is only one group */
  232. xkb_keymap_unref(keymap);
  233. keymap =
  234. test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev-modern",
  235. "pc105", "last-group,us", NULL, NULL);
  236. assert(keymap);
  237. assert(xkb_keymap_num_layouts(keymap) == 2);
  238. syms = NULL;
  239. nsyms = xkb_keymap_key_get_syms_by_level(
  240. keymap, KEY_Q + EVDEV_OFFSET, 0, 0, &syms
  241. );
  242. assert(nsyms == 0); /* `Last` does not work: there are multiple groups */
  243. nsyms = xkb_keymap_key_get_syms_by_level(
  244. keymap, KEY_Q + EVDEV_OFFSET, 1, 0, &syms
  245. );
  246. assert(nsyms == 1); /* Layout 2 is not impacted */
  247. assert(XKB_KEY_q == syms[0]);
  248. xkb_keymap_unref(keymap);
  249. #define KS(name) xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS)
  250. assert(test_rmlvo(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev",
  251. "pc105", "us,il,ru,ca", ",,,multix",
  252. "grp:alts_toggle,ctrl:nocaps,compose:rwin",
  253. KEY_Q, BOTH, XKB_KEY_q, NEXT,
  254. KEY_LEFTALT, DOWN, XKB_KEY_Alt_L, NEXT,
  255. KEY_RIGHTALT, DOWN, XKB_KEY_ISO_Next_Group, NEXT,
  256. KEY_RIGHTALT, UP, XKB_KEY_ISO_Level3_Shift, NEXT,
  257. KEY_LEFTALT, UP, XKB_KEY_Alt_L, NEXT,
  258. KEY_Q, BOTH, XKB_KEY_slash, NEXT,
  259. KEY_LEFTSHIFT, DOWN, XKB_KEY_Shift_L, NEXT,
  260. KEY_Q, BOTH, XKB_KEY_Q, NEXT,
  261. KEY_RIGHTMETA, BOTH, XKB_KEY_Multi_key, FINISH));
  262. assert(test_rmlvo(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev",
  263. "pc105", "us,in", "", "grp:alts_toggle",
  264. KEY_A, BOTH, XKB_KEY_a, NEXT,
  265. KEY_LEFTALT, DOWN, XKB_KEY_Alt_L, NEXT,
  266. KEY_RIGHTALT, DOWN, XKB_KEY_ISO_Next_Group, NEXT,
  267. KEY_RIGHTALT, UP, XKB_KEY_ISO_Level3_Shift, NEXT,
  268. KEY_LEFTALT, UP, XKB_KEY_Alt_L, NEXT,
  269. KEY_A, BOTH, KS("U094b"), FINISH));
  270. assert(test_rmlvo(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev",
  271. "pc105", "us", "intl", "",
  272. KEY_GRAVE, BOTH, XKB_KEY_dead_grave, FINISH));
  273. assert(test_rmlvo(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev",
  274. "pc105", "us", "intl", "grp:alts_toggle",
  275. KEY_GRAVE, BOTH, XKB_KEY_dead_grave, FINISH));
  276. /* 33 is not a legal group; make sure this is handled gracefully. */
  277. #define EXCESSIVE_GROUPS 33
  278. static_assert(EXCESSIVE_GROUPS > XKB_MAX_GROUPS, "Test upgrade required");
  279. assert(test_rmlvo(ctx, XKB_KEYMAP_FORMAT_TEXT_V2, "evdev",
  280. "", "us:" STRINGIFY(EXCESSIVE_GROUPS), "", "",
  281. KEY_A, BOTH, XKB_KEY_a, FINISH));
  282. #undef EXCESSIVE_GROUPS
  283. /* Don't choke on missing values in RMLVO. Should just skip them.
  284. Currently generates us,us,ca. */
  285. assert(test_rmlvo(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev",
  286. "", "us,,ca", "", "grp:alts_toggle",
  287. KEY_A, BOTH, XKB_KEY_a, NEXT,
  288. KEY_LEFTALT, DOWN, XKB_KEY_Alt_L, NEXT,
  289. KEY_RIGHTALT, DOWN, XKB_KEY_ISO_Next_Group, NEXT,
  290. KEY_RIGHTALT, UP, XKB_KEY_ISO_Next_Group, NEXT,
  291. KEY_LEFTALT, UP, XKB_KEY_Alt_L, NEXT,
  292. KEY_LEFTALT, DOWN, XKB_KEY_Alt_L, NEXT,
  293. KEY_RIGHTALT, DOWN, XKB_KEY_ISO_Next_Group, NEXT,
  294. KEY_RIGHTALT, UP, XKB_KEY_ISO_Level3_Shift, NEXT,
  295. KEY_LEFTALT, UP, XKB_KEY_Alt_L, NEXT,
  296. KEY_APOSTROPHE, BOTH, XKB_KEY_dead_grave, FINISH));
  297. assert(test_rmlvo(ctx, XKB_KEYMAP_FORMAT_TEXT_V1,
  298. "", "", "", "", "",
  299. KEY_A, BOTH, XKB_KEY_a, FINISH));
  300. assert(!test_rmlvo(ctx, XKB_KEYMAP_FORMAT_TEXT_V1,
  301. "does-not-exist", "", "", "", "",
  302. KEY_A, BOTH, XKB_KEY_a, FINISH));
  303. assert(test_rmlvo_env(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev",
  304. "", "us", "", "",
  305. KEY_A, BOTH, XKB_KEY_a, FINISH));
  306. assert(test_rmlvo_env(ctx, XKB_KEYMAP_FORMAT_TEXT_V1,
  307. "evdev", "", "us", "", "ctrl:nocaps",
  308. KEY_CAPSLOCK, BOTH, XKB_KEY_Control_L, FINISH));
  309. /* Ignores multix and generates us,ca. */
  310. assert(test_rmlvo_env(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev",
  311. "", "us,ca", ",,,multix", "grp:alts_toggle",
  312. KEY_A, BOTH, XKB_KEY_a, NEXT,
  313. KEY_LEFTALT, DOWN, XKB_KEY_Alt_L, NEXT,
  314. KEY_RIGHTALT, DOWN, XKB_KEY_ISO_Next_Group, NEXT,
  315. KEY_RIGHTALT, UP, XKB_KEY_ISO_Level3_Shift, NEXT,
  316. KEY_LEFTALT, UP, XKB_KEY_Alt_L, NEXT,
  317. KEY_GRAVE, UP, XKB_KEY_numbersign, FINISH));
  318. assert(!test_rmlvo_env(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "broken", "what-on-earth", "invalid", "", "",
  319. KEY_A, BOTH, XKB_KEY_a, FINISH));
  320. /* Ensure a keymap with an empty xkb_keycodes compiles fine. */
  321. assert(test_rmlvo_env(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "base", "empty", "empty", "", "",
  322. KEY_A, BOTH, XKB_KEY_NoSymbol, FINISH));
  323. /* Check replace merge mode: it should replace the whole <RALT> key */
  324. const char* replace_options[] = {
  325. "replace:single,grp:menu_toggle",
  326. "replace:first,grp:menu_toggle",
  327. "replace:later,grp:menu_toggle",
  328. };
  329. for (unsigned int k = 0; k < ARRAY_SIZE(replace_options); k++) {
  330. const char* const options = replace_options[k];
  331. assert(test_rmlvo(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "merge-mode-replace",
  332. "", "us,de", "", options,
  333. KEY_RIGHTALT, BOTH, XKB_KEY_Alt_R, NEXT,
  334. KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
  335. KEY_RIGHTALT, BOTH, XKB_KEY_Alt_R, FINISH));
  336. }
  337. /* Has an illegal escape sequence, but shouldn't fail. */
  338. assert(test_rmlvo_env(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev",
  339. "", "cz", "bksl", "",
  340. KEY_A, BOTH, XKB_KEY_a, FINISH));
  341. test_extended_groups(ctx);
  342. xkb_context_unref(ctx);
  343. ctx = test_get_context(CONTEXT_NO_FLAG);
  344. assert(test_rmlvo_env(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, "broken",
  345. "but", "ignored", "per", "ctx flags",
  346. KEY_A, BOTH, XKB_KEY_a, FINISH));
  347. xkb_context_unref(ctx);
  348. return EXIT_SUCCESS;
  349. }