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, ¶meters))) { 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, ¶meters)) 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