libevdev/tools/mouse-dpi-tool.c

236 lines
5.6 KiB
C
Raw Normal View History

/*
* 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;
}