summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2014-11-17 15:52:24 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2014-11-21 09:42:12 +1000
commitbb406fe411223000cbe80dd517194780a521ca71 (patch)
tree48f0602688c0e822965fdbeb1f454f5600eeab00
parent3320407c1205896a9d46f7c872ee34eacd9781c9 (diff)
downloadlibevdev-bb406fe411223000cbe80dd517194780a521ca71.tar.gz
tools: add a tool to estimate the resolution of a mouse
Relative devices don't provide a physical resolution to the host. For things like pointer acceleration, the physical amount of movement is better as baseline than the movement in device units. Alas, many devices don't come with any information at all, so the users have to guess. Help that guesswork by providing a tool that does the calculations for them. This tool measures the device units covered, then prints the frequency and an lookup table for various resolutions (in dpi) to match to the physical movement of the device. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
-rw-r--r--tools/.gitignore1
-rw-r--r--tools/Makefile.am6
-rw-r--r--tools/mouse-dpi-tool.c235
3 files changed, 241 insertions, 1 deletions
diff --git a/tools/.gitignore b/tools/.gitignore
index 8084fed..292c08e 100644
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -1,2 +1,3 @@
libevdev-events
touchpad-edge-detector
+mouse-dpi-tool
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 5d3600d..8e3950d 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,5 +1,7 @@
noinst_PROGRAMS = libevdev-events
-bin_PROGRAMS = touchpad-edge-detector
+bin_PROGRAMS = \
+ touchpad-edge-detector \
+ mouse-dpi-tool
AM_CPPFLAGS = $(GCC_CFLAGS) -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/libevdev
libevdev_ldadd = $(top_builddir)/libevdev/libevdev.la
@@ -10,3 +12,5 @@ libevdev_events_LDADD = $(libevdev_ldadd)
touchpad_edge_detector_SOURCES = touchpad-edge-detector.c
touchpad_edge_detector_LDADD = $(libevdev_ldadd)
+mouse_dpi_tool_SOURCES = mouse-dpi-tool.c
+mouse_dpi_tool_LDADD = $(libevdev_ldadd)
diff --git a/tools/mouse-dpi-tool.c b/tools/mouse-dpi-tool.c
new file mode 100644
index 0000000..f063028
--- /dev/null
+++ b/tools/mouse-dpi-tool.c
@@ -0,0 +1,235 @@
+/*
+ * 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 Red Hat
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission. Red
+ * Hat makes no representations about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL THE AUTHORS 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libevdev/libevdev.h>
+#include <sys/signalfd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+struct measurements {
+ int distance;
+ double frequency;
+ uint32_t ms;
+};
+
+static int
+usage(void) {
+ printf("Usage: %s /dev/input/event0\n", program_invocation_short_name);
+ printf("\n");
+ printf("This tool reads relative events from the kernel and calculates\n "
+ "the distance covered and frequency of the incoming events.\n");
+ return 1;
+}
+
+static inline uint32_t
+tv2ms(const struct timeval *tv)
+{
+ return tv->tv_sec * 1000 + tv->tv_usec/1000;
+}
+
+static inline double
+get_frequency(double last, double current)
+{
+ return 1000.0/(current - last);
+}
+
+static int
+print_current_values(const struct measurements *m)
+{
+ static int progress = 0;
+ char status = 0;
+
+ switch (progress) {
+ case 0: status = '|'; break;
+ case 1: status = '/'; break;
+ case 2: status = '-'; break;
+ case 3: status = '\\'; break;
+ default:
+ status = '?';
+ break;
+ }
+
+ progress = (progress + 1) % 4;
+
+ printf("\rCovered distance in device units: %8d at frequency %3.1fHz %c",
+ abs(m->distance), m->frequency, status);
+
+ return 0;
+}
+
+static int
+handle_event(struct measurements *m, const struct input_event *ev)
+{
+ if (ev->type == EV_SYN) {
+ const int idle_reset = 3000; /* ms */
+ uint32_t last_millis = m->ms;
+
+ m->ms = tv2ms(&ev->time);
+
+ /* reset after pause */
+ if (last_millis + idle_reset < m->ms) {
+ m->frequency = 0.0;
+ m->distance = 0;
+ } else {
+ double freq = get_frequency(last_millis, m->ms);
+ m->frequency = max(freq, m->frequency);
+ return print_current_values(m);
+ }
+
+ return 0;
+ } else if (ev->type != EV_REL)
+ return 0;
+
+ switch(ev->code) {
+ case REL_X:
+ m->distance += ev->value;
+ break;
+ }
+
+ return 0;
+}
+
+static int
+mainloop(struct libevdev *dev, struct measurements *m) {
+ struct pollfd fds[2];
+ sigset_t mask;
+
+ fds[0].fd = libevdev_get_fd(dev);
+ 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);
+
+ while (poll(fds, 2, -1)) {
+ struct input_event ev;
+ int rc;
+
+ if (fds[1].revents)
+ break;
+
+ do {
+ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+ if (rc == LIBEVDEV_READ_STATUS_SYNC) {
+ fprintf(stderr, "Error: cannot keep up\n");
+ return 1;
+ } else if (rc != -EAGAIN && rc < 0) {
+ fprintf(stderr, "Error: %s\n", strerror(-rc));
+ return 1;
+ } else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) {
+ handle_event(m, &ev);
+ }
+ } while (rc != -EAGAIN);
+ }
+
+ return 0;
+}
+
+static void
+print_summary(struct measurements *m)
+{
+ int res;
+
+ printf("Estimated sampling frequency: %dHz\n", (int)m->frequency);
+ printf("To calculate resolution, measure physical distance covered\n"
+ "and look up the matching resolution in the table below\n");
+
+ m->distance = abs(m->distance);
+
+ /* If the mouse has more than 2500dpi, the manufacturer usually
+ shows off on their website anyway */
+ for (res = 400; res <= 2500; res += 200) {
+ double inch = m->distance/(double)res;
+ printf("%8dmm %8.2fin %8ddpi\n",
+ (int)(inch * 25.4), inch, res);
+ }
+}
+
+int
+main (int argc, char **argv) {
+ int rc;
+ int fd;
+ const char *path;
+ struct libevdev *dev;
+ struct measurements measurements = {0};
+
+ if (argc < 2)
+ return usage();
+
+ path = argv[1];
+ if (path[0] == '-')
+ return usage();
+
+ fd = open(path, O_RDONLY|O_NONBLOCK);
+ if (fd < 0) {
+ fprintf(stderr, "Error opening the device: %s\n", strerror(errno));
+ return 1;
+ }
+
+ rc = libevdev_new_from_fd(fd, &dev);
+ if (rc != 0) {
+ fprintf(stderr, "Error fetching the device info: %s\n", strerror(-rc));
+ return 1;
+ }
+
+ if (libevdev_grab(dev, LIBEVDEV_GRAB) != 0) {
+ fprintf(stderr, "Error: cannot grab the device, something else is grabbing it.\n");
+ fprintf(stderr, "Use 'fuser -v %s' to find processes with an open fd\n", path);
+ return 1;
+ }
+ libevdev_grab(dev, LIBEVDEV_UNGRAB);
+
+ printf("Mouse %s on %s\n", libevdev_get_name(dev), path);
+ printf("Move the device along the x-axis.\n");
+ printf("Pause 3 seconds before movement to reset, Ctrl+C to exit.\n");
+ setbuf(stdout, NULL);
+
+ rc = mainloop(dev, &measurements);
+
+ printf("\n");
+
+ print_summary(&measurements);
+
+ libevdev_free(dev);
+ close(fd);
+
+ return rc;
+}