- entry = bsearch(s, name_to_keysym,
- ARRAY_SIZE(name_to_keysym),
- sizeof(*name_to_keysym),
- compare_by_name);
- entry = find_sym(entry, s, icase);
- if (entry)
- return entry->keysym;
-
- if (*s == 'U' || (icase && *s == 'u')) {
- val = strtoul(&s[1], &tmp, 16);
- if (tmp && *tmp != '\0')
+ /*
+ * We need to !icase case to be fast, for e.g. Compose file parsing.
+ * So do it in a fast path.
+ */
+ if (!icase) {
+ size_t pos = keysym_name_perfect_hash(name);
+ if (pos < ARRAY_SIZE(name_to_keysym)) {
+ const char *s = get_name(&name_to_keysym[pos]);
+ if (strcmp(name, s) == 0)
+ return name_to_keysym[pos].keysym;
+ }
+ }
+ /*
+ * Find the correct keysym for case-insensitive match.
+ *
+ * The name_to_keysym table is sorted by istrcmp(). So the binary
+ * search may return _any_ of all possible case-insensitive duplicates. This
+ * code searches the entry, all previous and all next entries that match by
+ * case-insensitive comparison and returns the "best" case-insensitive
+ * match.
+ *
+ * The "best" case-insensitive match is the lower-case keysym which we find
+ * with the help of xkb_keysym_is_lower(). The only keysyms that only differ
+ * by letter-case are keysyms that are available as lower-case and
+ * upper-case variant (like KEY_a and KEY_A). So returning the first
+ * lower-case match is enough in this case.
+ */
+ else {
+ int32_t lo = 0, hi = ARRAY_SIZE(name_to_keysym) - 1;
+ while (hi >= lo) {
+ int32_t mid = (lo + hi) / 2;
+ int cmp = istrcmp(name, get_name(&name_to_keysym[mid]));
+ if (cmp > 0) {
+ lo = mid + 1;
+ } else if (cmp < 0) {
+ hi = mid - 1;
+ } else {
+ entry = &name_to_keysym[mid];
+ break;
+ }
+ }
+ if (entry) {
+ const struct name_keysym *iter, *last;
+
+ if (icase && xkb_keysym_is_lower(entry->keysym))
+ return entry->keysym;
+
+ for (iter = entry - 1; iter >= name_to_keysym; --iter) {
+ if (istrcmp(get_name(iter), get_name(entry)) != 0)
+ break;
+ if (xkb_keysym_is_lower(iter->keysym))
+ return iter->keysym;
+ }
+
+ last = name_to_keysym + ARRAY_SIZE(name_to_keysym);
+ for (iter = entry + 1; iter < last; ++iter) {
+ if (istrcmp(get_name(iter), get_name(entry)) != 0)
+ break;
+ if (xkb_keysym_is_lower(iter->keysym))
+ return iter->keysym;
+ }
+
+ return entry->keysym;
+ }
+ }
+
+ if (*name == 'U' || (icase && *name == 'u')) {
+ if (!parse_keysym_hex(&name[1], &val))