keysym-unicode.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * Copyright © 2025 Pierre Le Marre <dev@wismill.eu>
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "config.h"
  6. #include "test-config.h"
  7. #include "test.h"
  8. #ifdef _WIN32
  9. int
  10. main(void)
  11. {
  12. test_init();
  13. return SKIP_TEST;
  14. }
  15. #else
  16. #include <assert.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <sys/wait.h>
  21. #include "xkbcommon/xkbcommon-keysyms.h"
  22. #include "xkbcommon/xkbcommon.h"
  23. #include "utils.h"
  24. #include "src/keysym.h"
  25. static void
  26. test_unicode_keysyms_consistency(uint32_t start, uint32_t end)
  27. {
  28. static_assert(XKB_KEYSYM_NAME_MAX_SIZE > XKB_KEYSYM_UTF8_MAX_SIZE,
  29. "Buffer too small");
  30. char buffer[XKB_KEYSYM_NAME_MAX_SIZE] = {0};
  31. char utf8[XKB_KEYSYM_UTF8_MAX_SIZE] = {0};
  32. for (uint32_t cp = start; cp <= end; cp++) {
  33. xkb_keysym_t unicode = XKB_KEYSYM_UNICODE_OFFSET + cp;
  34. xkb_keysym_t canonical = xkb_utf32_to_keysym(cp);
  35. int count = xkb_keysym_get_name(unicode, buffer, sizeof(buffer));
  36. assert(count > 0);
  37. if (cp == 0 || is_surrogate(cp)) {
  38. /*
  39. * Invalid code points
  40. */
  41. /* No conversion from code point */
  42. assert(canonical == XKB_KEY_NoSymbol);
  43. /* No conversion to code point */
  44. assert(xkb_keysym_to_utf32(unicode) == 0);
  45. char *end_ptr = NULL;
  46. if (cp == 0) {
  47. /* Corresponding name hexadecimal format, unchanged */
  48. assert(strtoull(buffer, &end_ptr, 16) == unicode);
  49. } else {
  50. /* Unicode notation */
  51. assert(count == 5 && buffer[0] == 'U');
  52. assert(strtoull(buffer + 1, &end_ptr, 16) == cp);
  53. }
  54. assert(*end_ptr == '\0');
  55. /* Roundtrip hexadecimal name */
  56. xkb_keysym_t ks = xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS);
  57. assert(ks == unicode);
  58. /* Check Unicode format */
  59. snprintf(buffer, sizeof(buffer), "U%"PRIX32, cp);
  60. ks = xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS);
  61. assert((cp == 0 && ks == XKB_KEY_NoSymbol) ^
  62. (is_surrogate(cp) && ks == unicode));
  63. /* Cannot convert to UTF-8 */
  64. count = xkb_keysym_to_utf8(unicode, buffer, sizeof(buffer));
  65. assert(count == 0);
  66. } else {
  67. /*
  68. * Valid code points
  69. */
  70. /* Canonical keysym may be different but is set */
  71. assert_printf((canonical == unicode) ^
  72. ((0x20 <= cp && cp <= 0x100 && cp != 0x7f && canonical == cp) ||
  73. (canonical != unicode &&
  74. canonical != XKB_KEY_NoSymbol &&
  75. (canonical != cp || canonical == XKB_KEY_EuroSign))),
  76. "Code point: U+%04"PRIX32", Unicode: %#"PRIx32", "
  77. "canonical: %#"PRIx32"\n",
  78. cp, unicode, canonical);
  79. /* Conversion to code point has the same expected result */
  80. assert(xkb_keysym_to_utf32(unicode) == cp);
  81. assert(xkb_keysym_to_utf32(canonical) == cp); /* roundtrip */
  82. /* Check name roundtrip */
  83. xkb_keysym_t ks = xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS);
  84. assert((unicode != canonical && ks == canonical) ^ (ks == unicode));
  85. /* Can use Unicode format */
  86. if (buffer[0] == 'U' && count > 4 && is_digit(buffer[1])) {
  87. /* Name already a Unicode notation: skip.
  88. * Note that the heuristic may fail to detect a Unicode notation,
  89. * but it is only meant to make the overall test faster. */
  90. } else {
  91. snprintf(buffer, sizeof(buffer), "U%"PRIX32, cp);
  92. ks = xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS);
  93. assert((unicode != canonical && ks == canonical) ^ (ks == unicode));
  94. }
  95. /* Roundtrip: numeric hexadecimal format for Unicode keysym */
  96. assert(snprintf(buffer, sizeof(buffer), "%#"PRIx32, unicode) == 9);
  97. ks = xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS);
  98. assert(ks == unicode);
  99. /* Can convert to UTF-8 (Unicode keysym) */
  100. count = xkb_keysym_to_utf8(unicode, buffer, sizeof(buffer));
  101. assert(count > 0);
  102. if (canonical != unicode) {
  103. /* Canonical keysym convert to same UTF-8 */
  104. const int count2 = xkb_keysym_to_utf8(canonical, utf8, sizeof(utf8));
  105. assert(count2 == count);
  106. assert(strcmp(buffer, utf8) == 0);
  107. /* Roundtrip: numeric hexadecimal format for canonical keysym */
  108. count = xkb_keysym_get_name(canonical, buffer, sizeof(buffer));
  109. assert(count > 0);
  110. assert(xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS) == canonical);
  111. assert(snprintf(buffer, sizeof(buffer), "%#"PRIx32, canonical) > 2);
  112. assert(xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS) == canonical);
  113. }
  114. }
  115. }
  116. }
  117. int
  118. main(int argc, char *argv[])
  119. {
  120. test_init();
  121. unsigned long int NUM_PROCESSES = 0;
  122. if (argc > 1) {
  123. NUM_PROCESSES = strtoul(argv[1], NULL, 10);
  124. }
  125. if (NUM_PROCESSES == 0 || NUM_PROCESSES > 32)
  126. NUM_PROCESSES = 4;
  127. const uint32_t max_codepoint = 0x10ffff;
  128. const uint32_t chunk = max_codepoint / NUM_PROCESSES;
  129. pid_t pids[NUM_PROCESSES];
  130. for (unsigned long int i = 0; i < NUM_PROCESSES; i++) {
  131. pid_t pid = fork();
  132. if (pid == 0) {
  133. /* Child */
  134. const uint32_t start = (uint32_t)i * chunk;
  135. const uint32_t end = (i == NUM_PROCESSES - 1)
  136. ? max_codepoint
  137. : start + chunk;
  138. test_unicode_keysyms_consistency(start, end);
  139. exit(EXIT_SUCCESS);
  140. } else if (pid > 0) {
  141. /* Parent */
  142. pids[i] = pid;
  143. } else {
  144. perror("fork");
  145. return TEST_SETUP_FAILURE;
  146. }
  147. }
  148. /* Wait for children */
  149. int exit_code = EXIT_SUCCESS;
  150. for (unsigned long int i = 0; i < NUM_PROCESSES; i++) {
  151. int status;
  152. if (waitpid(pids[i], &status, 0) == -1) {
  153. perror("waitpid");
  154. exit_code = EXIT_FAILURE;
  155. } else if (WIFEXITED(status)) {
  156. int code = WEXITSTATUS(status);
  157. if (code != EXIT_SUCCESS) {
  158. fprintf(stderr,
  159. "Child PID %d exited with code %d\n",
  160. pids[i], code);
  161. exit_code = EXIT_FAILURE;
  162. }
  163. } else if (WIFSIGNALED(status)) {
  164. fprintf(stderr,
  165. "Child PID %d terminated by signal %d\n",
  166. pids[i], WTERMSIG(status));
  167. exit_code = EXIT_FAILURE;
  168. }
  169. }
  170. return exit_code;
  171. }
  172. #endif