From 3444b29ceb47694fc3a1d0e3c3588376cd79354c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Jan 2014 11:25:40 +1000 Subject: [PATCH] tools: add a tool for basic event debugging MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Simply prints the various events to make it easier to check what's coming out of libinput. Works for --udev (the default) or for --device /dev/input/event0. Example output: event7 DEVICE_ADDED seat0 default event8 DEVICE_ADDED seat0 default event4 POINTER_BUTTON +1.35s 272 pressed event5 POINTER_MOTION +2.31s -3.00/ 2.00 Time is displayed relative to the starting time. Note: statically linked for easier debugging, but we don't distribute it (yet) anyway. Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- Makefile.am | 2 +- configure.ac | 3 +- tools/.gitignore | 1 + tools/Makefile.am | 7 + tools/event-debug.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 466 insertions(+), 2 deletions(-) create mode 100644 tools/.gitignore create mode 100644 tools/Makefile.am create mode 100644 tools/event-debug.c diff --git a/Makefile.am b/Makefile.am index 07bfcd4..08bf7ce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = src doc test +SUBDIRS = src doc test tools ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} diff --git a/configure.ac b/configure.ac index 7281bb4..44729a9 100644 --- a/configure.ac +++ b/configure.ac @@ -87,5 +87,6 @@ AC_CONFIG_FILES([Makefile src/Makefile src/libinput.pc src/libinput-version.h - test/Makefile]) + test/Makefile + tools/Makefile]) AC_OUTPUT diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..2cdd654 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1 @@ +event-debug diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..9c29f56 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,7 @@ +noinst_PROGRAMS = event-debug + +AM_CPPFLAGS = -I$(top_srcdir)/src + +event_debug_SOURCES = event-debug.c +event_debug_LDADD = ../src/libinput.la +event_debug_LDFLAGS = -static diff --git a/tools/event-debug.c b/tools/event-debug.c new file mode 100644 index 0000000..1024667 --- /dev/null +++ b/tools/event-debug.c @@ -0,0 +1,455 @@ +/* + * Copyright © 2014 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static enum { + MODE_UDEV, + MODE_DEVICE, +} mode = MODE_UDEV; +static const char *device; +static const char *seat = "seat0"; +static struct udev *udev; +uint32_t start_time; + +static void +usage(void) +{ + printf("Usage: %s [--udev []|--device /dev/input/event0]\n" + "--udev .... Use udev device discovery (default).\n" + " Specifying a seat ID is optional.\n" + "--device /path/to/device .... open the given device only\n", + program_invocation_short_name); +} + +static int +parse_args(int argc, char **argv) +{ + while (1) { + int c; + int option_index = 0; + static struct option opts[] = { + { "device", 1, 0, 'd' }, + { "udev", 0, 0, 'u' }, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "h", opts, &option_index); + if (c == -1) + break; + + switch(c) { + case 'h': /* --help */ + usage(); + exit(0); + case 'd': /* --device */ + mode = MODE_DEVICE; + if (!optarg) { + usage(); + return 1; + } + device = optarg; + break; + case 'u': /* --udev */ + mode = MODE_UDEV; + if (optarg) + seat = optarg; + break; + default: + usage(); + return 1; + } + + } + + if (optind < argc) { + usage(); + return 1; + } + + return 0; +} + +static int +open_restricted(const char *path, int flags, void *user_data) +{ + int fd = open(path, flags); + int clockid = CLOCK_MONOTONIC; + + if (fd >= 0 && ioctl(fd, EVIOCSCLOCKID, &clockid) < 0) + fprintf(stderr, "Changing clock on %s failed, timestamps " + "will be off\n", path); + + return fd < 0 ? -errno : fd; +} + +static void +close_restricted(int fd, void *user_data) +{ + close(fd); +} + +static void get_current_screen_dimensions(struct libinput_device *device, + int *width, + int *height, + void *user_data) +{ + /* display absdata in % of the screen */ + *width = 100; + *height = 100; +} + +const static struct libinput_interface interface = { + .open_restricted = open_restricted, + .close_restricted = close_restricted, + .get_current_screen_dimensions = get_current_screen_dimensions, +}; + +static int +open_udev(struct libinput **li) +{ + udev = udev_new(); + if (!udev) { + fprintf(stderr, "Failed to initialize udev\n"); + return 1; + } + + *li = libinput_create_from_udev(&interface, NULL, udev, seat); + if (!*li) { + fprintf(stderr, "Failed to initialize context from udev\n"); + return 1; + } + + return 0; +} + +static int +open_device(struct libinput **li, const char *path) +{ + *li = libinput_create_from_path(&interface, NULL, path); + if (!*li) { + fprintf(stderr, "Failed to initialize context from %s\n", path); + return 1; + } + return 0; +} + +static void +print_event_header(struct libinput_event *ev) +{ + struct libinput_device *dev = libinput_event_get_device(ev); + const char *type; + + switch(libinput_event_get_type(ev)) { + case LIBINPUT_EVENT_NONE: + abort(); + case LIBINPUT_EVENT_DEVICE_ADDED: + type = "DEVICE_ADDED"; + break; + case LIBINPUT_EVENT_DEVICE_REMOVED: + type = "DEVICE_REMOVED"; + break; + case LIBINPUT_EVENT_KEYBOARD_KEY: + type = "KEYBOARD_KEY"; + break; + case LIBINPUT_EVENT_POINTER_MOTION: + type = "POINTER_MOTION"; + break; + case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: + type = "POINTER_MOTION_ABSOLUTE"; + break; + case LIBINPUT_EVENT_POINTER_BUTTON: + type = "POINTER_BUTTON"; + break; + case LIBINPUT_EVENT_POINTER_AXIS: + type = "POINTER_AXIS"; + break; + case LIBINPUT_EVENT_TOUCH_TOUCH: + type = "TOUCH_TOUCH"; + break; + case LIBINPUT_EVENT_TOUCH_FRAME: + type = "TOUCH_FRAME"; + break; + } + + printf("%-7s %s ", libinput_device_get_sysname(dev), type); +} + +static void +print_event_time(uint32_t time) +{ + printf("%+6.2fs ", (time - start_time) / 1000.0); +} + +static void +print_device_notify(struct libinput_event *ev) +{ + struct libinput_device *dev = libinput_event_get_device(ev); + struct libinput_seat *seat = libinput_device_get_seat(dev); + + printf("%s %s\n", + libinput_seat_get_physical_name(seat), + libinput_seat_get_logical_name(seat)); +} + +static void +print_key_event(struct libinput_event *ev) +{ + struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev); + enum libinput_keyboard_key_state state; + + print_event_time(libinput_event_keyboard_get_time(k)); + state = libinput_event_keyboard_get_key_state(k); + printf("%d %s\n", + libinput_event_keyboard_get_key(k), + state == LIBINPUT_KEYBOARD_KEY_STATE_PRESSED ? "pressed" : "released"); +} + +static void +print_motion_event(struct libinput_event *ev) +{ + struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev); + li_fixed_t x = libinput_event_pointer_get_dx(p), + y = libinput_event_pointer_get_dy(p); + + print_event_time(libinput_event_pointer_get_time(p)); + + printf("%6.2f/%6.2f\n", + li_fixed_to_double(x), + li_fixed_to_double(y)); +} + +static void +print_absmotion_event(struct libinput_event *ev) +{ + struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev); + li_fixed_t x = libinput_event_pointer_get_absolute_x(p), + y = libinput_event_pointer_get_absolute_y(p); + + print_event_time(libinput_event_pointer_get_time(p)); + printf("%6.2f/%6.2f\n", + li_fixed_to_double(x), + li_fixed_to_double(y)); +} + +static void +print_button_event(struct libinput_event *ev) +{ + struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev); + enum libinput_pointer_button_state state; + + print_event_time(libinput_event_pointer_get_time(p)); + + state = libinput_event_pointer_get_button_state(p); + printf("%3d %s\n", + libinput_event_pointer_get_button(p), + state == LIBINPUT_POINTER_BUTTON_STATE_PRESSED ? "pressed" : "released"); +} + +static void +print_axis_event(struct libinput_event *ev) +{ + struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev); + enum libinput_pointer_axis axis = libinput_event_pointer_get_axis(p); + const char *ax; + li_fixed_t val; + + switch (axis) { + case LIBINPUT_POINTER_AXIS_VERTICAL_SCROLL: + ax = "vscroll"; + break; + case LIBINPUT_POINTER_AXIS_HORIZONTAL_SCROLL: + ax = "hscroll"; + break; + default: + abort(); + } + + print_event_time(libinput_event_pointer_get_time(p)); + val = libinput_event_pointer_get_axis_value(p); + printf("%s %.2f\n", + ax, li_fixed_to_double(val)); +} + +static void +print_touch_frame_event(struct libinput_event *ev) +{ + struct libinput_event_touch *t = libinput_event_get_touch_event(ev); + + print_event_time(libinput_event_touch_get_time(t)); + printf("\n"); +} + +static void +print_touch_event(struct libinput_event *ev) +{ + struct libinput_event_touch *t = libinput_event_get_touch_event(ev); + li_fixed_t x = libinput_event_touch_get_x(t), + y = libinput_event_touch_get_y(t); + const char *type; + + switch (libinput_event_touch_get_touch_type(t)) { + case LIBINPUT_TOUCH_TYPE_DOWN: type = "down"; break; + case LIBINPUT_TOUCH_TYPE_UP: type = "up"; break; + case LIBINPUT_TOUCH_TYPE_MOTION: type = "motion"; break; + case LIBINPUT_TOUCH_TYPE_CANCEL: type = "cancel"; break; + default: + abort(); + } + + print_event_time(libinput_event_touch_get_time(t)); + + printf("%6s %u %5.2f/%5.2f\n", + type, + libinput_event_touch_get_slot(t), + li_fixed_to_double(x), + li_fixed_to_double(y)); +} + +static int +handle_and_print_events(struct libinput *li) +{ + int rc = -1; + struct libinput_event *ev; + + libinput_dispatch(li); + while ((ev = libinput_get_event(li))) { + print_event_header(ev); + + switch (libinput_event_get_type(ev)) { + case LIBINPUT_EVENT_NONE: + abort(); + case LIBINPUT_EVENT_DEVICE_ADDED: + case LIBINPUT_EVENT_DEVICE_REMOVED: + print_device_notify(ev); + break; + case LIBINPUT_EVENT_KEYBOARD_KEY: + print_key_event(ev); + break; + case LIBINPUT_EVENT_POINTER_MOTION: + print_motion_event(ev); + break; + case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: + print_absmotion_event(ev); + break; + case LIBINPUT_EVENT_POINTER_BUTTON: + print_button_event(ev); + break; + case LIBINPUT_EVENT_POINTER_AXIS: + print_axis_event(ev); + break; + case LIBINPUT_EVENT_TOUCH_TOUCH: + print_touch_event(ev); + break; + case LIBINPUT_EVENT_TOUCH_FRAME: + print_touch_frame_event(ev); + break; + } + + libinput_event_destroy(ev); + libinput_dispatch(li); + rc = 0; + } + return rc; +} + +void +mainloop(struct libinput *li) +{ + struct pollfd fds[2]; + sigset_t mask; + + fds[0].fd = libinput_get_fd(li); + fds[0].events = POLLIN; + fds[0].revents = 0; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + + fds[1].fd = signalfd(-1, &mask, SFD_NONBLOCK); + fds[1].events = POLLIN; + fds[1].revents = 0; + + if (fds[1].fd == -1 || + sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { + fprintf(stderr, "Failed to set up signal handling (%s)\n", + strerror(errno)); + } + + /* Handle already-pending device added events */ + if (handle_and_print_events(li)) + fprintf(stderr, "Expected device added events on startup but got none. " + "Maybe you don't have the right permissions?\n"); + + while (poll(fds, 2, -1) > -1) { + if (fds[1].revents) + break; + + handle_and_print_events(li); + } + + close(fds[1].fd); +} + +int +main(int argc, char **argv) +{ + struct libinput *li; + struct timespec tp; + + if (parse_args(argc, argv)) + return 1; + + if (mode == MODE_UDEV) { + if (open_udev(&li)) + return 1; + } else if (mode == MODE_DEVICE) { + if (open_device(&li, device)) + return 1; + } else + abort(); + + clock_gettime(CLOCK_MONOTONIC, &tp); + start_time = tp.tv_sec * 1000 + tp.tv_nsec / 1000000; + + mainloop(li); + + if (udev) + udev_unref(udev); + + return 0; +} -- 2.7.4