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 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 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 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 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 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 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 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 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 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 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 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 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 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