This adds a flags argument to xkb_keysym_from_name() so we can perform a case-insensitive search. This should really be supported as many keysyms have really weird capitalization-rules.
However, as this may produce conflicts, users must be warned to only use this for fallback paths or error-recovery. This is also the reason why the internal XKB parsers still use the case-sensitive search. This also adds some test-cases so the expected results are really produced. The binary-size does _not_ change with this patch. However, case-sensitive search may be slightly slower with this patch. But this is barely measurable. Signed-off-by: David Herrmann <dh.herrm...@googlemail.com> --- Hi This is now revision 5. I solved all outstanding problems and fixed anything both of you mentioned. But feel free to change function/type-names before applying. Thanks David makekeys.py | 2 +- src/keysym.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++----- src/xkbcomp/expr.c | 2 +- src/xkbcomp/symbols.c | 2 +- test/keyseq.c | 2 +- test/keysym.c | 43 ++++++++++++++++++++++++++----- xkbcommon/xkbcommon.h | 15 ++++++++++- 7 files changed, 119 insertions(+), 18 deletions(-) diff --git a/makekeys.py b/makekeys.py index 94885c0..996ff3d 100644 --- a/makekeys.py +++ b/makekeys.py @@ -12,7 +12,7 @@ print('''struct name_keysym { };\n''') print('static const struct name_keysym name_to_keysym[] = {'); -for (name, _) in sorted(entries, key=lambda e: e[0]): +for (name, _) in sorted(entries, key=lambda e: e[0].lower()): print(' {{ "{name}", XKB_KEY_{name} }},'.format(name=name)) print('};\n') diff --git a/src/keysym.c b/src/keysym.c index f3685eb..7d5be62 100644 --- a/src/keysym.c +++ b/src/keysym.c @@ -62,7 +62,7 @@ static int compare_by_keysym(const void *a, const void *b) static int compare_by_name(const void *a, const void *b) { const struct name_keysym *key = a, *entry = b; - return strcmp(key->name, entry->name); + return strcasecmp(key->name, entry->name); } XKB_EXPORT int @@ -91,22 +91,80 @@ xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size) return snprintf(buffer, size, "0x%08x", ks); } +/* + * Find the correct keysym if one case-insensitive match is given. + * + * The name_to_keysym table is sorted by strcasecmp(). So bsearch() may return + * _any_ of all possible case-insensitive duplicates. This function searches the + * returned entry @entry, all previous and all next entries that match by + * case-insensitive comparison and returns the exact match to @name. If @icase + * is true, then this returns the best case-insensitive match instead of a + * correct 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. + */ +static const struct name_keysym * +find_sym(const struct name_keysym *entry, const char *name, int icase) +{ + const struct name_keysym *iter, *last; + size_t len = sizeof(name_to_keysym) / sizeof(*name_to_keysym); + + if (!entry) + return NULL; + + if (!icase && strcmp(entry->name, name) == 0) + return entry; + if (icase && xkb_keysym_is_lower(entry->keysym)) + return entry; + + for (iter = entry - 1; iter >= name_to_keysym; --iter) { + if (!icase && strcmp(iter->name, name) == 0) + return iter; + if (strcasecmp(iter->name, entry->name) != 0) + break; + if (icase && xkb_keysym_is_lower(iter->keysym)) + return iter; + } + + last = name_to_keysym + len; + for (iter = entry + 1; iter < last; --iter) { + if (!icase && strcmp(iter->name, name) == 0) + return iter; + if (strcasecmp(iter->name, entry->name) != 0) + break; + if (icase && xkb_keysym_is_lower(iter->keysym)) + return iter; + } + + if (icase) + return entry; + return NULL; +} + XKB_EXPORT xkb_keysym_t -xkb_keysym_from_name(const char *s) +xkb_keysym_from_name(const char *s, enum xkb_keysym_flags flags) { const struct name_keysym search = { .name = s, .keysym = 0 }; const struct name_keysym *entry; char *tmp; xkb_keysym_t val; + int icase = flags & XKB_KEYSYM_CASE_INSENSITIVE; + + if (flags & ~XKB_KEYSYM_CASE_INSENSITIVE) + return XKB_KEY_NoSymbol; entry = bsearch(&search, name_to_keysym, sizeof(name_to_keysym) / sizeof(*name_to_keysym), sizeof(*name_to_keysym), compare_by_name); + entry = find_sym(entry, s, icase); if (entry) return entry->keysym; - if (*s == 'U') { + if (*s == 'U' || (icase && *s == 'u')) { val = strtoul(&s[1], &tmp, 16); if (tmp && *tmp != '\0') return XKB_KEY_NoSymbol; @@ -119,7 +177,7 @@ xkb_keysym_from_name(const char *s) return XKB_KEY_NoSymbol; return val | 0x01000000; } - else if (s[0] == '0' && s[1] == 'x') { + else if (s[0] == '0' && (s[1] == 'x' || (icase && s[1] == 'X'))) { val = strtoul(&s[2], &tmp, 16); if (tmp && *tmp != '\0') return XKB_KEY_NoSymbol; @@ -130,13 +188,14 @@ xkb_keysym_from_name(const char *s) /* Stupid inconsistency between the headers and XKeysymDB: the former has * no separating underscore, while some XF86* syms in the latter did. * As a last ditch effort, try without. */ - if (strncmp(s, "XF86_", 5) == 0) { + if (strncmp(s, "XF86_", 5) == 0 || + (icase && strncasecmp(s, "XF86_", 5) == 0)) { xkb_keysym_t ret; tmp = strdup(s); if (!tmp) return XKB_KEY_NoSymbol; memmove(&tmp[4], &tmp[5], strlen(s) - 5 + 1); - ret = xkb_keysym_from_name(tmp); + ret = xkb_keysym_from_name(tmp, flags); free(tmp); return ret; } diff --git a/src/xkbcomp/expr.c b/src/xkbcomp/expr.c index eb043e1..dc64d78 100644 --- a/src/xkbcomp/expr.c +++ b/src/xkbcomp/expr.c @@ -641,7 +641,7 @@ ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr, if (expr->op == EXPR_IDENT) { const char *str; str = xkb_atom_text(ctx, expr->value.str); - *sym_rtrn = xkb_keysym_from_name(str); + *sym_rtrn = xkb_keysym_from_name(str, 0); if (*sym_rtrn != XKB_KEY_NoSymbol) return true; } diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c index 10efef0..9900469 100644 --- a/src/xkbcomp/symbols.c +++ b/src/xkbcomp/symbols.c @@ -652,7 +652,7 @@ LookupKeysym(const char *str, xkb_keysym_t *sym_rtrn) return 1; } - sym = xkb_keysym_from_name(str); + sym = xkb_keysym_from_name(str, 0); if (sym != XKB_KEY_NoSymbol) { *sym_rtrn = sym; return 1; diff --git a/test/keyseq.c b/test/keyseq.c index d5993ec..cb53739 100644 --- a/test/keyseq.c +++ b/test/keyseq.c @@ -305,7 +305,7 @@ main(void) KEY_RIGHTSHIFT, UP, XKB_KEY_Shift_R, NEXT, KEY_V, BOTH, XKB_KEY_Cyrillic_ZHE, FINISH)); -#define KS(name) xkb_keysym_from_name(name) +#define KS(name) xkb_keysym_from_name(name, 0) /* Test that levels (1-5) in de(neo) symbols map work. */ assert(test_key_seq(keymap, diff --git a/test/keysym.c b/test/keysym.c index ef934ab..ad8b1ce 100644 --- a/test/keysym.c +++ b/test/keysym.c @@ -29,7 +29,7 @@ test_string(const char *string, xkb_keysym_t expected) { xkb_keysym_t keysym; - keysym = xkb_keysym_from_name(string); + keysym = xkb_keysym_from_name(string, 0); fprintf(stderr, "Expected string %s -> %x\n", string, expected); fprintf(stderr, "Received string %s -> %x\n\n", string, keysym); @@ -38,6 +38,19 @@ test_string(const char *string, xkb_keysym_t expected) } static int +test_casestring(const char *string, xkb_keysym_t expected) +{ + xkb_keysym_t keysym; + + keysym = xkb_keysym_from_name(string, XKB_KEYSYM_CASE_INSENSITIVE); + + fprintf(stderr, "Expected casestring %s -> %x\n", string, expected); + fprintf(stderr, "Received casestring %s -> %x\n\n", string, keysym); + + return keysym == expected; +} + +static int test_keysym(xkb_keysym_t keysym, const char *expected) { char s[16]; @@ -81,6 +94,22 @@ main(void) assert(test_keysym(0x1008FE20, "XF86Ungrab")); assert(test_keysym(0x01001234, "U1234")); + assert(test_casestring("Undo", 0xFF65)); + assert(test_casestring("UNDO", 0xFF65)); + assert(test_casestring("A", 0x61)); + assert(test_casestring("a", 0x61)); + assert(test_casestring("ThisKeyShouldNotExist", XKB_KEY_NoSymbol)); + assert(test_casestring("XF86_Switch_vT_5", 0x1008FE05)); + assert(test_casestring("xF86_SwitcH_VT_5", 0x1008FE05)); + assert(test_casestring("xF86SwiTch_VT_5", 0x1008FE05)); + assert(test_casestring("xF86Switch_vt_5", 0x1008FE05)); + assert(test_casestring("VoidSymbol", 0xFFFFFF)); + assert(test_casestring("vOIDsymBol", 0xFFFFFF)); + assert(test_casestring("U4567", 0x1004567)); + assert(test_casestring("u4567", 0x1004567)); + assert(test_casestring("0x10203040", 0x10203040)); + assert(test_casestring("0X10203040", 0x10203040)); + assert(test_utf8(XKB_KEY_y, "y")); assert(test_utf8(XKB_KEY_u, "u")); assert(test_utf8(XKB_KEY_m, "m")); @@ -100,13 +129,13 @@ main(void) assert(xkb_keysym_is_lower(XKB_KEY_a)); assert(xkb_keysym_is_lower(XKB_KEY_Greek_lambda)); - assert(xkb_keysym_is_lower(xkb_keysym_from_name("U03b1"))); /* GREEK SMALL LETTER ALPHA */ - assert(xkb_keysym_is_lower(xkb_keysym_from_name("U03af"))); /* GREEK SMALL LETTER IOTA WITH TONOS */ + assert(xkb_keysym_is_lower(xkb_keysym_from_name("U03b1", 0))); /* GREEK SMALL LETTER ALPHA */ + assert(xkb_keysym_is_lower(xkb_keysym_from_name("U03af", 0))); /* GREEK SMALL LETTER IOTA WITH TONOS */ assert(xkb_keysym_is_upper(XKB_KEY_A)); assert(xkb_keysym_is_upper(XKB_KEY_Greek_LAMBDA)); - assert(xkb_keysym_is_upper(xkb_keysym_from_name("U0391"))); /* GREEK CAPITAL LETTER ALPHA */ - assert(xkb_keysym_is_upper(xkb_keysym_from_name("U0388"))); /* GREEK CAPITAL LETTER EPSILON WITH TONOS */ + assert(xkb_keysym_is_upper(xkb_keysym_from_name("U0391", 0))); /* GREEK CAPITAL LETTER ALPHA */ + assert(xkb_keysym_is_upper(xkb_keysym_from_name("U0388", 0))); /* GREEK CAPITAL LETTER EPSILON WITH TONOS */ assert(!xkb_keysym_is_upper(XKB_KEY_a)); assert(!xkb_keysym_is_lower(XKB_KEY_A)); @@ -114,8 +143,8 @@ main(void) assert(!xkb_keysym_is_upper(XKB_KEY_Return)); assert(!xkb_keysym_is_lower(XKB_KEY_hebrew_aleph)); assert(!xkb_keysym_is_upper(XKB_KEY_hebrew_aleph)); - assert(!xkb_keysym_is_upper(xkb_keysym_from_name("U05D0"))); /* HEBREW LETTER ALEF */ - assert(!xkb_keysym_is_lower(xkb_keysym_from_name("U05D0"))); /* HEBREW LETTER ALEF */ + assert(!xkb_keysym_is_upper(xkb_keysym_from_name("U05D0", 0))); /* HEBREW LETTER ALEF */ + assert(!xkb_keysym_is_lower(xkb_keysym_from_name("U05D0", 0))); /* HEBREW LETTER ALEF */ assert(!xkb_keysym_is_lower(XKB_KEY_8)); assert(!xkb_keysym_is_upper(XKB_KEY_8)); diff --git a/xkbcommon/xkbcommon.h b/xkbcommon/xkbcommon.h index 5d6a295..d9f7f6b 100644 --- a/xkbcommon/xkbcommon.h +++ b/xkbcommon/xkbcommon.h @@ -319,18 +319,31 @@ struct xkb_rule_names { int xkb_keysym_get_name(xkb_keysym_t keysym, char *buffer, size_t size); +/** Flags for @ref xkb_keysym_from_name() */ +enum xkb_keysym_flags { + /** Find keysym by case-insensitive search. */ + XKB_KEYSYM_CASE_INSENSITIVE = (1 << 0), +}; + /** * Get a keysym from its name. * * @param name The name of a keysym. See remarks in xkb_keysym_get_name(); * this function will accept any name returned by that function. + * @param flags A set of flags how the search is done. If invalid flags are + * passed, this will fail with XKB_KEY_NoSymbol. See @ref xkb_keysym_flags for + * all available flags. + * + * If you use the XKB_KEYSYM_CASE_INSENSITIVE flag and two keysym names differ + * only by case, then the lower-case keysym is returned. For instance, for KEY_a + * and KEY_A, this function would return KEY_a for the case-insensitive search. * * @returns The keysym. If the name is invalid, returns XKB_KEY_NoSymbol. * * @sa xkb_keysym_t */ xkb_keysym_t -xkb_keysym_from_name(const char *name); +xkb_keysym_from_name(const char *name, enum xkb_keysym_flags flags); /** * Get the Unicode/UTF-8 representation of a keysym. -- 1.7.12.3 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel