[PATCH libxkbcommon v4 2/2] Add xkb_keysym_from_casename() helper for case-insensitive search
David Herrmann
dh.herrmann at googlemail.com
Sun Oct 7 06:59:09 PDT 2012
This adds another helper that allows finding a keysym based on 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.
Signed-off-by: David Herrmann <dh.herrmann at googlemail.com>
---
Ran, could you check whether the new binary-size is acceptable? I get about 350K
for the new search-arrays.
Regards
David
makekeys.py | 6 ++++
src/keysym.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++
test/keysym.c | 29 ++++++++++++++++
xkbcommon/xkbcommon.h | 15 ++++++++
4 files changed, 144 insertions(+)
diff --git a/makekeys.py b/makekeys.py
index 94885c0..ab75cce 100644
--- a/makekeys.py
+++ b/makekeys.py
@@ -5,6 +5,7 @@ import re, sys, itertools
pattern = re.compile(r'^#define\s+XKB_KEY_(?P<name>\w+)\s+(?P<value>0x[0-9a-fA-F]+)\s')
matches = [pattern.match(line) for line in open(sys.argv[1])]
entries = [(m.group("name"), int(m.group("value"), 16)) for m in matches if m]
+lentries = [(m.group("name").lower(), m.group("name")) for m in matches if m]
print('''struct name_keysym {
const char *name;
@@ -16,6 +17,11 @@ for (name, _) in sorted(entries, key=lambda e: e[0]):
print(' {{ "{name}", XKB_KEY_{name} }},'.format(name=name))
print('};\n')
+print('static const struct name_keysym case_to_keysym[] = {');
+for (lname, name) in sorted(lentries, key=lambda e: e[0]):
+ print(' {{ "{lname}", XKB_KEY_{name} }},'.format(lname=lname, name=name))
+print('};\n')
+
# *.sort() is stable so we always get the first keysym for duplicate
print('static const struct name_keysym keysym_to_name[] = {');
for (name, _) in (next(g[1]) for g in itertools.groupby(sorted(entries, key=lambda e: e[1]), key=lambda e: e[1])):
diff --git a/src/keysym.c b/src/keysym.c
index 85d4386..d320055 100644
--- a/src/keysym.c
+++ b/src/keysym.c
@@ -65,6 +65,12 @@ static int compare_by_name(const void *a, const void *b)
return strcmp(key->name, entry->name);
}
+static int compare_by_casename(const void *a, const void *b)
+{
+ const struct name_keysym *key = a, *entry = b;
+ return strcasecmp(key->name, entry->name);
+}
+
XKB_EXPORT int
xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size)
{
@@ -144,6 +150,94 @@ xkb_keysym_from_name(const char *s)
return XKB_KEY_NoSymbol;
}
+/*
+ * Find the lower-case keysym of any keysym entry.
+ * If we use bsearch() to find a keysym based on a case-insensitive search, we
+ * may get _any_ of all possible duplicates back. This function looks at all
+ * entries before and after @entry which have the same name (case insensitive)
+ * and then returns the _best_ match of all.
+ * The _best_ match is the lower-case keysym which we find with the help of
+ * xkb_keysym_is_lower().
+ */
+static const struct name_keysym *find_lcase(const struct name_keysym *entry)
+{
+ const struct name_keysym *iter, *last;
+ size_t len = sizeof(case_to_keysym) / sizeof(*case_to_keysym);
+
+ if (xkb_keysym_is_lower(entry->keysym))
+ return entry;
+
+ for (iter = entry - 1; iter >= case_to_keysym; --iter) {
+ if (strcasecmp(iter->name, entry->name))
+ break;
+ if (xkb_keysym_is_lower(iter->keysym))
+ return iter;
+ }
+
+ last = case_to_keysym + len;
+ for (iter = entry + 1; iter < last; --iter) {
+ if (strcasecmp(iter->name, entry->name))
+ break;
+ if (xkb_keysym_is_lower(iter->keysym))
+ return iter;
+ }
+
+ return entry;
+}
+
+XKB_EXPORT xkb_keysym_t
+xkb_keysym_from_casename(const char *s)
+{
+ const struct name_keysym search = { .name = s, .keysym = 0 };
+ const struct name_keysym *entry;
+ char *tmp;
+ xkb_keysym_t val;
+
+ entry = bsearch(&search, case_to_keysym,
+ sizeof(case_to_keysym) / sizeof(*case_to_keysym),
+ sizeof(*case_to_keysym),
+ compare_by_casename);
+ if (entry)
+ return find_lcase(entry)->keysym;
+
+ if (*s == 'U' || *s == 'u') {
+ val = strtoul(&s[1], &tmp, 16);
+ if (tmp && *tmp != '\0')
+ return XKB_KEY_NoSymbol;
+
+ if (val < 0x20 || (val > 0x7e && val < 0xa0))
+ return XKB_KEY_NoSymbol;
+ if (val < 0x100)
+ return val;
+ if (val > 0x10ffff)
+ return XKB_KEY_NoSymbol;
+ return val | 0x01000000;
+ }
+ else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ val = strtoul(&s[2], &tmp, 16);
+ if (tmp && *tmp != '\0')
+ return XKB_KEY_NoSymbol;
+
+ return val;
+ }
+
+ /* 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 (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_casename(tmp);
+ free(tmp);
+ return ret;
+ }
+
+ return XKB_KEY_NoSymbol;
+}
+
enum keysym_case {
NONE,
LOWERCASE,
diff --git a/test/keysym.c b/test/keysym.c
index 1bf704b..2eb0f78 100644
--- a/test/keysym.c
+++ b/test/keysym.c
@@ -37,6 +37,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_casename(string);
+
+ 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];
@@ -80,6 +93,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"));
diff --git a/xkbcommon/xkbcommon.h b/xkbcommon/xkbcommon.h
index a6b0fa8..81c2a0b 100644
--- a/xkbcommon/xkbcommon.h
+++ b/xkbcommon/xkbcommon.h
@@ -230,6 +230,21 @@ xkb_keysym_t
xkb_keysym_from_name(const char *name);
/**
+ * @brief Get a keysym from its name (case-insensitive).
+ *
+ * @param name The name of a keysym.
+ *
+ * @remark This is the same as xkb_keysym_get_name() but does a case-insensitive
+ * lookup. If there are multiple keysyms with the same name, then the lower-case
+ * keysym is returned.
+ *
+ * @returns The keysym, if name is valid. Otherwise, XKB_KEY_NoSymbol is
+ * returned.
+ */
+xkb_keysym_t
+xkb_keysym_from_casename(const char *name);
+
+/**
* @brief Get the Unicode/UTF-8 representation of a keysym.
*
* @param[in] keysym The keysym.
--
1.7.12.2
More information about the wayland-devel
mailing list