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