[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