Commit 85b7f47d8a544dab204214de9c248f03e78261fe

Daniel Stone 2009-04-25T18:13:52

Add XkbcCanonicaliseComponents Canonicalises two sets of components (new and old), e.g.: new: +bar old: foo result: foo+bar This is required as part of the spec, so clients can reuse part of the device's old keymap. Signed-off-by: Daniel Stone <daniel@fooishbar.org>

diff --git a/include/X11/extensions/XKBcommon.h b/include/X11/extensions/XKBcommon.h
index 090c22b..1057895 100644
--- a/include/X11/extensions/XKBcommon.h
+++ b/include/X11/extensions/XKBcommon.h
@@ -144,6 +144,24 @@ extern XkbComponentListPtr
 XkbcListComponents(XkbComponentNamesPtr ptrns, int *maxMatch);
 
 /*
+ * Canonicalises component names by prepending the relevant component from
+ * 'old' to the one in 'names' when the latter has a leading '+' or '|', and
+ * by replacing a '%' with the relevant component, e.g.:
+ *
+ * names        old           output
+ * ------------------------------------------
+ * +bar         foo           foo+bar
+ * |quux        baz           baz|quux
+ * foo+%|baz    bar           foo+bar|baz
+ *
+ * If a component in names needs to be modified, the existing value will be
+ * free()d, and a new one allocated with malloc().
+ */
+extern void
+XkbcCanonicaliseComponents(XkbComponentNamesPtr names,
+                           const XkbComponentNamesPtr old);
+
+/*
  * Converts a keysym to a string; will return unknown Unicode codepoints
  * as "Ua1b2", and other unknown keysyms as "0xabcd1234".
  *
diff --git a/src/xkb.c b/src/xkb.c
index b0b0de6..7ebe856 100644
--- a/src/xkb.c
+++ b/src/xkb.c
@@ -31,6 +31,72 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "X11/extensions/XKBcommon.h"
 #include "XKBcommonint.h"
 
+static char *
+XkbcCanonicaliseComponent(char *name, const char *old)
+{
+    char *tmp;
+    int i;
+
+    if (!name)
+        return NULL;
+
+    /* Treachery. */
+    if (old && strchr(old, '%'))
+        return NULL;
+
+    if (name[0] == '+' || name[0] == '|') {
+        if (old) {
+            tmp = malloc(strlen(name) + strlen(old) + 1);
+            if (!tmp)
+                return NULL;
+            sprintf(tmp, "%s%s", old, name);
+            free(name);
+            name = tmp;
+        }
+        else {
+            memmove(name, &name[1], strlen(&name[1]) + 1);
+        }
+    }
+
+    for (i = 0; name[i]; i++) {
+        if (name[i] == '%') {
+            if (old) {
+                tmp = malloc(strlen(name) + strlen(old));
+                if (!tmp)
+                    return NULL;
+                strncpy(tmp, name, i);
+                strcat(tmp + i, old);
+                strcat(tmp + i + strlen(old), &name[i + 1]);
+                free(name);
+                name = tmp;
+                i--;
+            }
+            else {
+                memmove(&name[i - 1], &name[i + 1], strlen(&name[i + 1]) + 1);
+                i -= 2;
+            }
+        }
+    }
+
+    return name;
+}
+
+void
+XkbcCanonicaliseComponents(XkbComponentNamesPtr names,
+                           const XkbComponentNamesPtr old)
+{
+    names->keycodes = XkbcCanonicaliseComponent(names->keycodes,
+                                                old ? old->keycodes : NULL);
+    names->compat = XkbcCanonicaliseComponent(names->compat,
+                                              old ? old->compat : NULL);
+    names->geometry = XkbcCanonicaliseComponent(names->geometry,
+                                                old ? old->geometry : NULL);
+    names->symbols = XkbcCanonicaliseComponent(names->symbols,
+                                               old ? old->symbols : NULL);
+    names->types = XkbcCanonicaliseComponent(names->types,
+                                             old ? old->types : NULL);
+}
+
 Bool
 XkbcComputeEffectiveMap(XkbcDescPtr xkb, XkbKeyTypePtr type,
                         unsigned char *map_rtrn)