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