symbols: disallow changing global defaults from within a key statement
[platform/upstream/libxkbcommon.git] / src / xkbcomp / symbols.c
index 17b2eb7..91feea0 100644 (file)
 #include "vmod.h"
 #include "keycodes.h"
 #include "include.h"
+#include "keysym.h"
 
 enum key_repeat {
+    KEY_REPEAT_UNDEFINED = 0,
     KEY_REPEAT_YES = 1,
-    KEY_REPEAT_NO = 0,
-    KEY_REPEAT_UNDEFINED = -1
+    KEY_REPEAT_NO = 2,
 };
 
 enum group_field {
@@ -107,7 +108,7 @@ typedef struct _KeyInfo {
     xkb_atom_t dfltType;
 
     enum xkb_range_exceed_type out_of_range_group_action;
-    xkb_group_index_t out_of_range_group_number;
+    xkb_layout_index_t out_of_range_group_number;
 } KeyInfo;
 
 static void
@@ -121,33 +122,24 @@ ClearGroupInfo(GroupInfo *groupi)
 {
     darray_free(groupi->syms);
     darray_free(groupi->levels);
-    InitGroupInfo(groupi);
 }
 
 static void
 InitKeyInfo(KeyInfo *keyi, unsigned file_id)
 {
-    xkb_group_index_t i;
-    static const char dflt[4] = "*";
+    static const char dflt_key_name[XKB_KEY_NAME_LENGTH] = "*";
 
-    keyi->defined = 0;
+    memset(keyi, 0, sizeof(*keyi));
     keyi->file_id = file_id;
     keyi->merge = MERGE_OVERRIDE;
-    keyi->name = KeyNameToLong(dflt);
-    for (i = 0; i < XKB_NUM_GROUPS; i++)
-        InitGroupInfo(&keyi->groups[i]);
-    keyi->dfltType = XKB_ATOM_NONE;
-    keyi->vmodmap = 0;
-    keyi->repeat = KEY_REPEAT_UNDEFINED;
+    keyi->name = KeyNameToLong(dflt_key_name);
     keyi->out_of_range_group_action = RANGE_WRAP;
-    keyi->out_of_range_group_number = 0;
 }
 
 static void
 ClearKeyInfo(KeyInfo *keyi)
 {
-    xkb_group_index_t i;
-
+    xkb_layout_index_t i;
     for (i = 0; i < XKB_NUM_GROUPS; i++)
         ClearGroupInfo(&keyi->groups[i]);
 }
@@ -169,7 +161,7 @@ typedef struct _SymbolsInfo {
     int errorCount;
     unsigned file_id;
     enum merge_mode merge;
-    xkb_group_index_t explicit_group;
+    xkb_layout_index_t explicit_group;
     darray(KeyInfo) keys;
     KeyInfo dflt;
     VModInfo vmods;
@@ -184,40 +176,29 @@ static void
 InitSymbolsInfo(SymbolsInfo *info, struct xkb_keymap *keymap,
                 unsigned file_id, ActionsInfo *actions)
 {
-    xkb_group_index_t i;
-
-    info->name = NULL;
-    info->explicit_group = 0;
-    info->errorCount = 0;
+    memset(info, 0, sizeof(*info));
+    info->keymap = keymap;
     info->file_id = file_id;
     info->merge = MERGE_OVERRIDE;
-    darray_init(info->keys);
-    darray_growalloc(info->keys, 110);
-    darray_init(info->modMaps);
-    for (i = 0; i < XKB_NUM_GROUPS; i++)
-        info->groupNames[i] = XKB_ATOM_NONE;
     InitKeyInfo(&info->dflt, file_id);
     InitVModInfo(&info->vmods, keymap);
     info->actions = actions;
-    info->keymap = keymap;
 }
 
 static void
 ClearSymbolsInfo(SymbolsInfo * info)
 {
     KeyInfo *keyi;
-
     free(info->name);
     darray_foreach(keyi, info->keys)
         ClearKeyInfo(keyi);
     darray_free(info->keys);
     darray_free(info->modMaps);
-    memset(info, 0, sizeof(SymbolsInfo));
 }
 
 static bool
 MergeGroups(SymbolsInfo *info, GroupInfo *into, GroupInfo *from, bool clobber,
-            bool report, xkb_group_index_t group, unsigned long key_name)
+            bool report, xkb_layout_index_t group, unsigned long key_name)
 {
     xkb_level_index_t i, numLevels;
     enum { INTO = (1 << 0), FROM = (1 << 1) } using;
@@ -412,7 +393,7 @@ UseNewKeyField(enum key_field field, enum key_field old, enum key_field new,
 static bool
 MergeKeys(SymbolsInfo *info, KeyInfo *into, KeyInfo *from)
 {
-    xkb_group_index_t i;
+    xkb_layout_index_t i;
     enum key_field collide = 0;
     bool clobber, report;
     int verbosity = xkb_get_log_verbosity(info->keymap->ctx);
@@ -462,6 +443,7 @@ MergeKeys(SymbolsInfo *info, KeyInfo *into, KeyInfo *from)
                  (clobber ? "first" : "last"));
 
     ClearKeyInfo(from);
+    InitKeyInfo(from, info->file_id);
     return true;
 }
 
@@ -611,10 +593,19 @@ HandleIncludeSymbols(SymbolsInfo *info, IncludeStmt *stmt)
 
         InitSymbolsInfo(&next_incl, info->keymap, rtrn->id, info->actions);
         next_incl.merge = next_incl.dflt.merge = MERGE_OVERRIDE;
-        if (stmt->modifier)
+        if (stmt->modifier) {
             next_incl.explicit_group = atoi(stmt->modifier) - 1;
-        else
+            if (next_incl.explicit_group >= XKB_NUM_GROUPS) {
+                log_err(info->keymap->ctx,
+                        "Cannot set explicit group to %d - must be between 1..%d; "
+                        "Ignoring group number\n",
+                        next_incl.explicit_group + 1, XKB_NUM_GROUPS);
+                next_incl.explicit_group = info->explicit_group;
+            }
+        }
+        else {
             next_incl.explicit_group = info->explicit_group;
+        }
 
         HandleSymbolsFile(&next_incl, rtrn, MERGE_OVERRIDE);
 
@@ -635,12 +626,12 @@ HandleIncludeSymbols(SymbolsInfo *info, IncludeStmt *stmt)
 
 static bool
 GetGroupIndex(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx,
-              unsigned what, xkb_group_index_t *ndx_rtrn)
+              unsigned what, xkb_layout_index_t *ndx_rtrn)
 {
     const char *name = (what == SYMBOLS ? "symbols" : "actions");
 
     if (arrayNdx == NULL) {
-        xkb_group_index_t i;
+        xkb_layout_index_t i;
         enum group_field field = (what == SYMBOLS ?
                                   GROUP_FIELD_SYMS : GROUP_FIELD_ACTS);
 
@@ -698,7 +689,7 @@ static bool
 AddSymbolsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx,
                 ExprDef *value)
 {
-    xkb_group_index_t ndx;
+    xkb_layout_index_t ndx;
     GroupInfo *groupi;
     unsigned int nSyms;
     xkb_level_index_t nLevels;
@@ -788,7 +779,7 @@ AddActionsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx,
                 ExprDef *value)
 {
     unsigned int i;
-    xkb_group_index_t ndx;
+    xkb_layout_index_t ndx;
     GroupInfo *groupi;
     unsigned int nActs;
     ExprDef *act;
@@ -863,7 +854,7 @@ SetSymbolsField(SymbolsInfo *info, KeyInfo *keyi, const char *field,
     struct xkb_context *ctx = info->keymap->ctx;
 
     if (istreq(field, "type")) {
-        xkb_group_index_t ndx;
+        xkb_layout_index_t ndx;
         xkb_atom_t val;
 
         if (!ExprResolveString(ctx, value, &val))
@@ -989,7 +980,7 @@ SetSymbolsField(SymbolsInfo *info, KeyInfo *keyi, const char *field,
     }
     else if (istreq(field, "groupsredirect") ||
              istreq(field, "redirectgroups")) {
-        xkb_group_index_t grp;
+        xkb_layout_index_t grp;
 
         if (!ExprResolveGroup(ctx, value, &grp)) {
             log_err(info->keymap->ctx,
@@ -1017,7 +1008,7 @@ SetSymbolsField(SymbolsInfo *info, KeyInfo *keyi, const char *field,
 static int
 SetGroupName(SymbolsInfo *info, ExprDef *arrayNdx, ExprDef *value)
 {
-    xkb_group_index_t grp;
+    xkb_layout_index_t grp;
     xkb_atom_t name;
 
     if (!arrayNdx) {
@@ -1046,7 +1037,7 @@ SetGroupName(SymbolsInfo *info, ExprDef *arrayNdx, ExprDef *value)
 }
 
 static int
-HandleSymbolsVar(SymbolsInfo *info, VarDef *stmt)
+HandleGlobalVar(SymbolsInfo *info, VarDef *stmt)
 {
     const char *elem, *field;
     ExprDef *arrayNdx;
@@ -1104,7 +1095,9 @@ HandleSymbolsBody(SymbolsInfo *info, VarDef *def, KeyInfo *keyi)
 
     for (; def; def = (VarDef *) def->common.next) {
         if (def->name && def->name->op == EXPR_FIELD_REF) {
-            ok = HandleSymbolsVar(info, def);
+            log_err(info->keymap->ctx,
+                    "Cannot set a global default value from within a key statement; "
+                    "Move statements to the global file scope\n");
             continue;
         }
 
@@ -1130,7 +1123,7 @@ HandleSymbolsBody(SymbolsInfo *info, VarDef *def, KeyInfo *keyi)
 static bool
 SetExplicitGroup(SymbolsInfo *info, KeyInfo *keyi)
 {
-    xkb_group_index_t i;
+    xkb_layout_index_t i;
 
     if (info->explicit_group == 0)
         return true;
@@ -1158,7 +1151,7 @@ static int
 HandleSymbolsDef(SymbolsInfo *info, SymbolsDef *stmt)
 {
     KeyInfo keyi;
-    xkb_group_index_t i;
+    xkb_layout_index_t i;
 
     keyi = info->dflt;
     for (i = 0; i < XKB_NUM_GROUPS; i++) {
@@ -1249,7 +1242,7 @@ HandleSymbolsFile(SymbolsInfo *info, XkbFile *file, enum merge_mode merge)
             ok = HandleSymbolsDef(info, (SymbolsDef *) stmt);
             break;
         case STMT_VAR:
-            ok = HandleSymbolsVar(info, (VarDef *) stmt);
+            ok = HandleGlobalVar(info, (VarDef *) stmt);
             break;
         case STMT_VMOD:
             ok = HandleVModDef((VModDef *) stmt, info->keymap, merge,
@@ -1291,7 +1284,7 @@ static struct xkb_key *
 FindKeyForSymbol(struct xkb_keymap *keymap, xkb_keysym_t sym)
 {
     struct xkb_key *key, *ret = NULL;
-    xkb_group_index_t group, min_group = UINT32_MAX;
+    xkb_layout_index_t group, min_group = UINT32_MAX;
     xkb_level_index_t level, min_level = UINT16_MAX;
 
     xkb_foreach_key(key, keymap) {
@@ -1361,27 +1354,27 @@ FindNamedType(struct xkb_keymap *keymap, xkb_atom_t name, unsigned *type_rtrn)
  *        symbol per level.
  */
 static bool
-FindAutomaticType(struct xkb_keymap *keymap, xkb_level_index_t width,
+FindAutomaticType(struct xkb_context *ctx, xkb_level_index_t width,
                   const xkb_keysym_t *syms, xkb_atom_t *typeNameRtrn,
                   bool *autoType)
 {
     *autoType = false;
     if ((width == 1) || (width == 0)) {
-        *typeNameRtrn = xkb_atom_intern(keymap->ctx, "ONE_LEVEL");
+        *typeNameRtrn = xkb_atom_intern(ctx, "ONE_LEVEL");
         *autoType = true;
     }
     else if (width == 2) {
         if (syms && xkb_keysym_is_lower(syms[0]) &&
             xkb_keysym_is_upper(syms[1])) {
-            *typeNameRtrn = xkb_atom_intern(keymap->ctx, "ALPHABETIC");
+            *typeNameRtrn = xkb_atom_intern(ctx, "ALPHABETIC");
         }
         else if (syms && (xkb_keysym_is_keypad(syms[0]) ||
                           xkb_keysym_is_keypad(syms[1]))) {
-            *typeNameRtrn = xkb_atom_intern(keymap->ctx, "KEYPAD");
+            *typeNameRtrn = xkb_atom_intern(ctx, "KEYPAD");
             *autoType = true;
         }
         else {
-            *typeNameRtrn = xkb_atom_intern(keymap->ctx, "TWO_LEVEL");
+            *typeNameRtrn = xkb_atom_intern(ctx, "TWO_LEVEL");
             *autoType = true;
         }
     }
@@ -1390,47 +1383,60 @@ FindAutomaticType(struct xkb_keymap *keymap, xkb_level_index_t width,
             xkb_keysym_is_upper(syms[1]))
             if (xkb_keysym_is_lower(syms[2]) && xkb_keysym_is_upper(syms[3]))
                 *typeNameRtrn =
-                    xkb_atom_intern(keymap->ctx, "FOUR_LEVEL_ALPHABETIC");
+                    xkb_atom_intern(ctx, "FOUR_LEVEL_ALPHABETIC");
             else
-                *typeNameRtrn = xkb_atom_intern(keymap->ctx,
+                *typeNameRtrn = xkb_atom_intern(ctx,
                                                 "FOUR_LEVEL_SEMIALPHABETIC");
 
         else if (syms && (xkb_keysym_is_keypad(syms[0]) ||
                           xkb_keysym_is_keypad(syms[1])))
-            *typeNameRtrn = xkb_atom_intern(keymap->ctx, "FOUR_LEVEL_KEYPAD");
+            *typeNameRtrn = xkb_atom_intern(ctx, "FOUR_LEVEL_KEYPAD");
         else
-            *typeNameRtrn = xkb_atom_intern(keymap->ctx, "FOUR_LEVEL");
+            *typeNameRtrn = xkb_atom_intern(ctx, "FOUR_LEVEL");
         /* XXX: why not set autoType here? */
     }
     return width <= 4;
 }
 
-/**
- * Ensure the given KeyInfo is in a coherent state, i.e. no gaps between the
- * groups, and reduce to one group if all groups are identical anyway.
- */
-static void
-PrepareKeyDef(KeyInfo *keyi)
+static bool
+CopySymbolsDef(SymbolsInfo *info, KeyInfo *keyi)
 {
-    xkb_group_index_t i, lastGroup;
+    struct xkb_keymap *keymap = info->keymap;
+    struct xkb_key *key;
     const GroupInfo *group0;
-    bool identical;
+    xkb_layout_index_t i;
+    bool haveActions;
+    unsigned int sizeSyms;
+    unsigned int symIndex;
+
+    /*
+     * The name is guaranteed to be real and not an alias (see
+     * AddKeySymbols), so 'false' is safe here.
+     */
+    key = FindNamedKey(keymap, keyi->name, false);
+    if (!key) {
+        log_vrb(info->keymap->ctx, 5,
+                "Key %s not found in keycodes; Symbols ignored\n",
+                LongKeyNameText(keyi->name));
+        return false;
+    }
 
-    /* get highest group number */
-    for (i = XKB_NUM_GROUPS - 1; i > 0; i--)
+    /* Find the range of groups we need. */
+    key->num_groups = 0;
+    for (i = 0; i < XKB_NUM_GROUPS; i++)
         if (keyi->groups[i].defined)
-            break;
-    lastGroup = i;
+            key->num_groups = i + 1;
 
-    if (lastGroup == 0)
-        return;
+    if (key->num_groups <= 0)
+        return false; /* WSGO */
 
+    /*
+     * If there are empty groups between non-empty ones, fill them with data
+     * from the first group.
+     * We can make a wrong assumption here. But leaving gaps is worse.
+     */
     group0 = &keyi->groups[0];
-
-    /* If there are empty groups between non-empty ones fill them with data */
-    /* from the first group. */
-    /* We can make a wrong assumption here. But leaving gaps is worse. */
-    for (i = lastGroup; i > 0; i--) {
+    for (i = 1; i < key->num_groups - 1; i++) {
         GroupInfo *groupi = &keyi->groups[i];
 
         if (groupi->defined)
@@ -1442,101 +1448,47 @@ PrepareKeyDef(KeyInfo *keyi)
         groupi->defined = group0->defined;
     }
 
-    /* If all groups are completely identical remove them all */
-    /* exept the first one. */
-    /* XXX: This code needs testing... or removal. */
-    identical = true;
-    for (i = lastGroup; i > 0; i--) {
-        GroupInfo *groupi = &keyi->groups[i];
-
-        if (groupi->type != group0->type) {
-            identical = false;
-            break;
-        }
-        if (!darray_same(groupi->levels, group0->levels) &&
-            (darray_empty(groupi->levels) || darray_empty(group0->levels) ||
-             darray_size(groupi->levels) != darray_size(group0->levels) ||
-             memcmp(darray_mem(groupi->levels, 0),
-                    darray_mem(group0->levels, 0),
-                    darray_size(group0->levels) * sizeof(LevelInfo)))) {
-            identical = false;
-            break;
-        }
-        if (!darray_same(groupi->syms, group0->syms) &&
-            (darray_empty(groupi->syms) || darray_empty(group0->syms) ||
-             darray_size(groupi->syms) != darray_size(group0->syms) ||
-             memcmp(darray_mem(groupi->syms, 0),
-                    darray_mem(group0->syms, 0),
-                    darray_size(group0->syms) * sizeof(xkb_keysym_t)))) {
-            identical = false;
-            break;
+    /* See if we need to allocate an actions array. */
+    haveActions = false;
+    for (i = 0; i < key->num_groups; i++) {
+        LevelInfo *leveli;
+        darray_foreach(leveli, keyi->groups[i].levels) {
+            if (leveli->act.type != ACTION_TYPE_NONE) {
+                haveActions = true;
+                goto out_of_loops;
+            }
         }
     }
-
-    if (identical)
-        for (i = lastGroup; i > 0; i--)
-            ClearGroupInfo(&keyi->groups[i]);
-}
-
-static bool
-CopySymbolsDef(SymbolsInfo *info, KeyInfo *keyi)
-{
-    struct xkb_keymap *keymap = info->keymap;
-    struct xkb_key *key;
-    unsigned int sizeSyms = 0;
-    xkb_group_index_t i, nGroups;
-    xkb_level_index_t width, tmp;
-    struct xkb_key_type * type;
-    bool haveActions;
-    unsigned int symIndex = 0;
+out_of_loops:
 
     /*
-     * The name is guaranteed to be real and not an alias (see
-     * AddKeySymbols), so 'false' is safe here.
+     * Find and assign the groups' types in the keymap. Also find the
+     * key width according to the largest type.
      */
-    key = FindNamedKey(keymap, keyi->name, false);
-    if (!key) {
-        log_vrb(info->keymap->ctx, 5,
-                "Key %s not found in keycodes; Symbols ignored\n",
-                LongKeyNameText(keyi->name));
-        return false;
-    }
-
-    haveActions = false;
-    width = 0;
-    for (i = nGroups = 0; i < XKB_NUM_GROUPS; i++) {
+    key->width = 0;
+    for (i = 0; i < key->num_groups; i++) {
+        struct xkb_key_type *type;
         GroupInfo *groupi = &keyi->groups[i];
         bool autoType = false;
 
-        if (i + 1 > nGroups && groupi->defined)
-            nGroups = i + 1;
-
-        if (!haveActions) {
-            LevelInfo *leveli;
-            darray_foreach(leveli, groupi->levels) {
-                if (leveli->act.type != ACTION_TYPE_NONE) {
-                    haveActions = true;
-                    break;
-                }
-            }
-        }
-
-        /* Assign the type to the key, if it is missing. */
+        /* Find the type of the group, if it is missing. */
         if (groupi->type == XKB_ATOM_NONE) {
             if (keyi->dfltType != XKB_ATOM_NONE)
                 groupi->type = keyi->dfltType;
-            else if (FindAutomaticType(keymap, darray_size(groupi->levels),
+            else if (FindAutomaticType(keymap->ctx,
+                                       darray_size(groupi->levels),
                                        darray_mem(groupi->syms, 0),
                                        &groupi->type, &autoType)) { }
             else
                 log_vrb(info->keymap->ctx, 5,
                         "No automatic type for %d levels; "
                         "Using %s for the %s key\n",
-                        darray_size(groupi->levels),
+                        (int) darray_size(groupi->levels),
                         xkb_atom_text(keymap->ctx, groupi->type),
                         LongKeyNameText(keyi->name));
         }
 
+        /* Find the type in the keymap, if it was defined in xkb_types. */
         if (FindNamedType(keymap, groupi->type, &key->kt_index[i])) {
             if (!autoType || darray_size(groupi->levels) > 2)
                 key->explicit_groups |= (1 << i);
@@ -1554,7 +1506,7 @@ CopySymbolsDef(SymbolsInfo *info, KeyInfo *keyi)
             key->kt_index[i] = 0;
         }
 
-        /* if the type specifies fewer levels than the key has, shrink the key */
+        /* If the type specifies fewer levels than the key has, shrink the key. */
         type = &keymap->types[key->kt_index[i]];
         if (type->num_levels < darray_size(groupi->levels)) {
             log_vrb(info->keymap->ctx, 1,
@@ -1563,59 +1515,39 @@ CopySymbolsDef(SymbolsInfo *info, KeyInfo *keyi)
                     xkb_atom_text(keymap->ctx, type->name),
                     type->num_levels,
                     LongKeyNameText(keyi->name),
-                    darray_size(groupi->levels));
+                    (int) darray_size(groupi->levels));
             darray_resize(groupi->levels, type->num_levels);
         }
 
-        width = MAX(width, type->num_levels);
-        sizeSyms += darray_size(groupi->syms);
+        /*
+         * Why type->num_levels and not darray_size(groupi->levels)?
+         * Because the type may have more levels, and each group must
+         * have at least as many levels as its type. Because the
+         * key->syms array is indexed by (group * width + level), we
+         * must take the largest one.
+         * Maybe we can change it to save some space.
+         */
+        key->width = MAX(key->width, type->num_levels);
     }
 
-    key->syms = calloc(sizeSyms, sizeof(*key->syms));
-    key->num_groups = nGroups;
-    key->width = width;
-    key->sym_index = calloc(nGroups * width, sizeof(*key->sym_index));
-    key->num_syms = calloc(nGroups * width, sizeof(*key->num_syms));
-
-    if (haveActions) {
-        key->actions = calloc(nGroups * width, sizeof(*key->actions));
-        key->explicit |= EXPLICIT_INTERP;
-    }
+    /* Find the size of the syms array. */
+    sizeSyms = 0;
+    for (i = 0; i < key->num_groups; i++)
+        sizeSyms += darray_size(keyi->groups[i].syms);
 
+    /* Initialize the xkb_key, now that we know the sizes. */
+    key->syms = calloc(sizeSyms, sizeof(*key->syms));
+    key->sym_index = calloc(key->num_groups * key->width,
+                            sizeof(*key->sym_index));
+    key->num_syms = calloc(key->num_groups * key->width,
+                           sizeof(*key->num_syms));
     key->out_of_range_group_number = keyi->out_of_range_group_number;
     key->out_of_range_group_action = keyi->out_of_range_group_action;
-
-    for (i = 0; i < nGroups; i++) {
-        GroupInfo *groupi = &keyi->groups[i];
-
-        if (!darray_empty(groupi->syms)) {
-            /* fill key to "width" symbols*/
-            for (tmp = 0; tmp < width; tmp++) {
-                LevelInfo *leveli = NULL;
-
-                if (tmp < darray_size(groupi->levels))
-                    leveli = &darray_item(groupi->levels, tmp);
-
-                if (leveli && leveli->num_syms != 0) {
-                    memcpy(&key->syms[symIndex],
-                           &darray_item(groupi->syms, leveli->sym_index),
-                           leveli->num_syms * sizeof(*key->syms));
-                    key->sym_index[(i * width) + tmp] = symIndex;
-                    key->num_syms[(i * width) + tmp] = leveli->num_syms;
-                    symIndex += key->num_syms[(i * width) + tmp];
-                }
-                else {
-                    key->sym_index[(i * width) + tmp] = 0;
-                    key->num_syms[(i * width) + tmp] = 0;
-                }
-
-                if (key->actions && leveli &&
-                    leveli->act.type != ACTION_TYPE_NONE)
-                    key->actions[tmp] = leveli->act;
-            }
-        }
+    if (haveActions) {
+        key->actions = calloc(key->num_groups * key->width,
+                              sizeof(*key->actions));
+        key->explicit |= EXPLICIT_INTERP;
     }
-
     if (keyi->defined & KEY_FIELD_VMODMAP) {
         key->vmodmap = keyi->vmodmap;
         key->explicit |= EXPLICIT_VMODMAP;
@@ -1626,6 +1558,31 @@ CopySymbolsDef(SymbolsInfo *info, KeyInfo *keyi)
         key->explicit |= EXPLICIT_REPEAT;
     }
 
+    /* Copy keysyms and actions. */
+    symIndex = 0;
+    for (i = 0; i < key->num_groups; i++) {
+        GroupInfo *groupi = &keyi->groups[i];
+        xkb_level_index_t j;
+
+        /* We rely on calloc having zeroized the arrays up to key->width. */
+        for (j = 0; j < darray_size(groupi->levels); j++) {
+            LevelInfo *leveli = &darray_item(groupi->levels, j);
+
+            if (leveli->act.type != ACTION_TYPE_NONE)
+                key->actions[i * key->width + j] = leveli->act;
+
+            if (leveli->num_syms <= 0)
+                continue;
+
+            memcpy(&key->syms[symIndex],
+                   &darray_item(groupi->syms, leveli->sym_index),
+                   leveli->num_syms * sizeof(*key->syms));
+            key->sym_index[i * key->width + j] = symIndex;
+            key->num_syms[i * key->width + j] = leveli->num_syms;
+            symIndex += key->num_syms[i * key->width + j];
+        }
+    }
+
     return true;
 }
 
@@ -1667,7 +1624,7 @@ CopySymbolsToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info)
 {
     KeyInfo *keyi;
     ModMapEntry *mm;
-    xkb_group_index_t i;
+    xkb_layout_index_t i;
     struct xkb_key *key;
 
     keymap->symbols_section_name = strdup_safe(info->name);
@@ -1676,11 +1633,9 @@ CopySymbolsToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info)
         if (info->groupNames[i] != XKB_ATOM_NONE)
             keymap->group_names[i] = info->groupNames[i];
 
-    darray_foreach(keyi, info->keys) {
-        PrepareKeyDef(keyi);
+    darray_foreach(keyi, info->keys)
         if (!CopySymbolsDef(info, keyi))
             info->errorCount++;
-    }
 
     if (xkb_get_log_verbosity(keymap->ctx) > 3) {
         xkb_foreach_key(key, keymap) {