| /* |
| * 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> |
| */ |
| |
| #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, |
| "Geometry sections are not supported; ignoring\n"); |
| } else { |
| log_err(ctx, "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, |
| "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, "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, "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, "Failed to compile %s\n", |
| xkb_file_type_to_string(type)); |
| return false; |
| } |
| } |
| |
| return UpdateDerivedKeymapFields(keymap); |
| } |