/* * Copyright © 2009 Dan Nicholson * Copyright © 2012 Intel Corporation * Copyright © 2012 Ran Benita * * 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 * Daniel Stone * Ran Benita */ #include "config.h" #include "xkbcomp-priv.h" static void ComputeEffectiveMask(struct xkb_keymap *keymap, struct xkb_mods *mods) { 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: break; } } 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; } 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; } } 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)); } 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; } /* * 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; /* 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; } } return UpdateDerivedKeymapFields(keymap); }