Home | History | Annotate | Download | only in xkbcomp
      1 /*
      2  * Copyright  2009 Dan Nicholson
      3  * Copyright  2012 Intel Corporation
      4  * Copyright  2012 Ran Benita <ran234 (at) gmail.com>
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the next
     14  * paragraph) shall be included in all copies or substantial portions of the
     15  * Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     23  * DEALINGS IN THE SOFTWARE.
     24  *
     25  * Author: Dan Nicholson <dbn.lists (at) gmail.com>
     26  *         Daniel Stone <daniel (at) fooishbar.org>
     27  *         Ran Benita <ran234 (at) gmail.com>
     28  */
     29 
     30 #include "xkbcomp-priv.h"
     31 
     32 static void
     33 ComputeEffectiveMask(struct xkb_keymap *keymap, struct xkb_mods *mods)
     34 {
     35     mods->mask = mod_mask_get_effective(keymap, mods->mods);
     36 }
     37 
     38 static void
     39 UpdateActionMods(struct xkb_keymap *keymap, union xkb_action *act,
     40                  xkb_mod_mask_t modmap)
     41 {
     42     switch (act->type) {
     43     case ACTION_TYPE_MOD_SET:
     44     case ACTION_TYPE_MOD_LATCH:
     45     case ACTION_TYPE_MOD_LOCK:
     46         if (act->mods.flags & ACTION_MODS_LOOKUP_MODMAP)
     47             act->mods.mods.mods = modmap;
     48         ComputeEffectiveMask(keymap, &act->mods.mods);
     49         break;
     50     default:
     51         break;
     52     }
     53 }
     54 
     55 static const struct xkb_sym_interpret default_interpret = {
     56     .sym = XKB_KEY_NoSymbol,
     57     .repeat = true,
     58     .match = MATCH_ANY_OR_NONE,
     59     .mods = 0,
     60     .virtual_mod = XKB_MOD_INVALID,
     61     .action = { .type = ACTION_TYPE_NONE },
     62 };
     63 
     64 /**
     65  * Find an interpretation which applies to this particular level, either by
     66  * finding an exact match for the symbol and modifier combination, or a
     67  * generic XKB_KEY_NoSymbol match.
     68  */
     69 static const struct xkb_sym_interpret *
     70 FindInterpForKey(struct xkb_keymap *keymap, const struct xkb_key *key,
     71                  xkb_layout_index_t group, xkb_level_index_t level)
     72 {
     73     const xkb_keysym_t *syms;
     74     int num_syms;
     75 
     76     num_syms = xkb_keymap_key_get_syms_by_level(keymap, key->keycode, group,
     77                                                 level, &syms);
     78     if (num_syms == 0)
     79         return NULL;
     80 
     81     /*
     82      * There may be multiple matchings interprets; we should always return
     83      * the most specific. Here we rely on compat.c to set up the
     84      * sym_interprets array from the most specific to the least specific,
     85      * such that when we find a match we return immediately.
     86      */
     87     for (unsigned i = 0; i < keymap->num_sym_interprets; i++) {
     88         const struct xkb_sym_interpret *interp = &keymap->sym_interprets[i];
     89 
     90         xkb_mod_mask_t mods;
     91         bool found = false;
     92 
     93         if ((num_syms > 1 || interp->sym != syms[0]) &&
     94             interp->sym != XKB_KEY_NoSymbol)
     95             continue;
     96 
     97         if (interp->level_one_only && level != 0)
     98             mods = 0;
     99         else
    100             mods = key->modmap;
    101 
    102         switch (interp->match) {
    103         case MATCH_NONE:
    104             found = !(interp->mods & mods);
    105             break;
    106         case MATCH_ANY_OR_NONE:
    107             found = (!mods || (interp->mods & mods));
    108             break;
    109         case MATCH_ANY:
    110             found = (interp->mods & mods);
    111             break;
    112         case MATCH_ALL:
    113             found = ((interp->mods & mods) == interp->mods);
    114             break;
    115         case MATCH_EXACTLY:
    116             found = (interp->mods == mods);
    117             break;
    118         }
    119 
    120         if (found)
    121             return interp;
    122     }
    123 
    124     return &default_interpret;
    125 }
    126 
    127 static bool
    128 ApplyInterpsToKey(struct xkb_keymap *keymap, struct xkb_key *key)
    129 {
    130     xkb_mod_mask_t vmodmap = 0;
    131     xkb_layout_index_t group;
    132     xkb_level_index_t level;
    133 
    134     /* If we've been told not to bind interps to this key, then don't. */
    135     if (key->explicit & EXPLICIT_INTERP)
    136         return true;
    137 
    138     for (group = 0; group < key->num_groups; group++) {
    139         for (level = 0; level < XkbKeyNumLevels(key, group); level++) {
    140             const struct xkb_sym_interpret *interp;
    141 
    142             interp = FindInterpForKey(keymap, key, group, level);
    143             if (!interp)
    144                 continue;
    145 
    146             /* Infer default key behaviours from the base level. */
    147             if (group == 0 && level == 0)
    148                 if (!(key->explicit & EXPLICIT_REPEAT) && interp->repeat)
    149                     key->repeats = true;
    150 
    151             if ((group == 0 && level == 0) || !interp->level_one_only)
    152                 if (interp->virtual_mod != XKB_MOD_INVALID)
    153                     vmodmap |= (1u << interp->virtual_mod);
    154 
    155             if (interp->action.type != ACTION_TYPE_NONE)
    156                 key->groups[group].levels[level].action = interp->action;
    157         }
    158     }
    159 
    160     if (!(key->explicit & EXPLICIT_VMODMAP))
    161         key->vmodmap = vmodmap;
    162 
    163     return true;
    164 }
    165 
    166 /**
    167  * This collects a bunch of disparate functions which was done in the server
    168  * at various points that really should've been done within xkbcomp.  Turns out
    169  * your actions and types are a lot more useful when any of your modifiers
    170  * other than Shift actually do something ...
    171  */
    172 static bool
    173 UpdateDerivedKeymapFields(struct xkb_keymap *keymap)
    174 {
    175     struct xkb_key *key;
    176     struct xkb_mod *mod;
    177     struct xkb_led *led;
    178     unsigned int i, j;
    179 
    180     /* Find all the interprets for the key and bind them to actions,
    181      * which will also update the vmodmap. */
    182     xkb_keys_foreach(key, keymap)
    183         if (!ApplyInterpsToKey(keymap, key))
    184             return false;
    185 
    186     /* Update keymap->mods, the virtual -> real mod mapping. */
    187     xkb_keys_foreach(key, keymap)
    188         xkb_mods_enumerate(i, mod, &keymap->mods)
    189             if (key->vmodmap & (1u << i))
    190                 mod->mapping |= key->modmap;
    191 
    192     /* Now update the level masks for all the types to reflect the vmods. */
    193     for (i = 0; i < keymap->num_types; i++) {
    194         ComputeEffectiveMask(keymap, &keymap->types[i].mods);
    195 
    196         for (j = 0; j < keymap->types[i].num_entries; j++) {
    197             ComputeEffectiveMask(keymap, &keymap->types[i].entries[j].mods);
    198             ComputeEffectiveMask(keymap, &keymap->types[i].entries[j].preserve);
    199         }
    200     }
    201 
    202     /* Update action modifiers. */
    203     xkb_keys_foreach(key, keymap)
    204         for (i = 0; i < key->num_groups; i++)
    205             for (j = 0; j < XkbKeyNumLevels(key, i); j++)
    206                 UpdateActionMods(keymap, &key->groups[i].levels[j].action,
    207                                  key->modmap);
    208 
    209     /* Update vmod -> led maps. */
    210     xkb_leds_foreach(led, keymap)
    211         ComputeEffectiveMask(keymap, &led->mods);
    212 
    213     /* Find maximum number of groups out of all keys in the keymap. */
    214     xkb_keys_foreach(key, keymap)
    215         keymap->num_groups = MAX(keymap->num_groups, key->num_groups);
    216 
    217     return true;
    218 }
    219 
    220 typedef bool (*compile_file_fn)(XkbFile *file,
    221                                 struct xkb_keymap *keymap,
    222                                 enum merge_mode merge);
    223 
    224 static const compile_file_fn compile_file_fns[LAST_KEYMAP_FILE_TYPE + 1] = {
    225     [FILE_TYPE_KEYCODES] = CompileKeycodes,
    226     [FILE_TYPE_TYPES] = CompileKeyTypes,
    227     [FILE_TYPE_COMPAT] = CompileCompatMap,
    228     [FILE_TYPE_SYMBOLS] = CompileSymbols,
    229 };
    230 
    231 bool
    232 CompileKeymap(XkbFile *file, struct xkb_keymap *keymap, enum merge_mode merge)
    233 {
    234     bool ok;
    235     const char *main_name;
    236     XkbFile *files[LAST_KEYMAP_FILE_TYPE + 1] = { NULL };
    237     enum xkb_file_type type;
    238     struct xkb_context *ctx = keymap->ctx;
    239 
    240     main_name = file->name ? file->name : "(unnamed)";
    241 
    242     /* Collect section files and check for duplicates. */
    243     for (file = (XkbFile *) file->defs; file;
    244          file = (XkbFile *) file->common.next) {
    245         if (file->file_type < FIRST_KEYMAP_FILE_TYPE ||
    246             file->file_type > LAST_KEYMAP_FILE_TYPE) {
    247             log_err(ctx, "Cannot define %s in a keymap file\n",
    248                     xkb_file_type_to_string(file->file_type));
    249             continue;
    250         }
    251 
    252         if (files[file->file_type]) {
    253             log_err(ctx,
    254                     "More than one %s section in keymap file; "
    255                     "All sections after the first ignored\n",
    256                     xkb_file_type_to_string(file->file_type));
    257             continue;
    258         }
    259 
    260         if (!file->topName) {
    261             free(file->topName);
    262             file->topName = strdup(main_name);
    263         }
    264 
    265         files[file->file_type] = file;
    266     }
    267 
    268     /*
    269      * Check that all required section were provided.
    270      * Report everything before failing.
    271      */
    272     ok = true;
    273     for (type = FIRST_KEYMAP_FILE_TYPE;
    274          type <= LAST_KEYMAP_FILE_TYPE;
    275          type++) {
    276         if (files[type] == NULL) {
    277             log_err(ctx, "Required section %s missing from keymap\n",
    278                     xkb_file_type_to_string(type));
    279             ok = false;
    280         }
    281     }
    282     if (!ok)
    283         return false;
    284 
    285     /* Compile sections. */
    286     for (type = FIRST_KEYMAP_FILE_TYPE;
    287          type <= LAST_KEYMAP_FILE_TYPE;
    288          type++) {
    289         log_dbg(ctx, "Compiling %s \"%s\"\n",
    290                 xkb_file_type_to_string(type), files[type]->topName);
    291 
    292         ok = compile_file_fns[type](files[type], keymap, merge);
    293         if (!ok) {
    294             log_err(ctx, "Failed to compile %s\n",
    295                     xkb_file_type_to_string(type));
    296             return false;
    297         }
    298     }
    299 
    300     return UpdateDerivedKeymapFields(keymap);
    301 }
    302