keysym-utf: replace the Unicode characters for leftanglebracket and rightanglebracket
[platform/upstream/libxkbcommon.git] / src / keysym.c
index dd7954a..6d06de0 100644 (file)
  * DEALINGS IN THE SOFTWARE.
  */
 
+#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;
+}
+
+static int
+compare_by_keysym(const void *a, const void *b)
+{
+    const xkb_keysym_t *key = a;
+    const struct name_keysym *entry = b;
+    if (*key < entry->keysym)
+        return -1;
+    if (*key > entry->keysym)
+        return 1;
+    return 0;
+}
+
+static int
+compare_by_name(const void *a, const void *b)
+{
+    const char *key = a;
+    const struct name_keysym *entry = b;
+    return istrcmp(key, get_name(entry));
+}
 
 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;
+    const struct name_keysym *entry;
 
     if ((ks & ((unsigned long) ~0x1fffffff)) != 0) {
         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;
-        }
+    entry = bsearch(&ks, keysym_to_name,
+                    ARRAY_SIZE(keysym_to_name),
+                    sizeof(*keysym_to_name),
+                    compare_by_keysym);
+    if (entry)
+        return snprintf(buffer, size, "%s", get_name(entry));
+
+    /* 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);
     }
 
-    if (ks >= 0x01000100 && ks <= 0x0110ffff)
-        /* Unnamed Unicode codepoint. */
-        return snprintf(buffer, size, "U%lx", ks & 0xffffffUL);
-
     /* Unnamed, non-Unicode, symbol (shouldn't generally happen). */
     return snprintf(buffer, size, "0x%08x", ks);
 }
 
-XKB_EXPORT xkb_keysym_t
-xkb_keysym_from_name(const char *s)
+/*
+ * Find the correct keysym if one case-insensitive match is given.
+ *
+ * The name_to_keysym table is sorted by istrcmp(). 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, bool icase)
 {
-    int i, n, h, c, idx;
-    uint32_t sig = 0;
-    const char *p = s;
-    char *tmp;
-    const unsigned char *entry;
-    unsigned char sig1, sig2;
-    xkb_keysym_t val;
-
-    while ((c = *p++))
-        sig = (sig << 1) + c;
+    const struct name_keysym *iter, *last;
+    size_t len = ARRAY_SIZE(name_to_keysym);
 
-    i = sig % KTABLESIZE;
-    h = i + 1;
-    sig1 = (sig >> 8) & 0xff;
-    sig2 = sig & 0xff;
-    n = KMAXHASH;
+    if (!entry)
+        return NULL;
 
-    while ((idx = hashString[i])) {
-        entry = &_XkeyTable[idx];
+    if (!icase && strcmp(get_name(entry), name) == 0)
+        return entry;
+    if (icase && xkb_keysym_is_lower(entry->keysym))
+        return entry;
 
-        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;
-        }
-
-        if (!--n)
+    for (iter = entry - 1; iter >= name_to_keysym; --iter) {
+        if (!icase && strcmp(get_name(iter), name) == 0)
+            return iter;
+        if (istrcmp(get_name(iter), get_name(entry)) != 0)
             break;
+        if (icase && xkb_keysym_is_lower(iter->keysym))
+            return iter;
+    }
 
-        i += h;
-        if (i >= KTABLESIZE)
-            i -= KTABLESIZE;
+    last = name_to_keysym + len;
+    for (iter = entry + 1; iter < last; ++iter) {
+        if (!icase && strcmp(get_name(iter), name) == 0)
+            return iter;
+        if (istrcmp(get_name(iter), get_name(entry)) != 0)
+            break;
+        if (icase && xkb_keysym_is_lower(iter->keysym))
+            return iter;
     }
 
-    if (*s == 'U') {
+    if (icase)
+        return entry;
+    return NULL;
+}
+
+XKB_EXPORT xkb_keysym_t
+xkb_keysym_from_name(const char *s, enum xkb_keysym_flags flags)
+{
+    const struct name_keysym *entry;
+    char *tmp;
+    xkb_keysym_t val;
+    bool icase = (flags & XKB_KEYSYM_CASE_INSENSITIVE);
+
+    if (flags & ~XKB_KEYSYM_CASE_INSENSITIVE)
+        return XKB_KEY_NoSymbol;
+
+    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')
             return XKB_KEY_NoSymbol;
@@ -152,7 +191,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;
@@ -163,13 +202,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;
     }
@@ -183,6 +223,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 +264,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: