1c0abf9e6Sopenharmony_ci// SPDX-License-Identifier: MIT 2c0abf9e6Sopenharmony_ci/* 3c0abf9e6Sopenharmony_ci * Copyright © 2014 Red Hat, Inc. 4c0abf9e6Sopenharmony_ci */ 5c0abf9e6Sopenharmony_ci 6c0abf9e6Sopenharmony_ci#include "config.h" 7c0abf9e6Sopenharmony_ci 8c0abf9e6Sopenharmony_ci#include <errno.h> 9c0abf9e6Sopenharmony_ci#include <fcntl.h> 10c0abf9e6Sopenharmony_ci#include <libgen.h> 11c0abf9e6Sopenharmony_ci#include <limits.h> 12c0abf9e6Sopenharmony_ci#include <math.h> 13c0abf9e6Sopenharmony_ci#include <poll.h> 14c0abf9e6Sopenharmony_ci#include <signal.h> 15c0abf9e6Sopenharmony_ci#include <stdint.h> 16c0abf9e6Sopenharmony_ci#include <stdio.h> 17c0abf9e6Sopenharmony_ci#include <stdlib.h> 18c0abf9e6Sopenharmony_ci#include <string.h> 19c0abf9e6Sopenharmony_ci#include <unistd.h> 20c0abf9e6Sopenharmony_ci 21c0abf9e6Sopenharmony_ci#include "libevdev/libevdev.h" 22c0abf9e6Sopenharmony_ci 23c0abf9e6Sopenharmony_ci#define min(a, b) (((a) < (b)) ? (a) : (b)) 24c0abf9e6Sopenharmony_ci#define max(a, b) (((a) > (b)) ? (a) : (b)) 25c0abf9e6Sopenharmony_ci 26c0abf9e6Sopenharmony_cistatic int signalled = 0; 27c0abf9e6Sopenharmony_ci 28c0abf9e6Sopenharmony_cistatic int 29c0abf9e6Sopenharmony_ciusage(const char *progname) { 30c0abf9e6Sopenharmony_ci printf("Usage: %s 12x34 /dev/input/eventX\n", progname); 31c0abf9e6Sopenharmony_ci printf("\n"); 32c0abf9e6Sopenharmony_ci printf("This tool reads the touchpad events from the kernel and calculates\n " 33c0abf9e6Sopenharmony_ci "the minimum and maximum for the x and y coordinates, respectively.\n" 34c0abf9e6Sopenharmony_ci "The first argument is the physical size of the touchpad in mm (WIDTHxHEIGHT).\n"); 35c0abf9e6Sopenharmony_ci return 1; 36c0abf9e6Sopenharmony_ci} 37c0abf9e6Sopenharmony_ci 38c0abf9e6Sopenharmony_cistruct dimensions { 39c0abf9e6Sopenharmony_ci int top, bottom, left, right; 40c0abf9e6Sopenharmony_ci}; 41c0abf9e6Sopenharmony_ci 42c0abf9e6Sopenharmony_cistruct size { 43c0abf9e6Sopenharmony_ci int w, h; 44c0abf9e6Sopenharmony_ci}; 45c0abf9e6Sopenharmony_ci 46c0abf9e6Sopenharmony_cistatic int 47c0abf9e6Sopenharmony_ciprint_current_values(const struct dimensions *d) 48c0abf9e6Sopenharmony_ci{ 49c0abf9e6Sopenharmony_ci static int progress; 50c0abf9e6Sopenharmony_ci char status = 0; 51c0abf9e6Sopenharmony_ci 52c0abf9e6Sopenharmony_ci switch (progress) { 53c0abf9e6Sopenharmony_ci case 0: status = '|'; break; 54c0abf9e6Sopenharmony_ci case 1: status = '/'; break; 55c0abf9e6Sopenharmony_ci case 2: status = '-'; break; 56c0abf9e6Sopenharmony_ci case 3: status = '\\'; break; 57c0abf9e6Sopenharmony_ci } 58c0abf9e6Sopenharmony_ci 59c0abf9e6Sopenharmony_ci progress = (progress + 1) % 4; 60c0abf9e6Sopenharmony_ci 61c0abf9e6Sopenharmony_ci printf("\rTouchpad sends: x [%d..%d], y [%d..%d] %c", 62c0abf9e6Sopenharmony_ci d->left, d->right, d->top, d->bottom, status); 63c0abf9e6Sopenharmony_ci return 0; 64c0abf9e6Sopenharmony_ci} 65c0abf9e6Sopenharmony_ci 66c0abf9e6Sopenharmony_cistatic int 67c0abf9e6Sopenharmony_cihandle_event(struct dimensions *d, const struct input_event *ev) { 68c0abf9e6Sopenharmony_ci if (ev->type == EV_SYN) 69c0abf9e6Sopenharmony_ci return print_current_values(d); 70c0abf9e6Sopenharmony_ci 71c0abf9e6Sopenharmony_ci if (ev->type != EV_ABS) 72c0abf9e6Sopenharmony_ci return 0; 73c0abf9e6Sopenharmony_ci 74c0abf9e6Sopenharmony_ci switch(ev->code) { 75c0abf9e6Sopenharmony_ci case ABS_X: 76c0abf9e6Sopenharmony_ci case ABS_MT_POSITION_X: 77c0abf9e6Sopenharmony_ci d->left = min(d->left, ev->value); 78c0abf9e6Sopenharmony_ci d->right = max(d->right, ev->value); 79c0abf9e6Sopenharmony_ci break; 80c0abf9e6Sopenharmony_ci case ABS_Y: 81c0abf9e6Sopenharmony_ci case ABS_MT_POSITION_Y: 82c0abf9e6Sopenharmony_ci d->top = min(d->top, ev->value); 83c0abf9e6Sopenharmony_ci d->bottom = max(d->bottom, ev->value); 84c0abf9e6Sopenharmony_ci break; 85c0abf9e6Sopenharmony_ci } 86c0abf9e6Sopenharmony_ci 87c0abf9e6Sopenharmony_ci return 0; 88c0abf9e6Sopenharmony_ci} 89c0abf9e6Sopenharmony_ci 90c0abf9e6Sopenharmony_cistatic void 91c0abf9e6Sopenharmony_cisignal_handler(__attribute__((__unused__)) int signal) 92c0abf9e6Sopenharmony_ci{ 93c0abf9e6Sopenharmony_ci signalled++; 94c0abf9e6Sopenharmony_ci} 95c0abf9e6Sopenharmony_ci 96c0abf9e6Sopenharmony_cistatic int 97c0abf9e6Sopenharmony_cimainloop(struct libevdev *dev, struct dimensions *dim) { 98c0abf9e6Sopenharmony_ci struct pollfd fds; 99c0abf9e6Sopenharmony_ci 100c0abf9e6Sopenharmony_ci fds.fd = libevdev_get_fd(dev); 101c0abf9e6Sopenharmony_ci fds.events = POLLIN; 102c0abf9e6Sopenharmony_ci 103c0abf9e6Sopenharmony_ci signal(SIGINT, signal_handler); 104c0abf9e6Sopenharmony_ci 105c0abf9e6Sopenharmony_ci while (poll(&fds, 1, -1)) { 106c0abf9e6Sopenharmony_ci struct input_event ev; 107c0abf9e6Sopenharmony_ci int rc; 108c0abf9e6Sopenharmony_ci 109c0abf9e6Sopenharmony_ci if (signalled) 110c0abf9e6Sopenharmony_ci break; 111c0abf9e6Sopenharmony_ci 112c0abf9e6Sopenharmony_ci do { 113c0abf9e6Sopenharmony_ci rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); 114c0abf9e6Sopenharmony_ci if (rc == LIBEVDEV_READ_STATUS_SYNC) { 115c0abf9e6Sopenharmony_ci fprintf(stderr, "Error: cannot keep up\n"); 116c0abf9e6Sopenharmony_ci return 1; 117c0abf9e6Sopenharmony_ci } 118c0abf9e6Sopenharmony_ci 119c0abf9e6Sopenharmony_ci if (rc != -EAGAIN && rc < 0) { 120c0abf9e6Sopenharmony_ci fprintf(stderr, "Error: %s\n", strerror(-rc)); 121c0abf9e6Sopenharmony_ci return 1; 122c0abf9e6Sopenharmony_ci 123c0abf9e6Sopenharmony_ci } 124c0abf9e6Sopenharmony_ci 125c0abf9e6Sopenharmony_ci if (rc == LIBEVDEV_READ_STATUS_SUCCESS) 126c0abf9e6Sopenharmony_ci handle_event(dim, &ev); 127c0abf9e6Sopenharmony_ci } while (rc != -EAGAIN); 128c0abf9e6Sopenharmony_ci } 129c0abf9e6Sopenharmony_ci 130c0abf9e6Sopenharmony_ci return 0; 131c0abf9e6Sopenharmony_ci} 132c0abf9e6Sopenharmony_ci 133c0abf9e6Sopenharmony_cistatic inline void 134c0abf9e6Sopenharmony_cipid_vid_matchstr(struct libevdev *dev, char *match, size_t sz) 135c0abf9e6Sopenharmony_ci{ 136c0abf9e6Sopenharmony_ci snprintf(match, sz, "input:b%04Xv%04Xp%04X", 137c0abf9e6Sopenharmony_ci libevdev_get_id_bustype(dev), 138c0abf9e6Sopenharmony_ci libevdev_get_id_vendor(dev), 139c0abf9e6Sopenharmony_ci libevdev_get_id_product(dev)); 140c0abf9e6Sopenharmony_ci} 141c0abf9e6Sopenharmony_ci 142c0abf9e6Sopenharmony_cistatic inline void 143c0abf9e6Sopenharmony_cidmi_matchstr(struct libevdev *dev, char *match, size_t sz) 144c0abf9e6Sopenharmony_ci{ 145c0abf9e6Sopenharmony_ci char modalias[PATH_MAX]; 146c0abf9e6Sopenharmony_ci FILE *fp; 147c0abf9e6Sopenharmony_ci 148c0abf9e6Sopenharmony_ci fp = fopen("/sys/class/dmi/id/modalias", "r"); 149c0abf9e6Sopenharmony_ci if (!fp || fgets(modalias, sizeof(modalias), fp) == NULL) { 150c0abf9e6Sopenharmony_ci sprintf(match, "ERROR READING DMI MODALIAS"); 151c0abf9e6Sopenharmony_ci if (fp) 152c0abf9e6Sopenharmony_ci fclose(fp); 153c0abf9e6Sopenharmony_ci return; 154c0abf9e6Sopenharmony_ci } 155c0abf9e6Sopenharmony_ci 156c0abf9e6Sopenharmony_ci fclose(fp); 157c0abf9e6Sopenharmony_ci 158c0abf9e6Sopenharmony_ci modalias[strlen(modalias) - 1] = '\0'; /* drop \n */ 159c0abf9e6Sopenharmony_ci snprintf(match, sz, "name:%s:%s", libevdev_get_name(dev), modalias); 160c0abf9e6Sopenharmony_ci} 161c0abf9e6Sopenharmony_ci 162c0abf9e6Sopenharmony_cistatic void 163c0abf9e6Sopenharmony_ciprint_udev_override_rule(struct libevdev *dev, 164c0abf9e6Sopenharmony_ci const struct dimensions *dim, 165c0abf9e6Sopenharmony_ci const struct size *size) { 166c0abf9e6Sopenharmony_ci const struct input_absinfo *x, *y; 167c0abf9e6Sopenharmony_ci char match[PATH_MAX]; 168c0abf9e6Sopenharmony_ci int w, h; 169c0abf9e6Sopenharmony_ci int xres, yres; 170c0abf9e6Sopenharmony_ci 171c0abf9e6Sopenharmony_ci x = libevdev_get_abs_info(dev, ABS_X); 172c0abf9e6Sopenharmony_ci y = libevdev_get_abs_info(dev, ABS_Y); 173c0abf9e6Sopenharmony_ci w = dim->right - dim->left; 174c0abf9e6Sopenharmony_ci h = dim->bottom - dim->top; 175c0abf9e6Sopenharmony_ci xres = round((double)w/size->w); 176c0abf9e6Sopenharmony_ci yres = round((double)h/size->h); 177c0abf9e6Sopenharmony_ci 178c0abf9e6Sopenharmony_ci if (x->resolution && y->resolution) { 179c0abf9e6Sopenharmony_ci int width = x->maximum - x->minimum, 180c0abf9e6Sopenharmony_ci height = y->maximum - y->minimum; 181c0abf9e6Sopenharmony_ci printf("Touchpad size as listed by the kernel: %dx%dmm\n", 182c0abf9e6Sopenharmony_ci width/x->resolution, height/y->resolution); 183c0abf9e6Sopenharmony_ci } else { 184c0abf9e6Sopenharmony_ci printf("Touchpad has no resolution, size unknown\n"); 185c0abf9e6Sopenharmony_ci } 186c0abf9e6Sopenharmony_ci 187c0abf9e6Sopenharmony_ci printf("User-specified touchpad size: %dx%dmm\n", size->w, size->h); 188c0abf9e6Sopenharmony_ci printf("Calculated ranges: %d/%d\n", w, h); 189c0abf9e6Sopenharmony_ci printf("\n"); 190c0abf9e6Sopenharmony_ci printf("Suggested udev rule:\n"); 191c0abf9e6Sopenharmony_ci 192c0abf9e6Sopenharmony_ci switch(libevdev_get_id_bustype(dev)) { 193c0abf9e6Sopenharmony_ci case BUS_USB: 194c0abf9e6Sopenharmony_ci case BUS_BLUETOOTH: 195c0abf9e6Sopenharmony_ci pid_vid_matchstr(dev, match, sizeof(match)); 196c0abf9e6Sopenharmony_ci break; 197c0abf9e6Sopenharmony_ci default: 198c0abf9e6Sopenharmony_ci dmi_matchstr(dev, match, sizeof(match)); 199c0abf9e6Sopenharmony_ci break; 200c0abf9e6Sopenharmony_ci } 201c0abf9e6Sopenharmony_ci 202c0abf9e6Sopenharmony_ci printf("# <Laptop model description goes here>\n" 203c0abf9e6Sopenharmony_ci "evdev:%s*\n" 204c0abf9e6Sopenharmony_ci " EVDEV_ABS_00=%d:%d:%d\n" 205c0abf9e6Sopenharmony_ci " EVDEV_ABS_01=%d:%d:%d\n", 206c0abf9e6Sopenharmony_ci match, 207c0abf9e6Sopenharmony_ci dim->left, dim->right, xres, 208c0abf9e6Sopenharmony_ci dim->top, dim->bottom, yres); 209c0abf9e6Sopenharmony_ci if (libevdev_has_event_code(dev, EV_ABS, ABS_MT_POSITION_X)) 210c0abf9e6Sopenharmony_ci printf(" EVDEV_ABS_35=%d:%d:%d\n" 211c0abf9e6Sopenharmony_ci " EVDEV_ABS_36=%d:%d:%d\n", 212c0abf9e6Sopenharmony_ci dim->left, dim->right, xres, 213c0abf9e6Sopenharmony_ci dim->top, dim->bottom, yres); 214c0abf9e6Sopenharmony_ci} 215c0abf9e6Sopenharmony_ci 216c0abf9e6Sopenharmony_ciint main (int argc, char **argv) { 217c0abf9e6Sopenharmony_ci int rc; 218c0abf9e6Sopenharmony_ci int fd; 219c0abf9e6Sopenharmony_ci const char *path; 220c0abf9e6Sopenharmony_ci struct libevdev *dev; 221c0abf9e6Sopenharmony_ci struct dimensions dim; 222c0abf9e6Sopenharmony_ci struct size size; 223c0abf9e6Sopenharmony_ci 224c0abf9e6Sopenharmony_ci if (argc < 3) 225c0abf9e6Sopenharmony_ci return usage(basename(argv[0])); 226c0abf9e6Sopenharmony_ci 227c0abf9e6Sopenharmony_ci if (sscanf(argv[1], "%dx%d", &size.w, &size.h) != 2 || 228c0abf9e6Sopenharmony_ci size.w <= 0 || size.h <= 0) 229c0abf9e6Sopenharmony_ci return usage(basename(argv[0])); 230c0abf9e6Sopenharmony_ci 231c0abf9e6Sopenharmony_ci if (size.w < 30 || size.h < 30) { 232c0abf9e6Sopenharmony_ci fprintf(stderr, 233c0abf9e6Sopenharmony_ci "%dx%dmm is too small for a touchpad.\n" 234c0abf9e6Sopenharmony_ci "Please specify the touchpad size in mm.\n", 235c0abf9e6Sopenharmony_ci size.w, size.h); 236c0abf9e6Sopenharmony_ci return 1; 237c0abf9e6Sopenharmony_ci } 238c0abf9e6Sopenharmony_ci 239c0abf9e6Sopenharmony_ci path = argv[2]; 240c0abf9e6Sopenharmony_ci if (path[0] == '-') 241c0abf9e6Sopenharmony_ci return usage(basename(argv[0])); 242c0abf9e6Sopenharmony_ci 243c0abf9e6Sopenharmony_ci fd = open(path, O_RDONLY|O_NONBLOCK); 244c0abf9e6Sopenharmony_ci if (fd < 0) { 245c0abf9e6Sopenharmony_ci fprintf(stderr, "Error opening the device: %s\n", strerror(errno)); 246c0abf9e6Sopenharmony_ci return 1; 247c0abf9e6Sopenharmony_ci } 248c0abf9e6Sopenharmony_ci 249c0abf9e6Sopenharmony_ci rc = libevdev_new_from_fd(fd, &dev); 250c0abf9e6Sopenharmony_ci if (rc != 0) { 251c0abf9e6Sopenharmony_ci fprintf(stderr, "Error fetching the device info: %s\n", strerror(-rc)); 252c0abf9e6Sopenharmony_ci return 1; 253c0abf9e6Sopenharmony_ci } 254c0abf9e6Sopenharmony_ci 255c0abf9e6Sopenharmony_ci if (libevdev_grab(dev, LIBEVDEV_GRAB) != 0) { 256c0abf9e6Sopenharmony_ci fprintf(stderr, "Error: cannot grab the device, something else is grabbing it.\n"); 257c0abf9e6Sopenharmony_ci fprintf(stderr, "Use 'fuser -v %s' to find processes with an open fd\n", path); 258c0abf9e6Sopenharmony_ci return 1; 259c0abf9e6Sopenharmony_ci } 260c0abf9e6Sopenharmony_ci libevdev_grab(dev, LIBEVDEV_UNGRAB); 261c0abf9e6Sopenharmony_ci 262c0abf9e6Sopenharmony_ci if (!libevdev_has_event_code(dev, EV_ABS, ABS_X) || 263c0abf9e6Sopenharmony_ci !libevdev_has_event_code(dev, EV_ABS, ABS_Y)) { 264c0abf9e6Sopenharmony_ci fprintf(stderr, "Error: this device does not have abs axes\n"); 265c0abf9e6Sopenharmony_ci rc = EXIT_FAILURE; 266c0abf9e6Sopenharmony_ci goto out; 267c0abf9e6Sopenharmony_ci } 268c0abf9e6Sopenharmony_ci 269c0abf9e6Sopenharmony_ci dim.left = INT_MAX; 270c0abf9e6Sopenharmony_ci dim.right = INT_MIN; 271c0abf9e6Sopenharmony_ci dim.top = INT_MAX; 272c0abf9e6Sopenharmony_ci dim.bottom = INT_MIN; 273c0abf9e6Sopenharmony_ci 274c0abf9e6Sopenharmony_ci printf("Touchpad %s on %s\n", libevdev_get_name(dev), path); 275c0abf9e6Sopenharmony_ci printf("Move one finger around the touchpad to detect the actual edges\n"); 276c0abf9e6Sopenharmony_ci printf("Kernel says: x [%d..%d], y [%d..%d]\n", 277c0abf9e6Sopenharmony_ci libevdev_get_abs_minimum(dev, ABS_X), 278c0abf9e6Sopenharmony_ci libevdev_get_abs_maximum(dev, ABS_X), 279c0abf9e6Sopenharmony_ci libevdev_get_abs_minimum(dev, ABS_Y), 280c0abf9e6Sopenharmony_ci libevdev_get_abs_maximum(dev, ABS_Y)); 281c0abf9e6Sopenharmony_ci 282c0abf9e6Sopenharmony_ci setbuf(stdout, NULL); 283c0abf9e6Sopenharmony_ci 284c0abf9e6Sopenharmony_ci rc = mainloop(dev, &dim); 285c0abf9e6Sopenharmony_ci printf("\n\n"); 286c0abf9e6Sopenharmony_ci 287c0abf9e6Sopenharmony_ci print_udev_override_rule(dev, &dim, &size); 288c0abf9e6Sopenharmony_ci 289c0abf9e6Sopenharmony_ciout: 290c0abf9e6Sopenharmony_ci libevdev_free(dev); 291c0abf9e6Sopenharmony_ci close(fd); 292c0abf9e6Sopenharmony_ci 293c0abf9e6Sopenharmony_ci return rc; 294c0abf9e6Sopenharmony_ci} 295