Test: Catch SIGUSR1 from Xvfb for X11 tests
[platform/upstream/libxkbcommon.git] / src / keysym.c
index dd7954a..ef811bd 100644 (file)
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include "config.h"
+
+#include <stdlib.h>
 #include "xkbcommon/xkbcommon.h"
 #include "utils.h"
-#include "ks_tables.h"
 #include "keysym.h"
+#include "ks_tables.h"
+
+static inline const char *
+get_name(const struct name_keysym *entry)
+{
+    return keysym_names + entry->offset;
+}
 
 XKB_EXPORT int
 xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size)
 {
-    int i, n, h, idx;
-    const unsigned char *entry;
-    unsigned char val1, val2, val3, val4;
-
-    if ((ks & ((unsigned long) ~0x1fffffff)) != 0) {
+    if (ks > XKB_KEYSYM_MAX) {
         snprintf(buffer, size, "Invalid");
         return -1;
     }
 
-    /* Try to find it in our hash table. */
-    if (ks <= 0x1fffffff) {
-        val1 = ks >> 24;
-        val2 = (ks >> 16) & 0xff;
-        val3 = (ks >> 8) & 0xff;
-        val4 = ks & 0xff;
-        i = ks % VTABLESIZE;
-        h = i + 1;
-        n = VMAXHASH;
-
-        while ((idx = hashKeysym[i])) {
-            entry = &_XkeyTable[idx];
-
-            if ((entry[0] == val1) && (entry[1] == val2) &&
-                (entry[2] == val3) && (entry[3] == val4)) {
-                return snprintf(buffer, size, "%s", entry + 4);
-            }
-
-            if (!--n)
-                break;
-
-            i += h;
-            if (i >= VTABLESIZE)
-                i -= VTABLESIZE;
+    int32_t lo = 0, hi = ARRAY_SIZE(keysym_to_name) - 1;
+    while (hi >= lo) {
+        int32_t mid = (lo + hi) / 2;
+        if (ks > keysym_to_name[mid].keysym) {
+            lo = mid + 1;
+        } else if (ks < keysym_to_name[mid].keysym) {
+            hi = mid - 1;
+        } else {
+            return snprintf(buffer, size, "%s", get_name(&keysym_to_name[mid]));
         }
     }
 
-    if (ks >= 0x01000100 && ks <= 0x0110ffff)
-        /* Unnamed Unicode codepoint. */
-        return snprintf(buffer, size, "U%lx", ks & 0xffffffUL);
+    /* Unnamed Unicode codepoint. */
+    if (ks >= 0x01000100 && ks <= 0x0110ffff) {
+        const int width = (ks & 0xff0000UL) ? 8 : 4;
+        return snprintf(buffer, size, "U%0*lX", width, ks & 0xffffffUL);
+    }
 
     /* Unnamed, non-Unicode, symbol (shouldn't generally happen). */
     return snprintf(buffer, size, "0x%08x", ks);
 }
 
+/*
+ * Parse the numeric part of a 0xXXXX and UXXXX keysym.
+ * Not using strtoul -- it's slower and accepts a bunch of stuff
+ * we don't want to allow, like signs, spaces, even locale stuff.
+ */
+static bool
+parse_keysym_hex(const char *s, uint32_t *out)
+{
+    uint32_t result = 0;
+    int i;
+    for (i = 0; i < 8 && s[i] != '\0'; i++) {
+        result <<= 4;
+        if ('0' <= s[i] && s[i] <= '9')
+            result += s[i] - '0';
+        else if ('a' <= s[i] && s[i] <= 'f')
+            result += 10 + s[i] - 'a';
+        else if ('A' <= s[i] && s[i] <= 'F')
+            result += 10 + s[i] - 'A';
+        else
+            return false;
+    }
+    *out = result;
+    return s[i] == '\0' && i > 0;
+}
+
 XKB_EXPORT xkb_keysym_t
-xkb_keysym_from_name(const char *s)
+xkb_keysym_from_name(const char *name, enum xkb_keysym_flags flags)
 {
-    int i, n, h, c, idx;
-    uint32_t sig = 0;
-    const char *p = s;
+    const struct name_keysym *entry = NULL;
     char *tmp;
-    const unsigned char *entry;
-    unsigned char sig1, sig2;
-    xkb_keysym_t val;
-
-    while ((c = *p++))
-        sig = (sig << 1) + c;
-
-    i = sig % KTABLESIZE;
-    h = i + 1;
-    sig1 = (sig >> 8) & 0xff;
-    sig2 = sig & 0xff;
-    n = KMAXHASH;
-
-    while ((idx = hashString[i])) {
-        entry = &_XkeyTable[idx];
-
-        if ((entry[0] == sig1) && (entry[1] == sig2) &&
-            streq(s, (const char *) entry + 6)) {
-            val = (entry[2] << 24) | (entry[3] << 16) |
-                  (entry[4] << 8) | entry[5];
-            if (!val)
-                val = XKB_KEY_VoidSymbol;
-            return val;
+    uint32_t val;
+    bool icase = (flags & XKB_KEYSYM_CASE_INSENSITIVE);
+
+    if (flags & ~XKB_KEYSYM_CASE_INSENSITIVE)
+        return XKB_KEY_NoSymbol;
+
+    /*
+     * 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;
+            }
 
-        if (!--n)
-            break;
+            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;
+            }
 
-        i += h;
-        if (i >= KTABLESIZE)
-            i -= KTABLESIZE;
+            return entry->keysym;
+        }
     }
 
-    if (*s == 'U') {
-        val = strtoul(&s[1], &tmp, 16);
-        if (tmp && *tmp != '\0')
+    if (*name == 'U' || (icase && *name == 'u')) {
+        if (!parse_keysym_hex(&name[1], &val))
             return XKB_KEY_NoSymbol;
 
         if (val < 0x20 || (val > 0x7e && val < 0xa0))
             return XKB_KEY_NoSymbol;
         if (val < 0x100)
-            return val;
+            return (xkb_keysym_t) val;
         if (val > 0x10ffff)
             return XKB_KEY_NoSymbol;
-        return val | 0x01000000;
+        return (xkb_keysym_t) val | 0x01000000;
     }
-    else if (s[0] == '0' && s[1] == 'x') {
-        val = strtoul(&s[2], &tmp, 16);
-        if (tmp && *tmp != '\0')
+    else if (name[0] == '0' && (name[1] == 'x' || (icase && name[1] == 'X'))) {
+        if (!parse_keysym_hex(&name[2], &val))
             return XKB_KEY_NoSymbol;
-
-        return val;
+        if (val > XKB_KEYSYM_MAX)
+            return XKB_KEY_NoSymbol;
+        return (xkb_keysym_t) 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 (strncmp(s, "XF86_", 5) == 0) {
+    if (strncmp(name, "XF86_", 5) == 0 ||
+        (icase && istrncmp(name, "XF86_", 5) == 0)) {
         xkb_keysym_t ret;
-        tmp = strdup(s);
+        tmp = strdup(name);
         if (!tmp)
             return XKB_KEY_NoSymbol;
-        memmove(&tmp[4], &tmp[5], strlen(s) - 5 + 1);
-        ret = xkb_keysym_from_name(tmp);
+        memmove(&tmp[4], &tmp[5], strlen(name) - 5 + 1);
+        ret = xkb_keysym_from_name(tmp, flags);
         free(tmp);
         return ret;
     }
@@ -183,6 +237,18 @@ xkb_keysym_is_keypad(xkb_keysym_t keysym)
     return keysym >= XKB_KEY_KP_Space && keysym <= XKB_KEY_KP_Equal;
 }
 
+
+bool
+xkb_keysym_is_modifier(xkb_keysym_t keysym)
+{
+    return
+        (keysym >= XKB_KEY_Shift_L && keysym <= XKB_KEY_Hyper_R) ||
+        /* libX11 only goes upto XKB_KEY_ISO_Level5_Lock. */
+        (keysym >= XKB_KEY_ISO_Lock && keysym <= XKB_KEY_ISO_Last_Group_Lock) ||
+        keysym == XKB_KEY_Mode_switch ||
+        keysym == XKB_KEY_Num_Lock;
+}
+
 static void
 XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper);
 
@@ -212,6 +278,26 @@ xkb_keysym_is_upper(xkb_keysym_t ks)
     return (ks == upper ? true : false);
 }
 
+XKB_EXPORT xkb_keysym_t
+xkb_keysym_to_lower(xkb_keysym_t ks)
+{
+    xkb_keysym_t lower, upper;
+
+    XConvertCase(ks, &lower, &upper);
+
+    return lower;
+}
+
+XKB_EXPORT xkb_keysym_t
+xkb_keysym_to_upper(xkb_keysym_t ks)
+{
+    xkb_keysym_t lower, upper;
+
+    XConvertCase(ks, &lower, &upper);
+
+    return upper;
+}
+
 /*
  * The following is copied verbatim from libX11:src/KeyBind.c, commit
  * d45b3fc19fbe95c41afc4e51d768df6d42332010, with the following changes:
@@ -406,6 +492,8 @@ UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *upper)
             *upper = 0x0178;
         else if (code == 0x00b5)      /* micro sign */
             *upper = 0x039c;
+        else if (code == 0x00df)      /* ssharp */
+            *upper = 0x1e9e;
        return;
     }
 
@@ -535,6 +623,8 @@ UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *upper)
         }
         else if (code == 0x1e9b)
             *upper = 0x1e60;
+        else if (code == 0x1e9e)
+            *lower = 0x00df; /* ssharp */
     }
 
     /* Greek Extended, U+1F00 to U+1FFF */
@@ -655,9 +745,9 @@ XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper)
        break;
     case 6: /* Cyrillic */
        /* Assume the KeySym is a legal value (ignore discontinuities) */
-       if (sym >= XKB_KEY_Serbian_DJE && sym <= XKB_KEY_Serbian_DZE)
+       if (sym >= XKB_KEY_Serbian_DJE && sym <= XKB_KEY_Cyrillic_DZHE)
            *lower -= (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje);
-       else if (sym >= XKB_KEY_Serbian_dje && sym <= XKB_KEY_Serbian_dze)
+       else if (sym >= XKB_KEY_Serbian_dje && sym <= XKB_KEY_Cyrillic_dzhe)
            *upper += (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje);
        else if (sym >= XKB_KEY_Cyrillic_YU && sym <= XKB_KEY_Cyrillic_HARDSIGN)
            *lower -= (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu);