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