summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--meson.build20
-rw-r--r--tools/Makefile.am20
-rw-r--r--tools/libinput-measure-touchpad-tap.173
-rw-r--r--tools/libinput-measure-touchpad-tap.c512
-rw-r--r--tools/libinput-measure.130
-rw-r--r--tools/libinput-measure.c92
-rw-r--r--tools/libinput-tool.c52
-rw-r--r--tools/libinput.16
-rw-r--r--tools/shared.c131
-rw-r--r--tools/shared.h4
10 files changed, 890 insertions, 50 deletions
diff --git a/meson.build b/meson.build
index 3cfdb1d..d86f6c0 100644
--- a/meson.build
+++ b/meson.build
@@ -373,6 +373,26 @@ executable('libinput-list-devices',
)
install_man('tools/libinput-list-devices.1')
+libinput_measure_sources = [ 'tools/libinput-measure.c' ]
+executable('libinput-measure',
+ libinput_measure_sources,
+ dependencies : deps_tools,
+ include_directories : include_directories('src'),
+ install_dir : libinput_tool_path,
+ install : true,
+ )
+install_man('tools/libinput-measure.1')
+
+libinput_measure_touchpad_tap_sources = [ 'tools/libinput-measure-touchpad-tap.c' ]
+executable('libinput-measure-touchpad-tap',
+ libinput_measure_touchpad_tap_sources,
+ dependencies : deps_tools,
+ include_directories : include_directories('src'),
+ install_dir : libinput_tool_path,
+ install : true,
+ )
+install_man('tools/libinput-measure-touchpad-tap.1')
+
if get_option('debug-gui')
dep_gtk = dependency('gtk+-3.0')
dep_cairo = dependency('cairo')
diff --git a/tools/Makefile.am b/tools/Makefile.am
index cc1ce19..8ef2a3b 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -14,7 +14,8 @@ AM_CXXFLAGS = $(GCC_CXXFLAGS)
libshared_la_SOURCES = \
shared.c \
shared.h
-libshared_la_CFLAGS = $(AM_CFLAGS) $(LIBEVDEV_CFLAGS)
+libshared_la_CFLAGS = $(AM_CFLAGS) $(LIBEVDEV_CFLAGS) \
+ -DLIBINPUT_TOOL_PATH="\"@libexecdir@/libinput\""
libshared_la_LIBADD = $(LIBEVDEV_LIBS)
ptraccel_debug_SOURCES = ptraccel-debug.c
@@ -23,8 +24,7 @@ ptraccel_debug_LDFLAGS = -no-install
libinput_SOURCES = libinput-tool.c
libinput_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS)
-libinput_CFLAGS = $(AM_CFLAGS) $(LIBUDEV_CFLAGS) $(LIBEVDEV_CFLAGS) \
- -DLIBINPUT_TOOL_PATH="\"@libexecdir@/libinput\""
+libinput_CFLAGS = $(AM_CFLAGS) $(LIBUDEV_CFLAGS) $(LIBEVDEV_CFLAGS)
dist_man1_MANS = libinput.1
tools_PROGRAMS += libinput-list-devices
@@ -39,6 +39,20 @@ libinput_debug_events_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) $(
libinput_debug_events_CFLAGS = $(AM_CFLAGS) $(LIBUDEV_CFLAGS) $(LIBEVDEV_CFLAGS)
dist_man1_MANS += libinput-debug-events.1
+tools_PROGRAMS += libinput-measure
+libinput_measure_SOURCES = libinput-measure.c $(shared_sources)
+libinput_measure_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS)
+libinput_measure_CFLAGS = $(AM_CFLAGS) $(LIBUDEV_CFLAGS) $(LIBEVDEV_CFLAGS) \
+ -DLIBINPUT_TOOL_PATH="\"@libexecdir@/libinput\""
+dist_man1_MANS += libinput-measure.1
+
+tools_PROGRAMS += libinput-measure-touchpad-tap
+libinput_measure_touchpad_tap_SOURCES = \
+ libinput-measure-touchpad-tap.c
+libinput_measure_touchpad_tap_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS)
+libinput_measure_touchpad_tap_CFLAGS = $(AM_CFLAGS) $(LIBUDEV_CFLAGS) $(LIBEVDEV_CFLAGS)
+dist_man1_MANS += libinput-measure-touchpad-tap.1
+
if BUILD_DEBUG_GUI
tools_PROGRAMS += libinput-debug-gui
libinput_debug_gui_SOURCES = libinput-debug-gui.c
diff --git a/tools/libinput-measure-touchpad-tap.1 b/tools/libinput-measure-touchpad-tap.1
new file mode 100644
index 0000000..3cea584
--- /dev/null
+++ b/tools/libinput-measure-touchpad-tap.1
@@ -0,0 +1,73 @@
+.TH LIBINPUT-MEASURE-TOUCHPAD-TAP "1"
+.SH NAME
+libinput-measure-touchpad-tap \- measure tap-to-click properities of devices
+.SH SYNOPSIS
+.B libinput measure touchpad-tap [--help] [--format=<format>] [/dev/input/event0]
+.SH DESCRIPTION
+.PP
+The
+.B "libinput measure touchpad-tap"
+tool measures properties of the tap-to-click behavior of the user. This is
+an interactive tool. When executed, the tool will prompt the user to
+interact with the touchpad. On termination, the tool prints a summary of the
+tap interactions seen. This data should be attached to any tap-related bug
+report.
+.PP
+For a full description on how libinput's tap-to-click behavior works, see
+the online documentation here:
+.I https://wayland.freedesktop.org/libinput/doc/latest/tapping.html
+.PP
+This is a debugging tool only, its output may change at any time. Do not
+rely on the output.
+.PP
+This tool usually needs to be run as root to have access to the
+/dev/input/eventX nodes.
+.SH OPTIONS
+.TP 8
+.B --help
+Print help
+.TP 8
+.B --format=summary|dat
+Specify the data format to be printed. The default (or if
+.B --format
+is omitted) is "summary". See section
+.B DATA FORMATS
+
+.SH DATA FORMATS
+This section describes the data formats printed with the
+.B --format
+commandline argument. Note that any of the output may change at any time.
+.RE
+.PP
+summary
+.RS 4
+The
+.I summary
+format prints a summary of the data collected. This format is useful to
+get a quick overview of a user's tapping behavior and why some taps may or
+may not be detected.
+.RE
+.PP
+dat
+.RS 4
+The
+.I dat
+format prints the touch sequence data (raw and processed) in column-style
+format, suitable for processing by other tools such as
+.B gnuplot(1).
+The data is aligned in one row per touch with each column containing a
+separate data entry.
+.B libinput-measure-touchpad-tap
+prints comments at the top of the file to describe each column.
+.PP
+.B WARNING:
+The data contained in the output is grouped by different sort orders. For
+example, the first few columns may list tap information in the 'natural'
+sort order (i.e. as they occured), the data in the next few columns may list
+tap information sorted by the delta time between touch down and touch up.
+Comparing columns across these group boundaries will compare data of two
+different touch points and result in invalid analysis.
+.SH LIBINPUT
+Part of the
+.B libinput(1)
+suite
diff --git a/tools/libinput-measure-touchpad-tap.c b/tools/libinput-measure-touchpad-tap.c
new file mode 100644
index 0000000..123630c
--- /dev/null
+++ b/tools/libinput-measure-touchpad-tap.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright © 2017 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/signalfd.h>
+
+#include <libudev.h>
+#include <libevdev/libevdev.h>
+
+#include <libinput-util.h>
+#include <libinput-version.h>
+
+#include "shared.h"
+
+static bool print_dat_file;
+/* interactive goes to stdout unless we're redirected, then it goes to
+ * stderr */
+static FILE *pdest;
+
+#define error(...) fprintf(stderr, __VA_ARGS__)
+#define msg(...) fprintf(pdest, __VA_ARGS__)
+
+struct touch {
+ uint32_t tdown, tup; /* in ms */
+};
+
+struct tap_data {
+ struct touch *touches;
+ size_t touches_sz;
+ unsigned int count;
+
+ uint32_t toffset; /* in ms */
+};
+
+static inline uint32_t
+touch_tdelta_ms(const struct touch *t)
+{
+ return t->tup - t->tdown;
+}
+
+static inline struct tap_data *
+tap_data_new(void)
+{
+ struct tap_data *tap_data = zalloc(sizeof(struct tap_data));
+ assert(tap_data);
+
+ return tap_data;
+}
+
+static inline unsigned int
+tap_data_ntouches(struct tap_data *tap_data)
+{
+ return tap_data->count;
+}
+
+static inline void
+tap_data_free(struct tap_data **tap_data)
+{
+ free((*tap_data)->touches);
+ free((*tap_data));
+ *tap_data = NULL;
+}
+
+static inline struct touch *
+tap_data_get_current_touch(struct tap_data *tap_data)
+{
+ assert(tap_data->count > 0);
+
+ return &tap_data->touches[tap_data->count - 1];
+}
+
+static inline struct touch *
+tap_data_get_touch(struct tap_data *tap_data, unsigned int idx)
+{
+ assert(idx < tap_data->count);
+
+ return &tap_data->touches[idx];
+}
+
+#define tap_data_for_each(tapdata_, t_) \
+ for (unsigned i_ = 0; i_ < (tapdata_)->count && (t_ = &(tapdata_)->touches[i_]); i_++)
+
+static inline struct tap_data *
+tap_data_duplicate_sorted(const struct tap_data *src,
+ int (*cmp)(const void *a, const void *b))
+{
+ struct tap_data *dest= tap_data_new();
+
+ assert(src->count > 0);
+
+ dest->count = src->count;
+ dest->touches_sz = dest->count;
+ dest->touches = zalloc(dest->count * sizeof(*dest->touches));
+ memcpy(dest->touches,
+ src->touches,
+ dest->count * sizeof(*dest->touches));
+
+ if (cmp)
+ qsort(dest->touches,
+ dest->count,
+ sizeof(*dest->touches),
+ cmp);
+
+ return dest;
+}
+
+static inline struct touch*
+tap_data_new_touch(struct tap_data *tap_data)
+{
+ tap_data->count++;
+ if (tap_data->touches_sz < tap_data->count) {
+ tap_data->touches_sz += 50;
+ tap_data->touches = realloc(tap_data->touches,
+ tap_data->touches_sz * sizeof(*tap_data->touches));
+ if (tap_data->touches == NULL) {
+ error("Allocation error. Oops\n");
+ abort();
+ }
+ memset(&tap_data->touches[tap_data->count - 1],
+ 0,
+ sizeof(*tap_data->touches));
+ }
+
+ return &tap_data->touches[tap_data->count - 1];
+}
+
+static int
+sort_by_time_delta(const void *ap, const void *bp)
+{
+ const struct touch *a = ap;
+ const struct touch *b = bp;
+ uint32_t da, db;
+
+ da = touch_tdelta_ms(a);
+ db = touch_tdelta_ms(b);
+
+ return da == db ? 0 : da > db ? 1 : -1;
+}
+
+static inline void
+print_statistics(struct tap_data *tap_data)
+{
+ uint64_t delta_sum = 0;
+ uint32_t average;
+ uint32_t max = 0,
+ min = UINT_MAX;
+ struct tap_data *data_sorted_tdelta;
+ struct touch *t,
+ *median,
+ *pc90,
+ *pc95;
+
+ if (tap_data->count == 0) {
+ error("No tap data available.\n");
+ return;
+ }
+
+ tap_data_for_each(tap_data, t) {
+ uint32_t delta = touch_tdelta_ms(t);
+
+ delta_sum += delta;
+ max = max(delta, max);
+ min = min(delta, min);
+ }
+
+ average = delta_sum/tap_data_ntouches(tap_data);
+
+ printf("Time:\n");
+ printf(" Max delta: %dms\n", max);
+ printf(" Min delta: %dms\n", min);
+ printf(" Average delta: %dms\n", average);
+
+ /* Median, 90th, 95th percentile, requires sorting by time delta */
+ data_sorted_tdelta = tap_data_duplicate_sorted(tap_data,
+ sort_by_time_delta);
+ median = tap_data_get_touch(tap_data,
+ tap_data_ntouches(tap_data)/2);
+ pc90= tap_data_get_touch(tap_data,
+ tap_data_ntouches(tap_data) * 0.9);
+ pc95 = tap_data_get_touch(tap_data,
+ tap_data_ntouches(tap_data) * 0.95);
+ printf(" Median delta: %dms\n", touch_tdelta_ms(median));
+ printf(" 90th percentile: %dms\n", touch_tdelta_ms(pc90));
+ printf(" 95th percentile: %dms\n", touch_tdelta_ms(pc95));
+
+ tap_data_free(&data_sorted_tdelta);
+}
+
+static inline void
+print_dat(struct tap_data *tap_data)
+{
+ unsigned int i;
+ struct touch *t;
+ struct tap_data *sorted;
+
+ printf("# libinput-measure-touchpad-tap (v%s)\n", LIBINPUT_VERSION);
+ printf("# File contents:\n"
+ "# This file contains multiple prints of the data in different\n"
+ "# sort order. Row number is index of touch point within each group.\n"
+ "# Comparing data across groups will result in invalid analysis.\n"
+ "# Columns (1-indexed):\n");
+ printf("# Group 1, sorted by time of occurence\n"
+ "# 1: touch down time in ms, offset by first event\n"
+ "# 2: touch up time in ms, offset by first event\n"
+ "# 3: time delta in ms\n");
+ printf("# Group 2, sorted by touch down-up delta time (ascending)\n"
+ "# 4: touch down time in ms, offset by first event\n"
+ "# 5: touch up time in ms, offset by first event\n"
+ "# 6: time delta in ms\n");
+
+ sorted = tap_data_duplicate_sorted(tap_data, sort_by_time_delta);
+
+ for (i = 0; i < tap_data_ntouches(tap_data); i++) {
+ t = tap_data_get_touch(tap_data, i);
+ printf("%d %d %d ",
+ t->tdown,
+ t->tup,
+ touch_tdelta_ms(t));
+
+ t = tap_data_get_touch(sorted, i);
+ printf("%d %d %d ",
+ t->tdown,
+ t->tup,
+ touch_tdelta_ms(t));
+
+ printf("\n");
+ }
+
+ tap_data_free(&sorted);
+}
+
+static inline void
+handle_btn_touch(struct tap_data *tap_data,
+ struct libevdev *evdev,
+ const struct input_event *ev)
+{
+
+ if (ev->value) {
+ struct touch *new_touch = tap_data_new_touch(tap_data);
+
+ new_touch->tdown = us2ms(tv2us(&ev->time)) - tap_data->toffset;
+ } else {
+ struct touch *current = tap_data_get_current_touch(tap_data);
+
+ msg("\rTouch sequences detected: %d", tap_data->count);
+
+ current->tup = us2ms(tv2us(&ev->time)) - tap_data->toffset;
+ }
+}
+
+static inline bool
+handle_key(struct tap_data *tap_data,
+ struct libevdev *evdev,
+ const struct input_event *ev)
+{
+ switch (ev->code) {
+ case BTN_TOOL_DOUBLETAP:
+ case BTN_TOOL_TRIPLETAP:
+ case BTN_TOOL_QUADTAP:
+ case BTN_TOOL_QUINTTAP:
+ error("This tool only supports single-finger taps. Aborting.\n");
+ return false;
+ case BTN_TOUCH:
+ handle_btn_touch(tap_data, evdev, ev);
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static inline bool
+handle_abs(struct tap_data *tap_data,
+ struct libevdev *evdev,
+ const struct input_event *ev)
+{
+ return true;
+}
+
+static inline bool
+handle_event(struct tap_data *tap_data,
+ struct libevdev *evdev,
+ const struct input_event *ev)
+{
+ bool rc = true;
+
+ if (tap_data->toffset == 0)
+ tap_data->toffset = us2ms(tv2us(&ev->time));
+
+ switch (ev->type) {
+ default:
+ error("Unexpected event %s %s (%d, %d). Aborting.\n",
+ libevdev_event_type_get_name(ev->type),
+ libevdev_event_code_get_name(ev->type, ev->code),
+ ev->type,
+ ev->code);
+ break;
+ case EV_KEY:
+ rc = handle_key(tap_data, evdev, ev);
+ break;
+ case EV_ABS:
+ rc = handle_abs(tap_data, evdev, ev);
+ break;
+ case EV_SYN:
+ rc = true;
+ break;
+ }
+
+ return rc;
+}
+
+static int
+loop(struct tap_data *data, const char *path)
+{
+ struct libevdev *evdev;
+ struct pollfd fds[2];
+ sigset_t mask;
+ int fd;
+ int rc;
+
+ fd = open(path, O_RDONLY|O_NONBLOCK);
+ if (fd < 0) {
+ error("Failed to open device: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ rc = libevdev_new_from_fd(fd, &evdev);
+ if (rc < 0) {
+ error("Failed to init device: %s\n", strerror(-rc));
+ close(fd);
+ return EXIT_FAILURE;
+ }
+ libevdev_set_clock_id(evdev, CLOCK_MONOTONIC);
+
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ fds[1].fd = signalfd(-1, &mask, SFD_NONBLOCK);
+ fds[1].events = POLLIN;
+
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ rc = EXIT_FAILURE;
+
+ error("Ready for recording data.\n"
+ "Tap the touchpad multiple times with a single finger only.\n"
+ "For useful data we recommend at least 20 taps.\n"
+ "Ctrl+C to exit\n");
+
+ while (poll(fds, 2, -1)) {
+ struct input_event ev;
+ int rc;
+
+ if (fds[1].revents)
+ break;
+
+ do {
+ rc = libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+ if (rc == LIBEVDEV_READ_STATUS_SYNC) {
+ error("Error: cannot keep up\n");
+ goto out;
+ } else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) {
+ if (!handle_event(data, evdev, &ev))
+ goto out;
+ } else if (rc != -EAGAIN && rc < 0) {
+ error("Error: %s\n", strerror(-rc));
+ goto out;
+ }
+ } while (rc != -EAGAIN);
+ }
+
+ rc = EXIT_SUCCESS;
+out:
+ close(fd);
+ if (evdev)
+ libevdev_free(evdev);
+
+ printf("\n");
+
+ return rc;
+}
+
+static inline void
+usage(void)
+{
+ printf("Usage: libinput measure touchpad-tap [--help] [/dev/input/event0]\n");
+ printf("\n"
+ "Measure various properties related to tap-to-click.\n"
+ "If a path to the device is provided, that device is used. Otherwise, this tool\n"
+ "will pick the first suitable touchpad device.\n"
+ "\n"
+ "Options:\n"
+ "--help ...... show this help\n"
+ "\n"
+ "This tool requires access to the /dev/input/eventX nodes.\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ struct tap_data *tap_data;
+ char path[PATH_MAX];
+ int option_index = 0;
+ const char *format = "summary";
+ int rc;
+
+ while (1) {
+ enum opts {
+ OPT_HELP,
+ OPT_FORMAT,
+ };
+ static struct option opts[] = {
+ { "help", no_argument, 0, OPT_HELP },
+ { "format", required_argument, 0, OPT_FORMAT},
+ { 0, 0, 0, 0 },
+ };
+ int c;
+
+ c = getopt_long(argc, argv, "", opts, &option_index);
+ if (c == -1)
+ break;
+
+ switch(c) {
+ case OPT_HELP:
+ usage();
+ return EXIT_SUCCESS;;
+ case OPT_FORMAT:
+ format = optarg;
+ break;
+ default:
+ usage();
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (streq(format, "summary")) {
+ print_dat_file = false;
+ } else if (streq(format, "dat")) {
+ print_dat_file = true;
+ } else {
+ error("Unknown print format '%s'\n", format);
+ return EXIT_FAILURE;
+ }
+
+ if (optind == argc) {
+ if (!find_touchpad_device(path, sizeof(path))) {
+ error("Failed to find a touchpad device.\n");
+ return EXIT_FAILURE;
+ }
+ } else {
+ snprintf(path, sizeof(path), "%s", argv[optind]);
+ if (!is_touchpad_device(path)) {
+ error("Device is not a touchpad.\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (!isatty(STDOUT_FILENO)) {
+ pdest = stderr;
+ } else {
+ pdest = stdout;
+ setbuf(stdout, NULL);
+ }
+
+ tap_data = tap_data_new();
+ rc = loop(tap_data, path);
+
+ if (rc != EXIT_SUCCESS)
+ goto out;
+
+ if (print_dat_file)
+ print_dat(tap_data);
+ else
+ print_statistics(tap_data);
+
+ tap_data_free(&tap_data);
+out:
+ return rc;
+}
diff --git a/tools/libinput-measure.1 b/tools/libinput-measure.1
new file mode 100644
index 0000000..26d798f
--- /dev/null
+++ b/tools/libinput-measure.1
@@ -0,0 +1,30 @@
+.TH LIBINPUT-MEASURE "1"
+.SH NAME
+libinput-measure \- measure properties of devices
+.SH SYNOPSIS
+.B libinput measure [--help] <feature> [<args>]
+.SH DESCRIPTION
+.PP
+The
+.B "libinput measure"
+tool measures properties of one (or more) devices. Depending on what is to
+be measured, this too may not create a libinput context.
+.PP
+This is a debugging tool only, its output may change at any time. Do not
+rely on the output.
+.PP
+This tool usually needs to be run as root to have access to the
+/dev/input/eventX nodes.
+.SH OPTIONS
+.TP 8
+.B --help
+Print help
+.SH FEATURES
+Features that can be measured include
+.TP 8
+.B libinput-measure-touchpad-tap-time(1)
+Measure tap-to-click time.
+.SH LIBINPUT
+Part of the
+.B libinput(1)
+suite
diff --git a/tools/libinput-measure.c b/tools/libinput-measure.c
new file mode 100644
index 0000000..af9c1cf
--- /dev/null
+++ b/tools/libinput-measure.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright © 2017 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <libudev.h>
+
+#include <libinput-util.h>
+
+#include "shared.h"
+
+static inline void
+usage(void)
+{
+ printf("Usage: libinput measure [--help] FEATURE [/dev/input/event0]\n");
+ printf("\n"
+ "Collect measurements related to FEATURE on the given device.\n"
+ "\n"
+ "Options:\n"
+ "--help ...... show this help\n"
+ "\n"
+ "Available features are:\n"
+ " touchpad-tap-time\n"
+ " Measures the time for tap-to-click interactions\n"
+ " "
+ "For information about each feature, see the --help output for that feature.\n"
+ "\n"
+ "This tool requires access to the /dev/input/eventX nodes.\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ int option_index = 0;
+
+ while (1) {
+ int c;
+ static struct option opts[] = {
+ { "help", no_argument, 0, 'h' },
+ { 0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "h", opts, &option_index);
+ if (c == -1)
+ break;
+
+ switch(c) {
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ usage();
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind >= argc) {
+ usage();
+ return EXIT_FAILURE;
+ }
+
+ argc--;
+ argv++;
+
+ return tools_exec_command("libinput-measure", argc, argv);
+}
diff --git a/tools/libinput-tool.c b/tools/libinput-tool.c
index f37d961..aa07c42 100644
--- a/tools/libinput-tool.c
+++ b/tools/libinput-tool.c
@@ -35,6 +35,7 @@
#include <libinput-util.h>
#include <libinput-version.h>
+#include "shared.h"
static void
usage(void)
{
@@ -61,6 +62,9 @@ usage(void)
"\n"
" debug-gui\n"
" Display a simple GUI to visualize libinput's events.\n"
+ "\n"
+ " measure\n"
+ " Measure various device properties. See the --help output for more info\n"
"\n");
}
@@ -69,52 +73,6 @@ enum global_opts {
GOPT_VERSION,
};
-static inline void
-setup_path(void)
-{
- const char *path = getenv("PATH");
- char new_path[PATH_MAX];
-
- snprintf(new_path,
- sizeof(new_path),
- "%s:%s",
- LIBINPUT_TOOL_PATH,
- path ? path : "");
- setenv("PATH", new_path, 1);
-}
-
-static int
-exec_command(int real_argc, char **real_argv)
-{
- char *argv[64] = {NULL};
- char executable[128];
- const char *command;
- int rc;
-
- assert((size_t)real_argc < ARRAY_LENGTH(argv));
-
- command = real_argv[0];
-
- rc = snprintf(executable, sizeof(executable), "libinput-%s", command);
- if (rc >= (int)sizeof(executable)) {
- usage();
- return EXIT_FAILURE;
- }
-
- argv[0] = executable;
- for (int i = 1; i < real_argc; i++)
- argv[i] = real_argv[i];
-
- setup_path();
-
- rc = execvp(executable, argv);
- fprintf(stderr,
- "Failed to execute '%s' (%s)\n",
- command,
- strerror(errno));
- return EXIT_FAILURE;
-}
-
int
main(int argc, char **argv)
{
@@ -154,5 +112,5 @@ main(int argc, char **argv)
argv += optind;
argc -= optind;
- return exec_command(argc, argv);
+ return tools_exec_command("libinput-", argc, argv);
}
diff --git a/tools/libinput.1 b/tools/libinput.1
index bc19655..0734d3a 100644
--- a/tools/libinput.1
+++ b/tools/libinput.1
@@ -44,6 +44,12 @@ Show a GUI to visualize libinput's events.
.TP 8
.B libinput-list-devices(1)
List all devices recognized by libinput.
+.TP 8
+.B libinput-measure(1)
+Measure various properties of devices.
+.TP 8
+.B libinput-measure-touchpad-tap(1)
+Measure tap-to-click time.
.SH LIBINPUT
Part of the
.B libinput(1)
diff --git a/tools/shared.c b/tools/shared.c
index 51246ff..5eaca3f 100644
--- a/tools/shared.c
+++ b/tools/shared.c
@@ -29,6 +29,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
#include <libudev.h>
#include <libevdev/libevdev.h>
@@ -546,3 +547,133 @@ tools_device_apply_config(struct libinput_device *device,
options->profile);
}
}
+
+static char*
+find_device(const char *udev_tag)
+{
+ struct udev *udev;
+ struct udev_enumerate *e;
+ struct udev_list_entry *entry;
+ struct udev_device *device;
+ const char *path, *sysname;
+ char *device_node = NULL;
+
+ udev = udev_new();
+ e = udev_enumerate_new(udev);
+ udev_enumerate_add_match_subsystem(e, "input");
+ udev_enumerate_scan_devices(e);
+
+ udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
+ path = udev_list_entry_get_name(entry);
+ device = udev_device_new_from_syspath(udev, path);
+ if (!device)
+ continue;
+
+ sysname = udev_device_get_sysname(device);
+ if (strncmp("event", sysname, 5) != 0) {
+ udev_device_unref(device);
+ continue;
+ }
+
+ if (udev_device_get_property_value(device, udev_tag))
+ device_node = strdup(udev_device_get_devnode(device));
+
+ udev_device_unref(device);
+
+ if (device_node)
+ break;
+ }
+ udev_enumerate_unref(e);
+ udev_unref(udev);
+
+ return device_node;
+}
+
+bool
+find_touchpad_device(char *path, size_t path_len)
+{
+ char *devnode = find_device("ID_INPUT_TOUCHPAD");
+
+ if (devnode) {
+ snprintf(path, path_len, "%s", devnode);
+ free(devnode);
+ }
+
+ return devnode != NULL;
+}
+
+bool
+is_touchpad_device(const char *devnode)
+{
+ struct udev *udev;
+ struct udev_device *dev = NULL;
+ struct stat st;
+ bool is_touchpad = false;
+
+ if (stat(devnode, &st) < 0)
+ return false;
+
+ udev = udev_new();
+ dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
+ if (!dev)
+ goto out;
+
+ is_touchpad = udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD");
+out:
+ if (dev)
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ return is_touchpad;
+}
+
+static inline void
+setup_path(void)
+{
+ const char *path = getenv("PATH");
+ char new_path[PATH_MAX];
+
+ snprintf(new_path,
+ sizeof(new_path),
+ "%s:%s",
+ LIBINPUT_TOOL_PATH,
+ path ? path : "");
+ setenv("PATH", new_path, 1);
+}
+
+int
+tools_exec_command(const char *prefix, int real_argc, char **real_argv)
+{
+ char *argv[64] = {NULL};
+ char executable[128];
+ const char *command;
+ int rc;
+
+ assert((size_t)real_argc < ARRAY_LENGTH(argv));
+
+ command = real_argv[0];
+
+ rc = snprintf(executable,
+ sizeof(executable),
+ "%s-%s",
+ prefix,
+ command);
+ if (rc >= (int)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];
+
+ setup_path();
+
+ rc = execvp(executable, argv);
+ fprintf(stderr,
+ "Failed to execute '%s' (%s)\n",
+ command,
+ strerror(errno));
+
+ return EXIT_FAILURE;
+}
diff --git a/tools/shared.h b/tools/shared.h
index 0fb8c28..0a39484 100644
--- a/tools/shared.h
+++ b/tools/shared.h
@@ -71,5 +71,9 @@ struct libinput* tools_open_backend(struct tools_context *context);
void tools_device_apply_config(struct libinput_device *device,
struct tools_options *options);
void tools_usage(const char *command);
+int tools_exec_command(const char *prefix, int argc, char **argv);
+
+bool find_touchpad_device(char *path, size_t path_len);
+bool is_touchpad_device(const char *devnode);
#endif