[PATCH libxkbcommon 2/2] Add xkb_keysym_from_name() flags argument for case-insensitive search
David Herrmann
dh.herrmann at googlemail.com
Tue Oct 16 07:05:34 PDT 2012
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.herrmann at 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
More information about the wayland-devel
mailing list