[systemd-devel] [RFC 09/12] gfx: add kbd test

David Herrmann dh.herrmann at gmail.com
Wed Nov 27 10:48:44 PST 2013


The test-kbd helper simply attaches to the current session (or in case
--all is passed to the system) and prints all keyboard events generated by
attached keyboards.

It's meant to be used to debug sd_gfx_kbd/monitor issues regarding
keymap/kbd setup.
---
 .gitignore                    |   1 +
 Makefile.am                   |  16 +++
 src/libsystemd-gfx/test-kbd.c | 314 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 331 insertions(+)
 create mode 100644 src/libsystemd-gfx/test-kbd.c

diff --git a/.gitignore b/.gitignore
index f8f6c8a..f4921f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -131,6 +131,7 @@
 /test-journal-init
 /test-journal-syslog
 /test-journal-verify
+/test-kbd
 /test-libudev
 /test-list
 /test-log
diff --git a/Makefile.am b/Makefile.am
index 189cc89..e8822b2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3878,6 +3878,22 @@ libsystemd_gfx_la_LIBADD = \
 	libudev-internal.la \
 	src/libsystemd-gfx/unifont.bin.lo
 
+test_kbd_SOURCES = \
+	src/libsystemd-gfx/test-kbd.c
+
+test_kbd_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(GFX_CFLAGS)
+
+test_kbd_LDADD = \
+	$(GFX_LIBS) \
+	libsystemd-bus-internal.la \
+	libsystemd-shared.la \
+	libsystemd-gfx.la
+
+tests += \
+	test-kbd
+
 src/libsystemd-gfx/unifont.bin: make-unifont.py src/libsystemd-gfx/unifont.hex
 	$(AM_V_GEN)cat $(top_srcdir)/src/libsystemd-gfx/unifont.hex | $(PYTHON) $< >$@
 
diff --git a/src/libsystemd-gfx/test-kbd.c b/src/libsystemd-gfx/test-kbd.c
new file mode 100644
index 0000000..b902171
--- /dev/null
+++ b/src/libsystemd-gfx/test-kbd.c
@@ -0,0 +1,314 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 David Herrmann <dh.herrmann at gmail.com>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libevdev/libevdev.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <xkbcommon/xkbcommon.h>
+
+#include "build.h"
+#include "def.h"
+#include "log.h"
+#include "macro.h"
+#include "missing.h"
+#include "sd-bus.h"
+#include "sd-daemon.h"
+#include "sd-event.h"
+#include "sd-gfx.h"
+#include "util.h"
+
+typedef struct Test Test;
+
+struct Test {
+        sd_event *event;
+        sd_event_source *sigs[_NSIG];
+        sd_gfx_monitor *mon;
+};
+
+static bool arg_all;
+static const char *arg_gpus;
+static const char *arg_keymap;
+
+static void test_kbd_fn(sd_gfx_kbd *kbd, void *fn_data, sd_gfx_kbd_event *ev) {
+        unsigned int i;
+        const char *t;
+        char s[128];
+        uint32_t c;
+
+        printf(" %5d", ev->keycode);
+        t = libevdev_event_code_get_name(EV_KEY, ev->keycode);
+        printf(" | %20s", t ? : "<unknown>");
+
+        printf(" | %s", (ev->mods & SD_GFX_SHIFT) ? "SHIFT" : "     ");
+        printf(" | %s", (ev->mods & SD_GFX_CAPSL) ? "CAPSL" : "     ");
+        printf(" | %s", (ev->mods & SD_GFX_CTRL) ? "CTRL" : "    ");
+        printf(" | %s", (ev->mods & SD_GFX_ALT) ? "ALT" : "   ");
+        printf(" | %s", (ev->mods & SD_GFX_LOGO) ? "LOGO" : "    ");
+
+        printf(" |");
+        for (i = 0; i < ev->sym_count; ++i) {
+                xkb_keysym_get_name(ev->keysyms[i], s, sizeof(s));
+                printf(" %20s", s);
+        }
+
+        printf(" |");
+        for (i = 0; i < ev->sym_count; ++i) {
+                c = ev->codepoints[i];
+                if (c == 0xffffffff)
+                        continue;
+                if (c < 0x1f)
+                        continue;
+                if (c >= 0x7f && c <= 0x9f)
+                        continue;
+
+                /* Just a proof-of-concept hack. This works because glibc uses
+                 * UCS-4 as the internal wchar_t encoding. */
+                printf(" %lc", ev->codepoints[i]);
+        }
+
+        printf("\n");
+}
+
+static void test_add(Test *t, sd_gfx_kbd *kbd) {
+        sd_gfx_kbd_set_event_fn(kbd, test_kbd_fn);
+}
+
+static void test_remove(Test *t, sd_gfx_kbd *kbd) {
+        sd_gfx_kbd_set_event_fn(kbd, NULL);
+}
+
+static void test_event_fn(sd_gfx_monitor *mon, void *fn_data, sd_gfx_monitor_event *ev) {
+        struct Test *t = fn_data;
+
+        switch (ev->type) {
+        case SD_GFX_MONITOR_CREATE:
+                if (ev->devtype == SD_GFX_DEV_KBD)
+                        test_add(t, ev->kbd);
+                else
+                        log_notice("unknown device of type %d", ev->devtype);
+                break;
+        case SD_GFX_MONITOR_DESTROY:
+                if (ev->devtype == SD_GFX_DEV_KBD)
+                        test_remove(t, ev->kbd);
+                break;
+        default:
+                log_warning("unhandled monitor event: %d", ev->type);
+                break;
+        }
+}
+
+static int test_signal_fn(sd_event_source *s, const struct signalfd_siginfo *ssi, void *data) {
+        Test *t = data;
+
+        log_notice("catched signal %d, exiting..", (int)ssi->ssi_signo);
+        sd_event_request_quit(t->event);
+
+        return 0;
+}
+
+static int test_new(Test **out) {
+        static const int sigs[] = {
+                SIGINT, SIGTERM, SIGQUIT, SIGHUP, SIGPIPE, 0
+        };
+        unsigned int flags;
+        sigset_t mask;
+        Test *t;
+        int r, i;
+
+        t = calloc(1, sizeof(*t));
+        if (!t)
+                return log_oom();
+
+        r = sd_event_default(&t->event);
+        if (r < 0) {
+                log_error("cannot get default event-loop: %d", r);
+                goto err_t;
+        }
+
+        sigemptyset(&mask);
+        for (i = 0; sigs[i]; ++i) {
+                sigaddset(&mask, sigs[i]);
+                r = sd_event_add_signal(t->event,
+                                        sigs[i],
+                                        test_signal_fn,
+                                        t,
+                                        &t->sigs[i]);
+                if (r < 0) {
+                        log_error("cannot block signal %d: %d",
+                                  sigs[i], r);
+                        goto err_sigs;
+                }
+        }
+        sigprocmask(SIG_BLOCK, &mask, NULL);
+
+        flags = SD_GFX_MONITOR_DEFAULT;
+        if (arg_all)
+                flags |= SD_GFX_MONITOR_IGNORE_SEATS;
+
+        r = sd_gfx_monitor_new(&t->mon,
+                               SD_GFX_DEV_EVDEV_MASK,
+                               flags,
+                               t->event);
+        if (r < 0)
+                goto err_sigs;
+
+        sd_gfx_monitor_set_fn_data(t->mon, t);
+        sd_gfx_monitor_set_event_fn(t->mon, test_event_fn);
+        sd_gfx_monitor_parse_cmdline(t->mon);
+        if (arg_gpus)
+                sd_gfx_monitor_set_gpus(t->mon, arg_gpus);
+        if (arg_keymap)
+                sd_gfx_monitor_set_keymap(t->mon, arg_keymap);
+
+        *out = t;
+        return 0;
+
+err_sigs:
+        for (i = 0; t->sigs[i]; ++i)
+                sd_event_source_unref(t->sigs[i]);
+        sd_event_unref(t->event);
+err_t:
+        free(t);
+        return r;
+}
+
+static void test_free(Test *t) {
+        int i;
+
+        if (!t)
+                return;
+
+        sd_gfx_monitor_free(t->mon);
+        for (i = 0; t->sigs[i]; ++i)
+                sd_event_source_unref(t->sigs[i]);
+        sd_event_unref(t->event);
+        free(t);
+}
+
+static int test_run(Test *t) {
+        return sd_event_loop(t->event);
+}
+
+static int help(void) {
+        printf("%s [OPTIONS...] ...\n\n"
+               "sd-gfx keyboard test.\n\n"
+               "  -h --help           Show this help\n"
+               "     --version        Show package version\n"
+               "     --all            Use all devices\n"
+               "     --gpus=MATCH     List of GPUs to use\n"
+               "     --keymap=LMVO    XKB keymap to use\n"
+               , program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_ALL,
+                ARG_GPUS,
+                ARG_KEYMAP,
+        };
+        static const struct option options[] = {
+                { "help",    no_argument,       NULL, 'h'         },
+                { "version", no_argument,       NULL, ARG_VERSION },
+                { "all",     no_argument,       NULL, ARG_ALL     },
+                { "gpus",    required_argument, NULL, ARG_GPUS    },
+                { "keymap",  required_argument, NULL, ARG_KEYMAP  },
+                {}
+        };
+        int c;
+
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+                switch(c) {
+                case 'h':
+                        return help();
+                case ARG_VERSION:
+                        puts(PACKAGE_STRING);
+                        puts(SYSTEMD_FEATURES);
+                        return 0;
+                case ARG_ALL:
+                        arg_all = true;
+                        break;
+                case ARG_GPUS:
+                        arg_gpus = optarg;
+                        break;
+                case ARG_KEYMAP:
+                        arg_keymap = optarg;
+                        break;
+                case '?':
+                        return -EINVAL;
+                default:
+                        assert_not_reached("Unhandled option");
+                }
+        }
+
+        if (optind < argc) {
+                log_error("This program does not take arguments.");
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        Test *t = NULL;
+        int r;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+        setlocale(LC_ALL, "");
+
+        r = parse_argv(argc, argv);
+        if (r < 0)
+                return EXIT_FAILURE;
+        if (r == 0)
+                return EXIT_SUCCESS;
+
+        r = test_new(&t);
+        if (r < 0)
+                goto finish;
+
+        system("stty -echo");
+        r = test_run(t);
+        system("stty echo");
+
+finish:
+        if (t)
+                test_free(t);
+
+        log_debug("exiting..");
+        return abs(r);
+}
-- 
1.8.4.2



More information about the systemd-devel mailing list