xref: /openbsd/sbin/wsconsctl/mousecfg.c (revision 3bef86f7)
1 /* $OpenBSD: mousecfg.c,v 1.10 2023/07/02 21:44:04 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 BASESIZE ((WSMOUSECFG__FILTERS - WSMOUSECFG_DX_SCALE) \
39     + (WSMOUSECFG__DEBUG - WSMOUSECFG_LOG_INPUT))
40 
41 static const int range[][2] = {
42 	{ WSMOUSECFG_DX_SCALE, WSMOUSECFG__FILTERS - 1 },
43 	{ WSMOUSECFG_LOG_INPUT, WSMOUSECFG__DEBUG - 1 },
44 	{ WSMOUSECFG_DX_MAX, WSMOUSECFG__TPFILTERS - 1 },
45 	{ WSMOUSECFG_SOFTBUTTONS, WSMOUSECFG__TPFEATURES - 1 },
46 	{ WSMOUSECFG_LEFT_EDGE, WSMOUSECFG__TPSETUP - 1 },
47 };
48 
49 static const int touchpad_types[] = {
50 	WSMOUSE_TYPE_SYNAPTICS,		/* Synaptics touchpad */
51 	WSMOUSE_TYPE_ALPS,		/* ALPS touchpad */
52 	WSMOUSE_TYPE_ELANTECH,		/* Elantech touchpad */
53 	WSMOUSE_TYPE_SYNAP_SBTN,	/* Synaptics soft buttons */
54 	WSMOUSE_TYPE_TOUCHPAD,		/* Generic touchpad */
55 };
56 
57 struct wsmouse_parameters cfg_tapping = {
58 	(struct wsmouse_param[]) {
59 	    { WSMOUSECFG_TAP_ONE_BTNMAP, 0 },
60 	    { WSMOUSECFG_TAP_TWO_BTNMAP, 0 },
61 	    { WSMOUSECFG_TAP_THREE_BTNMAP, 0 }, },
62 	3
63 };
64 
65 struct wsmouse_parameters cfg_mtbuttons = {
66 	(struct wsmouse_param[]) {
67 	    { WSMOUSECFG_MTBUTTONS, 0 }, },
68 	1
69 };
70 
71 struct wsmouse_parameters cfg_scaling = {
72 	(struct wsmouse_param[]) {
73 	    { WSMOUSECFG_DX_SCALE, 0 },
74 	    { WSMOUSECFG_DY_SCALE, 0 } },
75 	2
76 };
77 
78 struct wsmouse_parameters cfg_edges = {
79 	(struct wsmouse_param[]) {
80 	    { WSMOUSECFG_TOP_EDGE, 0 },
81 	    { WSMOUSECFG_RIGHT_EDGE, 0 },
82 	    { WSMOUSECFG_BOTTOM_EDGE, 0 },
83 	    { WSMOUSECFG_LEFT_EDGE, 0 } },
84 	4
85 };
86 
87 struct wsmouse_parameters cfg_swapsides = {
88 	(struct wsmouse_param[]) {
89 	    { WSMOUSECFG_SWAPSIDES, 0 }, },
90 	1
91 };
92 
93 struct wsmouse_parameters cfg_disable = {
94 	(struct wsmouse_param[]) {
95 	    { WSMOUSECFG_DISABLE, 0 }, },
96 	1
97 };
98 
99 struct wsmouse_parameters cfg_revscroll = {
100 	(struct wsmouse_param[]) {
101 	    { WSMOUSECFG_REVERSE_SCROLLING, 0 }, },
102 	1
103 };
104 
105 struct wsmouse_parameters cfg_param = {
106 	(struct wsmouse_param[]) {
107 	    { -1, 0 },
108 	    { -1, 0 },
109 	    { -1, 0 },
110 	    { -1, 0 } },
111 	4
112 };
113 
114 int cfg_touchpad;
115 
116 static int cfg_horiz_res;
117 static int cfg_vert_res;
118 static struct wsmouse_param cfg_buffer[WSMOUSECFG_MAX];
119 
120 
121 int
122 mousecfg_init(int dev_fd, const char **errstr)
123 {
124 	struct wsmouse_calibcoords coords;
125 	struct wsmouse_parameters parameters;
126 	struct wsmouse_param *param;
127 	enum wsmousecfg k;
128 	int i, err, type;
129 
130 	*errstr = NULL;
131 
132 	if ((err = ioctl(dev_fd, WSMOUSEIO_GTYPE, &type))) {
133 		*errstr = "WSMOUSEIO_GTYPE";
134 		return err;
135 	}
136 	cfg_touchpad = 0;
137 	for (i = 0; !cfg_touchpad && i < nitems(touchpad_types); i++)
138 		cfg_touchpad = (type == touchpad_types[i]);
139 
140 	cfg_horiz_res = cfg_vert_res = 0;
141 	if (cfg_touchpad) {
142 		if ((err = ioctl(dev_fd, WSMOUSEIO_GCALIBCOORDS, &coords))) {
143 			*errstr = "WSMOUSEIO_GCALIBCOORDS";
144 			return err;
145 		}
146 		cfg_horiz_res = coords.resx;
147 		cfg_vert_res = coords.resy;
148 	}
149 
150 	param = cfg_buffer;
151 	for (i = 0; i < nitems(range); i++)
152 		for (k = range[i][0]; k <= range[i][1]; k++, param++) {
153 			param->key = k;
154 			param->value = 0;
155 		}
156 
157 	parameters.params = cfg_buffer;
158 	parameters.nparams = BASESIZE;
159 	if ((err = ioctl(dev_fd, WSMOUSEIO_GETPARAMS, &parameters))) {
160 		*errstr = "WSMOUSEIO_GETPARAMS";
161 		return (err);
162 	}
163 	if (cfg_touchpad) {
164 		parameters.params = cfg_buffer + BASESIZE;
165 		parameters.nparams = WSMOUSECFG_MAX - BASESIZE;
166 		if (ioctl(dev_fd, WSMOUSEIO_GETPARAMS, &parameters))
167 			cfg_touchpad = 0;
168 	}
169 
170 	return (0);
171 }
172 
173 /* Map a key to its buffer index. */
174 static int
175 index_of(enum wsmousecfg key)
176 {
177 	int i, n;
178 
179 	for (i = 0, n = 0; i < nitems(range); i++) {
180 		if (key <= range[i][1] && key >= range[i][0]) {
181 			return (key - range[i][0] + n);
182 		}
183 		n += range[i][1] - range[i][0] + 1;
184 		if (!cfg_touchpad && n >= BASESIZE)
185 			break;
186 	}
187 
188 	return (-1);
189 }
190 
191 int
192 mousecfg_get_field(struct wsmouse_parameters *field)
193 {
194 	int i, n;
195 
196 	for (i = 0; i < field->nparams; i++) {
197 		if ((n = index_of(field->params[i].key)) >= 0)
198 			field->params[i].value = cfg_buffer[n].value;
199 		else
200 			return (-1);
201 	}
202 	return (0);
203 }
204 
205 int
206 mousecfg_put_field(int fd, struct wsmouse_parameters *field)
207 {
208 	int i, n, d, err;
209 
210 	d = 0;
211 	for (i = 0; i < field->nparams; i++)
212 		if ((n = index_of(field->params[i].key)) < 0)
213 			return (-1);
214 		else
215 			d |= (cfg_buffer[n].value != field->params[i].value);
216 
217 	if (!d)
218 		return (0);
219 
220 	/* Write and read back immediately, wsmouse may normalize values. */
221 	if ((err = ioctl(fd, WSMOUSEIO_SETPARAMS, field))
222 	    || (err = ioctl(fd, WSMOUSEIO_GETPARAMS, field)))
223 		return err;
224 
225 	for (i = 0; i < field->nparams; i++)
226 		cfg_buffer[n].value = field->params[i].value;
227 
228 	return (0);
229 }
230 
231 static int
232 get_value(struct wsmouse_parameters *field, enum wsmousecfg key)
233 {
234 	int i;
235 
236 	for (i = 0; i < field->nparams && key != field->params[i].key; i++) {}
237 
238 	return (i < field->nparams ? field->params[i].value : 0);
239 }
240 
241 static void
242 set_value(struct wsmouse_parameters *field, enum wsmousecfg key, int value)
243 {
244 	int i;
245 
246 	for (i = 0; i < field->nparams && key != field->params[i].key; i++) {}
247 
248 	field->params[i].value = (i < field->nparams ? value : 0);
249 }
250 
251 static float
252 get_percent(struct wsmouse_parameters *field, enum wsmousecfg key)
253 {
254 	return ((float) get_value(field, key) * 100 / 4096);
255 }
256 
257 static void
258 set_percent(struct wsmouse_parameters *field, enum wsmousecfg key, float f)
259 {
260 	set_value(field, key, (int) ((f * 4096 + 50) / 100));
261 }
262 
263 static int
264 set_tapping(struct wsmouse_parameters *field, char *tapping)
265 {
266 	int i1, i2, i3;
267 
268 	switch (sscanf(tapping, "%d,%d,%d", &i1, &i2, &i3)) {
269 	case 1:
270 		if (i1 == 0) /* Disable */
271 			i2 = i3 = i1;
272 		else { /* Enable with defaults */
273 			i1 = 1; /* Left click */
274 			i2 = 3; /* Right click */
275 			i3 = 2; /* Middle click */
276 		}
277 		/* FALLTHROUGH */
278 	case 3:
279 		set_value(field, WSMOUSECFG_TAP_ONE_BTNMAP, i1);
280 		set_value(field, WSMOUSECFG_TAP_TWO_BTNMAP, i2);
281 		set_value(field, WSMOUSECFG_TAP_THREE_BTNMAP, i3);
282 		return (0);
283 	}
284 	return (-1);
285 }
286 
287 static int
288 set_edges(struct wsmouse_parameters *field, char *edges)
289 {
290 	float f1, f2, f3, f4;
291 
292 	if (sscanf(edges, "%f,%f,%f,%f", &f1, &f2, &f3, &f4) == 4) {
293 		set_percent(field, WSMOUSECFG_TOP_EDGE, f1);
294 		set_percent(field, WSMOUSECFG_RIGHT_EDGE,f2);
295 		set_percent(field, WSMOUSECFG_BOTTOM_EDGE, f3);
296 		set_percent(field, WSMOUSECFG_LEFT_EDGE, f4);
297 		return (0);
298 	}
299 	return (-1);
300 }
301 
302 /*
303  * Read or write up to four raw parameter values.  In this case
304  * reading is a 'put' operation that writes back a value from the
305  * buffer.
306  */
307 static int
308 read_param(struct wsmouse_parameters *field, char *val)
309 {
310 	int i, j, n;
311 
312 	n = sscanf(val, "%d:%d,%d:%d,%d:%d,%d:%d",
313 		&field->params[0].key, &field->params[0].value,
314 		&field->params[1].key, &field->params[1].value,
315 		&field->params[2].key, &field->params[2].value,
316 		&field->params[3].key, &field->params[3].value);
317 	if (n > 0 && (n & 1) == 0) {
318 		n /= 2;
319 		for (i = 0; i < n; i++) {
320 			if (index_of(field->params[i].key) < 0)
321 				return (-1);
322 		}
323 		field->nparams = n;
324 		return (0);
325 	}
326 	n = sscanf(val, "%d,%d,%d,%d",
327 		&field->params[0].key, &field->params[1].key,
328 		&field->params[2].key, &field->params[3].key);
329 	if (n > 0) {
330 		for (i = 0; i < n; i++) {
331 			if ((j = index_of(field->params[i].key)) < 0)
332 				return (-1);
333 			field->params[i].value = cfg_buffer[j].value;
334 		}
335 		field->nparams = n;
336 		return (0);
337 	}
338 	return (-1);
339 }
340 
341 void
342 mousecfg_pr_field(struct wsmouse_parameters *field)
343 {
344 	int i, value;
345 	float f;
346 
347 	if (field == &cfg_param) {
348 		for (i = 0; i < field->nparams; i++)
349 			printf(i > 0 ? ",%d:%d" : "%d:%d",
350 			    field->params[i].key,
351 			    field->params[i].value);
352 		return;
353 	}
354 
355 	if (field == &cfg_scaling) {
356 		value = get_value(field, WSMOUSECFG_DX_SCALE);
357 		f = (float) value / 4096;
358 		printf("%.3f", f);
359 		return;
360 	}
361 
362 	if (field == &cfg_edges) {
363 		printf("%.1f,%.1f,%.1f,%.1f",
364 		    get_percent(field, WSMOUSECFG_TOP_EDGE),
365 		    get_percent(field, WSMOUSECFG_RIGHT_EDGE),
366 		    get_percent(field, WSMOUSECFG_BOTTOM_EDGE),
367 		    get_percent(field, WSMOUSECFG_LEFT_EDGE));
368 		return;
369 	}
370 
371 	for (i = 0; i < field->nparams; i++)
372 		printf(i > 0 ? ",%d" : "%d", field->params[i].value);
373 }
374 
375 void
376 mousecfg_rd_field(struct wsmouse_parameters *field, char *val)
377 {
378 	int i, n;
379 	const char *s;
380 	float f;
381 
382 	if (field == &cfg_param) {
383 		if (read_param(field, val))
384 			errx(1, "invalid input (param)");
385 		return;
386 	}
387 
388 	if (field == &cfg_tapping) {
389 		if (set_tapping(field, val))
390 			errx(1, "invalid input (tapping)");
391 		return;
392 	}
393 
394 	if (field == &cfg_scaling) {
395 		if (sscanf(val, "%f", &f) == 1) {
396 			n = (int) (f * 4096);
397 			set_value(field, WSMOUSECFG_DX_SCALE, n);
398 			if (cfg_horiz_res && cfg_vert_res)
399 				n = n * cfg_horiz_res / cfg_vert_res;
400 			set_value(field, WSMOUSECFG_DY_SCALE, n);
401 		} else {
402 			errx(1, "invalid input (scaling)");
403 		}
404 		return;
405 	}
406 
407 	if (field == &cfg_edges) {
408 		if (set_edges(field, val))
409 			errx(1, "invalid input (edges)");
410 		return;
411 	}
412 
413 	s = val;
414 	for (i = 0; i < field->nparams; i++) {
415 		if (sscanf(s, (i > 0 ? ",%d" : "%d"), &n) != 1)
416 			break;
417 		field->params[i].value = abs(n);
418 		for (s++; *s != '\0' && *s != ','; s++) {}
419 	}
420 	if (i < field->nparams || *s != '\0')
421 		errx(1, "invalid input '%s'", val);
422 }
423