mirror of
https://gitlab.freedesktop.org/libevdev/libevdev.git
synced 2025-12-28 03:50:16 +01:00
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>
This commit is contained in:
parent
3320407c12
commit
bb406fe411
3 changed files with 241 additions and 1 deletions
1
tools/.gitignore
vendored
1
tools/.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
libevdev-events
|
||||
touchpad-edge-detector
|
||||
mouse-dpi-tool
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
235
tools/mouse-dpi-tool.c
Normal file
235
tools/mouse-dpi-tool.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue