1 /* $OpenBSD: wstpad.c,v 1.26 2020/09/13 10:05:46 fcambus Exp $ */ 2 3 /* 4 * Copyright (c) 2015, 2016 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 * touchpad input processing 21 */ 22 23 #include <sys/param.h> 24 #include <sys/kernel.h> 25 #include <sys/malloc.h> 26 #include <sys/proc.h> 27 #include <sys/systm.h> 28 #include <sys/signalvar.h> 29 #include <sys/timeout.h> 30 31 #include <dev/wscons/wsconsio.h> 32 #include <dev/wscons/wsmousevar.h> 33 #include <dev/wscons/wseventvar.h> 34 #include <dev/wscons/wsmouseinput.h> 35 36 #define LEFTBTN (1 << 0) 37 #define MIDDLEBTN (1 << 1) 38 #define RIGHTBTN (1 << 2) 39 40 #define PRIMARYBTN LEFTBTN 41 42 #define PRIMARYBTN_CLICKED(tp) ((tp)->btns_sync & PRIMARYBTN & (tp)->btns) 43 #define PRIMARYBTN_RELEASED(tp) ((tp)->btns_sync & PRIMARYBTN & ~(tp)->btns) 44 45 #define IS_MT(tp) ((tp)->features & WSTPAD_MT) 46 #define DISABLE(tp) ((tp)->features & WSTPAD_DISABLE) 47 48 /* 49 * Ratios to the height or width of the touchpad surface, in 50 * [*.12] fixed-point format: 51 */ 52 #define V_EDGE_RATIO_DEFAULT 205 53 #define B_EDGE_RATIO_DEFAULT 410 54 #define T_EDGE_RATIO_DEFAULT 512 55 #define CENTER_RATIO_DEFAULT 512 56 57 #define TAP_MAXTIME_DEFAULT 180 58 #define TAP_CLICKTIME_DEFAULT 180 59 #define TAP_LOCKTIME_DEFAULT 0 60 61 #define CLICKDELAY_MS 20 62 #define FREEZE_MS 100 63 #define MATCHINTERVAL_MS 45 64 #define STOPINTERVAL_MS 55 65 66 #define MAG_LOW (10 << 12) 67 #define MAG_MEDIUM (18 << 12) 68 69 enum tpad_handlers { 70 SOFTBUTTON_HDLR, 71 TOPBUTTON_HDLR, 72 TAP_HDLR, 73 F2SCROLL_HDLR, 74 EDGESCROLL_HDLR, 75 CLICK_HDLR, 76 }; 77 78 enum tap_state { 79 TAP_DETECT, 80 TAP_IGNORE, 81 TAP_LIFTED, 82 TAP_2ND_TOUCH, 83 TAP_LOCKED, 84 TAP_NTH_TOUCH, 85 }; 86 87 enum tpad_cmd { 88 CLEAR_MOTION_DELTAS, 89 SOFTBUTTON_DOWN, 90 SOFTBUTTON_UP, 91 TAPBUTTON_DOWN, 92 TAPBUTTON_UP, 93 TAPBUTTON_DOUBLECLK, 94 VSCROLL, 95 HSCROLL, 96 }; 97 98 /* 99 * tpad_touch.flags: 100 */ 101 #define L_EDGE (1 << 0) 102 #define R_EDGE (1 << 1) 103 #define T_EDGE (1 << 2) 104 #define B_EDGE (1 << 3) 105 #define THUMB (1 << 4) 106 107 #define EDGES (L_EDGE | R_EDGE | T_EDGE | B_EDGE) 108 109 /* 110 * A touch is "centered" if it does not start and remain at the top 111 * edge or one of the vertical edges. Two-finger scrolling and tapping 112 * require that at least one touch is centered. 113 */ 114 #define CENTERED(t) (((t)->flags & (L_EDGE | R_EDGE | T_EDGE)) == 0) 115 116 enum touchstates { 117 TOUCH_NONE, 118 TOUCH_BEGIN, 119 TOUCH_UPDATE, 120 TOUCH_END, 121 }; 122 123 struct tpad_touch { 124 u_int flags; 125 enum touchstates state; 126 int x; 127 int y; 128 int dir; 129 struct timespec start; 130 struct timespec match; 131 struct position *pos; 132 struct { 133 int x; 134 int y; 135 struct timespec time; 136 } orig; 137 }; 138 139 /* 140 * wstpad.features 141 */ 142 #define WSTPAD_SOFTBUTTONS (1 << 0) 143 #define WSTPAD_SOFTMBTN (1 << 1) 144 #define WSTPAD_TOPBUTTONS (1 << 2) 145 #define WSTPAD_TWOFINGERSCROLL (1 << 3) 146 #define WSTPAD_EDGESCROLL (1 << 4) 147 #define WSTPAD_HORIZSCROLL (1 << 5) 148 #define WSTPAD_SWAPSIDES (1 << 6) 149 #define WSTPAD_DISABLE (1 << 7) 150 #define WSTPAD_TAPPING (1 << 8) 151 152 #define WSTPAD_MT (1 << 31) 153 154 155 struct wstpad { 156 u_int features; 157 u_int handlers; 158 159 /* 160 * t always points into the tpad_touches array, which has at 161 * least one element. If there is more than one, t selects 162 * the pointer-controlling touch. 163 */ 164 struct tpad_touch *t; 165 struct tpad_touch *tpad_touches; 166 167 u_int mtcycle; 168 u_int ignore; 169 170 int contacts; 171 int prev_contacts; 172 u_int btns; 173 u_int btns_sync; 174 int ratio; 175 176 struct timespec time; 177 178 u_int freeze; 179 struct timespec freeze_ts; 180 181 /* edge coordinates */ 182 struct { 183 int left; 184 int right; 185 int top; 186 int bottom; 187 int center; 188 int center_left; 189 int center_right; 190 int low; 191 } edge; 192 193 struct { 194 /* ratios to the surface width or height */ 195 int left_edge; 196 int right_edge; 197 int top_edge; 198 int bottom_edge; 199 int center_width; 200 /* two-finger contacts */ 201 int f2pressure; 202 int f2width; 203 } params; 204 205 /* handler state and configuration: */ 206 207 u_int softbutton; 208 u_int sbtnswap; 209 210 struct { 211 enum tap_state state; 212 int contacts; 213 int centered; 214 u_int button; 215 int maxdist; 216 struct timeout to; 217 /* parameters: */ 218 struct timespec maxtime; 219 int clicktime; 220 int locktime; 221 } tap; 222 223 struct { 224 int dz; 225 int dw; 226 int hdist; 227 int vdist; 228 int mag; 229 } scroll; 230 }; 231 232 static const struct timespec match_interval = 233 { .tv_sec = 0, .tv_nsec = MATCHINTERVAL_MS * 1000000 }; 234 235 static const struct timespec stop_interval = 236 { .tv_sec = 0, .tv_nsec = STOPINTERVAL_MS * 1000000 }; 237 238 /* 239 * Coordinates in the wstpad struct are "normalized" device coordinates, 240 * the orientation is left-to-right and upward. 241 */ 242 static inline int 243 normalize_abs(struct axis_filter *filter, int val) 244 { 245 return (filter->inv ? filter->inv - val : val); 246 } 247 248 static inline int 249 normalize_rel(struct axis_filter *filter, int val) 250 { 251 return (filter->inv ? -val : val); 252 } 253 254 /* 255 * Directions of motion are represented by numbers in the range 0 - 11, 256 * corresponding to clockwise counted circle sectors: 257 * 258 * 11 | 0 259 * 10 | 1 260 * 9 | 2 261 * -------+------- 262 * 8 | 3 263 * 7 | 4 264 * 6 | 5 265 * 266 */ 267 /* Tangent constants in [*.12] fixed-point format: */ 268 #define TAN_DEG_60 7094 269 #define TAN_DEG_30 2365 270 271 #define NORTH(d) ((d) == 0 || (d) == 11) 272 #define SOUTH(d) ((d) == 5 || (d) == 6) 273 #define EAST(d) ((d) == 2 || (d) == 3) 274 #define WEST(d) ((d) == 8 || (d) == 9) 275 276 static inline int 277 direction(int dx, int dy, int ratio) 278 { 279 int rdy, dir = -1; 280 281 if (dx || dy) { 282 rdy = abs(dy) * ratio; 283 if (abs(dx) * TAN_DEG_60 < rdy) 284 dir = 0; 285 else if (abs(dx) * TAN_DEG_30 < rdy) 286 dir = 1; 287 else 288 dir = 2; 289 if ((dx < 0) != (dy < 0)) 290 dir = 5 - dir; 291 if (dx < 0) 292 dir += 6; 293 } 294 return dir; 295 } 296 297 static inline int 298 dircmp(int dir1, int dir2) 299 { 300 int diff = abs(dir1 - dir2); 301 return (diff <= 6 ? diff : 12 - diff); 302 } 303 304 /* 305 * Update direction and timespec attributes for a touch. They are used to 306 * determine whether it is moving - or resting - stably. 307 * 308 * The callers pass touches from the current frame and the touches that are 309 * no longer present in the update cycle to this function. Even though this 310 * ensures that pairs of zero deltas do not result from stale coordinates, 311 * zero deltas do not reset the state immediately. A short time span - the 312 * "stop interval" - must pass before the state is cleared, which is 313 * necessary because some touchpads report intermediate stops when a touch 314 * is moving very slowly. 315 */ 316 void 317 wstpad_set_direction(struct wstpad *tp, struct tpad_touch *t, int dx, int dy) 318 { 319 int dir; 320 struct timespec ts; 321 322 if (t->state != TOUCH_UPDATE) { 323 t->dir = -1; 324 memcpy(&t->start, &tp->time, sizeof(struct timespec)); 325 return; 326 } 327 328 dir = direction(dx, dy, tp->ratio); 329 if (dir >= 0) { 330 if (t->dir < 0 || dircmp(dir, t->dir) > 1) { 331 memcpy(&t->start, &tp->time, sizeof(struct timespec)); 332 } 333 t->dir = dir; 334 memcpy(&t->match, &tp->time, sizeof(struct timespec)); 335 } else if (t->dir >= 0) { 336 timespecsub(&tp->time, &t->match, &ts); 337 if (timespeccmp(&ts, &stop_interval, >=)) { 338 t->dir = -1; 339 memcpy(&t->start, &t->match, sizeof(struct timespec)); 340 } 341 } 342 } 343 344 /* 345 * Make a rough, but quick estimation of the speed of a touch. Its 346 * distance to the previous position is scaled by factors derived 347 * from the average update rate and the deceleration parameter 348 * (filter.dclr). The unit of the result is: 349 * (filter.dclr / 100) device units per millisecond 350 * 351 * Magnitudes are returned in [*.12] fixed-point format. For purposes 352 * of filtering, they are divided into medium and high speeds 353 * (> MAG_MEDIUM), low speeds, and very low speeds (< MAG_LOW). 354 * 355 * The scale factors are not affected if deceleration is turned off. 356 */ 357 static inline int 358 magnitude(struct wsmouseinput *input, int dx, int dy) 359 { 360 int h, v; 361 362 h = abs(dx) * input->filter.h.mag_scale; 363 v = abs(dy) * input->filter.v.mag_scale; 364 /* Return an "alpha-max-plus-beta-min" approximation: */ 365 return (h >= v ? h + 3 * v / 8 : v + 3 * h / 8); 366 } 367 368 /* 369 * Treat a touch as stable if it is moving at a medium or high speed, 370 * if it is moving continuously, or if it has stopped for a certain 371 * time span. 372 */ 373 int 374 wstpad_is_stable(struct wsmouseinput *input, struct tpad_touch *t) 375 { 376 struct timespec ts; 377 378 if (t->dir >= 0) { 379 if (magnitude(input, t->pos->dx, t->pos->dy) > MAG_MEDIUM) 380 return (1); 381 timespecsub(&t->match, &t->start, &ts); 382 } else { 383 timespecsub(&input->tp->time, &t->start, &ts); 384 } 385 386 return (timespeccmp(&ts, &match_interval, >=)); 387 } 388 389 /* 390 * If a touch starts in an edge area, pointer movement will be 391 * suppressed as long as it stays in that area. 392 */ 393 static inline u_int 394 edge_flags(struct wstpad *tp, int x, int y) 395 { 396 u_int flags = 0; 397 398 if (x < tp->edge.left) 399 flags |= L_EDGE; 400 else if (x >= tp->edge.right) 401 flags |= R_EDGE; 402 if (y < tp->edge.bottom) 403 flags |= B_EDGE; 404 else if (y >= tp->edge.top) 405 flags |= T_EDGE; 406 407 return (flags); 408 } 409 410 static inline struct tpad_touch * 411 get_2nd_touch(struct wsmouseinput *input) 412 { 413 struct wstpad *tp = input->tp; 414 int slot; 415 416 if (IS_MT(tp)) { 417 slot = ffs(input->mt.touches & ~(input->mt.ptr | tp->ignore)); 418 if (slot) 419 return &tp->tpad_touches[--slot]; 420 } 421 return NULL; 422 } 423 424 /* Suppress pointer motion for a short period of time. */ 425 static inline void 426 set_freeze_ts(struct wstpad *tp, int sec, int ms) 427 { 428 tp->freeze_ts.tv_sec = sec; 429 tp->freeze_ts.tv_nsec = ms * 1000000; 430 timespecadd(&tp->time, &tp->freeze_ts, &tp->freeze_ts); 431 } 432 433 434 /* Return TRUE if two-finger- or edge-scrolling would be valid. */ 435 int 436 wstpad_scroll_coords(struct wsmouseinput *input, int *dx, int *dy) 437 { 438 struct wstpad *tp = input->tp; 439 440 if (tp->contacts != tp->prev_contacts || tp->btns || tp->btns_sync) { 441 tp->scroll.dz = 0; 442 tp->scroll.dw = 0; 443 return (0); 444 } 445 if ((input->motion.sync & SYNC_POSITION) == 0) 446 return (0); 447 /* 448 * Try to exclude accidental scroll events by checking whether the 449 * pointer-controlling touch is stable. The check, which may cause 450 * a short delay, is only applied initially, a touch that stops and 451 * resumes scrolling is not affected. 452 */ 453 if (tp->scroll.dz || tp->scroll.dw || wstpad_is_stable(input, tp->t)) { 454 *dx = normalize_rel(&input->filter.h, input->motion.pos.dx); 455 *dy = normalize_rel(&input->filter.v, input->motion.pos.dy); 456 return (*dx || *dy); 457 } 458 459 return (0); 460 } 461 462 void 463 wstpad_scroll(struct wstpad *tp, int dx, int dy, int mag, u_int *cmds) 464 { 465 int dz, dw, n = 1; 466 467 /* 468 * The function applies strong deceleration, but only to input with 469 * very low speeds. A higher threshold might make applications 470 * without support for precision scrolling appear unresponsive. 471 */ 472 mag = tp->scroll.mag = imin(MAG_MEDIUM, 473 (mag + 3 * tp->scroll.mag) / 4); 474 if (mag < MAG_LOW) 475 n = (MAG_LOW - mag) / 4096 + 1; 476 477 if (dy && tp->scroll.vdist) { 478 if (tp->scroll.dw) { 479 /* 480 * Before switching the axis, wstpad_scroll_coords() 481 * should check again whether the movement is stable. 482 */ 483 tp->scroll.dw = 0; 484 return; 485 } 486 dz = -dy * 4096 / (tp->scroll.vdist * n); 487 if (tp->scroll.dz) { 488 if ((dy < 0) != (tp->scroll.dz > 0)) 489 tp->scroll.dz = -tp->scroll.dz; 490 dz = (dz + 3 * tp->scroll.dz) / 4; 491 } 492 if (dz) { 493 tp->scroll.dz = dz; 494 *cmds |= 1 << VSCROLL; 495 } 496 497 } else if (dx && tp->scroll.hdist) { 498 if (tp->scroll.dz) { 499 tp->scroll.dz = 0; 500 return; 501 } 502 dw = dx * 4096 / (tp->scroll.hdist * n); 503 if (tp->scroll.dw) { 504 if ((dx > 0) != (tp->scroll.dw > 0)) 505 tp->scroll.dw = -tp->scroll.dw; 506 dw = (dw + 3 * tp->scroll.dw) / 4; 507 } 508 if (dw) { 509 tp->scroll.dw = dw; 510 *cmds |= 1 << HSCROLL; 511 } 512 } 513 } 514 515 void 516 wstpad_f2scroll(struct wsmouseinput *input, u_int *cmds) 517 { 518 struct wstpad *tp = input->tp; 519 struct tpad_touch *t2; 520 int dir, dx, dy, centered; 521 522 if (tp->ignore == 0) { 523 if (tp->contacts != 2) 524 return; 525 } else if (tp->contacts != 3 || (tp->ignore == input->mt.ptr)) { 526 return; 527 } 528 529 if (!wstpad_scroll_coords(input, &dx, &dy)) 530 return; 531 532 dir = tp->t->dir; 533 if (!(NORTH(dir) || SOUTH(dir))) 534 dy = 0; 535 if (!(EAST(dir) || WEST(dir))) 536 dx = 0; 537 538 if (dx || dy) { 539 centered = CENTERED(tp->t); 540 if (IS_MT(tp)) { 541 t2 = get_2nd_touch(input); 542 if (t2 == NULL) 543 return; 544 dir = t2->dir; 545 if ((dy > 0 && !NORTH(dir)) || (dy < 0 && !SOUTH(dir))) 546 return; 547 if ((dx > 0 && !EAST(dir)) || (dx < 0 && !WEST(dir))) 548 return; 549 if (!wstpad_is_stable(input, t2) && 550 !(tp->scroll.dz || tp->scroll.dw)) 551 return; 552 centered |= CENTERED(t2); 553 } 554 if (centered) { 555 wstpad_scroll(tp, dx, dy, 556 magnitude(input, dx, dy), cmds); 557 set_freeze_ts(tp, 0, FREEZE_MS); 558 } 559 } 560 } 561 562 void 563 wstpad_edgescroll(struct wsmouseinput *input, u_int *cmds) 564 { 565 struct wstpad *tp = input->tp; 566 struct tpad_touch *t = tp->t; 567 u_int v_edge, b_edge; 568 int dx, dy; 569 570 if (!wstpad_scroll_coords(input, &dx, &dy) || tp->contacts != 1) 571 return; 572 573 v_edge = (tp->features & WSTPAD_SWAPSIDES) ? L_EDGE : R_EDGE; 574 b_edge = (tp->features & WSTPAD_HORIZSCROLL) ? B_EDGE : 0; 575 576 if ((t->flags & v_edge) == 0) 577 dy = 0; 578 if ((t->flags & b_edge) == 0) 579 dx = 0; 580 581 if (dx || dy) 582 wstpad_scroll(tp, dx, dy, magnitude(input, dx, dy), cmds); 583 } 584 585 static inline u_int 586 sbtn(struct wstpad *tp, int x, int y) 587 { 588 if (y >= tp->edge.bottom) 589 return (0); 590 if ((tp->features & WSTPAD_SOFTMBTN) 591 && x >= tp->edge.center_left 592 && x < tp->edge.center_right) 593 return (MIDDLEBTN); 594 return ((x < tp->edge.center ? LEFTBTN : RIGHTBTN) ^ tp->sbtnswap); 595 } 596 597 static inline u_int 598 top_sbtn(struct wstpad *tp, int x, int y) 599 { 600 if (y < tp->edge.top) 601 return (0); 602 if (x < tp->edge.center_left) 603 return (LEFTBTN ^ tp->sbtnswap); 604 return (x > tp->edge.center_right 605 ? (RIGHTBTN ^ tp->sbtnswap) : MIDDLEBTN); 606 } 607 608 u_int 609 wstpad_get_sbtn(struct wsmouseinput *input, int top) 610 { 611 struct wstpad *tp = input->tp; 612 struct tpad_touch *t = tp->t; 613 u_int btn; 614 615 btn = 0; 616 if (tp->contacts) { 617 btn = top ? top_sbtn(tp, t->x, t->y) : sbtn(tp, t->x, t->y); 618 /* 619 * If there is no middle-button area, but contacts in both 620 * halves of the edge zone, generate a middle-button event: 621 */ 622 if (btn && IS_MT(tp) && tp->contacts == 2 623 && !top && !(tp->features & WSTPAD_SOFTMBTN)) { 624 if ((t = get_2nd_touch(input)) != NULL) 625 btn |= sbtn(tp, t->x, t->y); 626 if (btn == (LEFTBTN | RIGHTBTN)) 627 btn = MIDDLEBTN; 628 } 629 } 630 return (btn != PRIMARYBTN ? btn : 0); 631 } 632 633 void 634 wstpad_softbuttons(struct wsmouseinput *input, u_int *cmds, int hdlr) 635 { 636 struct wstpad *tp = input->tp; 637 int top = (hdlr == TOPBUTTON_HDLR); 638 639 if (tp->softbutton && PRIMARYBTN_RELEASED(tp)) { 640 *cmds |= 1 << SOFTBUTTON_UP; 641 return; 642 } 643 644 if (tp->softbutton == 0 && PRIMARYBTN_CLICKED(tp)) { 645 tp->softbutton = wstpad_get_sbtn(input, top); 646 if (tp->softbutton) 647 *cmds |= 1 << SOFTBUTTON_DOWN; 648 } 649 } 650 651 int 652 wstpad_is_tap(struct wstpad *tp, struct tpad_touch *t) 653 { 654 struct timespec ts; 655 int dx, dy, dist = 0; 656 657 /* 658 * No distance limit applies if there has been more than one contact 659 * on a single-touch device. We cannot use (t->x - t->orig.x) in this 660 * case. Accumulated deltas might be an alternative, but some 661 * touchpads provide unreliable coordinates at the start or end of a 662 * multi-finger touch. 663 */ 664 if (IS_MT(tp) || tp->tap.contacts < 2) { 665 dx = abs(t->x - t->orig.x) << 12; 666 dy = abs(t->y - t->orig.y) * tp->ratio; 667 dist = (dx >= dy ? dx + 3 * dy / 8 : dy + 3 * dx / 8); 668 } 669 if (dist <= (tp->tap.maxdist << 12)) { 670 timespecsub(&tp->time, &t->orig.time, &ts); 671 return (timespeccmp(&ts, &tp->tap.maxtime, <)); 672 } 673 return (0); 674 } 675 676 /* 677 * Return the oldest touch in the TOUCH_END state, or NULL. 678 */ 679 struct tpad_touch * 680 wstpad_tap_touch(struct wsmouseinput *input) 681 { 682 struct wstpad *tp = input->tp; 683 struct tpad_touch *s, *t = NULL; 684 u_int lifted; 685 int slot; 686 687 if (IS_MT(tp)) { 688 lifted = (input->mt.sync[MTS_TOUCH] & ~input->mt.touches); 689 FOREACHBIT(lifted, slot) { 690 s = &tp->tpad_touches[slot]; 691 if (tp->tap.state == TAP_DETECT) 692 tp->tap.centered |= CENTERED(s); 693 if (t == NULL || timespeccmp(&t->orig.time, 694 &s->orig.time, >)) 695 t = s; 696 } 697 } else { 698 if (tp->t->state == TOUCH_END) { 699 t = tp->t; 700 if (tp->tap.state == TAP_DETECT) 701 tp->tap.centered = CENTERED(t); 702 } 703 } 704 705 return (t); 706 } 707 708 /* 709 * If each contact in a sequence of contacts that overlap in time 710 * is a tap, a button event may be generated when the number of 711 * contacts drops to zero, or to one if there is a masked touch. 712 */ 713 static inline int 714 tap_finished(struct wstpad *tp, int nmasked) 715 { 716 return (tp->contacts == nmasked 717 && (nmasked == 0 || !wstpad_is_tap(tp, tp->t))); 718 } 719 720 static inline u_int 721 tap_btn(struct wstpad *tp, int nmasked) 722 { 723 int n = tp->tap.contacts - nmasked; 724 725 return (n == 2 ? RIGHTBTN : (n == 3 ? MIDDLEBTN : LEFTBTN)); 726 } 727 728 /* 729 * This handler supports one-, two-, and three-finger-taps, which 730 * are mapped to left-button, right-button and middle-button events, 731 * respectively; moreover, it supports tap-and-drag operations with 732 * "locked drags", which are finished by a timeout or a tap-to-end 733 * gesture. 734 */ 735 void 736 wstpad_tap(struct wsmouseinput *input, u_int *cmds) 737 { 738 struct wstpad *tp = input->tp; 739 struct tpad_touch *t; 740 int nmasked, err = 0; 741 742 if (tp->btns) { 743 /* 744 * Don't process tapping while hardware buttons are being 745 * pressed. If the handler is not in its initial state, 746 * release the "tap button". 747 */ 748 if (tp->tap.state > TAP_IGNORE) { 749 timeout_del(&tp->tap.to); 750 *cmds |= 1 << TAPBUTTON_UP; 751 } 752 /* 753 * It might be possible to produce a click within the tap 754 * timeout; ignore the current touch. 755 */ 756 tp->tap.state = TAP_IGNORE; 757 tp->tap.contacts = 0; 758 tp->tap.centered = 0; 759 } 760 761 /* 762 * If a touch from the bottom area is masked, reduce the 763 * contact counts and ignore it. 764 */ 765 nmasked = (input->mt.ptr_mask ? 1 : 0); 766 767 /* 768 * Only touches in the TOUCH_END state are relevant here. 769 * t is NULL if no touch has been lifted. 770 */ 771 t = wstpad_tap_touch(input); 772 773 switch (tp->tap.state) { 774 case TAP_DETECT: 775 if (tp->contacts > tp->tap.contacts) 776 tp->tap.contacts = tp->contacts; 777 778 if (t) { 779 if (wstpad_is_tap(tp, t)) { 780 if (tap_finished(tp, nmasked)) { 781 if (tp->tap.centered) { 782 tp->tap.state = TAP_LIFTED; 783 tp->tap.button = 784 tap_btn(tp, nmasked); 785 } 786 tp->tap.contacts = 0; 787 tp->tap.centered = 0; 788 } 789 } else { 790 if (tp->contacts > nmasked) 791 tp->tap.state = TAP_IGNORE; 792 tp->tap.contacts = 0; 793 tp->tap.centered = 0; 794 } 795 if (tp->tap.state == TAP_LIFTED) { 796 *cmds |= 1 << TAPBUTTON_DOWN; 797 err = !timeout_add_msec(&tp->tap.to, 798 tp->tap.clicktime); 799 } 800 } 801 break; 802 803 case TAP_IGNORE: 804 if (tp->contacts == nmasked) 805 tp->tap.state = TAP_DETECT; 806 break; 807 case TAP_LIFTED: 808 if (tp->contacts > nmasked) { 809 timeout_del(&tp->tap.to); 810 if (tp->tap.button == LEFTBTN) { 811 tp->tap.state = TAP_2ND_TOUCH; 812 } else { 813 *cmds |= 1 << TAPBUTTON_UP; 814 tp->tap.state = TAP_DETECT; 815 } 816 } 817 break; 818 case TAP_2ND_TOUCH: 819 if (t) { 820 if (wstpad_is_tap(tp, t)) { 821 *cmds |= 1 << TAPBUTTON_DOUBLECLK; 822 tp->tap.state = TAP_LIFTED; 823 err = !timeout_add_msec(&tp->tap.to, 824 CLICKDELAY_MS); 825 } else if (tp->contacts == nmasked) { 826 if (tp->tap.locktime == 0) { 827 *cmds |= 1 << TAPBUTTON_UP; 828 tp->tap.state = TAP_DETECT; 829 } else { 830 tp->tap.state = TAP_LOCKED; 831 err = !timeout_add_msec(&tp->tap.to, 832 tp->tap.locktime); 833 } 834 } 835 } else if (tp->contacts != nmasked + 1) { 836 *cmds |= 1 << TAPBUTTON_UP; 837 tp->tap.state = TAP_DETECT; 838 } 839 break; 840 case TAP_LOCKED: 841 if (tp->contacts > nmasked) { 842 timeout_del(&tp->tap.to); 843 tp->tap.state = TAP_NTH_TOUCH; 844 } 845 break; 846 case TAP_NTH_TOUCH: 847 if (t) { 848 if (wstpad_is_tap(tp, t)) { 849 /* "tap-to-end" */ 850 *cmds |= 1 << TAPBUTTON_UP; 851 tp->tap.state = TAP_DETECT; 852 } else if (tp->contacts == nmasked) { 853 tp->tap.state = TAP_LOCKED; 854 err = !timeout_add_msec(&tp->tap.to, 855 tp->tap.locktime); 856 } 857 } else if (tp->contacts != nmasked + 1) { 858 *cmds |= 1 << TAPBUTTON_UP; 859 tp->tap.state = TAP_DETECT; 860 } 861 break; 862 } 863 864 if (err) { /* Did timeout_add fail? */ 865 if (tp->tap.state == TAP_LIFTED) 866 *cmds &= ~(1 << TAPBUTTON_DOWN); 867 else 868 *cmds |= 1 << TAPBUTTON_UP; 869 870 tp->tap.state = TAP_DETECT; 871 } 872 } 873 874 void 875 wstpad_tap_timeout(void *p) 876 { 877 struct wsmouseinput *input = p; 878 struct wstpad *tp = input->tp; 879 struct evq_access evq; 880 u_int btn; 881 int s; 882 883 s = spltty(); 884 evq.evar = *input->evar; 885 if (evq.evar != NULL && tp != NULL && 886 (tp->tap.state == TAP_LIFTED || tp->tap.state == TAP_LOCKED)) { 887 tp->tap.state = TAP_DETECT; 888 input->sbtn.buttons &= ~tp->tap.button; 889 btn = ffs(tp->tap.button) - 1; 890 evq.put = evq.evar->put; 891 evq.result = EVQ_RESULT_NONE; 892 getnanotime(&evq.ts); 893 wsmouse_evq_put(&evq, BTN_UP_EV, btn); 894 wsmouse_evq_put(&evq, SYNC_EV, 0); 895 if (evq.result == EVQ_RESULT_SUCCESS) { 896 if (input->flags & LOG_EVENTS) { 897 wsmouse_log_events(input, &evq); 898 } 899 evq.evar->put = evq.put; 900 WSEVENT_WAKEUP(evq.evar); 901 } else { 902 input->sbtn.sync |= tp->tap.button; 903 } 904 } 905 splx(s); 906 } 907 908 /* 909 * Suppress accidental pointer movements after a click on a clickpad. 910 */ 911 void 912 wstpad_click(struct wsmouseinput *input) 913 { 914 struct wstpad *tp = input->tp; 915 916 if (tp->contacts == 1 && 917 (PRIMARYBTN_CLICKED(tp) || PRIMARYBTN_RELEASED(tp))) 918 set_freeze_ts(tp, 0, FREEZE_MS); 919 } 920 921 /* 922 * Translate the "command" bits into the sync-state of wsmouse, or into 923 * wscons events. 924 */ 925 void 926 wstpad_cmds(struct wsmouseinput *input, struct evq_access *evq, u_int cmds) 927 { 928 struct wstpad *tp = input->tp; 929 u_int btn, sbtns_dn = 0, sbtns_up = 0; 930 int n; 931 932 FOREACHBIT(cmds, n) { 933 switch (n) { 934 case CLEAR_MOTION_DELTAS: 935 input->motion.dx = input->motion.dy = 0; 936 if (input->motion.dz == 0 && input->motion.dw == 0) 937 input->motion.sync &= ~SYNC_DELTAS; 938 continue; 939 case SOFTBUTTON_DOWN: 940 input->btn.sync &= ~PRIMARYBTN; 941 sbtns_dn |= tp->softbutton; 942 continue; 943 case SOFTBUTTON_UP: 944 input->btn.sync &= ~PRIMARYBTN; 945 sbtns_up |= tp->softbutton; 946 tp->softbutton = 0; 947 continue; 948 case TAPBUTTON_DOWN: 949 sbtns_dn |= tp->tap.button; 950 continue; 951 case TAPBUTTON_UP: 952 sbtns_up |= tp->tap.button; 953 continue; 954 case TAPBUTTON_DOUBLECLK: 955 /* 956 * We cannot add the final BTN_UP event here, a 957 * delay is required. This is the reason why the 958 * tap handler returns from the 2ND_TOUCH state 959 * into the LIFTED state with a short timeout 960 * (CLICKDELAY_MS). 961 */ 962 btn = ffs(PRIMARYBTN) - 1; 963 wsmouse_evq_put(evq, BTN_UP_EV, btn); 964 wsmouse_evq_put(evq, SYNC_EV, 0); 965 wsmouse_evq_put(evq, BTN_DOWN_EV, btn); 966 continue; 967 case HSCROLL: 968 input->motion.dw = tp->scroll.dw; 969 input->motion.sync |= SYNC_DELTAS; 970 continue; 971 case VSCROLL: 972 input->motion.dz = tp->scroll.dz; 973 input->motion.sync |= SYNC_DELTAS; 974 continue; 975 default: 976 printf("[wstpad] invalid cmd %d\n", n); 977 break; 978 } 979 } 980 if (sbtns_dn || sbtns_up) { 981 input->sbtn.buttons |= sbtns_dn; 982 input->sbtn.buttons &= ~sbtns_up; 983 input->sbtn.sync |= (sbtns_dn | sbtns_up); 984 } 985 } 986 987 988 /* 989 * Set the state of touches that have ended. TOUCH_END is a transitional 990 * state and will be changed to TOUCH_NONE before process_input() returns. 991 */ 992 static inline void 993 clear_touchstates(struct wsmouseinput *input, enum touchstates state) 994 { 995 u_int touches; 996 int slot; 997 998 touches = input->mt.sync[MTS_TOUCH] & ~input->mt.touches; 999 FOREACHBIT(touches, slot) 1000 input->tp->tpad_touches[slot].state = state; 1001 } 1002 1003 void 1004 wstpad_mt_inputs(struct wsmouseinput *input) 1005 { 1006 struct wstpad *tp = input->tp; 1007 struct tpad_touch *t; 1008 int slot, dx, dy; 1009 u_int touches, inactive; 1010 1011 /* TOUCH_BEGIN */ 1012 touches = input->mt.touches & input->mt.sync[MTS_TOUCH]; 1013 FOREACHBIT(touches, slot) { 1014 t = &tp->tpad_touches[slot]; 1015 t->state = TOUCH_BEGIN; 1016 t->x = normalize_abs(&input->filter.h, t->pos->x); 1017 t->y = normalize_abs(&input->filter.v, t->pos->y); 1018 t->orig.x = t->x; 1019 t->orig.y = t->y; 1020 memcpy(&t->orig.time, &tp->time, sizeof(struct timespec)); 1021 t->flags = edge_flags(tp, t->x, t->y); 1022 wstpad_set_direction(tp, t, 0, 0); 1023 } 1024 1025 /* TOUCH_UPDATE */ 1026 touches = input->mt.touches & input->mt.frame; 1027 if (touches & tp->mtcycle) { 1028 /* 1029 * Slot data may be synchronized separately, in any order, 1030 * or not at all if there is no delta. Identify the touches 1031 * without deltas. 1032 */ 1033 inactive = input->mt.touches & ~tp->mtcycle; 1034 tp->mtcycle = touches; 1035 } else { 1036 inactive = 0; 1037 tp->mtcycle |= touches; 1038 } 1039 touches = input->mt.touches & ~input->mt.sync[MTS_TOUCH]; 1040 FOREACHBIT(touches, slot) { 1041 t = &tp->tpad_touches[slot]; 1042 t->state = TOUCH_UPDATE; 1043 if ((1 << slot) & input->mt.frame) { 1044 dx = normalize_abs(&input->filter.h, t->pos->x) - t->x; 1045 t->x += dx; 1046 dy = normalize_abs(&input->filter.v, t->pos->y) - t->y; 1047 t->y += dy; 1048 t->flags &= (~EDGES | edge_flags(tp, t->x, t->y)); 1049 if (wsmouse_hysteresis(input, t->pos)) 1050 dx = dy = 0; 1051 wstpad_set_direction(tp, t, dx, dy); 1052 } else if ((1 << slot) & inactive) { 1053 wstpad_set_direction(tp, t, 0, 0); 1054 } 1055 } 1056 1057 clear_touchstates(input, TOUCH_END); 1058 } 1059 1060 /* 1061 * Identify "thumb" contacts in the bottom area. The identification 1062 * has three stages: 1063 * 1. If exactly one of two or more touches is in the bottom area, it 1064 * is masked, which means it does not receive pointer control as long 1065 * as there are alternatives. Once set, the mask will only be cleared 1066 * when the touch is released. 1067 * Tap detection ignores a masked touch if it does not participate in 1068 * a tap gesture. 1069 * 2. If the pointer-controlling touch is moving stably while a masked 1070 * touch in the bottom area is resting, or only moving minimally, the 1071 * pointer mask is copied to tp->ignore. In this stage, the masked 1072 * touch does not block pointer movement, and it is ignored by 1073 * wstpad_f2scroll(). 1074 * Decisions are made more or less immediately, there may be errors 1075 * in edge cases. If a fast or long upward movement is detected, 1076 * tp->ignore is cleared. There is no other transition from stage 2 1077 * to scrolling, or vice versa, for a pair of touches. 1078 * 3. If tp->ignore is set and the touch is resting, it is marked as 1079 * thumb, and it will be ignored until it ends. 1080 */ 1081 void 1082 wstpad_mt_masks(struct wsmouseinput *input) 1083 { 1084 struct wstpad *tp = input->tp; 1085 struct tpad_touch *t; 1086 struct position *pos; 1087 u_int mask; 1088 int slot; 1089 1090 tp->ignore &= input->mt.touches; 1091 1092 if (tp->contacts < 2) 1093 return; 1094 1095 if (tp->ignore) { 1096 slot = ffs(tp->ignore) - 1; 1097 t = &tp->tpad_touches[slot]; 1098 if (t->flags & THUMB) 1099 return; 1100 if (t->dir < 0 && wstpad_is_stable(input, t)) { 1101 t->flags |= THUMB; 1102 return; 1103 } 1104 /* The edge.low area is a bit larger than the bottom area. */ 1105 if (t->y >= tp->edge.low || (NORTH(t->dir) && 1106 magnitude(input, t->pos->dx, t->pos->dy) >= MAG_MEDIUM)) 1107 tp->ignore = 0; 1108 return; 1109 } 1110 1111 if (input->mt.ptr_mask == 0) { 1112 mask = ~0; 1113 FOREACHBIT(input->mt.touches, slot) { 1114 t = &tp->tpad_touches[slot]; 1115 if (t->flags & B_EDGE) { 1116 mask &= (1 << slot); 1117 input->mt.ptr_mask = mask; 1118 } 1119 } 1120 } 1121 1122 if ((input->mt.ptr_mask & ~input->mt.ptr) 1123 && !(tp->scroll.dz || tp->scroll.dw) 1124 && tp->t->dir >= 0 1125 && wstpad_is_stable(input, tp->t)) { 1126 1127 slot = ffs(input->mt.ptr_mask) - 1; 1128 t = &tp->tpad_touches[slot]; 1129 1130 if (t->y >= tp->edge.low) 1131 return; 1132 1133 if (!wstpad_is_stable(input, t)) 1134 return; 1135 1136 /* Default hysteresis limits are low. Make a strict check. */ 1137 pos = tp->t->pos; 1138 if (abs(pos->acc_dx) < 3 * input->filter.h.hysteresis 1139 && abs(pos->acc_dy) < 3 * input->filter.v.hysteresis) 1140 return; 1141 1142 if (t->dir >= 0) { 1143 /* Treat t as thumb if it is slow while tp->t is fast. */ 1144 if (magnitude(input, t->pos->dx, t->pos->dy) > MAG_LOW 1145 || magnitude(input, pos->dx, pos->dy) < MAG_MEDIUM) 1146 return; 1147 } 1148 1149 tp->ignore = input->mt.ptr_mask; 1150 } 1151 } 1152 1153 void 1154 wstpad_touch_inputs(struct wsmouseinput *input) 1155 { 1156 struct wstpad *tp = input->tp; 1157 struct tpad_touch *t; 1158 int slot, x, y, dx, dy; 1159 1160 tp->btns = input->btn.buttons; 1161 tp->btns_sync = input->btn.sync; 1162 1163 tp->prev_contacts = tp->contacts; 1164 tp->contacts = input->touch.contacts; 1165 1166 if (tp->contacts == 1 && 1167 ((tp->params.f2width && 1168 input->touch.width >= tp->params.f2width) 1169 || (tp->params.f2pressure && 1170 input->touch.pressure >= tp->params.f2pressure))) 1171 tp->contacts = 2; 1172 1173 if (IS_MT(tp)) { 1174 wstpad_mt_inputs(input); 1175 if (input->mt.ptr) { 1176 slot = ffs(input->mt.ptr) - 1; 1177 tp->t = &tp->tpad_touches[slot]; 1178 } 1179 wstpad_mt_masks(input); 1180 } else { 1181 t = tp->t; 1182 if (tp->contacts) 1183 t->state = (tp->prev_contacts ? 1184 TOUCH_UPDATE : TOUCH_BEGIN); 1185 else 1186 t->state = (tp->prev_contacts ? 1187 TOUCH_END : TOUCH_NONE); 1188 1189 dx = dy = 0; 1190 x = normalize_abs(&input->filter.h, t->pos->x); 1191 y = normalize_abs(&input->filter.v, t->pos->y); 1192 if (t->state == TOUCH_BEGIN) { 1193 t->x = t->orig.x = x; 1194 t->y = t->orig.y = y; 1195 memcpy(&t->orig.time, &tp->time, 1196 sizeof(struct timespec)); 1197 t->flags = edge_flags(tp, x, y); 1198 } else if (input->motion.sync & SYNC_POSITION) { 1199 if (!wsmouse_hysteresis(input, t->pos)) { 1200 dx = x - t->x; 1201 dy = y - t->y; 1202 } 1203 t->x = x; 1204 t->y = y; 1205 t->flags &= (~EDGES | edge_flags(tp, x, y)); 1206 } 1207 wstpad_set_direction(tp, t, dx, dy); 1208 } 1209 } 1210 1211 static inline int 1212 t2_ignore(struct wsmouseinput *input) 1213 { 1214 /* 1215 * If there are two touches, do not block pointer movement if they 1216 * perform a click-and-drag action, or if the second touch is 1217 * resting in the bottom area. 1218 */ 1219 return (input->tp->contacts == 2 && ((input->tp->btns & PRIMARYBTN) 1220 || (input->tp->ignore & ~input->mt.ptr))); 1221 } 1222 1223 void 1224 wstpad_process_input(struct wsmouseinput *input, struct evq_access *evq) 1225 { 1226 struct wstpad *tp = input->tp; 1227 u_int handlers, hdlr, cmds; 1228 1229 memcpy(&tp->time, &evq->ts, sizeof(struct timespec)); 1230 wstpad_touch_inputs(input); 1231 1232 cmds = 0; 1233 handlers = tp->handlers; 1234 if (DISABLE(tp)) 1235 handlers &= ((1 << TOPBUTTON_HDLR) | (1 << SOFTBUTTON_HDLR)); 1236 1237 FOREACHBIT(handlers, hdlr) { 1238 switch (hdlr) { 1239 case SOFTBUTTON_HDLR: 1240 case TOPBUTTON_HDLR: 1241 wstpad_softbuttons(input, &cmds, hdlr); 1242 continue; 1243 case TAP_HDLR: 1244 wstpad_tap(input, &cmds); 1245 continue; 1246 case F2SCROLL_HDLR: 1247 wstpad_f2scroll(input, &cmds); 1248 continue; 1249 case EDGESCROLL_HDLR: 1250 wstpad_edgescroll(input, &cmds); 1251 continue; 1252 case CLICK_HDLR: 1253 wstpad_click(input); 1254 continue; 1255 } 1256 } 1257 1258 /* Check whether pointer movement should be blocked. */ 1259 if (input->motion.dx || input->motion.dy) { 1260 if (DISABLE(tp) 1261 || (tp->t->flags & tp->freeze) 1262 || timespeccmp(&tp->time, &tp->freeze_ts, <) 1263 || (tp->contacts > 1 && !t2_ignore(input))) { 1264 1265 cmds |= 1 << CLEAR_MOTION_DELTAS; 1266 } 1267 } 1268 1269 wstpad_cmds(input, evq, cmds); 1270 1271 if (IS_MT(tp)) 1272 clear_touchstates(input, TOUCH_NONE); 1273 } 1274 1275 /* 1276 * Try to determine the average interval between two updates. Various 1277 * conditions are checked in order to ensure that only valid samples enter 1278 * into the calculation. Above all, it is restricted to motion events 1279 * occurring when there is only one contact. MT devices may need more than 1280 * one packet to transmit their state if there are multiple touches, and 1281 * the update frequency may be higher in this case. 1282 */ 1283 void 1284 wstpad_track_interval(struct wsmouseinput *input, struct timespec *time) 1285 { 1286 static const struct timespec limit = { 0, 30 * 1000000L }; 1287 struct timespec ts; 1288 int samples; 1289 1290 if (input->motion.sync == 0 1291 || (input->touch.sync & SYNC_CONTACTS) 1292 || (input->touch.contacts > 1)) { 1293 input->intv.track = 0; 1294 return; 1295 } 1296 if (input->intv.track) { 1297 timespecsub(time, &input->intv.ts, &ts); 1298 if (timespeccmp(&ts, &limit, <)) { 1299 /* The unit of the sum is 4096 nanoseconds. */ 1300 input->intv.sum += ts.tv_nsec >> 12; 1301 samples = ++input->intv.samples; 1302 /* 1303 * Make the first calculation quickly and later 1304 * a more reliable one: 1305 */ 1306 if (samples == 8) { 1307 input->intv.avg = input->intv.sum << 9; 1308 wstpad_init_deceleration(input); 1309 } else if (samples == 128) { 1310 input->intv.avg = input->intv.sum << 5; 1311 wstpad_init_deceleration(input); 1312 input->intv.samples = 0; 1313 input->intv.sum = 0; 1314 input->flags &= ~TRACK_INTERVAL; 1315 } 1316 } 1317 } 1318 memcpy(&input->intv.ts, time, sizeof(struct timespec)); 1319 input->intv.track = 1; 1320 } 1321 1322 1323 1324 /* 1325 * The default acceleration options of X don't work convincingly with 1326 * touchpads (the synaptics driver installs its own "acceleration 1327 * profile" and callback function). As a preliminary workaround, this 1328 * filter applies a simple deceleration scheme to small deltas, based 1329 * on the "magnitude" of the delta pair. A magnitude of 8 corresponds, 1330 * roughly, to a speed of (filter.dclr / 12.5) device units per milli- 1331 * second. If its magnitude is smaller than 7 a delta will be downscaled 1332 * by the factor 2/8, deltas with magnitudes from 7 to 11 by factors 1333 * ranging from 3/8 to 7/8. 1334 */ 1335 int 1336 wstpad_decelerate(struct wsmouseinput *input, int *dx, int *dy) 1337 { 1338 int mag, n, h, v; 1339 1340 mag = magnitude(input, *dx, *dy); 1341 1342 /* Don't change deceleration levels abruptly. */ 1343 mag = (mag + 7 * input->filter.mag) / 8; 1344 /* Don't use arbitrarily high values. */ 1345 input->filter.mag = imin(mag, 24 << 12); 1346 1347 n = imax((mag >> 12) - 4, 2); 1348 if (n < 8) { 1349 /* Scale by (n / 8). */ 1350 h = *dx * n + input->filter.h.dclr_rmdr; 1351 v = *dy * n + input->filter.v.dclr_rmdr; 1352 input->filter.h.dclr_rmdr = (h >= 0 ? h & 7 : -(-h & 7)); 1353 input->filter.v.dclr_rmdr = (v >= 0 ? v & 7 : -(-v & 7)); 1354 *dx = h / 8; 1355 *dy = v / 8; 1356 return (1); 1357 } 1358 return (0); 1359 } 1360 1361 void 1362 wstpad_filter(struct wsmouseinput *input) 1363 { 1364 struct axis_filter *h = &input->filter.h; 1365 struct axis_filter *v = &input->filter.v; 1366 struct position *pos = &input->motion.pos; 1367 int strength = input->filter.mode & 7; 1368 int dx, dy; 1369 1370 if (!(input->motion.sync & SYNC_POSITION) 1371 || (h->dmax && (abs(pos->dx) > h->dmax)) 1372 || (v->dmax && (abs(pos->dy) > v->dmax))) { 1373 dx = dy = 0; 1374 } else { 1375 dx = pos->dx; 1376 dy = pos->dy; 1377 } 1378 1379 if (wsmouse_hysteresis(input, pos)) 1380 dx = dy = 0; 1381 1382 if (input->filter.dclr && wstpad_decelerate(input, &dx, &dy)) 1383 /* Strong smoothing may hamper the precision at low speeds. */ 1384 strength = imin(strength, 2); 1385 1386 if (strength) { 1387 if ((input->touch.sync & SYNC_CONTACTS) 1388 || input->mt.ptr != input->mt.prev_ptr) { 1389 h->avg = v->avg = 0; 1390 } 1391 /* Use a weighted decaying average for smoothing. */ 1392 dx = dx * (8 - strength) + h->avg * strength + h->avg_rmdr; 1393 dy = dy * (8 - strength) + v->avg * strength + v->avg_rmdr; 1394 h->avg_rmdr = (dx >= 0 ? dx & 7 : -(-dx & 7)); 1395 v->avg_rmdr = (dy >= 0 ? dy & 7 : -(-dy & 7)); 1396 dx = h->avg = dx / 8; 1397 dy = v->avg = dy / 8; 1398 } 1399 1400 input->motion.dx = dx; 1401 input->motion.dy = dy; 1402 } 1403 1404 1405 /* 1406 * Compatibility-mode conversions. wstpad_filter transforms and filters 1407 * the coordinate inputs, extended functionality is provided by 1408 * wstpad_process_input. 1409 */ 1410 void 1411 wstpad_compat_convert(struct wsmouseinput *input, struct evq_access *evq) 1412 { 1413 if (input->flags & TRACK_INTERVAL) 1414 wstpad_track_interval(input, &evq->ts); 1415 1416 wstpad_filter(input); 1417 1418 if ((input->motion.dx || input->motion.dy) 1419 && !(input->motion.sync & SYNC_DELTAS)) { 1420 input->motion.dz = input->motion.dw = 0; 1421 input->motion.sync |= SYNC_DELTAS; 1422 } 1423 1424 if (input->tp != NULL) 1425 wstpad_process_input(input, evq); 1426 1427 input->motion.sync &= ~SYNC_POSITION; 1428 input->touch.sync = 0; 1429 } 1430 1431 int 1432 wstpad_init(struct wsmouseinput *input) 1433 { 1434 struct wstpad *tp = input->tp; 1435 int i, slots; 1436 1437 if (tp != NULL) 1438 return (0); 1439 1440 input->tp = tp = malloc(sizeof(struct wstpad), 1441 M_DEVBUF, M_WAITOK | M_ZERO); 1442 if (tp == NULL) 1443 return (-1); 1444 1445 slots = imax(input->mt.num_slots, 1); 1446 tp->tpad_touches = malloc(slots * sizeof(struct tpad_touch), 1447 M_DEVBUF, M_WAITOK | M_ZERO); 1448 if (tp->tpad_touches == NULL) { 1449 free(tp, M_DEVBUF, sizeof(struct wstpad)); 1450 return (-1); 1451 } 1452 1453 tp->t = &tp->tpad_touches[0]; 1454 if (input->mt.num_slots) { 1455 tp->features |= WSTPAD_MT; 1456 for (i = 0; i < input->mt.num_slots; i++) 1457 tp->tpad_touches[i].pos = &input->mt.slots[i].pos; 1458 } else { 1459 tp->t->pos = &input->motion.pos; 1460 } 1461 1462 timeout_set(&tp->tap.to, wstpad_tap_timeout, input); 1463 1464 tp->ratio = input->filter.ratio; 1465 1466 return (0); 1467 } 1468 1469 /* 1470 * Integer square root (Halleck's method) 1471 * 1472 * An adaption of code from John B. Halleck (from 1473 * http://www.cc.utah.edu/~nahaj/factoring/code.html). This version is 1474 * used and published under the OpenBSD license terms with his permission. 1475 * 1476 * Cf. also Martin Guy's "Square root by abacus" method. 1477 */ 1478 static inline u_int 1479 isqrt(u_int n) 1480 { 1481 u_int root, sqbit; 1482 1483 root = 0; 1484 sqbit = 1 << (sizeof(u_int) * 8 - 2); 1485 while (sqbit) { 1486 if (n >= (sqbit | root)) { 1487 n -= (sqbit | root); 1488 root = (root >> 1) | sqbit; 1489 } else { 1490 root >>= 1; 1491 } 1492 sqbit >>= 2; 1493 } 1494 return (root); 1495 } 1496 1497 void 1498 wstpad_init_deceleration(struct wsmouseinput *input) 1499 { 1500 int n, dclr; 1501 1502 if ((dclr = input->filter.dclr) == 0) 1503 return; 1504 1505 dclr = imax(dclr, 4); 1506 1507 /* 1508 * For a standard update rate of about 80Hz, (dclr) units 1509 * will be mapped to a magnitude of 8. If the average rate 1510 * is significantly higher or lower, adjust the coefficient 1511 * accordingly: 1512 */ 1513 if (input->intv.avg == 0) { 1514 n = 8; 1515 } else { 1516 n = 8 * 13000000 / input->intv.avg; 1517 n = imax(imin(n, 32), 4); 1518 } 1519 input->filter.h.mag_scale = (n << 12) / dclr; 1520 input->filter.v.mag_scale = (input->filter.ratio ? 1521 n * input->filter.ratio : n << 12) / dclr; 1522 input->filter.h.dclr_rmdr = 0; 1523 input->filter.v.dclr_rmdr = 0; 1524 input->flags |= TRACK_INTERVAL; 1525 } 1526 1527 int 1528 wstpad_configure(struct wsmouseinput *input) 1529 { 1530 struct wstpad *tp; 1531 int width, height, diag, offset, h_res, v_res, h_unit, v_unit; 1532 1533 width = abs(input->hw.x_max - input->hw.x_min); 1534 height = abs(input->hw.y_max - input->hw.y_min); 1535 if (width == 0 || height == 0) 1536 return (-1); /* We can't do anything. */ 1537 1538 if (input->tp == NULL && wstpad_init(input)) 1539 return (-1); 1540 tp = input->tp; 1541 1542 if (!(input->flags & CONFIGURED)) { 1543 /* 1544 * The filter parameters are derived from the length of the 1545 * diagonal in device units, with some magic constants which 1546 * are partly adapted from libinput or synaptics code, or are 1547 * based on tests and guess work. The absolute resolution 1548 * values might not be reliable, but if they are present the 1549 * settings are adapted to the ratio. 1550 */ 1551 h_res = input->hw.h_res; 1552 v_res = input->hw.v_res; 1553 if (h_res == 0 || v_res == 0) 1554 h_res = v_res = 1; 1555 diag = isqrt(width * width + height * height); 1556 input->filter.h.scale = (imin(920, diag) << 12) / diag; 1557 input->filter.v.scale = input->filter.h.scale * h_res / v_res; 1558 h_unit = imax(diag / 280, 3); 1559 v_unit = imax((h_unit * v_res + h_res / 2) / h_res, 3); 1560 input->filter.h.hysteresis = h_unit; 1561 input->filter.v.hysteresis = v_unit; 1562 input->filter.mode = FILTER_MODE_DEFAULT; 1563 input->filter.dclr = h_unit - h_unit / 5; 1564 wstpad_init_deceleration(input); 1565 1566 tp->features &= (WSTPAD_MT | WSTPAD_DISABLE); 1567 1568 if (input->hw.contacts_max != 1) 1569 tp->features |= WSTPAD_TWOFINGERSCROLL; 1570 else 1571 tp->features |= WSTPAD_EDGESCROLL; 1572 1573 if (input->hw.hw_type == WSMOUSEHW_CLICKPAD) { 1574 if (input->hw.type == WSMOUSE_TYPE_SYNAP_SBTN) { 1575 tp->features |= WSTPAD_TOPBUTTONS; 1576 } else { 1577 tp->features |= WSTPAD_SOFTBUTTONS; 1578 tp->features |= WSTPAD_SOFTMBTN; 1579 } 1580 } 1581 1582 tp->params.left_edge = V_EDGE_RATIO_DEFAULT; 1583 tp->params.right_edge = V_EDGE_RATIO_DEFAULT; 1584 tp->params.bottom_edge = ((tp->features & WSTPAD_SOFTBUTTONS) 1585 ? B_EDGE_RATIO_DEFAULT : 0); 1586 tp->params.top_edge = ((tp->features & WSTPAD_TOPBUTTONS) 1587 ? T_EDGE_RATIO_DEFAULT : 0); 1588 tp->params.center_width = CENTER_RATIO_DEFAULT; 1589 1590 tp->tap.maxtime.tv_nsec = TAP_MAXTIME_DEFAULT * 1000000; 1591 tp->tap.clicktime = TAP_CLICKTIME_DEFAULT; 1592 tp->tap.locktime = TAP_LOCKTIME_DEFAULT; 1593 1594 tp->scroll.hdist = 4 * h_unit; 1595 tp->scroll.vdist = 4 * v_unit; 1596 tp->tap.maxdist = 4 * h_unit; 1597 } 1598 1599 /* A touch with a flag set in this mask does not move the pointer. */ 1600 tp->freeze = EDGES; 1601 1602 offset = width * tp->params.left_edge / 4096; 1603 tp->edge.left = (offset ? input->hw.x_min + offset : INT_MIN); 1604 offset = width * tp->params.right_edge / 4096; 1605 tp->edge.right = (offset ? input->hw.x_max - offset : INT_MAX); 1606 offset = height * tp->params.bottom_edge / 4096; 1607 tp->edge.bottom = (offset ? input->hw.y_min + offset : INT_MIN); 1608 tp->edge.low = tp->edge.bottom + offset / 2; 1609 offset = height * tp->params.top_edge / 4096; 1610 tp->edge.top = (offset ? input->hw.y_max - offset : INT_MAX); 1611 1612 offset = width * abs(tp->params.center_width) / 8192; 1613 tp->edge.center = input->hw.x_min + width / 2; 1614 tp->edge.center_left = tp->edge.center - offset; 1615 tp->edge.center_right = tp->edge.center + offset; 1616 1617 tp->handlers = 0; 1618 1619 if (tp->features & WSTPAD_SOFTBUTTONS) 1620 tp->handlers |= 1 << SOFTBUTTON_HDLR; 1621 if (tp->features & WSTPAD_TOPBUTTONS) 1622 tp->handlers |= 1 << TOPBUTTON_HDLR; 1623 1624 if (tp->features & WSTPAD_TWOFINGERSCROLL) 1625 tp->handlers |= 1 << F2SCROLL_HDLR; 1626 else if (tp->features & WSTPAD_EDGESCROLL) 1627 tp->handlers |= 1 << EDGESCROLL_HDLR; 1628 1629 if (tp->features & WSTPAD_TAPPING) { 1630 tp->tap.clicktime = imin(imax(tp->tap.clicktime, 80), 350); 1631 if (tp->tap.locktime) 1632 tp->tap.locktime = 1633 imin(imax(tp->tap.locktime, 150), 5000); 1634 tp->handlers |= 1 << TAP_HDLR; 1635 } 1636 1637 if (input->hw.hw_type == WSMOUSEHW_CLICKPAD) 1638 tp->handlers |= 1 << CLICK_HDLR; 1639 1640 tp->sbtnswap = ((tp->features & WSTPAD_SWAPSIDES) 1641 ? (LEFTBTN | RIGHTBTN) : 0); 1642 1643 return (0); 1644 } 1645 1646 void 1647 wstpad_reset(struct wsmouseinput *input) 1648 { 1649 struct wstpad *tp = input->tp; 1650 1651 if (tp != NULL) { 1652 timeout_del(&tp->tap.to); 1653 tp->tap.state = TAP_DETECT; 1654 } 1655 1656 if (input->sbtn.buttons) { 1657 input->sbtn.sync = input->sbtn.buttons; 1658 input->sbtn.buttons = 0; 1659 } 1660 } 1661 1662 int 1663 wstpad_set_param(struct wsmouseinput *input, int key, int val) 1664 { 1665 struct wstpad *tp = input->tp; 1666 u_int flag; 1667 1668 if (tp == NULL) 1669 return (EINVAL); 1670 1671 switch (key) { 1672 case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_TAPPING: 1673 switch (key) { 1674 case WSMOUSECFG_SOFTBUTTONS: 1675 flag = WSTPAD_SOFTBUTTONS; 1676 break; 1677 case WSMOUSECFG_SOFTMBTN: 1678 flag = WSTPAD_SOFTMBTN; 1679 break; 1680 case WSMOUSECFG_TOPBUTTONS: 1681 flag = WSTPAD_TOPBUTTONS; 1682 break; 1683 case WSMOUSECFG_TWOFINGERSCROLL: 1684 flag = WSTPAD_TWOFINGERSCROLL; 1685 break; 1686 case WSMOUSECFG_EDGESCROLL: 1687 flag = WSTPAD_EDGESCROLL; 1688 break; 1689 case WSMOUSECFG_HORIZSCROLL: 1690 flag = WSTPAD_HORIZSCROLL; 1691 break; 1692 case WSMOUSECFG_SWAPSIDES: 1693 flag = WSTPAD_SWAPSIDES; 1694 break; 1695 case WSMOUSECFG_DISABLE: 1696 flag = WSTPAD_DISABLE; 1697 break; 1698 case WSMOUSECFG_TAPPING: 1699 flag = WSTPAD_TAPPING; 1700 break; 1701 } 1702 if (val) 1703 tp->features |= flag; 1704 else 1705 tp->features &= ~flag; 1706 break; 1707 case WSMOUSECFG_LEFT_EDGE: 1708 tp->params.left_edge = val; 1709 break; 1710 case WSMOUSECFG_RIGHT_EDGE: 1711 tp->params.right_edge = val; 1712 break; 1713 case WSMOUSECFG_TOP_EDGE: 1714 tp->params.top_edge = val; 1715 break; 1716 case WSMOUSECFG_BOTTOM_EDGE: 1717 tp->params.bottom_edge = val; 1718 break; 1719 case WSMOUSECFG_CENTERWIDTH: 1720 tp->params.center_width = val; 1721 break; 1722 case WSMOUSECFG_HORIZSCROLLDIST: 1723 tp->scroll.hdist = val; 1724 break; 1725 case WSMOUSECFG_VERTSCROLLDIST: 1726 tp->scroll.vdist = val; 1727 break; 1728 case WSMOUSECFG_F2WIDTH: 1729 tp->params.f2width = val; 1730 break; 1731 case WSMOUSECFG_F2PRESSURE: 1732 tp->params.f2pressure = val; 1733 break; 1734 case WSMOUSECFG_TAP_MAXTIME: 1735 tp->tap.maxtime.tv_nsec = imin(val, 999) * 1000000; 1736 break; 1737 case WSMOUSECFG_TAP_CLICKTIME: 1738 tp->tap.clicktime = val; 1739 break; 1740 case WSMOUSECFG_TAP_LOCKTIME: 1741 tp->tap.locktime = val; 1742 break; 1743 default: 1744 return (ENOTSUP); 1745 } 1746 1747 return (0); 1748 } 1749 1750 int 1751 wstpad_get_param(struct wsmouseinput *input, int key, int *pval) 1752 { 1753 struct wstpad *tp = input->tp; 1754 u_int flag; 1755 1756 if (tp == NULL) 1757 return (EINVAL); 1758 1759 switch (key) { 1760 case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_TAPPING: 1761 switch (key) { 1762 case WSMOUSECFG_SOFTBUTTONS: 1763 flag = WSTPAD_SOFTBUTTONS; 1764 break; 1765 case WSMOUSECFG_SOFTMBTN: 1766 flag = WSTPAD_SOFTMBTN; 1767 break; 1768 case WSMOUSECFG_TOPBUTTONS: 1769 flag = WSTPAD_TOPBUTTONS; 1770 break; 1771 case WSMOUSECFG_TWOFINGERSCROLL: 1772 flag = WSTPAD_TWOFINGERSCROLL; 1773 break; 1774 case WSMOUSECFG_EDGESCROLL: 1775 flag = WSTPAD_EDGESCROLL; 1776 break; 1777 case WSMOUSECFG_HORIZSCROLL: 1778 flag = WSTPAD_HORIZSCROLL; 1779 break; 1780 case WSMOUSECFG_SWAPSIDES: 1781 flag = WSTPAD_SWAPSIDES; 1782 break; 1783 case WSMOUSECFG_DISABLE: 1784 flag = WSTPAD_DISABLE; 1785 break; 1786 case WSMOUSECFG_TAPPING: 1787 flag = WSTPAD_TAPPING; 1788 break; 1789 } 1790 *pval = !!(tp->features & flag); 1791 break; 1792 case WSMOUSECFG_LEFT_EDGE: 1793 *pval = tp->params.left_edge; 1794 break; 1795 case WSMOUSECFG_RIGHT_EDGE: 1796 *pval = tp->params.right_edge; 1797 break; 1798 case WSMOUSECFG_TOP_EDGE: 1799 *pval = tp->params.top_edge; 1800 break; 1801 case WSMOUSECFG_BOTTOM_EDGE: 1802 *pval = tp->params.bottom_edge; 1803 break; 1804 case WSMOUSECFG_CENTERWIDTH: 1805 *pval = tp->params.center_width; 1806 break; 1807 case WSMOUSECFG_HORIZSCROLLDIST: 1808 *pval = tp->scroll.hdist; 1809 break; 1810 case WSMOUSECFG_VERTSCROLLDIST: 1811 *pval = tp->scroll.vdist; 1812 break; 1813 case WSMOUSECFG_F2WIDTH: 1814 *pval = tp->params.f2width; 1815 break; 1816 case WSMOUSECFG_F2PRESSURE: 1817 *pval = tp->params.f2pressure; 1818 break; 1819 case WSMOUSECFG_TAP_MAXTIME: 1820 *pval = tp->tap.maxtime.tv_nsec / 1000000; 1821 break; 1822 case WSMOUSECFG_TAP_CLICKTIME: 1823 *pval = tp->tap.clicktime; 1824 break; 1825 case WSMOUSECFG_TAP_LOCKTIME: 1826 *pval = tp->tap.locktime; 1827 break; 1828 default: 1829 return (ENOTSUP); 1830 } 1831 1832 return (0); 1833 } 1834