Commit 0e3e2d17302e99e68bdab3b5db0a1dcc835e5bee

Pierre Le Marre 2023-09-18T12:17:11

interactive-evdev: add option to print modmaps Add an option to print modmap and vmodmap of relevant keys, as well as virtual modifiers mapping to real modifier. This is useful for debugging. It uses private API, so we compile it separately in the fashion of `xkbcli-compile-keymap/compile-keymap`.

diff --git a/meson.build b/meson.build
index 7b941d0..0d6c8ce 100644
--- a/meson.build
+++ b/meson.build
@@ -416,10 +416,13 @@ man_pages = []
 # Tools
 build_tools = get_option('enable-tools') and cc.has_header_symbol('getopt.h', 'getopt_long', prefix: '#define _GNU_SOURCE')
 if build_tools
-    libxkbcommon_tools_internal = static_library(
-        'tools-internal',
+    libxkbcommon_tools_internal_sources = [
         'tools/tools-common.h',
         'tools/tools-common.c',
+    ]
+    libxkbcommon_tools_internal = static_library(
+        'tools-internal',
+        libxkbcommon_tools_internal_sources,
         dependencies: dep_libxkbcommon,
     )
     tools_dep = declare_dependency(
@@ -467,6 +470,15 @@ if build_tools
                    install_dir: dir_libexec)
         configh_data.set10('HAVE_XKBCLI_INTERACTIVE_EVDEV', true)
         install_man('tools/xkbcli-interactive-evdev.1')
+        # The same tool again, but with access to some private APIs.
+        executable('interactive-evdev',
+                'tools/interactive-evdev.c',
+                libxkbcommon_sources,
+                libxkbcommon_tools_internal_sources,
+                dependencies: [tools_dep],
+                c_args: ['-DENABLE_PRIVATE_APIS'],
+                include_directories: [include_directories('src', 'include')],
+                install: false)
     endif
     if get_option('enable-x11')
         x11_tools_dep = declare_dependency(
diff --git a/tools/interactive-evdev.c b/tools/interactive-evdev.c
index 2f03975..8da1056 100644
--- a/tools/interactive-evdev.c
+++ b/tools/interactive-evdev.c
@@ -58,7 +58,12 @@ static int evdev_offset = 8;
 static bool report_state_changes;
 static bool with_compose;
 static enum xkb_consumed_mode consumed_mode = XKB_CONSUMED_MODE_XKB;
+
+#ifdef ENABLE_PRIVATE_APIS
+#define DEFAULT_PRINT_FIELDS (PRINT_ALL_FIELDS & ~PRINT_MODMAPS)
+#else
 #define DEFAULT_PRINT_FIELDS PRINT_ALL_FIELDS
+#endif
 print_state_fields_mask_t print_fields = DEFAULT_PRINT_FIELDS;
 
 #define DEFAULT_INCLUDE_PATH_PLACEHOLDER "__defaults__"
@@ -378,6 +383,9 @@ usage(FILE *fp, char *progname)
         fprintf(fp, "      or: %s --keymap <path to keymap file>\n",
                 progname);
         fprintf(fp, "For both:\n"
+#ifdef ENABLE_PRIVATE_APIS
+                        "          --print-modmaps (print real & virtual key modmaps)\n"
+#endif
                         "          --short (do not print layout nor Unicode keysym translation)\n"
                         "          --report-state-changes (report changes to the state)\n"
                         "          --enable-compose (enable Compose)\n"
@@ -418,6 +426,9 @@ main(int argc, char *argv[])
         OPT_COMPOSE,
         OPT_SHORT,
         OPT_REPORT_STATE,
+#ifdef ENABLE_PRIVATE_APIS
+        OPT_PRINT_MODMAPS,
+#endif
     };
     static struct option opts[] = {
         {"help",                 no_argument,            0, 'h'},
@@ -434,6 +445,9 @@ main(int argc, char *argv[])
         {"short",                no_argument,            0, OPT_SHORT},
         {"report-state-changes", no_argument,            0, OPT_REPORT_STATE},
         {"without-x11-offset",   no_argument,            0, OPT_WITHOUT_X11_OFFSET},
+#ifdef ENABLE_PRIVATE_APIS
+        {"print-modmaps",        no_argument,            0, OPT_PRINT_MODMAPS},
+#endif
         {0, 0, 0, 0},
     };
 
@@ -503,6 +517,11 @@ main(int argc, char *argv[])
                 return EXIT_INVALID_USAGE;
             }
             break;
+#ifdef ENABLE_PRIVATE_APIS
+        case OPT_PRINT_MODMAPS:
+            print_fields |= PRINT_MODMAPS;
+            break;
+#endif
         case 'h':
             usage(stdout, argv[0]);
             return EXIT_SUCCESS;
@@ -584,6 +603,15 @@ main(int argc, char *argv[])
         goto out;
     }
 
+#ifdef ENABLE_PRIVATE_APIS
+    if (print_fields & PRINT_MODMAPS) {
+        print_keys_modmaps(keymap);
+        putchar('\n');
+        print_keymap_modmaps(keymap);
+        putchar('\n');
+    }
+#endif
+
     act.sa_handler = sigintr_handler;
     sigemptyset(&act.sa_mask);
     act.sa_flags = 0;
diff --git a/tools/tools-common.c b/tools/tools-common.c
index 3262f61..d48f74a 100644
--- a/tools/tools-common.c
+++ b/tools/tools-common.c
@@ -61,6 +61,87 @@ print_keycode(struct xkb_keymap *keymap, const char* prefix,
     }
 }
 
+#ifdef ENABLE_PRIVATE_APIS
+#include "src/keymap.h"
+
+void
+print_keymap_modmaps(struct xkb_keymap *keymap) {
+    printf("Modifiers mapping:\n");
+    for (xkb_mod_index_t vmod = 0; vmod < xkb_keymap_num_mods(keymap); vmod++) {
+        if (keymap->mods.mods[vmod].type & MOD_REAL)
+            continue;
+        printf("- %s: ", xkb_keymap_mod_get_name(keymap, vmod));
+        if (keymap->mods.mods[vmod].mapping) {
+            bool first = true;
+            for (xkb_mod_index_t mod = 0; mod < xkb_keymap_num_mods(keymap); mod++) {
+                if (keymap->mods.mods[vmod].mapping & (1u << mod)) {
+                    if (first) {
+                        first = false;
+                        printf("%s", xkb_keymap_mod_get_name(keymap, mod));
+                    } else {
+                        printf("+ %s", xkb_keymap_mod_get_name(keymap, mod));
+                    }
+                }
+            }
+        } else {
+            printf("(unmapped)");
+        }
+        printf("\n");
+    }
+}
+
+#define MODMAP_PADDING  7
+#define VMODMAP_PADDING 9
+static void
+print_key_modmaps(struct xkb_keymap *keymap, xkb_keycode_t keycode) {
+    const struct xkb_key *key = XkbKey(keymap, keycode);
+    if (key != NULL) {
+        xkb_mod_index_t mod;
+
+        printf("modmap [ ");
+        if (key->modmap) {
+            for (mod = 0; mod < xkb_keymap_num_mods(keymap); mod++) {
+                if (key->modmap & (1u << mod)) {
+                    printf("%-*s", (int) MODMAP_PADDING,
+                           xkb_keymap_mod_get_name(keymap, mod));
+                    break;
+                }
+            }
+        } else {
+            printf("%*c", (int) MODMAP_PADDING, ' ');
+        }
+
+        printf(" ] vmodmap [ ");
+        int length = 0;
+        const char *mod_name;
+        for (mod = 0; mod < xkb_keymap_num_mods(keymap); mod++) {
+            if (key->vmodmap & (1u << mod)) {
+                mod_name = xkb_keymap_mod_get_name(keymap, mod);
+                length += strlen(mod_name) + 1;
+                printf("%s ", mod_name);
+            }
+        }
+        if (length < VMODMAP_PADDING) {
+            printf("%*c", (int) VMODMAP_PADDING - length, ' ');
+        }
+        printf("] ");
+    }
+}
+
+void
+print_keys_modmaps(struct xkb_keymap *keymap) {
+    const struct xkb_key *key;
+    printf("Keys modmaps:\n");
+    xkb_keys_foreach(key, keymap) {
+        if (key->modmap || key->vmodmap) {
+            print_keycode(keymap, "- ", key->keycode, ": ");
+            print_key_modmaps(keymap, key->keycode);
+            putchar('\n');
+        }
+    }
+}
+#endif
+
 void
 tools_print_keycode_state(struct xkb_state *state,
                           struct xkb_compose_state *compose_state,
@@ -103,6 +184,12 @@ tools_print_keycode_state(struct xkb_state *state,
 
     print_keycode(keymap, "keycode [ ", keycode, " ] ");
 
+#ifdef ENABLE_PRIVATE_APIS
+    if (fields & PRINT_MODMAPS) {
+        print_key_modmaps(keymap, keycode);
+    }
+#endif
+
     printf("keysyms [ ");
     for (int i = 0; i < nsyms; i++) {
         xkb_keysym_get_name(syms[i], s, sizeof(s));
diff --git a/tools/tools-common.h b/tools/tools-common.h
index 21ba848..cc7771b 100644
--- a/tools/tools-common.h
+++ b/tools/tools-common.h
@@ -38,6 +38,9 @@
 
 /* Fields that are printed in the interactive tools. */
 enum print_state_fields {
+#ifdef ENABLE_PRIVATE_APIS
+    PRINT_MODMAPS = (1u << 1),
+#endif
     PRINT_LAYOUT = (1u << 2),
     PRINT_UNICODE = (1u << 3),
     PRINT_ALL_FIELDS = ((PRINT_UNICODE << 1) - 1),
@@ -50,6 +53,13 @@ enum print_state_fields {
 };
 typedef uint32_t print_state_fields_mask_t;
 
+#ifdef ENABLE_PRIVATE_APIS
+void
+print_keymap_modmaps(struct xkb_keymap *keymap);
+void
+print_keys_modmaps(struct xkb_keymap *keymap);
+#endif
+
 void
 tools_print_keycode_state(struct xkb_state *state,
                           struct xkb_compose_state *compose_state,