1 /* $OpenBSD: mousecfg.c,v 1.7 2020/04/02 17:17:04 deraadt 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_TAPPING 44 #define TP_SETUP_FIRST WSMOUSECFG_LEFT_EDGE 45 #define TP_SETUP_LAST WSMOUSECFG_TAP_LOCKTIME 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_TAPPING, 0 }, }, 75 1 76 }; 77 78 struct wsmouse_parameters cfg_scaling = { 79 (struct wsmouse_param[]) { 80 { WSMOUSECFG_DX_SCALE, 0 }, 81 { WSMOUSECFG_DY_SCALE, 0 } }, 82 2 83 }; 84 85 struct wsmouse_parameters cfg_edges = { 86 (struct wsmouse_param[]) { 87 { WSMOUSECFG_TOP_EDGE, 0 }, 88 { WSMOUSECFG_RIGHT_EDGE, 0 }, 89 { WSMOUSECFG_BOTTOM_EDGE, 0 }, 90 { WSMOUSECFG_LEFT_EDGE, 0 } }, 91 4 92 }; 93 94 struct wsmouse_parameters cfg_swapsides = { 95 (struct wsmouse_param[]) { 96 { WSMOUSECFG_SWAPSIDES, 0 }, }, 97 1 98 }; 99 100 struct wsmouse_parameters cfg_disable = { 101 (struct wsmouse_param[]) { 102 { WSMOUSECFG_DISABLE, 0 }, }, 103 1 104 }; 105 106 struct wsmouse_parameters cfg_revscroll = { 107 (struct wsmouse_param[]) { 108 { WSMOUSECFG_REVERSE_SCROLLING, 0 }, }, 109 1 110 }; 111 112 struct wsmouse_parameters cfg_param = { 113 (struct wsmouse_param[]) { 114 { -1, 0 }, 115 { -1, 0 }, 116 { -1, 0 }, 117 { -1, 0 } }, 118 4 119 }; 120 121 int cfg_touchpad; 122 123 static int cfg_horiz_res; 124 static int cfg_vert_res; 125 static struct wsmouse_param cfg_buffer[BUFSIZE]; 126 127 128 int 129 mousecfg_init(int dev_fd, const char **errstr) 130 { 131 struct wsmouse_calibcoords coords; 132 struct wsmouse_parameters parameters; 133 struct wsmouse_param *param; 134 enum wsmousecfg k; 135 int i, err, type; 136 137 *errstr = NULL; 138 139 if ((err = ioctl(dev_fd, WSMOUSEIO_GTYPE, &type))) { 140 *errstr = "WSMOUSEIO_GTYPE"; 141 return err; 142 } 143 cfg_touchpad = 0; 144 for (i = 0; !cfg_touchpad && i < nitems(touchpad_types); i++) 145 cfg_touchpad = (type == touchpad_types[i]); 146 147 cfg_horiz_res = cfg_vert_res = 0; 148 if (cfg_touchpad) { 149 if ((err = ioctl(dev_fd, WSMOUSEIO_GCALIBCOORDS, &coords))) { 150 *errstr = "WSMOUSEIO_GCALIBCOORDS"; 151 return err; 152 } 153 cfg_horiz_res = coords.resx; 154 cfg_vert_res = coords.resy; 155 } 156 157 param = cfg_buffer; 158 for (i = 0; i < nitems(range); i++) 159 for (k = range[i][0]; k <= range[i][1]; k++, param++) { 160 param->key = k; 161 param->value = 0; 162 } 163 164 parameters.params = cfg_buffer; 165 parameters.nparams = (cfg_touchpad ? BUFSIZE : BASESIZE); 166 if ((err = ioctl(dev_fd, WSMOUSEIO_GETPARAMS, ¶meters))) { 167 *errstr = "WSMOUSEIO_GETPARAMS"; 168 return (err); 169 } 170 171 return (0); 172 } 173 174 /* Map a key to its buffer index. */ 175 static int 176 index_of(enum wsmousecfg key) 177 { 178 int i, n; 179 180 for (i = 0, n = 0; i < nitems(range); i++) { 181 if (key <= range[i][1] && key >= range[i][0]) { 182 return (key - range[i][0] + n); 183 } 184 n += range[i][1] - range[i][0] + 1; 185 if (!cfg_touchpad && n >= BASESIZE) 186 break; 187 } 188 189 return (-1); 190 } 191 192 int 193 mousecfg_get_field(struct wsmouse_parameters *field) 194 { 195 int i, n; 196 197 for (i = 0; i < field->nparams; i++) { 198 if ((n = index_of(field->params[i].key)) >= 0) 199 field->params[i].value = cfg_buffer[n].value; 200 else 201 return (-1); 202 } 203 return (0); 204 } 205 206 int 207 mousecfg_put_field(int fd, struct wsmouse_parameters *field) 208 { 209 int i, n, d, err; 210 211 d = 0; 212 for (i = 0; i < field->nparams; i++) 213 if ((n = index_of(field->params[i].key)) < 0) 214 return (-1); 215 else 216 d |= (cfg_buffer[n].value != field->params[i].value); 217 218 if (!d) 219 return (0); 220 221 /* Write and read back immediately, wsmouse may normalize values. */ 222 if ((err = ioctl(fd, WSMOUSEIO_SETPARAMS, field)) 223 || (err = ioctl(fd, WSMOUSEIO_GETPARAMS, field))) 224 return err; 225 226 for (i = 0; i < field->nparams; i++) 227 cfg_buffer[n].value = field->params[i].value; 228 229 return (0); 230 } 231 232 static int 233 get_value(struct wsmouse_parameters *field, enum wsmousecfg key) 234 { 235 int i; 236 237 for (i = 0; i < field->nparams && key != field->params[i].key; i++) {} 238 239 return (i < field->nparams ? field->params[i].value : 0); 240 } 241 242 static void 243 set_value(struct wsmouse_parameters *field, enum wsmousecfg key, int value) 244 { 245 int i; 246 247 for (i = 0; i < field->nparams && key != field->params[i].key; i++) {} 248 249 field->params[i].value = (i < field->nparams ? value : 0); 250 } 251 252 static float 253 get_percent(struct wsmouse_parameters *field, enum wsmousecfg key) 254 { 255 return ((float) get_value(field, key) * 100 / 4096); 256 } 257 258 static void 259 set_percent(struct wsmouse_parameters *field, enum wsmousecfg key, float f) 260 { 261 set_value(field, key, (int) ((f * 4096 + 50) / 100)); 262 } 263 264 static int 265 set_edges(struct wsmouse_parameters *field, char *edges) 266 { 267 float f1, f2, f3, f4; 268 269 if (sscanf(edges, "%f,%f,%f,%f", &f1, &f2, &f3, &f4) == 4) { 270 set_percent(field, WSMOUSECFG_TOP_EDGE, f1); 271 set_percent(field, WSMOUSECFG_RIGHT_EDGE,f2); 272 set_percent(field, WSMOUSECFG_BOTTOM_EDGE, f3); 273 set_percent(field, WSMOUSECFG_LEFT_EDGE, f4); 274 return (0); 275 } 276 return (-1); 277 } 278 279 /* 280 * Read or write up to four raw parameter values. In this case 281 * reading is a 'put' operation that writes back a value from the 282 * buffer. 283 */ 284 static int 285 read_param(struct wsmouse_parameters *field, char *val) 286 { 287 int i, j, n; 288 289 n = sscanf(val, "%d:%d,%d:%d,%d:%d,%d:%d", 290 &field->params[0].key, &field->params[0].value, 291 &field->params[1].key, &field->params[1].value, 292 &field->params[2].key, &field->params[2].value, 293 &field->params[3].key, &field->params[3].value); 294 if (n > 0 && (n & 1) == 0) { 295 n /= 2; 296 for (i = 0; i < n; i++) { 297 if (index_of(field->params[i].key) < 0) 298 return (-1); 299 } 300 field->nparams = n; 301 return (0); 302 } 303 n = sscanf(val, "%d,%d,%d,%d", 304 &field->params[0].key, &field->params[1].key, 305 &field->params[2].key, &field->params[3].key); 306 if (n > 0) { 307 for (i = 0; i < n; i++) { 308 if ((j = index_of(field->params[i].key)) < 0) 309 return (-1); 310 field->params[i].value = cfg_buffer[j].value; 311 } 312 field->nparams = n; 313 return (0); 314 } 315 return (-1); 316 } 317 318 void 319 mousecfg_pr_field(struct wsmouse_parameters *field) 320 { 321 int i, value; 322 float f; 323 324 if (field == &cfg_param) { 325 for (i = 0; i < field->nparams; i++) 326 printf(i > 0 ? ",%d:%d" : "%d:%d", 327 field->params[i].key, 328 field->params[i].value); 329 return; 330 } 331 332 if (field == &cfg_scaling) { 333 value = get_value(field, WSMOUSECFG_DX_SCALE); 334 f = (float) value / 4096; 335 printf("%.3f", f); 336 return; 337 } 338 339 if (field == &cfg_edges) { 340 printf("%.1f,%.1f,%.1f,%.1f", 341 get_percent(field, WSMOUSECFG_TOP_EDGE), 342 get_percent(field, WSMOUSECFG_RIGHT_EDGE), 343 get_percent(field, WSMOUSECFG_BOTTOM_EDGE), 344 get_percent(field, WSMOUSECFG_LEFT_EDGE)); 345 return; 346 } 347 348 for (i = 0; i < field->nparams; i++) 349 printf(i > 0 ? ",%d" : "%d", field->params[i].value); 350 } 351 352 void 353 mousecfg_rd_field(struct wsmouse_parameters *field, char *val) 354 { 355 int i, n; 356 const char *s; 357 float f; 358 359 if (field == &cfg_param) { 360 if (read_param(field, val)) 361 errx(1, "invalid input (param)"); 362 return; 363 } 364 365 if (field == &cfg_scaling) { 366 if (sscanf(val, "%f", &f) == 1) { 367 n = (int) (f * 4096); 368 set_value(field, WSMOUSECFG_DX_SCALE, n); 369 if (cfg_horiz_res && cfg_vert_res) 370 n = n * cfg_horiz_res / cfg_vert_res; 371 set_value(field, WSMOUSECFG_DY_SCALE, n); 372 } else { 373 errx(1, "invalid input (scaling)"); 374 } 375 return; 376 } 377 378 if (field == &cfg_edges) { 379 if (set_edges(field, val)) 380 errx(1, "invalid input (edges)"); 381 return; 382 } 383 384 s = val; 385 for (i = 0; i < field->nparams; i++) { 386 if (sscanf(s, (i > 0 ? ",%d" : "%d"), &n) != 1) 387 break; 388 field->params[i].value = abs(n); 389 for (s++; *s != '\0' && *s != ','; s++) {} 390 } 391 if (i < field->nparams || *s != '\0') 392 errx(1, "invalid input '%s'", val); 393 } 394