1*c08965e9Schrisz /* $OpenBSD: mousecfg.c,v 1.12 2024/10/05 13:27:16 chrisz Exp $ */ 2d22ea701Sbru 3d22ea701Sbru /* 4d22ea701Sbru * Copyright (c) 2017 Ulf Brosziewski 5d22ea701Sbru * 6d22ea701Sbru * Permission to use, copy, modify, and distribute this software for any 7d22ea701Sbru * purpose with or without fee is hereby granted, provided that the above 8d22ea701Sbru * copyright notice and this permission notice appear in all copies. 9d22ea701Sbru * 10d22ea701Sbru * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11d22ea701Sbru * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12d22ea701Sbru * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13d22ea701Sbru * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14d22ea701Sbru * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15d22ea701Sbru * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16d22ea701Sbru * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17d22ea701Sbru */ 18d22ea701Sbru 19d22ea701Sbru /* 20d22ea701Sbru * Read/write wsmouse parameters for touchpad configuration. 21d22ea701Sbru */ 22d22ea701Sbru 239b9064ccSderaadt #include <sys/types.h> 24d22ea701Sbru #include <sys/ioctl.h> 259b9064ccSderaadt #include <sys/time.h> 26d22ea701Sbru #include <dev/wscons/wsconsio.h> 27d22ea701Sbru #include <stdio.h> 28d22ea701Sbru #include <stdlib.h> 29d22ea701Sbru #include <string.h> 30d22ea701Sbru #include <err.h> 31d22ea701Sbru #include <errno.h> 32d22ea701Sbru #include "mousecfg.h" 33d22ea701Sbru 349b9064ccSderaadt #ifndef nitems 359b9064ccSderaadt #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 369b9064ccSderaadt #endif 379b9064ccSderaadt 389310c18aSbru #define BASESIZE ((WSMOUSECFG__FILTERS - WSMOUSECFG_DX_SCALE) \ 399310c18aSbru + (WSMOUSECFG__DEBUG - WSMOUSECFG_LOG_INPUT)) 40d22ea701Sbru 41d22ea701Sbru static const int range[][2] = { 429310c18aSbru { WSMOUSECFG_DX_SCALE, WSMOUSECFG__FILTERS - 1 }, 439310c18aSbru { WSMOUSECFG_LOG_INPUT, WSMOUSECFG__DEBUG - 1 }, 449310c18aSbru { WSMOUSECFG_DX_MAX, WSMOUSECFG__TPFILTERS - 1 }, 459310c18aSbru { WSMOUSECFG_SOFTBUTTONS, WSMOUSECFG__TPFEATURES - 1 }, 469310c18aSbru { WSMOUSECFG_LEFT_EDGE, WSMOUSECFG__TPSETUP - 1 }, 47d22ea701Sbru }; 48d22ea701Sbru 49d22ea701Sbru static const int touchpad_types[] = { 50d22ea701Sbru WSMOUSE_TYPE_SYNAPTICS, /* Synaptics touchpad */ 51d22ea701Sbru WSMOUSE_TYPE_ALPS, /* ALPS touchpad */ 52d22ea701Sbru WSMOUSE_TYPE_ELANTECH, /* Elantech touchpad */ 53d22ea701Sbru WSMOUSE_TYPE_SYNAP_SBTN, /* Synaptics soft buttons */ 54c2ca70daSjcs WSMOUSE_TYPE_TOUCHPAD, /* Generic touchpad */ 55d22ea701Sbru }; 56d22ea701Sbru 57d22ea701Sbru struct wsmouse_parameters cfg_tapping = { 58d22ea701Sbru (struct wsmouse_param[]) { 59b1ba6288Sbru { WSMOUSECFG_TAP_ONE_BTNMAP, 0 }, 60b1ba6288Sbru { WSMOUSECFG_TAP_TWO_BTNMAP, 0 }, 61b1ba6288Sbru { WSMOUSECFG_TAP_THREE_BTNMAP, 0 }, }, 62b1ba6288Sbru 3 63d22ea701Sbru }; 64d22ea701Sbru 659310c18aSbru struct wsmouse_parameters cfg_mtbuttons = { 669310c18aSbru (struct wsmouse_param[]) { 679310c18aSbru { WSMOUSECFG_MTBUTTONS, 0 }, }, 689310c18aSbru 1 699310c18aSbru }; 709310c18aSbru 71d22ea701Sbru struct wsmouse_parameters cfg_scaling = { 72d22ea701Sbru (struct wsmouse_param[]) { 73d22ea701Sbru { WSMOUSECFG_DX_SCALE, 0 }, 74d22ea701Sbru { WSMOUSECFG_DY_SCALE, 0 } }, 75d22ea701Sbru 2 76d22ea701Sbru }; 77d22ea701Sbru 780137e658Sbru struct wsmouse_parameters cfg_edges = { 790137e658Sbru (struct wsmouse_param[]) { 800137e658Sbru { WSMOUSECFG_TOP_EDGE, 0 }, 810137e658Sbru { WSMOUSECFG_RIGHT_EDGE, 0 }, 820137e658Sbru { WSMOUSECFG_BOTTOM_EDGE, 0 }, 830137e658Sbru { WSMOUSECFG_LEFT_EDGE, 0 } }, 840137e658Sbru 4 850137e658Sbru }; 860137e658Sbru 87d22ea701Sbru struct wsmouse_parameters cfg_swapsides = { 88d22ea701Sbru (struct wsmouse_param[]) { 89d22ea701Sbru { WSMOUSECFG_SWAPSIDES, 0 }, }, 90d22ea701Sbru 1 91d22ea701Sbru }; 92d22ea701Sbru 93d22ea701Sbru struct wsmouse_parameters cfg_disable = { 94d22ea701Sbru (struct wsmouse_param[]) { 95d22ea701Sbru { WSMOUSECFG_DISABLE, 0 }, }, 96d22ea701Sbru 1 97d22ea701Sbru }; 98d22ea701Sbru 9977727bd5Sbru struct wsmouse_parameters cfg_revscroll = { 10077727bd5Sbru (struct wsmouse_param[]) { 10177727bd5Sbru { WSMOUSECFG_REVERSE_SCROLLING, 0 }, }, 10277727bd5Sbru 1 10377727bd5Sbru }; 10477727bd5Sbru 105d22ea701Sbru struct wsmouse_parameters cfg_param = { 106d22ea701Sbru (struct wsmouse_param[]) { 107d22ea701Sbru { -1, 0 }, 108d22ea701Sbru { -1, 0 }, 109d22ea701Sbru { -1, 0 }, 110d22ea701Sbru { -1, 0 } }, 111d22ea701Sbru 4 112d22ea701Sbru }; 113d22ea701Sbru 114a143f7beSbru int cfg_touchpad; 115a143f7beSbru 116d22ea701Sbru static int cfg_horiz_res; 117d22ea701Sbru static int cfg_vert_res; 1189310c18aSbru static struct wsmouse_param cfg_buffer[WSMOUSECFG_MAX]; 119d22ea701Sbru 120d22ea701Sbru 121d22ea701Sbru int 122d22ea701Sbru mousecfg_init(int dev_fd, const char **errstr) 123d22ea701Sbru { 124d22ea701Sbru struct wsmouse_calibcoords coords; 125d22ea701Sbru struct wsmouse_parameters parameters; 126d22ea701Sbru struct wsmouse_param *param; 127d22ea701Sbru enum wsmousecfg k; 128d22ea701Sbru int i, err, type; 129d22ea701Sbru 130d22ea701Sbru *errstr = NULL; 131d22ea701Sbru 132d22ea701Sbru if ((err = ioctl(dev_fd, WSMOUSEIO_GTYPE, &type))) { 133d22ea701Sbru *errstr = "WSMOUSEIO_GTYPE"; 134d22ea701Sbru return err; 135d22ea701Sbru } 136a143f7beSbru cfg_touchpad = 0; 137a143f7beSbru for (i = 0; !cfg_touchpad && i < nitems(touchpad_types); i++) 138a143f7beSbru cfg_touchpad = (type == touchpad_types[i]); 139d22ea701Sbru 140a143f7beSbru cfg_horiz_res = cfg_vert_res = 0; 141a143f7beSbru if (cfg_touchpad) { 142d22ea701Sbru if ((err = ioctl(dev_fd, WSMOUSEIO_GCALIBCOORDS, &coords))) { 143d22ea701Sbru *errstr = "WSMOUSEIO_GCALIBCOORDS"; 144d22ea701Sbru return err; 145d22ea701Sbru } 146d22ea701Sbru cfg_horiz_res = coords.resx; 147d22ea701Sbru cfg_vert_res = coords.resy; 148a143f7beSbru } 149d22ea701Sbru 150d22ea701Sbru param = cfg_buffer; 151d22ea701Sbru for (i = 0; i < nitems(range); i++) 152d22ea701Sbru for (k = range[i][0]; k <= range[i][1]; k++, param++) { 153d22ea701Sbru param->key = k; 154d22ea701Sbru param->value = 0; 155d22ea701Sbru } 156d22ea701Sbru 157d22ea701Sbru parameters.params = cfg_buffer; 1588665dd6dSbru parameters.nparams = BASESIZE; 159d22ea701Sbru if ((err = ioctl(dev_fd, WSMOUSEIO_GETPARAMS, ¶meters))) { 160d22ea701Sbru *errstr = "WSMOUSEIO_GETPARAMS"; 161d22ea701Sbru return (err); 162d22ea701Sbru } 1638665dd6dSbru if (cfg_touchpad) { 1648665dd6dSbru parameters.params = cfg_buffer + BASESIZE; 1659310c18aSbru parameters.nparams = WSMOUSECFG_MAX - BASESIZE; 1668665dd6dSbru if (ioctl(dev_fd, WSMOUSEIO_GETPARAMS, ¶meters)) 1678665dd6dSbru cfg_touchpad = 0; 1688665dd6dSbru } 169d22ea701Sbru 170d22ea701Sbru return (0); 171d22ea701Sbru } 172d22ea701Sbru 173d22ea701Sbru /* Map a key to its buffer index. */ 174d22ea701Sbru static int 175d22ea701Sbru index_of(enum wsmousecfg key) 176d22ea701Sbru { 177d22ea701Sbru int i, n; 178d22ea701Sbru 179a143f7beSbru for (i = 0, n = 0; i < nitems(range); i++) { 180a143f7beSbru if (key <= range[i][1] && key >= range[i][0]) { 181d22ea701Sbru return (key - range[i][0] + n); 182a143f7beSbru } 183d22ea701Sbru n += range[i][1] - range[i][0] + 1; 184a143f7beSbru if (!cfg_touchpad && n >= BASESIZE) 185a143f7beSbru break; 186a143f7beSbru } 187d22ea701Sbru 188d22ea701Sbru return (-1); 189d22ea701Sbru } 190d22ea701Sbru 191d22ea701Sbru int 192d22ea701Sbru mousecfg_get_field(struct wsmouse_parameters *field) 193d22ea701Sbru { 194d22ea701Sbru int i, n; 195d22ea701Sbru 196d22ea701Sbru for (i = 0; i < field->nparams; i++) { 197d22ea701Sbru if ((n = index_of(field->params[i].key)) >= 0) 198d22ea701Sbru field->params[i].value = cfg_buffer[n].value; 199d22ea701Sbru else 200d22ea701Sbru return (-1); 201d22ea701Sbru } 202d22ea701Sbru return (0); 203d22ea701Sbru } 204d22ea701Sbru 205d22ea701Sbru int 206d22ea701Sbru mousecfg_put_field(int fd, struct wsmouse_parameters *field) 207d22ea701Sbru { 208d22ea701Sbru int i, n, d, err; 209d22ea701Sbru 210d22ea701Sbru d = 0; 211d22ea701Sbru for (i = 0; i < field->nparams; i++) 212d22ea701Sbru if ((n = index_of(field->params[i].key)) < 0) 213d22ea701Sbru return (-1); 214d22ea701Sbru else 215d22ea701Sbru d |= (cfg_buffer[n].value != field->params[i].value); 216d22ea701Sbru 217d22ea701Sbru if (!d) 218d22ea701Sbru return (0); 219d22ea701Sbru 220d22ea701Sbru /* Write and read back immediately, wsmouse may normalize values. */ 221d22ea701Sbru if ((err = ioctl(fd, WSMOUSEIO_SETPARAMS, field)) 222d22ea701Sbru || (err = ioctl(fd, WSMOUSEIO_GETPARAMS, field))) 223d22ea701Sbru return err; 224d22ea701Sbru 22521cd3483Sbru for (i = 0; i < field->nparams; i++) { 22621cd3483Sbru n = index_of(field->params[i].key); 227d22ea701Sbru cfg_buffer[n].value = field->params[i].value; 22821cd3483Sbru } 229d22ea701Sbru 230d22ea701Sbru return (0); 231d22ea701Sbru } 232d22ea701Sbru 233d22ea701Sbru static int 234d22ea701Sbru get_value(struct wsmouse_parameters *field, enum wsmousecfg key) 235d22ea701Sbru { 236d22ea701Sbru int i; 237d22ea701Sbru 238d22ea701Sbru for (i = 0; i < field->nparams && key != field->params[i].key; i++) {} 239d22ea701Sbru 240d22ea701Sbru return (i < field->nparams ? field->params[i].value : 0); 241d22ea701Sbru } 242d22ea701Sbru 243d22ea701Sbru static void 244d22ea701Sbru set_value(struct wsmouse_parameters *field, enum wsmousecfg key, int value) 245d22ea701Sbru { 246d22ea701Sbru int i; 247d22ea701Sbru 248d22ea701Sbru for (i = 0; i < field->nparams && key != field->params[i].key; i++) {} 249d22ea701Sbru 250d22ea701Sbru field->params[i].value = (i < field->nparams ? value : 0); 251d22ea701Sbru } 252d22ea701Sbru 2530137e658Sbru static float 2540137e658Sbru get_percent(struct wsmouse_parameters *field, enum wsmousecfg key) 2550137e658Sbru { 2560137e658Sbru return ((float) get_value(field, key) * 100 / 4096); 2570137e658Sbru } 2580137e658Sbru 2590137e658Sbru static void 2600137e658Sbru set_percent(struct wsmouse_parameters *field, enum wsmousecfg key, float f) 2610137e658Sbru { 2620137e658Sbru set_value(field, key, (int) ((f * 4096 + 50) / 100)); 2630137e658Sbru } 2640137e658Sbru 2650137e658Sbru static int 266b1ba6288Sbru set_tapping(struct wsmouse_parameters *field, char *tapping) 267b1ba6288Sbru { 268b1ba6288Sbru int i1, i2, i3; 269b1ba6288Sbru 270b1ba6288Sbru switch (sscanf(tapping, "%d,%d,%d", &i1, &i2, &i3)) { 271b1ba6288Sbru case 1: 272b1ba6288Sbru if (i1 == 0) /* Disable */ 273b1ba6288Sbru i2 = i3 = i1; 274b1ba6288Sbru else { /* Enable with defaults */ 275b1ba6288Sbru i1 = 1; /* Left click */ 276b1ba6288Sbru i2 = 3; /* Right click */ 277b1ba6288Sbru i3 = 2; /* Middle click */ 278b1ba6288Sbru } 279b1ba6288Sbru /* FALLTHROUGH */ 280b1ba6288Sbru case 3: 281b1ba6288Sbru set_value(field, WSMOUSECFG_TAP_ONE_BTNMAP, i1); 282b1ba6288Sbru set_value(field, WSMOUSECFG_TAP_TWO_BTNMAP, i2); 283b1ba6288Sbru set_value(field, WSMOUSECFG_TAP_THREE_BTNMAP, i3); 284b1ba6288Sbru return (0); 285b1ba6288Sbru } 286b1ba6288Sbru return (-1); 287b1ba6288Sbru } 288b1ba6288Sbru 289b1ba6288Sbru static int 2900137e658Sbru set_edges(struct wsmouse_parameters *field, char *edges) 2910137e658Sbru { 2920137e658Sbru float f1, f2, f3, f4; 2930137e658Sbru 2940137e658Sbru if (sscanf(edges, "%f,%f,%f,%f", &f1, &f2, &f3, &f4) == 4) { 2950137e658Sbru set_percent(field, WSMOUSECFG_TOP_EDGE, f1); 2960137e658Sbru set_percent(field, WSMOUSECFG_RIGHT_EDGE,f2); 2970137e658Sbru set_percent(field, WSMOUSECFG_BOTTOM_EDGE, f3); 2980137e658Sbru set_percent(field, WSMOUSECFG_LEFT_EDGE, f4); 2990137e658Sbru return (0); 3000137e658Sbru } 3010137e658Sbru return (-1); 3020137e658Sbru } 3030137e658Sbru 304d22ea701Sbru /* 305d22ea701Sbru * Read or write up to four raw parameter values. In this case 306d22ea701Sbru * reading is a 'put' operation that writes back a value from the 307d22ea701Sbru * buffer. 308d22ea701Sbru */ 309d22ea701Sbru static int 310d22ea701Sbru read_param(struct wsmouse_parameters *field, char *val) 311d22ea701Sbru { 312d22ea701Sbru int i, j, n; 313d22ea701Sbru 314d22ea701Sbru n = sscanf(val, "%d:%d,%d:%d,%d:%d,%d:%d", 315d22ea701Sbru &field->params[0].key, &field->params[0].value, 316d22ea701Sbru &field->params[1].key, &field->params[1].value, 317d22ea701Sbru &field->params[2].key, &field->params[2].value, 318d22ea701Sbru &field->params[3].key, &field->params[3].value); 319d22ea701Sbru if (n > 0 && (n & 1) == 0) { 320d22ea701Sbru n /= 2; 321d22ea701Sbru for (i = 0; i < n; i++) { 322d22ea701Sbru if (index_of(field->params[i].key) < 0) 323d22ea701Sbru return (-1); 324d22ea701Sbru } 325d22ea701Sbru field->nparams = n; 326d22ea701Sbru return (0); 327d22ea701Sbru } 328d22ea701Sbru n = sscanf(val, "%d,%d,%d,%d", 329d22ea701Sbru &field->params[0].key, &field->params[1].key, 330d22ea701Sbru &field->params[2].key, &field->params[3].key); 331d22ea701Sbru if (n > 0) { 332d22ea701Sbru for (i = 0; i < n; i++) { 333d22ea701Sbru if ((j = index_of(field->params[i].key)) < 0) 334d22ea701Sbru return (-1); 335d22ea701Sbru field->params[i].value = cfg_buffer[j].value; 336d22ea701Sbru } 337d22ea701Sbru field->nparams = n; 338d22ea701Sbru return (0); 339d22ea701Sbru } 340d22ea701Sbru return (-1); 341d22ea701Sbru } 342d22ea701Sbru 343d22ea701Sbru void 344d22ea701Sbru mousecfg_pr_field(struct wsmouse_parameters *field) 345d22ea701Sbru { 346d22ea701Sbru int i, value; 347d22ea701Sbru float f; 348d22ea701Sbru 349d22ea701Sbru if (field == &cfg_param) { 350d22ea701Sbru for (i = 0; i < field->nparams; i++) 351d22ea701Sbru printf(i > 0 ? ",%d:%d" : "%d:%d", 352d22ea701Sbru field->params[i].key, 353d22ea701Sbru field->params[i].value); 354d22ea701Sbru return; 355d22ea701Sbru } 356d22ea701Sbru 357d22ea701Sbru if (field == &cfg_scaling) { 358d22ea701Sbru value = get_value(field, WSMOUSECFG_DX_SCALE); 359*c08965e9Schrisz value = value == 0 ? 4096 : value; 360d22ea701Sbru f = (float) value / 4096; 361d22ea701Sbru printf("%.3f", f); 362d22ea701Sbru return; 363d22ea701Sbru } 364d22ea701Sbru 3650137e658Sbru if (field == &cfg_edges) { 3660137e658Sbru printf("%.1f,%.1f,%.1f,%.1f", 3670137e658Sbru get_percent(field, WSMOUSECFG_TOP_EDGE), 3680137e658Sbru get_percent(field, WSMOUSECFG_RIGHT_EDGE), 3690137e658Sbru get_percent(field, WSMOUSECFG_BOTTOM_EDGE), 3700137e658Sbru get_percent(field, WSMOUSECFG_LEFT_EDGE)); 3710137e658Sbru return; 3720137e658Sbru } 3730137e658Sbru 374d22ea701Sbru for (i = 0; i < field->nparams; i++) 375d22ea701Sbru printf(i > 0 ? ",%d" : "%d", field->params[i].value); 376d22ea701Sbru } 377d22ea701Sbru 378d22ea701Sbru void 379d22ea701Sbru mousecfg_rd_field(struct wsmouse_parameters *field, char *val) 380d22ea701Sbru { 381d22ea701Sbru int i, n; 382d22ea701Sbru const char *s; 383d22ea701Sbru float f; 384d22ea701Sbru 385d22ea701Sbru if (field == &cfg_param) { 386d22ea701Sbru if (read_param(field, val)) 387d22ea701Sbru errx(1, "invalid input (param)"); 388d22ea701Sbru return; 389d22ea701Sbru } 390d22ea701Sbru 391b1ba6288Sbru if (field == &cfg_tapping) { 392b1ba6288Sbru if (set_tapping(field, val)) 393b1ba6288Sbru errx(1, "invalid input (tapping)"); 394b1ba6288Sbru return; 395b1ba6288Sbru } 396b1ba6288Sbru 397d22ea701Sbru if (field == &cfg_scaling) { 398d22ea701Sbru if (sscanf(val, "%f", &f) == 1) { 399d22ea701Sbru n = (int) (f * 4096); 400d22ea701Sbru set_value(field, WSMOUSECFG_DX_SCALE, n); 401d22ea701Sbru if (cfg_horiz_res && cfg_vert_res) 402d22ea701Sbru n = n * cfg_horiz_res / cfg_vert_res; 403d22ea701Sbru set_value(field, WSMOUSECFG_DY_SCALE, n); 404d22ea701Sbru } else { 405d22ea701Sbru errx(1, "invalid input (scaling)"); 406d22ea701Sbru } 407d22ea701Sbru return; 408d22ea701Sbru } 409d22ea701Sbru 4100137e658Sbru if (field == &cfg_edges) { 4110137e658Sbru if (set_edges(field, val)) 4120137e658Sbru errx(1, "invalid input (edges)"); 4130137e658Sbru return; 4140137e658Sbru } 4150137e658Sbru 416d22ea701Sbru s = val; 417d22ea701Sbru for (i = 0; i < field->nparams; i++) { 418d22ea701Sbru if (sscanf(s, (i > 0 ? ",%d" : "%d"), &n) != 1) 419d22ea701Sbru break; 420d22ea701Sbru field->params[i].value = abs(n); 421d22ea701Sbru for (s++; *s != '\0' && *s != ','; s++) {} 422d22ea701Sbru } 423d22ea701Sbru if (i < field->nparams || *s != '\0') 424d22ea701Sbru errx(1, "invalid input '%s'", val); 425d22ea701Sbru } 426