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