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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}