xref: /openbsd-src/sbin/wsconsctl/mousecfg.c (revision f1dd7b858388b4a23f4f67a4957ec5ff656ebbe8)
1 /* $OpenBSD: mousecfg.c,v 1.9 2021/03/03 19:44:37 bru Exp $ */
2 
3 /*
4  * Copyright (c) 2017 Ulf Brosziewski
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Read/write wsmouse parameters for touchpad configuration.
21  */
22 
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
25 #include <sys/time.h>
26 #include <dev/wscons/wsconsio.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <err.h>
31 #include <errno.h>
32 #include "mousecfg.h"
33 
34 #ifndef nitems
35 #define nitems(_a)       (sizeof((_a)) / sizeof((_a)[0]))
36 #endif
37 
38 #define BASE_FIRST		WSMOUSECFG_DX_SCALE
39 #define BASE_LAST		WSMOUSECFG_REVERSE_SCROLLING
40 #define TP_FILTER_FIRST		WSMOUSECFG_DX_MAX
41 #define TP_FILTER_LAST		WSMOUSECFG_SMOOTHING
42 #define TP_FEATURES_FIRST	WSMOUSECFG_SOFTBUTTONS
43 #define TP_FEATURES_LAST	WSMOUSECFG_DISABLE
44 #define TP_SETUP_FIRST		WSMOUSECFG_LEFT_EDGE
45 #define TP_SETUP_LAST		WSMOUSECFG_TAP_THREE_BTNMAP
46 #define LOG_FIRST		WSMOUSECFG_LOG_INPUT
47 #define LOG_LAST		WSMOUSECFG_LOG_EVENTS
48 
49 #define BASESIZE ((BASE_LAST - BASE_FIRST + 1) + (LOG_LAST - LOG_FIRST + 1))
50 
51 #define BUFSIZE (BASESIZE \
52     + (TP_FILTER_LAST - TP_FILTER_FIRST + 1) \
53     + (TP_FEATURES_LAST - TP_FEATURES_FIRST + 1) \
54     + (TP_SETUP_LAST - TP_SETUP_FIRST + 1))
55 
56 static const int range[][2] = {
57 	{ BASE_FIRST, BASE_LAST },
58 	{ LOG_FIRST, LOG_LAST },
59 	{ TP_FILTER_FIRST, TP_FILTER_LAST },
60 	{ TP_FEATURES_FIRST, TP_FEATURES_LAST },
61 	{ TP_SETUP_FIRST, TP_SETUP_LAST },
62 };
63 
64 static const int touchpad_types[] = {
65 	WSMOUSE_TYPE_SYNAPTICS,		/* Synaptics touchpad */
66 	WSMOUSE_TYPE_ALPS,		/* ALPS touchpad */
67 	WSMOUSE_TYPE_ELANTECH,		/* Elantech touchpad */
68 	WSMOUSE_TYPE_SYNAP_SBTN,	/* Synaptics soft buttons */
69 	WSMOUSE_TYPE_TOUCHPAD,		/* Generic touchpad */
70 };
71 
72 struct wsmouse_parameters cfg_tapping = {
73 	(struct wsmouse_param[]) {
74 	    { WSMOUSECFG_TAP_ONE_BTNMAP, 0 },
75 	    { WSMOUSECFG_TAP_TWO_BTNMAP, 0 },
76 	    { WSMOUSECFG_TAP_THREE_BTNMAP, 0 }, },
77 	3
78 };
79 
80 struct wsmouse_parameters cfg_scaling = {
81 	(struct wsmouse_param[]) {
82 	    { WSMOUSECFG_DX_SCALE, 0 },
83 	    { WSMOUSECFG_DY_SCALE, 0 } },
84 	2
85 };
86 
87 struct wsmouse_parameters cfg_edges = {
88 	(struct wsmouse_param[]) {
89 	    { WSMOUSECFG_TOP_EDGE, 0 },
90 	    { WSMOUSECFG_RIGHT_EDGE, 0 },
91 	    { WSMOUSECFG_BOTTOM_EDGE, 0 },
92 	    { WSMOUSECFG_LEFT_EDGE, 0 } },
93 	4
94 };
95 
96 struct wsmouse_parameters cfg_swapsides = {
97 	(struct wsmouse_param[]) {
98 	    { WSMOUSECFG_SWAPSIDES, 0 }, },
99 	1
100 };
101 
102 struct wsmouse_parameters cfg_disable = {
103 	(struct wsmouse_param[]) {
104 	    { WSMOUSECFG_DISABLE, 0 }, },
105 	1
106 };
107 
108 struct wsmouse_parameters cfg_revscroll = {
109 	(struct wsmouse_param[]) {
110 	    { WSMOUSECFG_REVERSE_SCROLLING, 0 }, },
111 	1
112 };
113 
114 struct wsmouse_parameters cfg_param = {
115 	(struct wsmouse_param[]) {
116 	    { -1, 0 },
117 	    { -1, 0 },
118 	    { -1, 0 },
119 	    { -1, 0 } },
120 	4
121 };
122 
123 int cfg_touchpad;
124 
125 static int cfg_horiz_res;
126 static int cfg_vert_res;
127 static struct wsmouse_param cfg_buffer[BUFSIZE];
128 
129 
130 int
131 mousecfg_init(int dev_fd, const char **errstr)
132 {
133 	struct wsmouse_calibcoords coords;
134 	struct wsmouse_parameters parameters;
135 	struct wsmouse_param *param;
136 	enum wsmousecfg k;
137 	int i, err, type;
138 
139 	*errstr = NULL;
140 
141 	if ((err = ioctl(dev_fd, WSMOUSEIO_GTYPE, &type))) {
142 		*errstr = "WSMOUSEIO_GTYPE";
143 		return err;
144 	}
145 	cfg_touchpad = 0;
146 	for (i = 0; !cfg_touchpad && i < nitems(touchpad_types); i++)
147 		cfg_touchpad = (type == touchpad_types[i]);
148 
149 	cfg_horiz_res = cfg_vert_res = 0;
150 	if (cfg_touchpad) {
151 		if ((err = ioctl(dev_fd, WSMOUSEIO_GCALIBCOORDS, &coords))) {
152 			*errstr = "WSMOUSEIO_GCALIBCOORDS";
153 			return err;
154 		}
155 		cfg_horiz_res = coords.resx;
156 		cfg_vert_res = coords.resy;
157 	}
158 
159 	param = cfg_buffer;
160 	for (i = 0; i < nitems(range); i++)
161 		for (k = range[i][0]; k <= range[i][1]; k++, param++) {
162 			param->key = k;
163 			param->value = 0;
164 		}
165 
166 	parameters.params = cfg_buffer;
167 	parameters.nparams = BASESIZE;
168 	if ((err = ioctl(dev_fd, WSMOUSEIO_GETPARAMS, &parameters))) {
169 		*errstr = "WSMOUSEIO_GETPARAMS";
170 		return (err);
171 	}
172 	if (cfg_touchpad) {
173 		parameters.params = cfg_buffer + BASESIZE;
174 		parameters.nparams = BUFSIZE - BASESIZE;
175 		if (ioctl(dev_fd, WSMOUSEIO_GETPARAMS, &parameters))
176 			cfg_touchpad = 0;
177 	}
178 
179 	return (0);
180 }
181 
182 /* Map a key to its buffer index. */
183 static int
184 index_of(enum wsmousecfg key)
185 {
186 	int i, n;
187 
188 	for (i = 0, n = 0; i < nitems(range); i++) {
189 		if (key <= range[i][1] && key >= range[i][0]) {
190 			return (key - range[i][0] + n);
191 		}
192 		n += range[i][1] - range[i][0] + 1;
193 		if (!cfg_touchpad && n >= BASESIZE)
194 			break;
195 	}
196 
197 	return (-1);
198 }
199 
200 int
201 mousecfg_get_field(struct wsmouse_parameters *field)
202 {
203 	int i, n;
204 
205 	for (i = 0; i < field->nparams; i++) {
206 		if ((n = index_of(field->params[i].key)) >= 0)
207 			field->params[i].value = cfg_buffer[n].value;
208 		else
209 			return (-1);
210 	}
211 	return (0);
212 }
213 
214 int
215 mousecfg_put_field(int fd, struct wsmouse_parameters *field)
216 {
217 	int i, n, d, err;
218 
219 	d = 0;
220 	for (i = 0; i < field->nparams; i++)
221 		if ((n = index_of(field->params[i].key)) < 0)
222 			return (-1);
223 		else
224 			d |= (cfg_buffer[n].value != field->params[i].value);
225 
226 	if (!d)
227 		return (0);
228 
229 	/* Write and read back immediately, wsmouse may normalize values. */
230 	if ((err = ioctl(fd, WSMOUSEIO_SETPARAMS, field))
231 	    || (err = ioctl(fd, WSMOUSEIO_GETPARAMS, field)))
232 		return err;
233 
234 	for (i = 0; i < field->nparams; i++)
235 		cfg_buffer[n].value = field->params[i].value;
236 
237 	return (0);
238 }
239 
240 static int
241 get_value(struct wsmouse_parameters *field, enum wsmousecfg key)
242 {
243 	int i;
244 
245 	for (i = 0; i < field->nparams && key != field->params[i].key; i++) {}
246 
247 	return (i < field->nparams ? field->params[i].value : 0);
248 }
249 
250 static void
251 set_value(struct wsmouse_parameters *field, enum wsmousecfg key, int value)
252 {
253 	int i;
254 
255 	for (i = 0; i < field->nparams && key != field->params[i].key; i++) {}
256 
257 	field->params[i].value = (i < field->nparams ? value : 0);
258 }
259 
260 static float
261 get_percent(struct wsmouse_parameters *field, enum wsmousecfg key)
262 {
263 	return ((float) get_value(field, key) * 100 / 4096);
264 }
265 
266 static void
267 set_percent(struct wsmouse_parameters *field, enum wsmousecfg key, float f)
268 {
269 	set_value(field, key, (int) ((f * 4096 + 50) / 100));
270 }
271 
272 static int
273 set_tapping(struct wsmouse_parameters *field, char *tapping)
274 {
275 	int i1, i2, i3;
276 
277 	switch (sscanf(tapping, "%d,%d,%d", &i1, &i2, &i3)) {
278 	case 1:
279 		if (i1 == 0) /* Disable */
280 			i2 = i3 = i1;
281 		else { /* Enable with defaults */
282 			i1 = 1; /* Left click */
283 			i2 = 3; /* Right click */
284 			i3 = 2; /* Middle click */
285 		}
286 		/* FALLTHROUGH */
287 	case 3:
288 		set_value(field, WSMOUSECFG_TAP_ONE_BTNMAP, i1);
289 		set_value(field, WSMOUSECFG_TAP_TWO_BTNMAP, i2);
290 		set_value(field, WSMOUSECFG_TAP_THREE_BTNMAP, i3);
291 		return (0);
292 	}
293 	return (-1);
294 }
295 
296 static int
297 set_edges(struct wsmouse_parameters *field, char *edges)
298 {
299 	float f1, f2, f3, f4;
300 
301 	if (sscanf(edges, "%f,%f,%f,%f", &f1, &f2, &f3, &f4) == 4) {
302 		set_percent(field, WSMOUSECFG_TOP_EDGE, f1);
303 		set_percent(field, WSMOUSECFG_RIGHT_EDGE,f2);
304 		set_percent(field, WSMOUSECFG_BOTTOM_EDGE, f3);
305 		set_percent(field, WSMOUSECFG_LEFT_EDGE, f4);
306 		return (0);
307 	}
308 	return (-1);
309 }
310 
311 /*
312  * Read or write up to four raw parameter values.  In this case
313  * reading is a 'put' operation that writes back a value from the
314  * buffer.
315  */
316 static int
317 read_param(struct wsmouse_parameters *field, char *val)
318 {
319 	int i, j, n;
320 
321 	n = sscanf(val, "%d:%d,%d:%d,%d:%d,%d:%d",
322 		&field->params[0].key, &field->params[0].value,
323 		&field->params[1].key, &field->params[1].value,
324 		&field->params[2].key, &field->params[2].value,
325 		&field->params[3].key, &field->params[3].value);
326 	if (n > 0 && (n & 1) == 0) {
327 		n /= 2;
328 		for (i = 0; i < n; i++) {
329 			if (index_of(field->params[i].key) < 0)
330 				return (-1);
331 		}
332 		field->nparams = n;
333 		return (0);
334 	}
335 	n = sscanf(val, "%d,%d,%d,%d",
336 		&field->params[0].key, &field->params[1].key,
337 		&field->params[2].key, &field->params[3].key);
338 	if (n > 0) {
339 		for (i = 0; i < n; i++) {
340 			if ((j = index_of(field->params[i].key)) < 0)
341 				return (-1);
342 			field->params[i].value = cfg_buffer[j].value;
343 		}
344 		field->nparams = n;
345 		return (0);
346 	}
347 	return (-1);
348 }
349 
350 void
351 mousecfg_pr_field(struct wsmouse_parameters *field)
352 {
353 	int i, value;
354 	float f;
355 
356 	if (field == &cfg_param) {
357 		for (i = 0; i < field->nparams; i++)
358 			printf(i > 0 ? ",%d:%d" : "%d:%d",
359 			    field->params[i].key,
360 			    field->params[i].value);
361 		return;
362 	}
363 
364 	if (field == &cfg_scaling) {
365 		value = get_value(field, WSMOUSECFG_DX_SCALE);
366 		f = (float) value / 4096;
367 		printf("%.3f", f);
368 		return;
369 	}
370 
371 	if (field == &cfg_edges) {
372 		printf("%.1f,%.1f,%.1f,%.1f",
373 		    get_percent(field, WSMOUSECFG_TOP_EDGE),
374 		    get_percent(field, WSMOUSECFG_RIGHT_EDGE),
375 		    get_percent(field, WSMOUSECFG_BOTTOM_EDGE),
376 		    get_percent(field, WSMOUSECFG_LEFT_EDGE));
377 		return;
378 	}
379 
380 	for (i = 0; i < field->nparams; i++)
381 		printf(i > 0 ? ",%d" : "%d", field->params[i].value);
382 }
383 
384 void
385 mousecfg_rd_field(struct wsmouse_parameters *field, char *val)
386 {
387 	int i, n;
388 	const char *s;
389 	float f;
390 
391 	if (field == &cfg_param) {
392 		if (read_param(field, val))
393 			errx(1, "invalid input (param)");
394 		return;
395 	}
396 
397 	if (field == &cfg_tapping) {
398 		if (set_tapping(field, val))
399 			errx(1, "invalid input (tapping)");
400 		return;
401 	}
402 
403 	if (field == &cfg_scaling) {
404 		if (sscanf(val, "%f", &f) == 1) {
405 			n = (int) (f * 4096);
406 			set_value(field, WSMOUSECFG_DX_SCALE, n);
407 			if (cfg_horiz_res && cfg_vert_res)
408 				n = n * cfg_horiz_res / cfg_vert_res;
409 			set_value(field, WSMOUSECFG_DY_SCALE, n);
410 		} else {
411 			errx(1, "invalid input (scaling)");
412 		}
413 		return;
414 	}
415 
416 	if (field == &cfg_edges) {
417 		if (set_edges(field, val))
418 			errx(1, "invalid input (edges)");
419 		return;
420 	}
421 
422 	s = val;
423 	for (i = 0; i < field->nparams; i++) {
424 		if (sscanf(s, (i > 0 ? ",%d" : "%d"), &n) != 1)
425 			break;
426 		field->params[i].value = abs(n);
427 		for (s++; *s != '\0' && *s != ','; s++) {}
428 	}
429 	if (i < field->nparams || *s != '\0')
430 		errx(1, "invalid input '%s'", val);
431 }
432