1 /*        $NetBSD: mouse.c,v 1.11 2021/09/28 06:20:09 nia Exp $ */
2 
3 /*-
4  * Copyright (c) 1998, 2006, 2012 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Juergen Hannken-Illjes and Julio M. Merino Vidal.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/ioctl.h>
33 #include <sys/time.h>
34 #include <dev/wscons/wsconsio.h>
35 
36 #include <err.h>
37 #include <errno.h>
38 #include <limits.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 #include "wsconsctl.h"
44 
45 static int reverse_scrolling;
46 static int horiz_scroll_dist;
47 static int vert_scroll_dist;
48 static int mstype;
49 static int resolution;
50 static int samplerate;
51 static struct wsmouse_calibcoords calibration;
52 static char *calibration_samples;
53 static struct wsmouse_repeat repeat;
54 
55 static void mouse_get_parameters(int);
56 static void mouse_put_parameters(int);
57 
58 static void mouse_get_calibration(int);
59 static void mouse_put_calibration(int);
60 
61 static void mouse_get_repeat(int);
62 static void mouse_put_repeat(int);
63 
64 struct field mouse_field_tab[] = {
65     { "resolution",           &resolution,                  FMT_UINT, FLG_WRONLY },
66     { "samplerate",           &samplerate,                  FMT_UINT, FLG_WRONLY },
67     { "type",                           &mstype,            FMT_MSTYPE,         FLG_RDONLY },
68     { "scroll.reverse",                 &reverse_scrolling, FMT_INT,  FLG_MODIFY },
69     { "scroll.distance.x",    &horiz_scroll_dist, FMT_INT,  FLG_MODIFY },
70     { "scroll.distance.y",    &vert_scroll_dist,  FMT_INT,  FLG_MODIFY },
71     { "calibration.minx",     &calibration.minx,
72                                                                                 FMT_INT,  FLG_MODIFY },
73     { "calibration.miny",     &calibration.miny,
74                                                                       FMT_INT,  FLG_MODIFY },
75     { "calibration.maxx",     &calibration.maxx,
76                                                                       FMT_INT,  FLG_MODIFY },
77     { "calibration.maxy",     &calibration.maxy,
78                                                                       FMT_INT,  FLG_MODIFY },
79     { "calibration.samples",  &calibration_samples,
80                                                                       FMT_STRING,         FLG_MODIFY },
81     { "repeat.buttons",                 &repeat.wr_buttons,
82                                                                       FMT_BITFIELD, FLG_MODIFY },
83     { "repeat.delay.first",   &repeat.wr_delay_first,
84                                                                       FMT_UINT, FLG_MODIFY },
85     { "repeat.delay.decrement",         &repeat.wr_delay_decrement,
86                                                                       FMT_UINT, FLG_MODIFY },
87     { "repeat.delay.minimum", &repeat.wr_delay_minimum,
88                                                                       FMT_UINT, FLG_MODIFY },
89 };
90 
91 int mouse_field_tab_len = sizeof(mouse_field_tab)/
92                                  sizeof(mouse_field_tab[0]);
93 
94 void
mouse_get_values(int fd)95 mouse_get_values(int fd)
96 {
97 
98           if (field_by_value(&mstype)->flags & FLG_GET)
99                     if (ioctl(fd, WSMOUSEIO_GTYPE, &mstype) < 0)
100                               err(EXIT_FAILURE, "WSMOUSEIO_GTYPE");
101 
102           if (field_by_value(&calibration.minx)->flags & FLG_GET ||
103               field_by_value(&calibration.miny)->flags & FLG_GET ||
104               field_by_value(&calibration.maxx)->flags & FLG_GET ||
105               field_by_value(&calibration.maxy)->flags & FLG_GET ||
106               field_by_value(&calibration_samples)->flags & FLG_GET)
107                     mouse_get_calibration(fd);
108 
109           if (field_by_value(&repeat.wr_buttons)->flags & FLG_GET ||
110               field_by_value(&repeat.wr_delay_first)->flags & FLG_GET ||
111               field_by_value(&repeat.wr_delay_decrement)->flags & FLG_GET ||
112               field_by_value(&repeat.wr_delay_minimum)->flags & FLG_GET)
113                     mouse_get_repeat(fd);
114 
115           if (field_by_value(&horiz_scroll_dist)->flags & FLG_GET ||
116               field_by_value(&vert_scroll_dist)->flags & FLG_GET ||
117               field_by_value(&reverse_scrolling)->flags & FLG_GET)
118                     mouse_get_parameters(fd);
119 }
120 
121 static void
mouse_get_parameters(int fd)122 mouse_get_parameters(int fd)
123 {
124           struct wsmouse_param params[WSMOUSECFG_MAX];
125           struct wsmouse_parameters pl;
126           unsigned int i;
127 
128           pl.nparams = 0;
129           pl.params = params;
130 
131           if (field_by_value(&reverse_scrolling)->flags & FLG_GET)
132                     params[pl.nparams++].key = WSMOUSECFG_REVERSE_SCROLLING;
133           if (field_by_value(&horiz_scroll_dist)->flags & FLG_GET)
134                     params[pl.nparams++].key = WSMOUSECFG_HORIZSCROLLDIST;
135           if (field_by_value(&vert_scroll_dist)->flags & FLG_GET)
136                     params[pl.nparams++].key = WSMOUSECFG_VERTSCROLLDIST;
137 
138           if (ioctl(fd, WSMOUSEIO_GETPARAMS, &pl) < 0) {
139                     if (field_by_value(&horiz_scroll_dist)->flags & FLG_GET)
140                               field_disable_by_value(&horiz_scroll_dist);
141                     if (field_by_value(&vert_scroll_dist)->flags & FLG_GET)
142                               field_disable_by_value(&vert_scroll_dist);
143                     if (field_by_value(&reverse_scrolling)->flags & FLG_GET)
144                               field_disable_by_value(&reverse_scrolling);
145                     return;
146           }
147 
148           for (i = 0; i < pl.nparams; ++i) {
149                     switch (params[i].key) {
150                     case WSMOUSECFG_REVERSE_SCROLLING:
151                               reverse_scrolling = params[i].value;
152                               break;
153                     case WSMOUSECFG_HORIZSCROLLDIST:
154                               horiz_scroll_dist = params[i].value;
155                               break;
156                     case WSMOUSECFG_VERTSCROLLDIST:
157                               vert_scroll_dist = params[i].value;
158                               break;
159                     }
160           }
161 }
162 
163 static void
mouse_get_calibration(int fd)164 mouse_get_calibration(int fd)
165 {
166           struct wsmouse_calibcoords tmp;
167           char *samples;
168           char buf[48];
169           int i;
170 
171           if (ioctl(fd, WSMOUSEIO_GCALIBCOORDS, &tmp) < 0) {
172                     field_disable_by_value(&calibration.minx);
173                     field_disable_by_value(&calibration.miny);
174                     field_disable_by_value(&calibration.maxx);
175                     field_disable_by_value(&calibration.maxy);
176                     field_disable_by_value(&calibration_samples);
177                     return;
178           }
179 
180           if (field_by_value(&calibration.minx)->flags & FLG_GET)
181                     calibration.minx = tmp.minx;
182           if (field_by_value(&calibration.miny)->flags & FLG_GET)
183                     calibration.miny = tmp.miny;
184           if (field_by_value(&calibration.maxx)->flags & FLG_GET)
185                     calibration.maxx = tmp.maxx;
186           if (field_by_value(&calibration.maxy)->flags & FLG_GET)
187                     calibration.maxy = tmp.maxy;
188           if (field_by_value(&calibration_samples)->flags & FLG_GET) {
189                     free(calibration_samples);
190                     if (tmp.samplelen <= 0) {
191                               calibration_samples = strdup("");
192                               if (calibration_samples == NULL)
193                                         err(EXIT_FAILURE, "could not list calibration"
194                                                             " samples");
195                     } else {
196                               samples = malloc(tmp.samplelen * sizeof(buf));
197                               if (samples == NULL)
198                                         err(EXIT_FAILURE, "could not list calibration"
199                                                             " samples");
200                               samples[0] = '\0';
201                               for (i = 0; i < tmp.samplelen; i++) {
202                                         snprintf(buf, sizeof(buf), "%s%d,%d,%d,%d",
203                                                             (i == 0) ? "" : ":",
204                                                             tmp.samples[i].rawx,
205                                                             tmp.samples[i].rawy,
206                                                             tmp.samples[i].x,
207                                                             tmp.samples[i].y);
208                                         strcat(samples, buf);
209                               }
210                               calibration_samples = samples;
211                     }
212           }
213 }
214 
215 static void
mouse_get_repeat(int fd)216 mouse_get_repeat(int fd)
217 {
218           struct wsmouse_repeat tmp;
219 
220           if (ioctl(fd, WSMOUSEIO_GETREPEAT, &tmp) < 0)
221                     err(EXIT_FAILURE, "WSMOUSEIO_GETREPEAT");
222 
223           if (field_by_value(&repeat.wr_buttons)->flags & FLG_GET)
224                     repeat.wr_buttons = tmp.wr_buttons;
225           if (field_by_value(&repeat.wr_delay_first)->flags & FLG_GET)
226                     repeat.wr_delay_first = tmp.wr_delay_first;
227           if (field_by_value(&repeat.wr_delay_decrement)->flags & FLG_GET)
228                     repeat.wr_delay_decrement = tmp.wr_delay_decrement;
229           if (field_by_value(&repeat.wr_delay_minimum)->flags & FLG_GET)
230                     repeat.wr_delay_minimum = tmp.wr_delay_minimum;
231 }
232 
233 void
mouse_put_values(int fd)234 mouse_put_values(int fd)
235 {
236           int tmp;
237 
238           if (field_by_value(&resolution)->flags & FLG_SET) {
239                     tmp = resolution;
240                     if (ioctl(fd, WSMOUSEIO_SRES, &tmp) < 0)
241                               err(EXIT_FAILURE, "WSMOUSEIO_SRES");
242                     pr_field(field_by_value(&resolution), " -> ");
243           }
244 
245           if (field_by_value(&samplerate)->flags & FLG_SET) {
246                     tmp = samplerate;
247                     if (ioctl(fd, WSMOUSEIO_SRATE, &tmp) < 0)
248                               err(EXIT_FAILURE, "WSMOUSEIO_SRATE");
249                     pr_field(field_by_value(&samplerate), " -> ");
250           }
251 
252           if (field_by_value(&calibration.minx)->flags & FLG_SET ||
253               field_by_value(&calibration.miny)->flags & FLG_SET ||
254               field_by_value(&calibration.maxx)->flags & FLG_SET ||
255               field_by_value(&calibration.maxy)->flags & FLG_SET ||
256               field_by_value(&calibration_samples)->flags & FLG_SET)
257                     mouse_put_calibration(fd);
258 
259           if (field_by_value(&repeat.wr_buttons)->flags & FLG_SET ||
260               field_by_value(&repeat.wr_delay_first)->flags & FLG_SET ||
261               field_by_value(&repeat.wr_delay_decrement)->flags & FLG_SET ||
262               field_by_value(&repeat.wr_delay_minimum)->flags & FLG_SET)
263                     mouse_put_repeat(fd);
264 
265           if (field_by_value(&horiz_scroll_dist)->flags & FLG_SET ||
266               field_by_value(&vert_scroll_dist)->flags & FLG_SET ||
267               field_by_value(&reverse_scrolling)->flags & FLG_SET)
268                     mouse_put_parameters(fd);
269 }
270 
271 static void
mouse_put_parameters(int fd)272 mouse_put_parameters(int fd)
273 {
274           struct wsmouse_param params[WSMOUSECFG_MAX];
275           struct wsmouse_parameters pl;
276 
277           pl.nparams = 0;
278           pl.params = params;
279 
280           if (field_by_value(&reverse_scrolling)->flags & FLG_SET) {
281                     params[pl.nparams].key = WSMOUSECFG_REVERSE_SCROLLING;
282                     params[pl.nparams++].value = reverse_scrolling;
283           }
284 
285           if (field_by_value(&horiz_scroll_dist)->flags & FLG_SET) {
286                     params[pl.nparams].key = WSMOUSECFG_HORIZSCROLLDIST;
287                     params[pl.nparams++].value = horiz_scroll_dist;
288           }
289 
290           if (field_by_value(&vert_scroll_dist)->flags & FLG_SET) {
291                     params[pl.nparams].key = WSMOUSECFG_VERTSCROLLDIST;
292                     params[pl.nparams++].value = vert_scroll_dist;
293           }
294 
295           if (ioctl(fd, WSMOUSEIO_SETPARAMS, &pl) < 0) {
296                     if (field_by_value(&horiz_scroll_dist)->flags & FLG_SET)
297                               field_disable_by_value(&horiz_scroll_dist);
298                     if (field_by_value(&vert_scroll_dist)->flags & FLG_SET)
299                               field_disable_by_value(&vert_scroll_dist);
300                     if (field_by_value(&vert_scroll_dist)->flags & FLG_SET)
301                               field_disable_by_value(&reverse_scrolling);
302                     return;
303           }
304 }
305 
306 static void
mouse_put_calibration(int fd)307 mouse_put_calibration(int fd)
308 {
309           struct wsmouse_calibcoords tmp;
310           int i;
311           const char *p;
312           char *q;
313 
314           /* Fetch current values into the temporary structure. */
315           if (ioctl(fd, WSMOUSEIO_GCALIBCOORDS, &tmp) < 0)
316                     err(EXIT_FAILURE, "WSMOUSEIO_GCALIBCOORDS");
317 
318           /* Overwrite the desired values in the temporary structure. */
319           if (field_by_value(&calibration.minx)->flags & FLG_SET)
320                     tmp.minx = calibration.minx;
321           if (field_by_value(&calibration.miny)->flags & FLG_SET)
322                     tmp.miny = calibration.miny;
323           if (field_by_value(&calibration.maxx)->flags & FLG_SET)
324                     tmp.maxx = calibration.maxx;
325           if (field_by_value(&calibration.maxy)->flags & FLG_SET)
326                     tmp.maxy = calibration.maxy;
327           if (field_by_value(&calibration_samples)->flags & FLG_SET) {
328                     p = calibration_samples;
329                     for (i = 0; p[0] != '\0' && i < WSMOUSE_CALIBCOORDS_MAX; i++) {
330                               tmp.samples[i].rawx = strtol(p, &q, 0);
331                               if (*q != ',')
332                                         break;
333                               p = q + 1;
334                               tmp.samples[i].rawy = strtol(p, &q, 0);
335                               if (*q != ',')
336                                         break;
337                               p = q + 1;
338                               tmp.samples[i].x = strtol(p, &q, 0);
339                               if (*q != ',')
340                                         break;
341                               p = q + 1;
342                               tmp.samples[i].y = strtol(p, &q, 0);
343                               p = q + 1;
344                               if (*q != '\0' && *q != ':')
345                                         break;
346                     }
347                     if (p[0] != '\0')
348                               errx(EXIT_FAILURE, "%s: invalid calibration data",
349                                                   calibration_samples);
350                     tmp.samplelen = i;
351           }
352 
353           /* Set new values for calibrating events. */
354           if (ioctl(fd, WSMOUSEIO_SCALIBCOORDS, &tmp) < 0)
355                     err(EXIT_FAILURE, "WSMOUSEIO_SCALIBCOORDS");
356 
357           /* Now print what changed. */
358           if (field_by_value(&calibration.minx)->flags & FLG_SET)
359                     pr_field(field_by_value(&calibration.minx), " -> ");
360           if (field_by_value(&calibration.miny)->flags & FLG_SET)
361                     pr_field(field_by_value(&calibration.miny), " -> ");
362           if (field_by_value(&calibration.maxx)->flags & FLG_SET)
363                     pr_field(field_by_value(&calibration.maxx), " -> ");
364           if (field_by_value(&calibration.maxy)->flags & FLG_SET)
365                     pr_field(field_by_value(&calibration.maxy), " -> ");
366           if (field_by_value(&calibration_samples)->flags & FLG_SET)
367                     pr_field(field_by_value(&calibration_samples), " -> ");
368 }
369 
370 static void
mouse_put_repeat(int fd)371 mouse_put_repeat(int fd)
372 {
373           struct wsmouse_repeat tmp;
374 
375           /* Fetch current values into the temporary structure. */
376           if (ioctl(fd, WSMOUSEIO_GETREPEAT, &tmp) < 0)
377                     err(EXIT_FAILURE, "WSMOUSEIO_GETREPEAT");
378 
379           /* Overwrite the desired values in the temporary structure. */
380           if (field_by_value(&repeat.wr_buttons)->flags & FLG_SET)
381                     tmp.wr_buttons = repeat.wr_buttons;
382           if (field_by_value(&repeat.wr_delay_first)->flags & FLG_SET)
383                     tmp.wr_delay_first = repeat.wr_delay_first;
384           if (field_by_value(&repeat.wr_delay_decrement)->flags & FLG_SET)
385                     tmp.wr_delay_decrement = repeat.wr_delay_decrement;
386           if (field_by_value(&repeat.wr_delay_minimum)->flags & FLG_SET)
387                     tmp.wr_delay_minimum = repeat.wr_delay_minimum;
388 
389           /* Set new values for repeating events. */
390           if (ioctl(fd, WSMOUSEIO_SETREPEAT, &tmp) < 0)
391                     err(EXIT_FAILURE, "WSMOUSEIO_SETREPEAT");
392 
393           /* Now print what changed. */
394           if (field_by_value(&repeat.wr_buttons)->flags & FLG_SET)
395                     pr_field(field_by_value(&repeat.wr_buttons), " -> ");
396           if (field_by_value(&repeat.wr_delay_first)->flags & FLG_SET)
397                     pr_field(field_by_value(&repeat.wr_delay_first), " -> ");
398           if (field_by_value(&repeat.wr_delay_decrement)->flags & FLG_SET)
399                     pr_field(field_by_value(&repeat.wr_delay_decrement), " -> ");
400           if (field_by_value(&repeat.wr_delay_minimum)->flags & FLG_SET)
401                     pr_field(field_by_value(&repeat.wr_delay_minimum), " -> ");
402 }
403