Update modifiers after building keymap
authorDaniel Stone <daniel@fooishbar.org>
Wed, 14 Mar 2012 18:24:37 +0000 (18:24 +0000)
committerDaniel Stone <daniel@fooishbar.org>
Wed, 14 Mar 2012 18:24:37 +0000 (18:24 +0000)
The server used to have to go and do this on our own, but we can do
better than that: after we've compiled the keymap, go through and bind
virtual modifiers to everything that needs it.

Signed-off-by: Daniel Stone <daniel@fooishbar.org>
src/xkbcomp/compat.c
src/xkbcomp/keymap.c
src/xkbcomp/xkbcomp.h

index 5483cd7..932cc2f 100644 (file)
@@ -849,3 +849,257 @@ CompileCompatMap(XkbFile *file, struct xkb_desc * xkb, unsigned merge,
     free(info.interps);
     return False;
 }
+
+static uint32_t
+VModsToReal(struct xkb_desc *xkb, uint32_t vmodmask)
+{
+    uint32_t ret = 0;
+    int i;
+
+    if (!vmodmask)
+        return 0;
+
+    for (i = 0; i < XkbNumVirtualMods; i++) {
+        if (!(vmodmask & (1 << i)))
+            continue;
+        ret |= xkb->server->vmods[i];
+    }
+
+    return ret;
+}
+
+static void
+UpdateActionMods(struct xkb_desc *xkb, union xkb_action *act, uint32_t rmodmask)
+{
+    uint32_t vmodmask; /* actually real mods, inferred from vmods */
+
+    switch (act->type) {
+    case XkbSA_SetMods:
+    case XkbSA_LatchMods:
+    case XkbSA_LockMods:
+        if (act->mods.flags & XkbSA_UseModMapMods) {
+            act->mods.real_mods = rmodmask;
+            act->mods.mask = act->mods.real_mods;
+        }
+        vmodmask = VModsToReal(xkb, act->mods.vmods);
+        act->mods.mask |= vmodmask;
+        break;
+    case XkbSA_ISOLock:
+        if (act->iso.flags & XkbSA_UseModMapMods) {
+            act->iso.real_mods = rmodmask;
+            act->iso.mask = act->iso.real_mods;
+        }
+        vmodmask = VModsToReal(xkb, act->iso.vmods);
+        act->iso.mask |= vmodmask;
+        break;
+    default:
+        break;
+    }
+}
+
+/**
+ * Find an interpretation which applies to this particular level, either by
+ * finding an exact match for the symbol and modifier combination, or a
+ * generic NoSymbol match.
+ */
+static struct xkb_sym_interpret *
+FindInterpForKey(struct xkb_desc *xkb, xkb_keycode_t key, uint32_t group, uint32_t level)
+{
+    struct xkb_sym_interpret *ret = NULL;
+    xkb_keysym_t *syms;
+    int num_syms;
+    int i;
+
+    num_syms = xkb_key_get_syms_by_level(xkb, key, group, level, &syms);
+    if (num_syms == 0)
+        return NULL;
+
+    for (i = 0; i < xkb->compat->num_si; i++) {
+        struct xkb_sym_interpret *interp = &xkb->compat->sym_interpret[i];
+        uint32_t mods;
+        Bool found;
+
+        if ((num_syms != 1 || interp->sym != syms[0]) &&
+            interp->sym != NoSymbol)
+            continue;
+
+        if (level == 0 || !(interp->match & XkbSI_LevelOneOnly))
+            mods = xkb->map->modmap[key];
+        else
+            mods = 0;
+
+        switch (interp->match & XkbSI_OpMask) {
+        case XkbSI_NoneOf:
+            found = !(interp->mods & mods);
+            break;
+        case XkbSI_AnyOfOrNone:
+            found = (!mods || (interp->mods & mods));
+            break;
+        case XkbSI_AnyOf:
+            found = !!(interp->mods & mods);
+            break;
+        case XkbSI_AllOf:
+            found = ((interp->mods & mods) == mods);
+            break;
+        case XkbSI_Exactly:
+            found = (interp->mods == mods);
+            break;
+        default:
+            found = False;
+            break;
+        }
+
+        if (found && interp->sym != NoSymbol)
+            return interp;
+        else if (found && !ret)
+            ret = interp;
+    }
+
+    return ret;
+}
+
+/**
+ */
+static Bool
+ApplyInterpsToKey(struct xkb_desc *xkb, xkb_keycode_t key)
+{
+#define INTERP_SIZE (8 * 4)
+    struct xkb_sym_interpret *interps[INTERP_SIZE];
+    union xkb_action *acts;
+    uint32_t vmodmask = 0;
+    int num_acts = 0;
+    int group, level;
+    int width = XkbKeyGroupsWidth(xkb, key);
+    int i;
+
+    /* If we've been told not to bind interps to this key, then don't. */
+    if (xkb->server->explicit[key] & XkbExplicitInterpretMask)
+        return True;
+
+    for (i = 0; i < INTERP_SIZE; i++)
+        interps[i] = NULL;
+
+    for (group = 0; group < XkbKeyNumGroups(xkb, key); group++) {
+        for (level = 0; level < XkbKeyGroupWidth(xkb, key, group); level++) {
+            i = (group * width) + level;
+            if (i >= INTERP_SIZE) /* XXX FIXME */
+                return False;
+            interps[i] = FindInterpForKey(xkb, key, group, level);
+            if (interps[i])
+                num_acts++;
+        }
+    }
+
+    if (num_acts)
+        num_acts = XkbKeyNumGroups(xkb, key) * width;
+    acts = XkbcResizeKeyActions(xkb, key, num_acts);
+    if (!num_acts)
+        return True;
+    else if (!acts)
+        return False;
+
+    for (group = 0; group < XkbKeyNumGroups(xkb, key); group++) {
+        for (level = 0; level < XkbKeyGroupWidth(xkb, key, group); level++) {
+            struct xkb_sym_interpret *interp;
+
+            i = (group * width) + level;
+            interp = interps[i];
+
+            /* Infer default key behaviours from the base level. */
+            if (group == 0 && level == 0) {
+                if (!(xkb->server->explicit[key] & XkbExplicitAutoRepeatMask) &&
+                    (!interp || interp->flags & XkbSI_AutoRepeat))
+                    xkb->ctrls->per_key_repeat[key / 8] |= (1 << (key % 8));
+                if (!(xkb->server->explicit[key] & XkbExplicitBehaviorMask) &&
+                    interp && (interp->flags & XkbSI_LockingKey))
+                    xkb->server->behaviors[key].type = XkbKB_Lock;
+            }
+
+            if (!interp)
+                continue;
+
+            if ((group == 0 && level == 0) ||
+                !(interp->match & XkbSI_LevelOneOnly)) {
+                if (interp->virtual_mod != XkbNoModifier)
+                    vmodmask |= (1 << interp->virtual_mod);
+            }
+            acts[i] = interp->act;
+        }
+    }
+
+    if (!(xkb->server->explicit[key] & XkbExplicitVModMapMask))
+        xkb->server->vmodmap[key] = vmodmask;
+
+    return True;
+#undef INTERP_SIZE
+}
+
+/**
+ * This collects a bunch of disparate functions which was done in the server
+ * at various points that really should've been done within xkbcomp.  Turns out
+ * your actions and types are a lot more useful when any of your modifiers
+ * other than Shift actually do something ...
+ */
+Bool
+UpdateModifiersFromCompat(struct xkb_desc *xkb)
+{
+    xkb_keycode_t key;
+    int i;
+
+    /* Find all the interprets for the key and bind them to actions,
+     * which will also update the vmodmap. */
+    for (key = xkb->min_key_code; key <= xkb->max_key_code; key++)
+        if (!ApplyInterpsToKey(xkb, key))
+            return False;
+
+    /* Update xkb->server->vmods, the virtual -> real mod mapping. */
+    for (i = 0; i < XkbNumVirtualMods; i++)
+        xkb->server->vmods[i] = 0;
+    for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+        if (!xkb->server->vmodmap[key])
+            continue;
+        for (i = 0; i < XkbNumVirtualMods; i++) {
+            if (!(xkb->server->vmodmap[key] & (1 << i)))
+                continue;
+            xkb->server->vmods[i] |= xkb->map->modmap[key];
+        }
+    }
+
+    /* Now update the level masks for all the types to reflect the vmods. */
+    for (i = 0; i < xkb->map->num_types; i++) {
+        struct xkb_key_type *type = &xkb->map->types[i];
+        uint32_t mask = 0;
+        int j;
+        type->mods.mask = type->mods.real_mods;
+        type->mods.mask |= VModsToReal(xkb, type->mods.vmods);
+        for (j = 0; j < XkbNumVirtualMods; j++) {
+            if (!(type->mods.vmods & (1 << j)))
+                continue;
+            mask |= xkb->server->vmods[j];
+        }
+        for (j = 0; j < type->map_count; j++) {
+            struct xkb_mods *mods = &type->map[j].mods;
+            mods->mask = mods->real_mods | VModsToReal(xkb, mods->vmods);
+        }
+    }
+
+    /* Update action modifiers. */
+    for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+        union xkb_action *acts = XkbKeyActionsPtr(xkb, key);
+        for (i = 0; i < XkbKeyNumActions(xkb, key); i++) {
+            if (acts[i].any.type == XkbSA_NoAction)
+                continue;
+            UpdateActionMods(xkb, &acts[i], xkb->map->modmap[key]);
+        }
+    }
+
+    /* Update group modifiers. */
+    for (i = 0; i < XkbNumKbdGroups; i++) {
+        struct xkb_mods *group = &xkb->compat->groups[i];
+        group->mask = group->real_mods | VModsToReal(xkb, group->vmods);
+    }
+
+    /* Update vmod -> indicator maps. */
+
+    return True;
+}
index fd973b0..eba36bd 100644 (file)
@@ -190,6 +190,10 @@ CompileKeymap(XkbFile *file, unsigned merge)
     if (!ok)
         goto err;
 
+    ok = UpdateModifiersFromCompat(xkb);
+    if (!ok)
+        goto err;
+
     return xkb;
 
 err:
index db81e80..1261c73 100644 (file)
@@ -285,4 +285,7 @@ CompileCompatMap(XkbFile *file, struct xkb_desc * xkb, unsigned merge,
 extern Bool
 CompileSymbols(XkbFile *file, struct xkb_desc * xkb, unsigned merge);
 
+extern Bool
+UpdateModifiersFromCompat(struct xkb_desc *xkb);
+
 #endif /* XKBCOMP_H */