LCOV - code coverage report
Current view: top level - dev/wscons - wstpad.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 806 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 42 0.0 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.13