1 /* $OpenBSD: wstpad.c,v 1.35 2025/01/30 08:53:29 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 			mtx_enter(&evq.evar->ws_mtx);
935 			evq.put = evq.evar->ws_put;
936 			mtx_leave(&evq.evar->ws_mtx);
937 			evq.result = EVQ_RESULT_NONE;
938 			getnanotime(&evq.ts);
939 			wsmouse_evq_put(&evq, ev, btn);
940 			wsmouse_evq_put(&evq, SYNC_EV, 0);
941 			if (evq.result == EVQ_RESULT_SUCCESS) {
942 				if (input->flags & LOG_EVENTS) {
943 					wsmouse_log_events(input, &evq);
944 				}
945 				mtx_enter(&evq.evar->ws_mtx);
946 				evq.evar->ws_put = evq.put;
947 				mtx_leave(&evq.evar->ws_mtx);
948 				wsevent_wakeup(evq.evar);
949 			} else {
950 				input->sbtn.sync |= tp->tap.button;
951 			}
952 		}
953 	}
954 	splx(s);
955 }
956 
957 /*
958  * Suppress accidental pointer movements after a click on a clickpad.
959  */
960 void
wstpad_click(struct wsmouseinput * input)961 wstpad_click(struct wsmouseinput *input)
962 {
963 	struct wstpad *tp = input->tp;
964 
965 	if (tp->contacts == 1 &&
966 	    (PRIMARYBTN_CLICKED(tp) || PRIMARYBTN_RELEASED(tp)))
967 		set_freeze_ts(tp, 0, FREEZE_MS);
968 }
969 
970 /* Translate the "command" bits into the sync-state of wsmouse. */
971 void
wstpad_cmds(struct wsmouseinput * input,u_int cmds)972 wstpad_cmds(struct wsmouseinput *input, u_int cmds)
973 {
974 	struct wstpad *tp = input->tp;
975 	int n;
976 
977 	FOREACHBIT(cmds, n) {
978 		switch (n) {
979 		case CLEAR_MOTION_DELTAS:
980 			input->motion.dx = input->motion.dy = 0;
981 			if (input->motion.dz == 0 && input->motion.dw == 0)
982 				input->motion.sync &= ~SYNC_DELTAS;
983 			continue;
984 		case SOFTBUTTON_DOWN:
985 			input->btn.sync &= ~PRIMARYBTN;
986 			input->sbtn.buttons |= tp->softbutton;
987 			if (tp->softbutton != tp->tap.button)
988 				input->sbtn.sync |= tp->softbutton;
989 			continue;
990 		case SOFTBUTTON_UP:
991 			input->btn.sync &= ~PRIMARYBTN;
992 			if (tp->softbutton != tp->tap.button) {
993 				input->sbtn.buttons &= ~tp->softbutton;
994 				input->sbtn.sync |= tp->softbutton;
995 			}
996 			tp->softbutton = 0;
997 			continue;
998 		case TAPBUTTON_SYNC:
999 			if (tp->tap.button)
1000 				input->btn.sync &= ~tp->tap.button;
1001 			continue;
1002 		case TAPBUTTON_DOWN:
1003 			tp->tap.button = tp->tap.pending;
1004 			tp->tap.pending = 0;
1005 			input->sbtn.buttons |= tp->tap.button;
1006 			if (wstpad_tap_sync(input))
1007 				input->sbtn.sync |= tp->tap.button;
1008 			continue;
1009 		case TAPBUTTON_UP:
1010 			if (tp->tap.button != tp->softbutton)
1011 				input->sbtn.buttons &= ~tp->tap.button;
1012 			if (wstpad_tap_sync(input))
1013 				input->sbtn.sync |= tp->tap.button;
1014 			tp->tap.button = 0;
1015 			continue;
1016 		case HSCROLL:
1017 			input->motion.dw = tp->scroll.dw;
1018 			input->motion.sync |= SYNC_DELTAS;
1019 			continue;
1020 		case VSCROLL:
1021 			input->motion.dz = tp->scroll.dz;
1022 			input->motion.sync |= SYNC_DELTAS;
1023 			continue;
1024 		default:
1025 			printf("[wstpad] invalid cmd %d\n", n);
1026 			break;
1027 		}
1028 	}
1029 }
1030 
1031 
1032 /*
1033  * Set the state of touches that have ended. TOUCH_END is a transitional
1034  * state and will be changed to TOUCH_NONE before process_input() returns.
1035  */
1036 static inline void
clear_touchstates(struct wsmouseinput * input,enum touchstates state)1037 clear_touchstates(struct wsmouseinput *input, enum touchstates state)
1038 {
1039 	u_int touches;
1040 	int slot;
1041 
1042 	touches = input->mt.sync[MTS_TOUCH] & ~input->mt.touches;
1043 	FOREACHBIT(touches, slot)
1044 		input->tp->tpad_touches[slot].state = state;
1045 }
1046 
1047 void
wstpad_mt_inputs(struct wsmouseinput * input)1048 wstpad_mt_inputs(struct wsmouseinput *input)
1049 {
1050 	struct wstpad *tp = input->tp;
1051 	struct tpad_touch *t;
1052 	int slot, dx, dy;
1053 	u_int touches, inactive;
1054 
1055 	/* TOUCH_BEGIN */
1056 	touches = input->mt.touches & input->mt.sync[MTS_TOUCH];
1057 	FOREACHBIT(touches, slot) {
1058 		t = &tp->tpad_touches[slot];
1059 		t->state = TOUCH_BEGIN;
1060 		t->x = normalize_abs(&input->filter.h, t->pos->x);
1061 		t->y = normalize_abs(&input->filter.v, t->pos->y);
1062 		t->orig.x = t->x;
1063 		t->orig.y = t->y;
1064 		memcpy(&t->orig.time, &tp->time, sizeof(struct timespec));
1065 		t->flags = edge_flags(tp, t->x, t->y);
1066 		wstpad_set_direction(tp, t, 0, 0);
1067 	}
1068 
1069 	/* TOUCH_UPDATE */
1070 	touches = input->mt.touches & input->mt.frame;
1071 	if (touches & tp->mtcycle) {
1072 		/*
1073 		 * Slot data may be synchronized separately, in any order,
1074 		 * or not at all if there is no delta.  Identify the touches
1075 		 * without deltas.
1076 		 */
1077 		inactive = input->mt.touches & ~tp->mtcycle;
1078 		tp->mtcycle = touches;
1079 	} else {
1080 		inactive = 0;
1081 		tp->mtcycle |= touches;
1082 	}
1083 	touches = input->mt.touches & ~input->mt.sync[MTS_TOUCH];
1084 	FOREACHBIT(touches, slot) {
1085 		t = &tp->tpad_touches[slot];
1086 		t->state = TOUCH_UPDATE;
1087 		if ((1 << slot) & input->mt.frame) {
1088 			dx = normalize_abs(&input->filter.h, t->pos->x) - t->x;
1089 			t->x += dx;
1090 			dy = normalize_abs(&input->filter.v, t->pos->y) - t->y;
1091 			t->y += dy;
1092 			t->flags &= (~EDGES | edge_flags(tp, t->x, t->y));
1093 			if (wsmouse_hysteresis(input, t->pos))
1094 				dx = dy = 0;
1095 			wstpad_set_direction(tp, t, dx, dy);
1096 		} else if ((1 << slot) & inactive) {
1097 			wstpad_set_direction(tp, t, 0, 0);
1098 		}
1099 	}
1100 
1101 	clear_touchstates(input, TOUCH_END);
1102 }
1103 
1104 /*
1105  * Identify "thumb" contacts in the bottom area.  The identification
1106  * has three stages:
1107  * 1. If exactly one of two or more touches is in the bottom area, it
1108  * is masked, which means it does not receive pointer control as long
1109  * as there are alternatives.  Once set, the mask will only be cleared
1110  * when the touch is released.
1111  * Tap detection ignores a masked touch if it does not participate in
1112  * a tap gesture.
1113  * 2. If the pointer-controlling touch is moving stably while a masked
1114  * touch in the bottom area is resting, or only moving minimally, the
1115  * pointer mask is copied to tp->ignore.  In this stage, the masked
1116  * touch does not block pointer movement, and it is ignored by
1117  * wstpad_f2scroll().
1118  * Decisions are made more or less immediately, there may be errors
1119  * in edge cases.  If a fast or long upward movement is detected,
1120  * tp->ignore is cleared.  There is no other transition from stage 2
1121  * to scrolling, or vice versa, for a pair of touches.
1122  * 3. If tp->ignore is set and the touch is resting, it is marked as
1123  * thumb, and it will be ignored until it ends.
1124  */
1125 void
wstpad_mt_masks(struct wsmouseinput * input)1126 wstpad_mt_masks(struct wsmouseinput *input)
1127 {
1128 	struct wstpad *tp = input->tp;
1129 	struct tpad_touch *t;
1130 	struct position *pos;
1131 	u_int mask;
1132 	int slot;
1133 
1134 	tp->ignore &= input->mt.touches;
1135 
1136 	if (tp->contacts < 2)
1137 		return;
1138 
1139 	if (tp->ignore) {
1140 		slot = ffs(tp->ignore) - 1;
1141 		t = &tp->tpad_touches[slot];
1142 		if (t->flags & THUMB)
1143 			return;
1144 		if (t->dir < 0 && wstpad_is_stable(input, t)) {
1145 			t->flags |= THUMB;
1146 			return;
1147 		}
1148 		/* The edge.low area is a bit larger than the bottom area. */
1149 		if (t->y >= tp->edge.low || (NORTH(t->dir) &&
1150 		    magnitude(input, t->pos->dx, t->pos->dy) >= MAG_MEDIUM))
1151 			tp->ignore = 0;
1152 		return;
1153 	}
1154 
1155 	if (input->mt.ptr_mask == 0) {
1156 		mask = ~0;
1157 		FOREACHBIT(input->mt.touches, slot) {
1158 			t = &tp->tpad_touches[slot];
1159 			if (t->flags & B_EDGE) {
1160 				mask &= (1 << slot);
1161 				input->mt.ptr_mask = mask;
1162 			}
1163 		}
1164 	}
1165 
1166 	if ((input->mt.ptr_mask & ~input->mt.ptr)
1167 	    && !(tp->scroll.dz || tp->scroll.dw)
1168 	    && tp->t->dir >= 0
1169 	    && wstpad_is_stable(input, tp->t)) {
1170 
1171 		slot = ffs(input->mt.ptr_mask) - 1;
1172 		t = &tp->tpad_touches[slot];
1173 
1174 		if (t->y >= tp->edge.low)
1175 			return;
1176 
1177 		if (!wstpad_is_stable(input, t))
1178 			return;
1179 
1180 		/* Default hysteresis limits are low.  Make a strict check. */
1181 		pos = tp->t->pos;
1182 		if (abs(pos->acc_dx) < 3 * input->filter.h.hysteresis
1183 		    && abs(pos->acc_dy) < 3 * input->filter.v.hysteresis)
1184 			return;
1185 
1186 		if (t->dir >= 0) {
1187 			/* Treat t as thumb if it is slow while tp->t is fast. */
1188 			if (magnitude(input, t->pos->dx, t->pos->dy) > MAG_LOW
1189 			    || magnitude(input, pos->dx, pos->dy) < MAG_MEDIUM)
1190 				return;
1191 		}
1192 
1193 		tp->ignore = input->mt.ptr_mask;
1194 	}
1195 }
1196 
1197 void
wstpad_touch_inputs(struct wsmouseinput * input)1198 wstpad_touch_inputs(struct wsmouseinput *input)
1199 {
1200 	struct wstpad *tp = input->tp;
1201 	struct tpad_touch *t;
1202 	int slot, x, y, dx, dy;
1203 
1204 	tp->btns = input->btn.buttons;
1205 	tp->btns_sync = input->btn.sync;
1206 
1207 	tp->prev_contacts = tp->contacts;
1208 	tp->contacts = input->touch.contacts;
1209 
1210 	if (tp->contacts == 1 &&
1211 	    ((tp->params.f2width &&
1212 	    input->touch.width >= tp->params.f2width)
1213 	    || (tp->params.f2pressure &&
1214 	    input->touch.pressure >= tp->params.f2pressure)))
1215 		tp->contacts = 2;
1216 
1217 	if (IS_MT(tp)) {
1218 		wstpad_mt_inputs(input);
1219 		if (input->mt.ptr) {
1220 			slot = ffs(input->mt.ptr) - 1;
1221 			tp->t = &tp->tpad_touches[slot];
1222 		}
1223 		wstpad_mt_masks(input);
1224 	} else {
1225 		t = tp->t;
1226 		if (tp->contacts)
1227 			t->state = (tp->prev_contacts ?
1228 			    TOUCH_UPDATE : TOUCH_BEGIN);
1229 		else
1230 			t->state = (tp->prev_contacts ?
1231 			    TOUCH_END : TOUCH_NONE);
1232 
1233 		dx = dy = 0;
1234 		x = normalize_abs(&input->filter.h, t->pos->x);
1235 		y = normalize_abs(&input->filter.v, t->pos->y);
1236 		if (t->state == TOUCH_BEGIN) {
1237 			t->x = t->orig.x = x;
1238 			t->y = t->orig.y = y;
1239 			memcpy(&t->orig.time, &tp->time,
1240 			    sizeof(struct timespec));
1241 			t->flags = edge_flags(tp, x, y);
1242 		} else if (input->motion.sync & SYNC_POSITION) {
1243 			if (!wsmouse_hysteresis(input, t->pos)) {
1244 				dx = x - t->x;
1245 				dy = y - t->y;
1246 			}
1247 			t->x = x;
1248 			t->y = y;
1249 			t->flags &= (~EDGES | edge_flags(tp, x, y));
1250 		}
1251 		wstpad_set_direction(tp, t, dx, dy);
1252 	}
1253 }
1254 
1255 static inline int
t2_ignore(struct wsmouseinput * input)1256 t2_ignore(struct wsmouseinput *input)
1257 {
1258 	/*
1259 	 * If there are two touches, do not block pointer movement if they
1260 	 * perform a click-and-drag action, or if the second touch is
1261 	 * resting in the bottom area.
1262 	 */
1263 	return (input->tp->contacts == 2 && ((input->tp->btns & PRIMARYBTN)
1264 	    || (input->tp->ignore & ~input->mt.ptr)));
1265 }
1266 
1267 void
wstpad_process_input(struct wsmouseinput * input,struct evq_access * evq)1268 wstpad_process_input(struct wsmouseinput *input, struct evq_access *evq)
1269 {
1270 	struct wstpad *tp = input->tp;
1271 	u_int handlers, hdlr, cmds;
1272 
1273 	memcpy(&tp->time, &evq->ts, sizeof(struct timespec));
1274 	wstpad_touch_inputs(input);
1275 
1276 	cmds = 0;
1277 	handlers = tp->handlers;
1278 	if (DISABLE(tp))
1279 		handlers &= ((1 << TOPBUTTON_HDLR) | (1 << SOFTBUTTON_HDLR));
1280 
1281 	FOREACHBIT(handlers, hdlr) {
1282 		switch (hdlr) {
1283 		case SOFTBUTTON_HDLR:
1284 		case TOPBUTTON_HDLR:
1285 			wstpad_softbuttons(input, &cmds, hdlr);
1286 			continue;
1287 		case TAP_HDLR:
1288 			wstpad_tap(input, &cmds);
1289 			continue;
1290 		case F2SCROLL_HDLR:
1291 			wstpad_f2scroll(input, &cmds);
1292 			continue;
1293 		case EDGESCROLL_HDLR:
1294 			wstpad_edgescroll(input, &cmds);
1295 			continue;
1296 		case CLICK_HDLR:
1297 			wstpad_click(input);
1298 			continue;
1299 		}
1300 	}
1301 
1302 	/* Check whether pointer movement should be blocked. */
1303 	if (input->motion.dx || input->motion.dy) {
1304 		if (DISABLE(tp)
1305 		    || (tp->t->flags & tp->freeze)
1306 		    || timespeccmp(&tp->time, &tp->freeze_ts, <)
1307 		    || (tp->contacts > 1 && !t2_ignore(input))) {
1308 
1309 			cmds |= 1 << CLEAR_MOTION_DELTAS;
1310 		}
1311 	}
1312 
1313 	wstpad_cmds(input, cmds);
1314 
1315 	if (IS_MT(tp))
1316 		clear_touchstates(input, TOUCH_NONE);
1317 }
1318 
1319 /*
1320  * Try to determine the average interval between two updates. Various
1321  * conditions are checked in order to ensure that only valid samples enter
1322  * into the calculation. Above all, it is restricted to motion events
1323  * occurring when there is only one contact. MT devices may need more than
1324  * one packet to transmit their state if there are multiple touches, and
1325  * the update frequency may be higher in this case.
1326  */
1327 void
wstpad_track_interval(struct wsmouseinput * input,struct timespec * time)1328 wstpad_track_interval(struct wsmouseinput *input, struct timespec *time)
1329 {
1330 	static const struct timespec limit = { 0, 30 * 1000000L };
1331 	struct timespec ts;
1332 	int samples;
1333 
1334 	if (input->motion.sync == 0
1335 	    || (input->touch.sync & SYNC_CONTACTS)
1336 	    || (input->touch.contacts > 1)) {
1337 		input->intv.track = 0;
1338 		return;
1339 	}
1340 	if (input->intv.track) {
1341 		timespecsub(time, &input->intv.ts, &ts);
1342 		if (timespeccmp(&ts, &limit, <)) {
1343 			/* The unit of the sum is 4096 nanoseconds. */
1344 			input->intv.sum += ts.tv_nsec >> 12;
1345 			samples = ++input->intv.samples;
1346 			/*
1347 			 * Make the first calculation quickly and later
1348 			 * a more reliable one:
1349 			 */
1350 			if (samples == 8) {
1351 				input->intv.avg = input->intv.sum << 9;
1352 				wstpad_init_deceleration(input);
1353 			} else if (samples == 128) {
1354 				input->intv.avg = input->intv.sum << 5;
1355 				wstpad_init_deceleration(input);
1356 				input->intv.samples = 0;
1357 				input->intv.sum = 0;
1358 				input->flags &= ~TRACK_INTERVAL;
1359 			}
1360 		}
1361 	}
1362 	memcpy(&input->intv.ts, time, sizeof(struct timespec));
1363 	input->intv.track = 1;
1364 }
1365 
1366 
1367 
1368 /*
1369  * The default acceleration options of X don't work convincingly with
1370  * touchpads (the synaptics driver installs its own "acceleration
1371  * profile" and callback function). As a preliminary workaround, this
1372  * filter applies a simple deceleration scheme to small deltas, based
1373  * on the "magnitude" of the delta pair. A magnitude of 8 corresponds,
1374  * roughly, to a speed of (filter.dclr / 12.5) device units per milli-
1375  * second. If its magnitude is smaller than 7 a delta will be downscaled
1376  * by the factor 2/8, deltas with magnitudes from 7 to 11 by factors
1377  * ranging from 3/8 to 7/8.
1378  */
1379 int
wstpad_decelerate(struct wsmouseinput * input,int * dx,int * dy)1380 wstpad_decelerate(struct wsmouseinput *input, int *dx, int *dy)
1381 {
1382 	int mag, n, h, v;
1383 
1384 	mag = magnitude(input, *dx, *dy);
1385 
1386 	/* Don't change deceleration levels abruptly. */
1387 	mag = (mag + 7 * input->filter.mag) / 8;
1388 	/* Don't use arbitrarily high values. */
1389 	input->filter.mag = imin(mag, 24 << 12);
1390 
1391 	n = imax((mag >> 12) - 4, 2);
1392 	if (n < 8) {
1393 		/* Scale by (n / 8). */
1394 		h = *dx * n + input->filter.h.dclr_rmdr;
1395 		v = *dy * n + input->filter.v.dclr_rmdr;
1396 		input->filter.h.dclr_rmdr = (h >= 0 ? h & 7 : -(-h & 7));
1397 		input->filter.v.dclr_rmdr = (v >= 0 ? v & 7 : -(-v & 7));
1398 		*dx = h / 8;
1399 		*dy = v / 8;
1400 		return (1);
1401 	}
1402 	return (0);
1403 }
1404 
1405 void
wstpad_filter(struct wsmouseinput * input)1406 wstpad_filter(struct wsmouseinput *input)
1407 {
1408 	struct axis_filter *h = &input->filter.h;
1409 	struct axis_filter *v = &input->filter.v;
1410 	struct position *pos = &input->motion.pos;
1411 	int strength = input->filter.mode & 7;
1412 	int dx, dy;
1413 
1414 	if (!(input->motion.sync & SYNC_POSITION)
1415 	    || (h->dmax && (abs(pos->dx) > h->dmax))
1416 	    || (v->dmax && (abs(pos->dy) > v->dmax))) {
1417 		dx = dy = 0;
1418 	} else {
1419 		dx = pos->dx;
1420 		dy = pos->dy;
1421 	}
1422 
1423 	if (wsmouse_hysteresis(input, pos))
1424 		dx = dy = 0;
1425 
1426 	if (input->filter.dclr && wstpad_decelerate(input, &dx, &dy))
1427 		/* Strong smoothing may hamper the precision at low speeds. */
1428 		strength = imin(strength, 2);
1429 
1430 	if (strength) {
1431 		if ((input->touch.sync & SYNC_CONTACTS)
1432 		    || input->mt.ptr != input->mt.prev_ptr) {
1433 			h->avg = v->avg = 0;
1434 		}
1435 		/* Use a weighted decaying average for smoothing. */
1436 		dx = dx * (8 - strength) + h->avg * strength + h->avg_rmdr;
1437 		dy = dy * (8 - strength) + v->avg * strength + v->avg_rmdr;
1438 		h->avg_rmdr = (dx >= 0 ? dx & 7 : -(-dx & 7));
1439 		v->avg_rmdr = (dy >= 0 ? dy & 7 : -(-dy & 7));
1440 		dx = h->avg = dx / 8;
1441 		dy = v->avg = dy / 8;
1442 	}
1443 
1444 	input->motion.dx = dx;
1445 	input->motion.dy = dy;
1446 }
1447 
1448 
1449 /*
1450  * Compatibility-mode conversions. wstpad_filter transforms and filters
1451  * the coordinate inputs, extended functionality is provided by
1452  * wstpad_process_input.
1453  */
1454 void
wstpad_compat_convert(struct wsmouseinput * input,struct evq_access * evq)1455 wstpad_compat_convert(struct wsmouseinput *input, struct evq_access *evq)
1456 {
1457 	if (input->flags & TRACK_INTERVAL)
1458 		wstpad_track_interval(input, &evq->ts);
1459 
1460 	wstpad_filter(input);
1461 
1462 	if ((input->motion.dx || input->motion.dy)
1463 	    && !(input->motion.sync & SYNC_DELTAS)) {
1464 		input->motion.dz = input->motion.dw = 0;
1465 		input->motion.sync |= SYNC_DELTAS;
1466 	}
1467 
1468 	if (input->tp != NULL)
1469 		wstpad_process_input(input, evq);
1470 
1471 	input->motion.sync &= ~SYNC_POSITION;
1472 	input->touch.sync = 0;
1473 }
1474 
1475 int
wstpad_init(struct wsmouseinput * input)1476 wstpad_init(struct wsmouseinput *input)
1477 {
1478 	struct wstpad *tp = input->tp;
1479 	int i, slots;
1480 
1481 	if (tp != NULL)
1482 		return (0);
1483 
1484 	input->tp = tp = malloc(sizeof(struct wstpad),
1485 	    M_DEVBUF, M_WAITOK | M_ZERO);
1486 	if (tp == NULL)
1487 		return (-1);
1488 
1489 	slots = imax(input->mt.num_slots, 1);
1490 	tp->tpad_touches = malloc(slots * sizeof(struct tpad_touch),
1491 	    M_DEVBUF, M_WAITOK | M_ZERO);
1492 	if (tp->tpad_touches == NULL) {
1493 		free(tp, M_DEVBUF, sizeof(struct wstpad));
1494 		return (-1);
1495 	}
1496 
1497 	tp->t = &tp->tpad_touches[0];
1498 	if (input->mt.num_slots) {
1499 		tp->features |= WSTPAD_MT;
1500 		for (i = 0; i < input->mt.num_slots; i++)
1501 			tp->tpad_touches[i].pos = &input->mt.slots[i].pos;
1502 	} else {
1503 		tp->t->pos = &input->motion.pos;
1504 	}
1505 
1506 	timeout_set(&tp->tap.to, wstpad_tap_timeout, input);
1507 
1508 	tp->ratio = input->filter.ratio;
1509 
1510 	return (0);
1511 }
1512 
1513 /*
1514  * Integer square root (Halleck's method)
1515  *
1516  * An adaption of code from John B. Halleck (from
1517  * http://www.cc.utah.edu/~nahaj/factoring/code.html). This version is
1518  * used and published under the OpenBSD license terms with his permission.
1519  *
1520  * Cf. also Martin Guy's "Square root by abacus" method.
1521  */
1522 static inline u_int
isqrt(u_int n)1523 isqrt(u_int n)
1524 {
1525 	u_int root, sqbit;
1526 
1527 	root = 0;
1528 	sqbit = 1 << (sizeof(u_int) * 8 - 2);
1529 	while (sqbit) {
1530 		if (n >= (sqbit | root)) {
1531 			n -= (sqbit | root);
1532 			root = (root >> 1) | sqbit;
1533 		} else {
1534 			root >>= 1;
1535 		}
1536 		sqbit >>= 2;
1537 	}
1538 	return (root);
1539 }
1540 
1541 void
wstpad_init_deceleration(struct wsmouseinput * input)1542 wstpad_init_deceleration(struct wsmouseinput *input)
1543 {
1544 	int n, dclr;
1545 
1546 	if ((dclr = input->filter.dclr) == 0)
1547 		return;
1548 
1549 	dclr = imax(dclr, 4);
1550 
1551 	/*
1552 	 * For a standard update rate of about 80Hz, (dclr) units
1553 	 * will be mapped to a magnitude of 8. If the average rate
1554 	 * is significantly higher or lower, adjust the coefficient
1555 	 * accordingly:
1556 	 */
1557 	if (input->intv.avg == 0) {
1558 		n = 8;
1559 	} else {
1560 		n = 8 * 13000000 / input->intv.avg;
1561 		n = imax(imin(n, 32), 4);
1562 	}
1563 	input->filter.h.mag_scale = (n << 12) / dclr;
1564 	input->filter.v.mag_scale = (input->filter.ratio ?
1565 	    n * input->filter.ratio : n << 12) / dclr;
1566 	input->filter.h.dclr_rmdr = 0;
1567 	input->filter.v.dclr_rmdr = 0;
1568 	input->flags |= TRACK_INTERVAL;
1569 }
1570 
1571 int
wstpad_configure(struct wsmouseinput * input)1572 wstpad_configure(struct wsmouseinput *input)
1573 {
1574 	struct wstpad *tp;
1575 	int width, height, diag, offset, h_res, v_res, h_unit, v_unit, i;
1576 
1577 	width = abs(input->hw.x_max - input->hw.x_min);
1578 	height = abs(input->hw.y_max - input->hw.y_min);
1579 	if (width == 0 || height == 0)
1580 		return (-1);	/* We can't do anything. */
1581 
1582 	if (input->tp == NULL && wstpad_init(input))
1583 		return (-1);
1584 	tp = input->tp;
1585 
1586 	if (!(input->flags & CONFIGURED)) {
1587 		/*
1588 		 * The filter parameters are derived from the length of the
1589 		 * diagonal in device units, with some magic constants which
1590 		 * are partly adapted from libinput or synaptics code, or are
1591 		 * based on tests and guess work.  The absolute resolution
1592 		 * values might not be reliable, but if they are present the
1593 	         * settings are adapted to the ratio.
1594 		 */
1595 		h_res = input->hw.h_res;
1596 		v_res = input->hw.v_res;
1597 		if (h_res == 0 || v_res == 0)
1598 			h_res = v_res = 1;
1599 		diag = isqrt(width * width + height * height);
1600 		input->filter.h.scale = (imin(920, diag) << 12) / diag;
1601 		input->filter.v.scale = input->filter.h.scale * h_res / v_res;
1602 		h_unit = imax(diag / 280, 3);
1603 		v_unit = imax((h_unit * v_res + h_res / 2) / h_res, 3);
1604 		input->filter.h.hysteresis = h_unit;
1605 		input->filter.v.hysteresis = v_unit;
1606 		input->filter.mode = FILTER_MODE_DEFAULT;
1607 		input->filter.dclr = h_unit - h_unit / 5;
1608 		wstpad_init_deceleration(input);
1609 
1610 		tp->features &= (WSTPAD_MT | WSTPAD_DISABLE);
1611 
1612 		if (input->hw.contacts_max != 1)
1613 			tp->features |= WSTPAD_TWOFINGERSCROLL;
1614 		else
1615 			tp->features |= WSTPAD_EDGESCROLL;
1616 
1617 		if (input->hw.hw_type == WSMOUSEHW_CLICKPAD) {
1618 			if (input->hw.type == WSMOUSE_TYPE_SYNAP_SBTN) {
1619 				tp->features |= WSTPAD_TOPBUTTONS;
1620 			} else {
1621 				tp->features |= WSTPAD_SOFTBUTTONS;
1622 				tp->features |= WSTPAD_SOFTMBTN;
1623 			}
1624 		}
1625 
1626 		tp->params.left_edge = V_EDGE_RATIO_DEFAULT;
1627 		tp->params.right_edge = V_EDGE_RATIO_DEFAULT;
1628 		tp->params.bottom_edge = ((tp->features & WSTPAD_SOFTBUTTONS)
1629 		    ? B_EDGE_RATIO_DEFAULT : 0);
1630 		tp->params.top_edge = ((tp->features & WSTPAD_TOPBUTTONS)
1631 		    ? T_EDGE_RATIO_DEFAULT : 0);
1632 		tp->params.center_width = CENTER_RATIO_DEFAULT;
1633 
1634 		tp->tap.maxtime.tv_nsec = TAP_MAXTIME_DEFAULT * 1000000;
1635 		tp->tap.clicktime = TAP_CLICKTIME_DEFAULT;
1636 		tp->tap.locktime = TAP_LOCKTIME_DEFAULT;
1637 
1638 		tp->scroll.hdist = 4 * h_unit;
1639 		tp->scroll.vdist = 4 * v_unit;
1640 		tp->tap.maxdist = 4 * h_unit;
1641 
1642 		if (IS_MT(tp) && h_res > 1 && v_res > 1 &&
1643 		    input->hw.hw_type == WSMOUSEHW_CLICKPAD &&
1644 		    (width + h_res / 2) / h_res > 100 &&
1645 		    (height + v_res / 2) / v_res > 60) {
1646 			tp->params.mtbtn_maxdist = h_res * 35;
1647 		} else {
1648 			tp->params.mtbtn_maxdist = -1; /* not available */
1649 		}
1650 	}
1651 
1652 	/* A touch with a flag set in this mask does not move the pointer. */
1653 	tp->freeze = EDGES;
1654 
1655 	offset = width * tp->params.left_edge / 4096;
1656 	tp->edge.left = (offset ? input->hw.x_min + offset : INT_MIN);
1657 	offset = width * tp->params.right_edge / 4096;
1658 	tp->edge.right = (offset ? input->hw.x_max - offset : INT_MAX);
1659 	offset = height * tp->params.bottom_edge / 4096;
1660 	tp->edge.bottom = (offset ? input->hw.y_min + offset : INT_MIN);
1661 	tp->edge.low = tp->edge.bottom + offset / 2;
1662 	offset = height * tp->params.top_edge / 4096;
1663 	tp->edge.top = (offset ? input->hw.y_max - offset : INT_MAX);
1664 
1665 	offset = width * abs(tp->params.center_width) / 8192;
1666 	tp->edge.center = input->hw.x_min + width / 2;
1667 	tp->edge.center_left = tp->edge.center - offset;
1668 	tp->edge.center_right = tp->edge.center + offset;
1669 
1670 	/*
1671 	 * Make the MTBUTTONS configuration consistent.  A non-negative 'maxdist'
1672 	 * value makes the feature visible in wsconsctl.  0-values are replaced
1673 	 * by a default (one fourth of the length of the touchpad diagonal).
1674 	 */
1675 	if (tp->params.mtbtn_maxdist < 0) {
1676 		tp->features &= ~WSTPAD_MTBUTTONS;
1677 	} else if (tp->params.mtbtn_maxdist == 0) {
1678 		diag = isqrt(width * width + height * height);
1679 		tp->params.mtbtn_maxdist = diag / 4;
1680 	}
1681 
1682 	tp->handlers = 0;
1683 
1684 	if (tp->features & (WSTPAD_SOFTBUTTONS | WSTPAD_MTBUTTONS))
1685 		tp->handlers |= 1 << SOFTBUTTON_HDLR;
1686 	if (tp->features & WSTPAD_TOPBUTTONS)
1687 		tp->handlers |= 1 << TOPBUTTON_HDLR;
1688 	if (tp->features & WSTPAD_TWOFINGERSCROLL)
1689 		tp->handlers |= 1 << F2SCROLL_HDLR;
1690 	else if (tp->features & WSTPAD_EDGESCROLL)
1691 		tp->handlers |= 1 << EDGESCROLL_HDLR;
1692 
1693 	for (i = 0; i < TAP_BTNMAP_SIZE; i++) {
1694 		if (tp->tap.btnmap[i] == 0)
1695 			continue;
1696 
1697 		tp->tap.clicktime = imin(imax(tp->tap.clicktime, 80), 350);
1698 		if (tp->tap.locktime)
1699 			tp->tap.locktime =
1700 			    imin(imax(tp->tap.locktime, 150), 5000);
1701 		tp->handlers |= 1 << TAP_HDLR;
1702 		break;
1703 	}
1704 
1705 	if (input->hw.hw_type == WSMOUSEHW_CLICKPAD)
1706 		tp->handlers |= 1 << CLICK_HDLR;
1707 
1708 	tp->sbtnswap = ((tp->features & WSTPAD_SWAPSIDES)
1709 	    ? (LEFTBTN | RIGHTBTN) : 0);
1710 
1711 	return (0);
1712 }
1713 
1714 void
wstpad_reset(struct wsmouseinput * input)1715 wstpad_reset(struct wsmouseinput *input)
1716 {
1717 	struct wstpad *tp = input->tp;
1718 
1719 	if (tp != NULL) {
1720 		timeout_del(&tp->tap.to);
1721 		tp->tap.state = TAP_DETECT;
1722 	}
1723 
1724 	if (input->sbtn.buttons) {
1725 		input->sbtn.sync = input->sbtn.buttons;
1726 		input->sbtn.buttons = 0;
1727 	}
1728 }
1729 
1730 void
wstpad_cleanup(struct wsmouseinput * input)1731 wstpad_cleanup(struct wsmouseinput *input)
1732 {
1733 	struct wstpad *tp = input->tp;
1734 	int slots;
1735 
1736 	timeout_del(&tp->tap.to);
1737 	slots = imax(input->mt.num_slots, 1);
1738 	free(tp->tpad_touches, M_DEVBUF, slots * sizeof(struct tpad_touch));
1739 	free(tp, M_DEVBUF, sizeof(struct wstpad));
1740 	input->tp = NULL;
1741 }
1742 
1743 int
wstpad_set_param(struct wsmouseinput * input,int key,int val)1744 wstpad_set_param(struct wsmouseinput *input, int key, int val)
1745 {
1746 	struct wstpad *tp = input->tp;
1747 	u_int flag;
1748 
1749 	if (tp == NULL)
1750 		return (EINVAL);
1751 
1752 	switch (key) {
1753 	case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_MTBUTTONS:
1754 		switch (key) {
1755 		case WSMOUSECFG_SOFTBUTTONS:
1756 			flag = WSTPAD_SOFTBUTTONS;
1757 			break;
1758 		case WSMOUSECFG_SOFTMBTN:
1759 			flag = WSTPAD_SOFTMBTN;
1760 			break;
1761 		case WSMOUSECFG_TOPBUTTONS:
1762 			flag = WSTPAD_TOPBUTTONS;
1763 			break;
1764 		case WSMOUSECFG_TWOFINGERSCROLL:
1765 			flag = WSTPAD_TWOFINGERSCROLL;
1766 			break;
1767 		case WSMOUSECFG_EDGESCROLL:
1768 			flag = WSTPAD_EDGESCROLL;
1769 			break;
1770 		case WSMOUSECFG_HORIZSCROLL:
1771 			flag = WSTPAD_HORIZSCROLL;
1772 			break;
1773 		case WSMOUSECFG_SWAPSIDES:
1774 			flag = WSTPAD_SWAPSIDES;
1775 			break;
1776 		case WSMOUSECFG_DISABLE:
1777 			flag = WSTPAD_DISABLE;
1778 			break;
1779 		case WSMOUSECFG_MTBUTTONS:
1780 			flag = WSTPAD_MTBUTTONS;
1781 			break;
1782 		}
1783 		if (val)
1784 			tp->features |= flag;
1785 		else
1786 			tp->features &= ~flag;
1787 		break;
1788 	case WSMOUSECFG_LEFT_EDGE:
1789 		tp->params.left_edge = val;
1790 		break;
1791 	case WSMOUSECFG_RIGHT_EDGE:
1792 		tp->params.right_edge = val;
1793 		break;
1794 	case WSMOUSECFG_TOP_EDGE:
1795 		tp->params.top_edge = val;
1796 		break;
1797 	case WSMOUSECFG_BOTTOM_EDGE:
1798 		tp->params.bottom_edge = val;
1799 		break;
1800 	case WSMOUSECFG_CENTERWIDTH:
1801 		tp->params.center_width = val;
1802 		break;
1803 	case WSMOUSECFG_HORIZSCROLLDIST:
1804 		tp->scroll.hdist = val;
1805 		break;
1806 	case WSMOUSECFG_VERTSCROLLDIST:
1807 		tp->scroll.vdist = val;
1808 		break;
1809 	case WSMOUSECFG_F2WIDTH:
1810 		tp->params.f2width = val;
1811 		break;
1812 	case WSMOUSECFG_F2PRESSURE:
1813 		tp->params.f2pressure = val;
1814 		break;
1815 	case WSMOUSECFG_TAP_MAXTIME:
1816 		tp->tap.maxtime.tv_nsec = imin(val, 999) * 1000000;
1817 		break;
1818 	case WSMOUSECFG_TAP_CLICKTIME:
1819 		tp->tap.clicktime = val;
1820 		break;
1821 	case WSMOUSECFG_TAP_LOCKTIME:
1822 		tp->tap.locktime = val;
1823 		break;
1824 	case WSMOUSECFG_TAP_ONE_BTNMAP:
1825 		tp->tap.btnmap[0] = BTNMASK(val);
1826 		break;
1827 	case WSMOUSECFG_TAP_TWO_BTNMAP:
1828 		tp->tap.btnmap[1] = BTNMASK(val);
1829 		break;
1830 	case WSMOUSECFG_TAP_THREE_BTNMAP:
1831 		tp->tap.btnmap[2] = BTNMASK(val);
1832 		break;
1833 	case WSMOUSECFG_MTBTN_MAXDIST:
1834 		if (IS_MT(tp))
1835 			tp->params.mtbtn_maxdist = val;
1836 		break;
1837 	default:
1838 		return (ENOTSUP);
1839 	}
1840 
1841 	return (0);
1842 }
1843 
1844 int
wstpad_get_param(struct wsmouseinput * input,int key,int * pval)1845 wstpad_get_param(struct wsmouseinput *input, int key, int *pval)
1846 {
1847 	struct wstpad *tp = input->tp;
1848 	u_int flag;
1849 
1850 	if (tp == NULL)
1851 		return (EINVAL);
1852 
1853 	switch (key) {
1854 	case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_MTBUTTONS:
1855 		switch (key) {
1856 		case WSMOUSECFG_SOFTBUTTONS:
1857 			flag = WSTPAD_SOFTBUTTONS;
1858 			break;
1859 		case WSMOUSECFG_SOFTMBTN:
1860 			flag = WSTPAD_SOFTMBTN;
1861 			break;
1862 		case WSMOUSECFG_TOPBUTTONS:
1863 			flag = WSTPAD_TOPBUTTONS;
1864 			break;
1865 		case WSMOUSECFG_TWOFINGERSCROLL:
1866 			flag = WSTPAD_TWOFINGERSCROLL;
1867 			break;
1868 		case WSMOUSECFG_EDGESCROLL:
1869 			flag = WSTPAD_EDGESCROLL;
1870 			break;
1871 		case WSMOUSECFG_HORIZSCROLL:
1872 			flag = WSTPAD_HORIZSCROLL;
1873 			break;
1874 		case WSMOUSECFG_SWAPSIDES:
1875 			flag = WSTPAD_SWAPSIDES;
1876 			break;
1877 		case WSMOUSECFG_DISABLE:
1878 			flag = WSTPAD_DISABLE;
1879 			break;
1880 		case WSMOUSECFG_MTBUTTONS:
1881 			flag = WSTPAD_MTBUTTONS;
1882 			break;
1883 		}
1884 		*pval = !!(tp->features & flag);
1885 		break;
1886 	case WSMOUSECFG_LEFT_EDGE:
1887 		*pval = tp->params.left_edge;
1888 		break;
1889 	case WSMOUSECFG_RIGHT_EDGE:
1890 		*pval = tp->params.right_edge;
1891 		break;
1892 	case WSMOUSECFG_TOP_EDGE:
1893 		*pval = tp->params.top_edge;
1894 		break;
1895 	case WSMOUSECFG_BOTTOM_EDGE:
1896 		*pval = tp->params.bottom_edge;
1897 		break;
1898 	case WSMOUSECFG_CENTERWIDTH:
1899 		*pval = tp->params.center_width;
1900 		break;
1901 	case WSMOUSECFG_HORIZSCROLLDIST:
1902 		*pval = tp->scroll.hdist;
1903 		break;
1904 	case WSMOUSECFG_VERTSCROLLDIST:
1905 		*pval = tp->scroll.vdist;
1906 		break;
1907 	case WSMOUSECFG_F2WIDTH:
1908 		*pval = tp->params.f2width;
1909 		break;
1910 	case WSMOUSECFG_F2PRESSURE:
1911 		*pval = tp->params.f2pressure;
1912 		break;
1913 	case WSMOUSECFG_TAP_MAXTIME:
1914 		*pval = tp->tap.maxtime.tv_nsec / 1000000;
1915 		break;
1916 	case WSMOUSECFG_TAP_CLICKTIME:
1917 		*pval = tp->tap.clicktime;
1918 		break;
1919 	case WSMOUSECFG_TAP_LOCKTIME:
1920 		*pval = tp->tap.locktime;
1921 		break;
1922 	case WSMOUSECFG_TAP_ONE_BTNMAP:
1923 		*pval = ffs(tp->tap.btnmap[0]);
1924 		break;
1925 	case WSMOUSECFG_TAP_TWO_BTNMAP:
1926 		*pval = ffs(tp->tap.btnmap[1]);
1927 		break;
1928 	case WSMOUSECFG_TAP_THREE_BTNMAP:
1929 		*pval = ffs(tp->tap.btnmap[2]);
1930 		break;
1931 	case WSMOUSECFG_MTBTN_MAXDIST:
1932 		*pval = tp->params.mtbtn_maxdist;
1933 		break;
1934 	default:
1935 		return (ENOTSUP);
1936 	}
1937 
1938 	return (0);
1939 }
1940