Edit

kc3-lang/libxkbcommon/src/xkbcomp/vmod.c

Branch :

  • Show log

    Commit

  • Author : Pierre Le Marre
    Date : 2025-07-07 12:28:24
    Hash : dc63e5f8
    Message : Ensure config.h is always included first While `config.h` may not be necessary in every file, it ensures consistency and makes code refactoring safer.

  • src/xkbcomp/vmod.c
  • /*
     * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
     * SPDX-License-Identifier: HPND
     */
    
    #include "config.h"
    
    #include <stdint.h>
    
    #include "xkbcommon/xkbcommon.h"
    #include "atom.h"
    #include "keymap.h"
    #include "text.h"
    #include "expr.h"
    #include "xkbcomp/ast.h"
    #include "vmod.h"
    
    void
    InitVMods(struct xkb_mod_set *info, const struct xkb_mod_set *mods, bool reset)
    {
        *info = *mods;
    
        if (!reset)
            return;
    
        struct xkb_mod *mod;
        xkb_mod_index_t vmod = 0;
        xkb_mods_enumerate(vmod, mod, info) {
            mod->mapping = 0;
        }
        info->explicit_vmods = 0;
    }
    
    void
    MergeModSets(struct xkb_context *ctx, struct xkb_mod_set *into,
                 const struct xkb_mod_set *from, enum merge_mode merge) {
        const bool clobber = (merge != MERGE_AUGMENT);
        xkb_mod_index_t vmod;
        const struct xkb_mod *mod;
        assert(into->num_mods <= from->num_mods);
        xkb_mods_enumerate(vmod, mod, from) {
            const xkb_mod_mask_t mask = UINT32_C(1) << vmod;
    
            if ((mod->type != MOD_VIRT)) {
                /* No modifier in `from` or real modifier: nothing to do */
                assert((mod->type == 0 &&
                        mod->name == XKB_ATOM_NONE) ||
                       ((mod->type & MOD_REAL) &&
                        into->mods[vmod].type == mod->type &&
                        mod->name != XKB_ATOM_NONE &&
                        into->mods[vmod].name == mod->name));
                continue;
            } else if (into->mods[vmod].type == 0) {
                /* No modifier in `into`: copy the whole modifier definition */
                assert(into->mods[vmod].name == XKB_ATOM_NONE);
                assert(mod->type == MOD_VIRT);
                assert(mod->name != XKB_ATOM_NONE);
                assert(vmod >= into->num_mods);
                into->mods[vmod] = *mod;
                if (from->explicit_vmods & mask)
                    into->explicit_vmods |= mask;
            } else {
                /* Modifier exists in both */
                assert(mod->type == MOD_VIRT);
                assert(mod->name != XKB_ATOM_NONE);
                assert(into->mods[vmod].type == mod->type);
                assert(into->mods[vmod].name == mod->name);
    
                if (!(from->explicit_vmods & mask)) {
                    /* Implicit mapping in `from`: do nothing */
                    assert(mod->mapping == 0);
                } else if (!(into->explicit_vmods & mask)) {
                    /* Implicit mapping in `into`: replace */
                    assert(into->mods[vmod].mapping == 0);
                    into->mods[vmod].mapping = mod->mapping;
                    into->explicit_vmods |= mask;
                } else if (mod->mapping != into->mods[vmod].mapping) {
                    /* Handle conflicting mappings */
                    const xkb_mod_mask_t use = (clobber)
                        ? mod->mapping
                        : into->mods[vmod].mapping;
                    const xkb_mod_mask_t ignore = (clobber)
                        ? into->mods[vmod].mapping
                        : mod->mapping;
    
                    log_warn(ctx, XKB_LOG_MESSAGE_NO_ID,
                             "Virtual modifier %s mapping defined multiple times; "
                             "Using %s, ignoring %s\n",
                             xkb_atom_text(ctx, mod->name),
                             ModMaskText(ctx, MOD_REAL, from, use),
                             ModMaskText(ctx, MOD_REAL, from, ignore));
    
                    into->mods[vmod].mapping = use;
                }
            }
        }
        into->num_mods = from->num_mods;
    }
    
    bool
    HandleVModDef(struct xkb_context *ctx, struct xkb_mod_set *mods, VModDef *stmt)
    {
        xkb_mod_mask_t mapping = 0;
        if (stmt->value) {
            /*
             * This is a statement such as 'virtualModifiers NumLock = Mod1';
             * it initialize the vmod-to-real-mod[s] mapping before going
             * through modifier_map.
             */
            if (!ExprResolveModMask(ctx, stmt->value, MOD_REAL, mods, &mapping)) {
                log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
                        "Declaration of %s ignored\n",
                        xkb_atom_text(ctx, stmt->name));
                return false;
            }
        }
    
        xkb_mod_index_t vmod;
        struct xkb_mod *mod;
        xkb_mods_enumerate(vmod, mod, mods) {
            if (mod->name == stmt->name) {
                if (mod->type != MOD_VIRT) {
                    log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
                            "Can't add a virtual modifier named \"%s\"; there is "
                            "already a non-virtual modifier with this name! "
                            "Ignored\n",
                            xkb_atom_text(ctx, mod->name));
                    return false;
                }
    
                const xkb_mod_mask_t mask = UINT32_C(1) << vmod;
    
                if (!stmt->value) {
                    /* No new explicit mapping: do nothing */
                    return true;
                } else if (!(mods->explicit_vmods & mask)) {
                    /* No previous explicit mapping: add mapping */
                    mod->mapping = mapping;
                } else if (mod->mapping != mapping) {
                    /* Handle conflicting mappings */
                    const bool clobber = (stmt->merge != MERGE_AUGMENT);
                    const xkb_mod_mask_t use =
                        (clobber ? mapping : mod->mapping);
                    const xkb_mod_mask_t ignore =
                        (clobber ? mod->mapping : mapping);
    
                    log_warn(ctx, XKB_LOG_MESSAGE_NO_ID,
                             "Virtual modifier %s mapping defined multiple times; "
                             "Using %s, ignoring %s\n",
                             xkb_atom_text(ctx, stmt->name),
                             ModMaskText(ctx, MOD_REAL, mods, use),
                             ModMaskText(ctx, MOD_REAL, mods, ignore));
    
                    mod->mapping = use;
                }
    
                mods->explicit_vmods |= mask;
    
                return true;
            }
        }
    
        if (mods->num_mods >= XKB_MAX_MODS) {
            log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
                    "Cannot define virtual modifier %s: "
                    "too many modifiers defined (maximum %u)\n",
                    xkb_atom_text(ctx, stmt->name), XKB_MAX_MODS);
            return false;
        }
    
        mods->mods[mods->num_mods].name = stmt->name;
        mods->mods[mods->num_mods].type = MOD_VIRT;
        mods->mods[mods->num_mods].mapping = mapping;
        if (stmt->value) {
            const xkb_mod_mask_t mask = UINT32_C(1) << mods->num_mods;
            mods->explicit_vmods |= mask;
        }
        mods->num_mods++;
        return true;
    }