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