1 /* $OpenBSD: mousecfg.c,v 1.12 2024/10/05 13:27:16 chrisz 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
mousecfg_init(int dev_fd,const char ** errstr)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, ¶meters))) {
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, ¶meters))
167 cfg_touchpad = 0;
168 }
169
170 return (0);
171 }
172
173 /* Map a key to its buffer index. */
174 static int
index_of(enum wsmousecfg key)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
mousecfg_get_field(struct wsmouse_parameters * field)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
mousecfg_put_field(int fd,struct wsmouse_parameters * field)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 n = index_of(field->params[i].key);
227 cfg_buffer[n].value = field->params[i].value;
228 }
229
230 return (0);
231 }
232
233 static int
get_value(struct wsmouse_parameters * field,enum wsmousecfg key)234 get_value(struct wsmouse_parameters *field, enum wsmousecfg key)
235 {
236 int i;
237
238 for (i = 0; i < field->nparams && key != field->params[i].key; i++) {}
239
240 return (i < field->nparams ? field->params[i].value : 0);
241 }
242
243 static void
set_value(struct wsmouse_parameters * field,enum wsmousecfg key,int value)244 set_value(struct wsmouse_parameters *field, enum wsmousecfg key, int value)
245 {
246 int i;
247
248 for (i = 0; i < field->nparams && key != field->params[i].key; i++) {}
249
250 field->params[i].value = (i < field->nparams ? value : 0);
251 }
252
253 static float
get_percent(struct wsmouse_parameters * field,enum wsmousecfg key)254 get_percent(struct wsmouse_parameters *field, enum wsmousecfg key)
255 {
256 return ((float) get_value(field, key) * 100 / 4096);
257 }
258
259 static void
set_percent(struct wsmouse_parameters * field,enum wsmousecfg key,float f)260 set_percent(struct wsmouse_parameters *field, enum wsmousecfg key, float f)
261 {
262 set_value(field, key, (int) ((f * 4096 + 50) / 100));
263 }
264
265 static int
set_tapping(struct wsmouse_parameters * field,char * tapping)266 set_tapping(struct wsmouse_parameters *field, char *tapping)
267 {
268 int i1, i2, i3;
269
270 switch (sscanf(tapping, "%d,%d,%d", &i1, &i2, &i3)) {
271 case 1:
272 if (i1 == 0) /* Disable */
273 i2 = i3 = i1;
274 else { /* Enable with defaults */
275 i1 = 1; /* Left click */
276 i2 = 3; /* Right click */
277 i3 = 2; /* Middle click */
278 }
279 /* FALLTHROUGH */
280 case 3:
281 set_value(field, WSMOUSECFG_TAP_ONE_BTNMAP, i1);
282 set_value(field, WSMOUSECFG_TAP_TWO_BTNMAP, i2);
283 set_value(field, WSMOUSECFG_TAP_THREE_BTNMAP, i3);
284 return (0);
285 }
286 return (-1);
287 }
288
289 static int
set_edges(struct wsmouse_parameters * field,char * edges)290 set_edges(struct wsmouse_parameters *field, char *edges)
291 {
292 float f1, f2, f3, f4;
293
294 if (sscanf(edges, "%f,%f,%f,%f", &f1, &f2, &f3, &f4) == 4) {
295 set_percent(field, WSMOUSECFG_TOP_EDGE, f1);
296 set_percent(field, WSMOUSECFG_RIGHT_EDGE,f2);
297 set_percent(field, WSMOUSECFG_BOTTOM_EDGE, f3);
298 set_percent(field, WSMOUSECFG_LEFT_EDGE, f4);
299 return (0);
300 }
301 return (-1);
302 }
303
304 /*
305 * Read or write up to four raw parameter values. In this case
306 * reading is a 'put' operation that writes back a value from the
307 * buffer.
308 */
309 static int
read_param(struct wsmouse_parameters * field,char * val)310 read_param(struct wsmouse_parameters *field, char *val)
311 {
312 int i, j, n;
313
314 n = sscanf(val, "%d:%d,%d:%d,%d:%d,%d:%d",
315 &field->params[0].key, &field->params[0].value,
316 &field->params[1].key, &field->params[1].value,
317 &field->params[2].key, &field->params[2].value,
318 &field->params[3].key, &field->params[3].value);
319 if (n > 0 && (n & 1) == 0) {
320 n /= 2;
321 for (i = 0; i < n; i++) {
322 if (index_of(field->params[i].key) < 0)
323 return (-1);
324 }
325 field->nparams = n;
326 return (0);
327 }
328 n = sscanf(val, "%d,%d,%d,%d",
329 &field->params[0].key, &field->params[1].key,
330 &field->params[2].key, &field->params[3].key);
331 if (n > 0) {
332 for (i = 0; i < n; i++) {
333 if ((j = index_of(field->params[i].key)) < 0)
334 return (-1);
335 field->params[i].value = cfg_buffer[j].value;
336 }
337 field->nparams = n;
338 return (0);
339 }
340 return (-1);
341 }
342
343 void
mousecfg_pr_field(struct wsmouse_parameters * field)344 mousecfg_pr_field(struct wsmouse_parameters *field)
345 {
346 int i, value;
347 float f;
348
349 if (field == &cfg_param) {
350 for (i = 0; i < field->nparams; i++)
351 printf(i > 0 ? ",%d:%d" : "%d:%d",
352 field->params[i].key,
353 field->params[i].value);
354 return;
355 }
356
357 if (field == &cfg_scaling) {
358 value = get_value(field, WSMOUSECFG_DX_SCALE);
359 value = value == 0 ? 4096 : value;
360 f = (float) value / 4096;
361 printf("%.3f", f);
362 return;
363 }
364
365 if (field == &cfg_edges) {
366 printf("%.1f,%.1f,%.1f,%.1f",
367 get_percent(field, WSMOUSECFG_TOP_EDGE),
368 get_percent(field, WSMOUSECFG_RIGHT_EDGE),
369 get_percent(field, WSMOUSECFG_BOTTOM_EDGE),
370 get_percent(field, WSMOUSECFG_LEFT_EDGE));
371 return;
372 }
373
374 for (i = 0; i < field->nparams; i++)
375 printf(i > 0 ? ",%d" : "%d", field->params[i].value);
376 }
377
378 void
mousecfg_rd_field(struct wsmouse_parameters * field,char * val)379 mousecfg_rd_field(struct wsmouse_parameters *field, char *val)
380 {
381 int i, n;
382 const char *s;
383 float f;
384
385 if (field == &cfg_param) {
386 if (read_param(field, val))
387 errx(1, "invalid input (param)");
388 return;
389 }
390
391 if (field == &cfg_tapping) {
392 if (set_tapping(field, val))
393 errx(1, "invalid input (tapping)");
394 return;
395 }
396
397 if (field == &cfg_scaling) {
398 if (sscanf(val, "%f", &f) == 1) {
399 n = (int) (f * 4096);
400 set_value(field, WSMOUSECFG_DX_SCALE, n);
401 if (cfg_horiz_res && cfg_vert_res)
402 n = n * cfg_horiz_res / cfg_vert_res;
403 set_value(field, WSMOUSECFG_DY_SCALE, n);
404 } else {
405 errx(1, "invalid input (scaling)");
406 }
407 return;
408 }
409
410 if (field == &cfg_edges) {
411 if (set_edges(field, val))
412 errx(1, "invalid input (edges)");
413 return;
414 }
415
416 s = val;
417 for (i = 0; i < field->nparams; i++) {
418 if (sscanf(s, (i > 0 ? ",%d" : "%d"), &n) != 1)
419 break;
420 field->params[i].value = abs(n);
421 for (s++; *s != '\0' && *s != ','; s++) {}
422 }
423 if (i < field->nparams || *s != '\0')
424 errx(1, "invalid input '%s'", val);
425 }
426