[PATCH libevdev] tools: add a tool to estimate the resolution of a mouse

Peter Hutterer peter.hutterer at who-t.net
Sun Nov 16 22:08:47 PST 2014


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 at who-t.net>
---
Example output:

$ sudo ./tools/mouse-dpi-tool /dev/input/event5
Mouse Lenovo Optical USB Mouse on /dev/input/event5
Move the device along the x-axis.
Pause for 3 seconds to reset, Ctrl+C to exit.
Covered distance in device units:     1157 at frequency 125.0Hz         \^C
Estimated sampling frequency: 125Hz
To calculate resolution, measure physical distance covered
and look up the matching resolution in the table below
      73mm          2.89in           400dpi
      48mm          1.93in           600dpi
      36mm          1.45in           800dpi
      29mm          1.16in          1000dpi
      24mm          0.96in          1200dpi
      20mm          0.83in          1400dpi
      18mm          0.72in          1600dpi
      16mm          0.64in          1800dpi
      14mm          0.58in          2000dpi
      13mm          0.53in          2200dpi
      12mm          0.48in          2400dpi



 tools/.gitignore       |   1 +
 tools/Makefile.am      |   6 +-
 tools/mouse-dpi-tool.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 237 insertions(+), 1 deletion(-)
 create mode 100644 tools/mouse-dpi-tool.c

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..de5e09a
--- /dev/null
+++ b/tools/mouse-dpi-tool.c
@@ -0,0 +1,231 @@
+/*
+ * 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;
+	char status = 0;
+
+	switch (progress) {
+		case 0: status = '|'; break;
+		case 1: status = '/'; break;
+		case 2: status = '-'; break;
+		case 3: 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);
+		}
+	} 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 for 3 seconds 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;
+}
+
-- 
2.1.0



More information about the Input-tools mailing list