Removed build dependency on xproto.
[platform/upstream/libxkbcommon.git] / src / xkbcomp / keymap.c
index 2c2420d..0aaed1f 100644 (file)
-/************************************************************
- Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
-
- Permission to use, copy, modify, and distribute this
- software and its documentation for any purpose and without
- fee is hereby granted, provided that the above copyright
- notice appear in all copies and that both that copyright
- notice and this permission notice appear in supporting
- documentation, and that the name of Silicon Graphics not be
- used in advertising or publicity pertaining to distribution
- of the software without specific prior written permission.
- Silicon Graphics makes no representation about the suitability
- of this software for any purpose. It is provided "as is"
- without any express or implied warranty.
-
- SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
- SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
- GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
- DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
- THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
- ********************************************************/
-
-#include "xkbcomp.h"
-#include "xkbmisc.h"
-#include "expr.h"
-#include "vmod.h"
-#include "action.h"
-#include "misc.h"
-#include "indicators.h"
-
-#define        KEYCODES        0
-#define        GEOMETRY        1
-#define        TYPES           2
-#define        COMPAT          3
-#define        SYMBOLS         4
-#define        MAX_SECTIONS    5
-
-static XkbFile *sections[MAX_SECTIONS];
-
-/**
- * Compile the given file and store the output in xkb.
- * @param file A list of XkbFiles, each denoting one type (e.g.
- * XkmKeyNamesIdx, etc.)
+/*
+ * Copyright © 2009 Dan Nicholson
+ * Copyright © 2012 Intel Corporation
+ * Copyright © 2012 Ran Benita <ran234@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Dan Nicholson <dbn.lists@gmail.com>
+ *         Daniel Stone <daniel@fooishbar.org>
+ *         Ran Benita <ran234@gmail.com>
  */
-Bool
-CompileKeymap(XkbFile *file, struct xkb_desc * xkb, unsigned merge)
+
+#include "config.h"
+
+#include "xkbcomp-priv.h"
+
+static void
+ComputeEffectiveMask(struct xkb_keymap *keymap, struct xkb_mods *mods)
 {
-    unsigned have;
-    Bool ok;
-    unsigned required, legal;
-    unsigned mainType;
-    char *mainName;
-    LEDInfo *unbound = NULL;
-
-    bzero(sections, MAX_SECTIONS * sizeof(XkbFile *));
-    mainType = file->type;
-    mainName = file->name;
-    switch (mainType)
-    {
-    case XkmSemanticsFile:
-        required = XkmSemanticsRequired;
-        legal = XkmSemanticsLegal;
-        break;
-    case XkmLayoutFile:        /* standard type  if setxkbmap -print */
-        required = XkmLayoutRequired;
-        legal = XkmKeymapLegal;
-        break;
-    case XkmKeymapFile:
-        required = XkmKeymapRequired;
-        legal = XkmKeymapLegal;
+    mods->mask = mod_mask_get_effective(keymap, mods->mods);
+}
+
+static void
+UpdateActionMods(struct xkb_keymap *keymap, union xkb_action *act,
+                 xkb_mod_mask_t modmap)
+{
+    switch (act->type) {
+    case ACTION_TYPE_MOD_SET:
+    case ACTION_TYPE_MOD_LATCH:
+    case ACTION_TYPE_MOD_LOCK:
+        if (act->mods.flags & ACTION_MODS_LOOKUP_MODMAP)
+            act->mods.mods.mods = modmap;
+        ComputeEffectiveMask(keymap, &act->mods.mods);
         break;
     default:
-        ERROR("Cannot compile %s alone into an XKM file\n",
-               XkbcConfigText(mainType));
-        return False;
+        break;
     }
-    have = 0;
-    ok = 1;
-    file = (XkbFile *) file->defs;
-    /* Check for duplicate entries in the input file */
-    while ((file) && (ok))
-    {
-        file->topName = mainName;
-        if ((have & (1 << file->type)) != 0)
-        {
-            ERROR("More than one %s section in a %s file\n",
-                   XkbcConfigText(file->type), XkbcConfigText(mainType));
-            ACTION("All sections after the first ignored\n");
-            ok = False;
+}
+
+static const struct xkb_sym_interpret default_interpret = {
+    .sym = XKB_KEY_NoSymbol,
+    .repeat = true,
+    .match = MATCH_ANY_OR_NONE,
+    .mods = 0,
+    .virtual_mod = XKB_MOD_INVALID,
+    .action = { .type = ACTION_TYPE_NONE },
+};
+
+/**
+ * Find an interpretation which applies to this particular level, either by
+ * finding an exact match for the symbol and modifier combination, or a
+ * generic XKB_KEY_NoSymbol match.
+ */
+static const struct xkb_sym_interpret *
+FindInterpForKey(struct xkb_keymap *keymap, const struct xkb_key *key,
+                 xkb_layout_index_t group, xkb_level_index_t level)
+{
+    const xkb_keysym_t *syms;
+    int num_syms;
+
+    num_syms = xkb_keymap_key_get_syms_by_level(keymap, key->keycode, group,
+                                                level, &syms);
+    if (num_syms == 0)
+        return NULL;
+
+    /*
+     * There may be multiple matchings interprets; we should always return
+     * the most specific. Here we rely on compat.c to set up the
+     * sym_interprets array from the most specific to the least specific,
+     * such that when we find a match we return immediately.
+     */
+    for (unsigned i = 0; i < keymap->num_sym_interprets; i++) {
+        const struct xkb_sym_interpret *interp = &keymap->sym_interprets[i];
+
+        xkb_mod_mask_t mods;
+        bool found = false;
+
+        if ((num_syms > 1 || interp->sym != syms[0]) &&
+            interp->sym != XKB_KEY_NoSymbol)
+            continue;
+
+        if (interp->level_one_only && level != 0)
+            mods = 0;
+        else
+            mods = key->modmap;
+
+        switch (interp->match) {
+        case MATCH_NONE:
+            found = !(interp->mods & mods);
+            break;
+        case MATCH_ANY_OR_NONE:
+            found = (!mods || (interp->mods & mods));
+            break;
+        case MATCH_ANY:
+            found = (interp->mods & mods);
+            break;
+        case MATCH_ALL:
+            found = ((interp->mods & mods) == interp->mods);
+            break;
+        case MATCH_EXACTLY:
+            found = (interp->mods == mods);
+            break;
         }
-        else if ((1 << file->type) & (~legal))
-        {
-            ERROR("Cannot define %s in a %s file\n",
-                   XkbcConfigText(file->type), XkbcConfigText(mainType));
-            ok = False;
+
+        if (found)
+            return interp;
+    }
+
+    return &default_interpret;
+}
+
+static bool
+ApplyInterpsToKey(struct xkb_keymap *keymap, struct xkb_key *key)
+{
+    xkb_mod_mask_t vmodmap = 0;
+    xkb_layout_index_t group;
+    xkb_level_index_t level;
+
+    /* If we've been told not to bind interps to this key, then don't. */
+    if (key->explicit & EXPLICIT_INTERP)
+        return true;
+
+    for (group = 0; group < key->num_groups; group++) {
+        for (level = 0; level < XkbKeyNumLevels(key, group); level++) {
+            const struct xkb_sym_interpret *interp;
+
+            interp = FindInterpForKey(keymap, key, group, level);
+            if (!interp)
+                continue;
+
+            /* Infer default key behaviours from the base level. */
+            if (group == 0 && level == 0)
+                if (!(key->explicit & EXPLICIT_REPEAT) && interp->repeat)
+                    key->repeats = true;
+
+            if ((group == 0 && level == 0) || !interp->level_one_only)
+                if (interp->virtual_mod != XKB_MOD_INVALID)
+                    vmodmap |= (1u << interp->virtual_mod);
+
+            if (interp->action.type != ACTION_TYPE_NONE)
+                key->groups[group].levels[level].action = interp->action;
         }
-        else
-            switch (file->type)
-            {
-            case XkmSemanticsFile:
-            case XkmLayoutFile:
-            case XkmKeymapFile:
-                WSGO("Illegal %s configuration in a %s file\n",
-                      XkbcConfigText(file->type), XkbcConfigText(mainType));
-                ACTION("Ignored\n");
-                ok = False;
-                break;
-            case XkmKeyNamesIndex:
-                sections[KEYCODES] = file;
-                break;
-            case XkmTypesIndex:
-                sections[TYPES] = file;
-                break;
-            case XkmSymbolsIndex:
-                sections[SYMBOLS] = file;
-                break;
-            case XkmCompatMapIndex:
-                sections[COMPAT] = file;
-                break;
-            case XkmGeometryIndex:
-            case XkmGeometryFile:
-                sections[GEOMETRY] = file;
-                break;
-            case XkmVirtualModsIndex:
-            case XkmIndicatorsIndex:
-                WSGO("Found an isolated %s section\n",
-                      XkbcConfigText(file->type));
-                break;
-            default:
-                WSGO("Unknown file type %d\n", file->type);
-                break;
+    }
+
+    if (!(key->explicit & EXPLICIT_VMODMAP))
+        key->vmodmap = vmodmap;
+
+    return true;
+}
+
+/**
+ * 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 ...
+ */
+static bool
+UpdateDerivedKeymapFields(struct xkb_keymap *keymap)
+{
+    struct xkb_key *key;
+    struct xkb_mod *mod;
+    struct xkb_led *led;
+    unsigned int i, j;
+
+    /* Find all the interprets for the key and bind them to actions,
+     * which will also update the vmodmap. */
+    xkb_keys_foreach(key, keymap)
+        if (!ApplyInterpsToKey(keymap, key))
+            return false;
+
+    /* Update keymap->mods, the virtual -> real mod mapping. */
+    xkb_keys_foreach(key, keymap)
+        xkb_mods_enumerate(i, mod, &keymap->mods)
+            if (key->vmodmap & (1u << i))
+                mod->mapping |= key->modmap;
+
+    /* Now update the level masks for all the types to reflect the vmods. */
+    for (i = 0; i < keymap->num_types; i++) {
+        ComputeEffectiveMask(keymap, &keymap->types[i].mods);
+
+        for (j = 0; j < keymap->types[i].num_entries; j++) {
+            ComputeEffectiveMask(keymap, &keymap->types[i].entries[j].mods);
+            ComputeEffectiveMask(keymap, &keymap->types[i].entries[j].preserve);
+        }
+    }
+
+    /* Update action modifiers. */
+    xkb_keys_foreach(key, keymap)
+        for (i = 0; i < key->num_groups; i++)
+            for (j = 0; j < XkbKeyNumLevels(key, i); j++)
+                UpdateActionMods(keymap, &key->groups[i].levels[j].action,
+                                 key->modmap);
+
+    /* Update vmod -> led maps. */
+    xkb_leds_foreach(led, keymap)
+        ComputeEffectiveMask(keymap, &led->mods);
+
+    /* Find maximum number of groups out of all keys in the keymap. */
+    xkb_keys_foreach(key, keymap)
+        keymap->num_groups = MAX(keymap->num_groups, key->num_groups);
+
+    return true;
+}
+
+typedef bool (*compile_file_fn)(XkbFile *file,
+                                struct xkb_keymap *keymap,
+                                enum merge_mode merge);
+
+static const compile_file_fn compile_file_fns[LAST_KEYMAP_FILE_TYPE + 1] = {
+    [FILE_TYPE_KEYCODES] = CompileKeycodes,
+    [FILE_TYPE_TYPES] = CompileKeyTypes,
+    [FILE_TYPE_COMPAT] = CompileCompatMap,
+    [FILE_TYPE_SYMBOLS] = CompileSymbols,
+};
+
+bool
+CompileKeymap(XkbFile *file, struct xkb_keymap *keymap, enum merge_mode merge)
+{
+    bool ok;
+    XkbFile *files[LAST_KEYMAP_FILE_TYPE + 1] = { NULL };
+    enum xkb_file_type type;
+    struct xkb_context *ctx = keymap->ctx;
+
+    /* Collect section files and check for duplicates. */
+    for (file = (XkbFile *) file->defs; file;
+         file = (XkbFile *) file->common.next) {
+        if (file->file_type < FIRST_KEYMAP_FILE_TYPE ||
+            file->file_type > LAST_KEYMAP_FILE_TYPE) {
+            if (file->file_type == FILE_TYPE_GEOMETRY) {
+                log_vrb(ctx, 1,
+                        XKB_WARNING_UNSUPPORTED_GEOMETRY_SECTION,
+                        "Geometry sections are not supported; ignoring\n");
+            } else {
+                log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
+                        "Cannot define %s in a keymap file\n",
+                        xkb_file_type_to_string(file->file_type));
             }
-        if (ok)
-            have |= (1 << file->type);
-        file = (XkbFile *) file->common.next;
+            continue;
+        }
+
+        if (files[file->file_type]) {
+            log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
+                    "More than one %s section in keymap file; "
+                    "All sections after the first ignored\n",
+                    xkb_file_type_to_string(file->file_type));
+            continue;
+        }
+
+        files[file->file_type] = file;
     }
-    /* compile the sections we have in the file one-by-one, or fail. */
-    if (ok)
-    {
-        if (ok && (sections[KEYCODES] != NULL))
-            ok = CompileKeycodes(sections[KEYCODES], xkb, MergeOverride);
-        if (ok && (sections[GEOMETRY] != NULL))
-            ok = CompileGeometry(sections[GEOMETRY], xkb, MergeOverride);
-        if (ok && (sections[TYPES] != NULL))
-            ok = CompileKeyTypes(sections[TYPES], xkb, MergeOverride);
-        if (ok && (sections[COMPAT] != NULL))
-            ok = CompileCompatMap(sections[COMPAT], xkb, MergeOverride,
-                                  &unbound);
-        if (ok && (sections[SYMBOLS] != NULL))
-            ok = CompileSymbols(sections[SYMBOLS], xkb, MergeOverride);
+
+    /*
+     * Check that all required section were provided.
+     * Report everything before failing.
+     */
+    ok = true;
+    for (type = FIRST_KEYMAP_FILE_TYPE;
+         type <= LAST_KEYMAP_FILE_TYPE;
+         type++) {
+        if (files[type] == NULL) {
+            log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
+                    "Required section %s missing from keymap\n",
+                    xkb_file_type_to_string(type));
+            ok = false;
+        }
     }
     if (!ok)
-        return False;
-    xkb->defined = have;
-    if (required & (~have))
-    {
-        register int i, bit;
-        unsigned missing;
-        missing = required & (~have);
-        for (i = 0, bit = 1; missing != 0; i++, bit <<= 1)
-        {
-            if (missing & bit)
-            {
-                ERROR("Missing %s section in a %s file\n",
-                       XkbcConfigText(i), XkbcConfigText(mainType));
-                missing &= ~bit;
-            }
+        return false;
+
+    /* Compile sections. */
+    for (type = FIRST_KEYMAP_FILE_TYPE;
+         type <= LAST_KEYMAP_FILE_TYPE;
+         type++) {
+        log_dbg(ctx, XKB_LOG_MESSAGE_NO_ID,
+                "Compiling %s \"%s\"\n",
+                xkb_file_type_to_string(type), files[type]->name);
+
+        ok = compile_file_fns[type](files[type], keymap, merge);
+        if (!ok) {
+            log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
+                    "Failed to compile %s\n",
+                    xkb_file_type_to_string(type));
+            return false;
         }
-        ACTION("Description of %s not compiled\n",
-                XkbcConfigText(mainType));
-        ok = False;
     }
-    ok = BindIndicators(xkb, True, unbound, NULL);
-    return ok;
+
+    return UpdateDerivedKeymapFields(keymap);
 }