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