• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
26 static int signalled = 0;
27 
28 static int
usage(const char * progname)29 usage(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 
38 struct dimensions {
39 	int top, bottom, left, right;
40 };
41 
42 struct size {
43 	int w, h;
44 };
45 
46 static int
print_current_values(const struct dimensions * d)47 print_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 
66 static int
handle_event(struct dimensions * d,const struct input_event * ev)67 handle_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 
90 static void
signal_handler(int signal)91 signal_handler(__attribute__((__unused__)) int signal)
92 {
93 	signalled++;
94 }
95 
96 static int
mainloop(struct libevdev * dev,struct dimensions * dim)97 mainloop(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 
133 static inline void
pid_vid_matchstr(struct libevdev * dev,char * match,size_t sz)134 pid_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 
142 static inline void
dmi_matchstr(struct libevdev * dev,char * match,size_t sz)143 dmi_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 
162 static void
print_udev_override_rule(struct libevdev * dev,const struct dimensions * dim,const struct size * size)163 print_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 
main(int argc,char ** argv)216 int 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 
289 out:
290 	libevdev_free(dev);
291 	close(fd);
292 
293 	return rc;
294 }
295