Edit

kc3-lang/libxkbcommon/tools/tools-common.c

Branch :

  • Show log

    Commit

  • Author : Pierre Le Marre
    Date : 2025-05-14 15:08:37
    Hash : ae80b244
    Message : tools: Add --modmaps option to compile-keymap - Remove the `ENABLE_PRIVATE_APIS` guards on modmaps printing functions, since we use no private symbols. - Copy the `--print-modmaps` options from `interactive-evdev` into `compile-keymap` and rename it to `--modmaps`. This enables a feature similar to `xmodmap -pm`.

  • tools/tools-common.c
  • /*
     * Copyright © 2009 Dan Nicholson <dbn.lists@gmail.com>
     * Copyright © 2012 Intel Corporation
     * Copyright © 2012 Ran Benita <ran234@gmail.com>
     * SPDX-License-Identifier: MIT-open-group
     *
     * Author: Dan Nicholson <dbn.lists@gmail.com>
     *         Daniel Stone <daniel@fooishbar.org>
     *         Ran Benita <ran234@gmail.com>
     */
    
    #include "config.h"
    
    #include <errno.h>
    #include <limits.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #ifdef _WIN32
    #include <io.h>
    #include <windows.h>
    #include <process.h>
    #else
    #include <unistd.h>
    #include <termios.h>
    #endif
    
    #include "xkbcommon/xkbcommon.h"
    #include "tools-common.h"
    #include "src/utils.h"
    #include "src/keysym.h"
    #include "src/compose/parser.h"
    #include "src/keymap.h"
    
    #ifdef _WIN32
    #define S_ISFIFO(mode) 0
    #endif
    
    static void
    print_keycode(struct xkb_keymap *keymap, const char* prefix,
                  xkb_keycode_t keycode, const char *suffix) {
        const char *keyname = xkb_keymap_key_get_name(keymap, keycode);
        if (keyname) {
            printf("%s%-4s%s", prefix, keyname, suffix);
        } else {
            printf("%s%-4d%s", prefix, keycode, suffix);
        }
    }
    
    /* Variant of ModMaskText of main lib */
    static void
    print_mod_mask(struct xkb_keymap *keymap,
                   enum mod_type type, xkb_mod_mask_t mask)
    {
        /* We want to avoid boolean blindness, but we expected only 2 values */
        assert(type == MOD_REAL || type == MOD_BOTH);
    
        if (!mask) {
            printf("0");
            return;
        }
    
        const xkb_mod_index_t num_mods = xkb_keymap_num_mods(keymap);
        const xkb_mod_mask_t keymap_named_mods = (type == MOD_REAL)
            ? MOD_REAL_MASK_ALL
            : (xkb_mod_mask_t) ((UINT64_C(1) << num_mods) - 1);
    
        /* Print known mods */
        bool first = true;
        xkb_mod_mask_t named = mask & keymap_named_mods;
        for (xkb_mod_index_t mod = 0; named && mod < num_mods; mod++, named >>= 1) {
            if (named & UINT32_C(0x1)) {
                if (first) {
                    first = false;
                    printf("%s", xkb_keymap_mod_get_name(keymap, mod));
                } else {
                    printf(" + %s", xkb_keymap_mod_get_name(keymap, mod));
                }
            }
        }
        if (mask & ~keymap_named_mods) {
            /* If some bits of the mask cannot be expressed with the known modifiers
             * of the given type, print it as hexadecimal */
            printf("%s%#"PRIx32, (first ? "" : " + "), mask & ~keymap_named_mods);
        }
    }
    
    /* Modifiers encodings, formatted as YAML */
    void
    print_modifiers_encodings(struct xkb_keymap *keymap) {
        printf("Modifiers encodings:");
    
        /* Find the padding required for modifier names */
        int padding = 0;
        for (xkb_mod_index_t mod = 0; mod < xkb_keymap_num_mods(keymap); mod++) {
            const char* name = xkb_keymap_mod_get_name(keymap, mod);
            padding = MAX(padding, (int) strlen(name));
        }
    
        /* Print encodings */
        static const char indent[] = "\n  ";
        for (xkb_mod_index_t mod = 0; mod < xkb_keymap_num_mods(keymap); mod++) {
            if (mod == 0)
                printf("%s# Real modifiers (predefined)", indent);
            else if (mod == _XKB_MOD_INDEX_NUM_ENTRIES)
                printf("\n%s# Virtual modifiers (keymap-dependent)", indent);
            const char* name = xkb_keymap_mod_get_name(keymap, mod);
            const xkb_mod_mask_t encoding = xkb_keymap_mod_get_mask(keymap, name);
            const int count = printf("%s%s", indent, name);
            printf(":%*s 0x%08"PRIx32,
                   MAX(0, padding - count + (int)sizeof(indent) - 1), "", encoding);
            if (mod >= _XKB_MOD_INDEX_NUM_ENTRIES) {
                printf(" # ");
                if (encoding) {
                    if (!(encoding & MOD_REAL_MASK_ALL)) {
                        /* Prevent printing the numeric form again */
                        if (encoding == (UINT32_C(1) << mod))
                            printf("Canonical virtual modifier");
                        else
                            printf("Non-canonical virtual modifier");
                    } else {
                        print_mod_mask(keymap, MOD_REAL, encoding);
                    }
                    if (encoding & ~MOD_REAL_MASK_ALL)
                        printf(" (incompatible with X11)");
                } else {
                    printf("(unmapped)");
                }
            }
        }
        printf("\n");
    }
    
    /* Key modifier maps, formatted as YAML */
    void
    print_keys_modmaps(struct xkb_keymap *keymap) {
        printf("Keys modifier maps:");
        uint32_t count = 0;
        const struct xkb_key *key;
        xkb_keys_foreach(key, keymap) {
            if (!key->modmap && !key->vmodmap)
                continue;
            print_keycode(keymap, "\n  ", key->keycode, ":");
            printf("\n    real:    ");
            print_mod_mask(keymap, MOD_REAL, key->modmap);
            printf("\n    virtual: ");
            print_mod_mask(keymap, MOD_BOTH, key->vmodmap);
            count++;
        }
        if (count == 0)
            printf(" {} # No modifier map");
        printf("\n");
    }
    
    void
    tools_print_keycode_state(const char *prefix,
                              struct xkb_state *state,
                              struct xkb_compose_state *compose_state,
                              xkb_keycode_t keycode,
                              enum xkb_consumed_mode consumed_mode,
                              print_state_fields_mask_t fields)
    {
        struct xkb_keymap *keymap;
    
        xkb_keysym_t sym;
        const xkb_keysym_t *syms;
        int nsyms;
    #define BUFFER_SIZE MAX(XKB_COMPOSE_MAX_STRING_SIZE, XKB_KEYSYM_NAME_MAX_SIZE)
        assert(XKB_KEYSYM_UTF8_MAX_SIZE <= BUFFER_SIZE);
        char s[BUFFER_SIZE];
    #undef BUFFER_SIZE
        xkb_layout_index_t layout;
        enum xkb_compose_status status;
    
        keymap = xkb_state_get_keymap(state);
    
        nsyms = xkb_state_key_get_syms(state, keycode, &syms);
    
        if (nsyms <= 0)
            return;
    
        status = XKB_COMPOSE_NOTHING;
        if (compose_state)
            status = xkb_compose_state_get_status(compose_state);
    
        if (status == XKB_COMPOSE_COMPOSING || status == XKB_COMPOSE_CANCELLED)
            return;
    
        if (status == XKB_COMPOSE_COMPOSED) {
            sym = xkb_compose_state_get_one_sym(compose_state);
            syms = &sym;
            nsyms = 1;
        }
        else if (nsyms == 1) {
            sym = xkb_state_key_get_one_sym(state, keycode);
            syms = &sym;
        }
    
        if (prefix)
            printf("%s", prefix);
    
        print_keycode(keymap, "keycode [ ", keycode, " ] ");
    
        printf("keysyms [ ");
        for (int i = 0; i < nsyms; i++) {
            xkb_keysym_get_name(syms[i], s, sizeof(s));
            printf("%-*s ", XKB_KEYSYM_NAME_MAX_SIZE, s);
        }
        printf("] ");
    
        if (fields & PRINT_UNICODE) {
            if (status == XKB_COMPOSE_COMPOSED)
                xkb_compose_state_get_utf8(compose_state, s, sizeof(s));
            else
                xkb_state_key_get_utf8(state, keycode, s, sizeof(s));
            /* HACK: escape single control characters from C0 set using the
            * Unicode codepoint convention. Ideally we would like to escape
            * any non-printable character in the string.
            */
            if (!*s) {
                printf("unicode [   ] ");
            } else if (strlen(s) == 1 && (*s <= 0x1F || *s == 0x7F)) {
                printf("unicode [ U+%04hX ] ", *s);
            } else {
                printf("unicode [ %s ] ", s);
            }
        }
    
        layout = xkb_state_key_get_layout(state, keycode);
        if (fields & PRINT_LAYOUT) {
            printf("layout [ %s (%"PRIu32") ] ",
                   xkb_keymap_layout_get_name(keymap, layout), layout);
        }
    
        printf("level [ %"PRIu32" ] ",
               xkb_state_key_get_level(state, keycode, layout));
    
        printf("mods [ ");
        for (xkb_mod_index_t mod = 0; mod < xkb_keymap_num_mods(keymap); mod++) {
            if (xkb_state_mod_index_is_active(state, mod,
                                              XKB_STATE_MODS_EFFECTIVE) <= 0)
                continue;
            if (xkb_state_mod_index_is_consumed2(state, keycode, mod,
                                                 consumed_mode))
                printf("-%s ", xkb_keymap_mod_get_name(keymap, mod));
            else
                printf("%s ", xkb_keymap_mod_get_name(keymap, mod));
        }
        printf("] ");
    
        printf("leds [ ");
        for (xkb_led_index_t led = 0; led < xkb_keymap_num_leds(keymap); led++) {
            if (xkb_state_led_index_is_active(state, led) <= 0)
                continue;
            printf("%s ", xkb_keymap_led_get_name(keymap, led));
        }
        printf("] ");
    
        printf("\n");
    }
    
    void
    tools_print_state_changes(enum xkb_state_component changed)
    {
        if (changed == 0)
            return;
    
        printf("changed [ ");
        if (changed & XKB_STATE_LAYOUT_EFFECTIVE)
            printf("effective-layout ");
        if (changed & XKB_STATE_LAYOUT_DEPRESSED)
            printf("depressed-layout ");
        if (changed & XKB_STATE_LAYOUT_LATCHED)
            printf("latched-layout ");
        if (changed & XKB_STATE_LAYOUT_LOCKED)
            printf("locked-layout ");
        if (changed & XKB_STATE_MODS_EFFECTIVE)
            printf("effective-mods ");
        if (changed & XKB_STATE_MODS_DEPRESSED)
            printf("depressed-mods ");
        if (changed & XKB_STATE_MODS_LATCHED)
            printf("latched-mods ");
        if (changed & XKB_STATE_MODS_LOCKED)
            printf("locked-mods ");
        if (changed & XKB_STATE_LEDS)
            printf("leds ");
        printf("]\n");
    }
    
    #ifdef _WIN32
    void
    tools_disable_stdin_echo(void)
    {
        HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
        DWORD mode = 0;
        GetConsoleMode(stdin_handle, &mode);
        SetConsoleMode(stdin_handle, mode & ~ENABLE_ECHO_INPUT);
    }
    
    void
    tools_enable_stdin_echo(void)
    {
        HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
        DWORD mode = 0;
        GetConsoleMode(stdin_handle, &mode);
        SetConsoleMode(stdin_handle, mode | ENABLE_ECHO_INPUT);
    }
    #else
    void
    tools_disable_stdin_echo(void)
    {
        /* Same as `stty -echo`. */
        struct termios termios;
        if (tcgetattr(STDIN_FILENO, &termios) == 0) {
            termios.c_lflag &= ~ECHO;
            (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &termios);
        }
    }
    
    void
    tools_enable_stdin_echo(void)
    {
        /* Same as `stty echo`. */
        struct termios termios;
        if (tcgetattr(STDIN_FILENO, &termios) == 0) {
            termios.c_lflag |= ECHO;
            (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &termios);
        }
    }
    
    #endif
    
    int
    tools_exec_command(const char *prefix, int real_argc, char **real_argv)
    {
        char *argv[64] = {NULL};
        char executable[PATH_MAX];
        const char *command;
        int rc;
    
        if (((size_t)real_argc >= ARRAY_SIZE(argv))) {
            fprintf(stderr, "Too many arguments\n");
            return EXIT_INVALID_USAGE;
        }
    
        command = real_argv[0];
    
        rc = snprintf(executable, sizeof(executable),
                      "%s/%s-%s", LIBXKBCOMMON_TOOL_PATH, prefix, command);
        if (rc < 0 || (size_t) rc >= sizeof(executable)) {
            fprintf(stderr, "Failed to assemble command\n");
            return EXIT_FAILURE;
        }
    
        argv[0] = executable;
        for (int i = 1; i < real_argc; i++)
            argv[i] = real_argv[i];
    
        execv(executable, argv);
        if (errno == ENOENT) {
            fprintf(stderr, "Command '%s' is not available\n", command);
            return EXIT_INVALID_USAGE;
        } else {
            fprintf(stderr, "Failed to execute '%s' (%s)\n",
                    command, strerror(errno));
        }
    
        return EXIT_FAILURE;
    }
    
    bool
    is_pipe_or_regular_file(int fd)
    {
        struct stat info;
        if (fstat(fd, &info) == 0) {
            return S_ISFIFO(info.st_mode) || S_ISREG(info.st_mode);
        } else {
            return false;
        }
    }
    
    FILE*
    tools_read_stdin(void)
    {
        FILE *file = tmpfile();
        if (!file) {
            fprintf(stderr, "Failed to create tmpfile\n");
            return NULL;
        }
    
        while (true) {
            char buf[4096] = {0};
            const size_t len = fread(buf, 1, sizeof(buf), stdin);
            if (ferror(stdin)) {
                fprintf(stderr, "Failed to read from stdin\n");
                goto err;
            }
            if (len > 0) {
                size_t wlen = fwrite(buf, 1, len, file);
                if (wlen != len) {
                    fprintf(stderr, "Failed to write to tmpfile\n");
                    goto err;
                }
            }
            if (feof(stdin))
                break;
        }
        fseek(file, 0, SEEK_SET);
        return file;
    err:
        fclose(file);
        return NULL;
    }