Blame tools/touchpad-edge-detector.c

Packit 57e92c
/*
Packit 57e92c
 * Copyright © 2014 Red Hat, Inc.
Packit 57e92c
 *
Packit 57e92c
 * Permission to use, copy, modify, distribute, and sell this software
Packit 57e92c
 * and its documentation for any purpose is hereby granted without
Packit 57e92c
 * fee, provided that the above copyright notice appear in all copies
Packit 57e92c
 * and that both that copyright notice and this permission notice
Packit 57e92c
 * appear in supporting documentation, and that the name of Red Hat
Packit 57e92c
 * not be used in advertising or publicity pertaining to distribution
Packit 57e92c
 * of the software without specific, written prior permission.  Red
Packit 57e92c
 * Hat makes no representations about the suitability of this software
Packit 57e92c
 * for any purpose.  It is provided "as is" without express or implied
Packit 57e92c
 * warranty.
Packit 57e92c
 *
Packit 57e92c
 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
Packit 57e92c
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
Packit 57e92c
 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
Packit 57e92c
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
Packit 57e92c
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
Packit 57e92c
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
Packit 57e92c
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Packit 57e92c
 */
Packit 57e92c
Packit 57e92c
#ifdef HAVE_CONFIG_H
Packit 57e92c
#include "config.h"
Packit 57e92c
#endif
Packit 57e92c
Packit 57e92c
#include <libevdev/libevdev.h>
Packit 57e92c
#include <sys/signalfd.h>
Packit 57e92c
#include <errno.h>
Packit 57e92c
#include <fcntl.h>
Packit 57e92c
#include <limits.h>
Packit 57e92c
#include <math.h>
Packit 57e92c
#include <poll.h>
Packit 57e92c
#include <signal.h>
Packit 57e92c
#include <stdint.h>
Packit 57e92c
#include <stdlib.h>
Packit 57e92c
#include <stdio.h>
Packit 57e92c
#include <string.h>
Packit 57e92c
#include <unistd.h>
Packit 57e92c
Packit 57e92c
#define min(a, b) (((a) < (b)) ? (a) : (b))
Packit 57e92c
#define max(a, b) (((a) > (b)) ? (a) : (b))
Packit 57e92c
Packit 57e92c
static int
Packit 57e92c
usage(void) {
Packit 57e92c
	printf("Usage: %s 12x34 /dev/input/event0\n", program_invocation_short_name);
Packit 57e92c
	printf("\n");
Packit 57e92c
	printf("This tool reads the touchpad events from the kernel and calculates\n "
Packit 57e92c
	       "the minimum and maximum for the x and y coordinates, respectively.\n"
Packit 57e92c
	       "The first argument is the physical size of the touchpad in mm.\n");
Packit 57e92c
	return 1;
Packit 57e92c
}
Packit 57e92c
Packit 57e92c
struct dimensions {
Packit 57e92c
	int top, bottom, left, right;
Packit 57e92c
};
Packit 57e92c
Packit 57e92c
struct size {
Packit 57e92c
	int w, h;
Packit 57e92c
};
Packit 57e92c
Packit 57e92c
static int
Packit 57e92c
print_current_values(const struct dimensions *d)
Packit 57e92c
{
Packit 57e92c
	static int progress;
Packit 57e92c
	char status = 0;
Packit 57e92c
Packit 57e92c
	switch (progress) {
Packit 57e92c
		case 0: status = '|'; break;
Packit 57e92c
		case 1: status = '/'; break;
Packit 57e92c
		case 2: status = '-'; break;
Packit 57e92c
		case 3: status = '\\'; break;
Packit 57e92c
	}
Packit 57e92c
Packit 57e92c
	progress = (progress + 1) % 4;
Packit 57e92c
Packit 57e92c
	printf("\rTouchpad sends:	x [%d..%d], y [%d..%d] %c",
Packit 57e92c
			d->left, d->right, d->top, d->bottom, status);
Packit 57e92c
	return 0;
Packit 57e92c
}
Packit 57e92c
Packit 57e92c
static int
Packit 57e92c
handle_event(struct dimensions *d, const struct input_event *ev) {
Packit 57e92c
	if (ev->type == EV_SYN) {
Packit 57e92c
		return print_current_values(d);
Packit 57e92c
	} else if (ev->type != EV_ABS)
Packit 57e92c
		return 0;
Packit 57e92c
Packit 57e92c
	switch(ev->code) {
Packit 57e92c
		case ABS_X:
Packit 57e92c
		case ABS_MT_POSITION_X:
Packit 57e92c
			d->left = min(d->left, ev->value);
Packit 57e92c
			d->right = max(d->right, ev->value);
Packit 57e92c
			break;
Packit 57e92c
		case ABS_Y:
Packit 57e92c
		case ABS_MT_POSITION_Y:
Packit 57e92c
			d->top = min(d->top, ev->value);
Packit 57e92c
			d->bottom = max(d->bottom, ev->value);
Packit 57e92c
			break;
Packit 57e92c
	}
Packit 57e92c
Packit 57e92c
	return 0;
Packit 57e92c
}
Packit 57e92c
Packit 57e92c
static int
Packit 57e92c
mainloop(struct libevdev *dev, struct dimensions *dim) {
Packit 57e92c
	struct pollfd fds[2];
Packit 57e92c
	sigset_t mask;
Packit 57e92c
Packit 57e92c
	fds[0].fd = libevdev_get_fd(dev);
Packit 57e92c
	fds[0].events = POLLIN;
Packit 57e92c
Packit 57e92c
	sigemptyset(&mask);
Packit 57e92c
	sigaddset(&mask, SIGINT);
Packit 57e92c
	fds[1].fd = signalfd(-1, &mask, SFD_NONBLOCK);
Packit 57e92c
	fds[1].events = POLLIN;
Packit 57e92c
Packit 57e92c
	sigprocmask(SIG_BLOCK, &mask, NULL);
Packit 57e92c
Packit 57e92c
	while (poll(fds, 2, -1)) {
Packit 57e92c
		struct input_event ev;
Packit 57e92c
		int rc;
Packit 57e92c
Packit 57e92c
		if (fds[1].revents)
Packit 57e92c
			break;
Packit 57e92c
Packit 57e92c
		do {
Packit 57e92c
			rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev;;
Packit 57e92c
			if (rc == LIBEVDEV_READ_STATUS_SYNC) {
Packit 57e92c
				fprintf(stderr, "Error: cannot keep up\n");
Packit 57e92c
				return 1;
Packit 57e92c
			} else if (rc != -EAGAIN && rc < 0) {
Packit 57e92c
				fprintf(stderr, "Error: %s\n", strerror(-rc));
Packit 57e92c
				return 1;
Packit 57e92c
			} else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) {
Packit 57e92c
				handle_event(dim, &ev;;
Packit 57e92c
			}
Packit 57e92c
		} while (rc != -EAGAIN);
Packit 57e92c
	}
Packit 57e92c
Packit 57e92c
	return 0;
Packit 57e92c
}
Packit 57e92c
Packit 57e92c
static inline void
Packit 57e92c
pid_vid_matchstr(struct libevdev *dev, char *match, size_t sz)
Packit 57e92c
{
Packit 57e92c
	snprintf(match, sz, "input:b%04Xv%04Xp%04X",
Packit 57e92c
		libevdev_get_id_bustype(dev),
Packit 57e92c
		libevdev_get_id_vendor(dev),
Packit 57e92c
		libevdev_get_id_product(dev));
Packit 57e92c
}
Packit 57e92c
Packit 57e92c
static inline void
Packit 57e92c
dmi_matchstr(struct libevdev *dev, char *match, size_t sz)
Packit 57e92c
{
Packit 57e92c
	char modalias[PATH_MAX];
Packit 57e92c
	FILE *fp;
Packit 57e92c
Packit 57e92c
	fp = fopen("/sys/class/dmi/id/modalias", "r");
Packit 57e92c
	if (!fp || fgets(modalias, sizeof(modalias), fp) == NULL) {
Packit 57e92c
		sprintf(match, "ERROR READING DMI MODALIAS");
Packit 57e92c
		if (fp)
Packit 57e92c
			fclose(fp);
Packit 57e92c
		return;
Packit 57e92c
	}
Packit 57e92c
Packit 57e92c
	fclose(fp);
Packit 57e92c
Packit 57e92c
	modalias[strlen(modalias) - 1] = '\0'; /* drop \n */
Packit 57e92c
	snprintf(match, sz, "name:%s:%s", libevdev_get_name(dev), modalias);
Packit 57e92c
Packit 57e92c
	return;
Packit 57e92c
}
Packit 57e92c
Packit 57e92c
static void
Packit 57e92c
print_udev_override_rule(struct libevdev *dev,
Packit 57e92c
			 const struct dimensions *dim,
Packit 57e92c
			 const struct size *size) {
Packit 57e92c
	const struct input_absinfo *x, *y;
Packit 57e92c
	char match[PATH_MAX];
Packit 57e92c
	int w, h;
Packit 57e92c
	int xres, yres;
Packit 57e92c
Packit 57e92c
	x = libevdev_get_abs_info(dev, ABS_X);
Packit 57e92c
	y = libevdev_get_abs_info(dev, ABS_Y);
Packit 57e92c
	w = dim->right - dim->left;
Packit 57e92c
	h = dim->bottom - dim->top;
Packit 57e92c
	xres = round((double)w/size->w);
Packit 57e92c
	yres = round((double)h/size->h);
Packit 57e92c
Packit 57e92c
	if (x->resolution && y->resolution) {
Packit 57e92c
		int width = x->maximum - x->minimum,
Packit 57e92c
		    height = y->maximum - y->minimum;
Packit 57e92c
		printf("Touchpad size as listed by the kernel: %dx%dmm\n",
Packit 57e92c
		       width/x->resolution, height/y->resolution);
Packit 57e92c
	} else {
Packit 57e92c
		printf("Touchpad has no resolution, size unknown\n");
Packit 57e92c
	}
Packit 57e92c
Packit 57e92c
	printf("User-specified touchpad size: %dx%dmm\n", size->w, size->h);
Packit 57e92c
	printf("Calculated ranges: %d/%d\n", w, h);
Packit 57e92c
	printf("\n");
Packit 57e92c
	printf("Suggested udev rule:\n");
Packit 57e92c
Packit 57e92c
	switch(libevdev_get_id_bustype(dev)) {
Packit 57e92c
	case BUS_USB:
Packit 57e92c
	case BUS_BLUETOOTH:
Packit 57e92c
		pid_vid_matchstr(dev, match, sizeof(match));
Packit 57e92c
		break;
Packit 57e92c
	default:
Packit 57e92c
		dmi_matchstr(dev, match, sizeof(match));
Packit 57e92c
		break;
Packit 57e92c
	}
Packit 57e92c
Packit 57e92c
	printf("# <Laptop model description goes here>\n"
Packit 57e92c
	       "evdev:%s*\n"
Packit 57e92c
	       " EVDEV_ABS_00=%d:%d:%d\n"
Packit 57e92c
	       " EVDEV_ABS_01=%d:%d:%d\n",
Packit 57e92c
	       match,
Packit 57e92c
	       dim->left, dim->right, xres,
Packit 57e92c
	       dim->top, dim->bottom, yres);
Packit 57e92c
	if (libevdev_has_event_code(dev, EV_ABS, ABS_MT_POSITION_X))
Packit 57e92c
		printf(" EVDEV_ABS_35=%d:%d:%d\n"
Packit 57e92c
		       " EVDEV_ABS_36=%d:%d:%d\n",
Packit 57e92c
		       dim->left, dim->right, xres,
Packit 57e92c
		       dim->top, dim->bottom, yres);
Packit 57e92c
}
Packit 57e92c
Packit 57e92c
int main (int argc, char **argv) {
Packit 57e92c
	int rc;
Packit 57e92c
	int fd;
Packit 57e92c
	const char *path;
Packit 57e92c
	struct libevdev *dev;
Packit 57e92c
	struct dimensions dim;
Packit 57e92c
	struct size size;
Packit 57e92c
Packit 57e92c
	if (argc < 3)
Packit 57e92c
		return usage();
Packit 57e92c
Packit 57e92c
	if (sscanf(argv[1], "%dx%d", &size.w, &size.h) != 2 ||
Packit 57e92c
	    size.w <= 0 || size.h <= 0)
Packit 57e92c
		return usage();
Packit 57e92c
Packit 57e92c
	if (size.w < 30 || size.h < 30) {
Packit 57e92c
		fprintf(stderr,
Packit 57e92c
			"%dx%dmm is too small for a touchpad.\n"
Packit 57e92c
			"Please specify the touchpad size in mm.\n",
Packit 57e92c
			size.w, size.h);
Packit 57e92c
		return 1;
Packit 57e92c
	}
Packit 57e92c
Packit 57e92c
	path = argv[2];
Packit 57e92c
	if (path[0] == '-')
Packit 57e92c
		return usage();
Packit 57e92c
Packit 57e92c
	fd = open(path, O_RDONLY|O_NONBLOCK);
Packit 57e92c
	if (fd < 0) {
Packit 57e92c
		fprintf(stderr, "Error opening the device: %s\n", strerror(errno));
Packit 57e92c
		return 1;
Packit 57e92c
	}
Packit 57e92c
Packit 57e92c
	rc = libevdev_new_from_fd(fd, &dev;;
Packit 57e92c
	if (rc != 0) {
Packit 57e92c
		fprintf(stderr, "Error fetching the device info: %s\n", strerror(-rc));
Packit 57e92c
		return 1;
Packit 57e92c
	}
Packit 57e92c
Packit 57e92c
	if (libevdev_grab(dev, LIBEVDEV_GRAB) != 0) {
Packit 57e92c
		fprintf(stderr, "Error: cannot grab the device, something else is grabbing it.\n");
Packit 57e92c
		fprintf(stderr, "Use 'fuser -v %s' to find processes with an open fd\n", path);
Packit 57e92c
		return 1;
Packit 57e92c
	}
Packit 57e92c
	libevdev_grab(dev, LIBEVDEV_UNGRAB);
Packit 57e92c
Packit 57e92c
	if (!libevdev_has_event_code(dev, EV_ABS, ABS_X) ||
Packit 57e92c
	    !libevdev_has_event_code(dev, EV_ABS, ABS_Y)) {
Packit 57e92c
		fprintf(stderr, "Error: this device does not have abs axes\n");
Packit 57e92c
		rc = EXIT_FAILURE;
Packit 57e92c
		goto out;
Packit 57e92c
	}
Packit 57e92c
Packit 57e92c
	dim.left = INT_MAX;
Packit 57e92c
	dim.right = INT_MIN;
Packit 57e92c
	dim.top = INT_MAX;
Packit 57e92c
	dim.bottom = INT_MIN;
Packit 57e92c
Packit 57e92c
	printf("Touchpad %s on %s\n", libevdev_get_name(dev), path);
Packit 57e92c
	printf("Move one finger around the touchpad to detect the actual edges\n");
Packit 57e92c
	printf("Kernel says:	x [%d..%d], y [%d..%d]\n",
Packit 57e92c
			libevdev_get_abs_minimum(dev, ABS_X),
Packit 57e92c
			libevdev_get_abs_maximum(dev, ABS_X),
Packit 57e92c
			libevdev_get_abs_minimum(dev, ABS_Y),
Packit 57e92c
			libevdev_get_abs_maximum(dev, ABS_Y));
Packit 57e92c
Packit 57e92c
	setbuf(stdout, NULL);
Packit 57e92c
Packit 57e92c
	rc = mainloop(dev, &dim;;
Packit 57e92c
	printf("\n\n");
Packit 57e92c
Packit 57e92c
	print_udev_override_rule(dev, &dim, &size);
Packit 57e92c
Packit 57e92c
out:
Packit 57e92c
	libevdev_free(dev);
Packit 57e92c
	close(fd);
Packit 57e92c
Packit 57e92c
	return rc;
Packit 57e92c
}