The diffs below contain a complete and extensive rewrite of the
input-processing parts of wsmouse and the interface it provides to
the hardware drivers. It prepares the support for various kinds of
multitouch input, as well as an extended support for touchpads by
wsmouse.

Tests for regression with all kinds of "pointing devices" would be
welcome. Only a small set of touchpads and USB mice is available to
me, which is a somewhat uncomfortable situation when you are working
on things like this.

Please note that the first diff is for the synaptics driver in
xenocara, the rest is for the kernel. Patching that driver will be
necessary if you test with touchpads (and compiling it requires
the modified version of wsconsio.h in /usr/include/dev/wscons/).

In most drivers I have made only short and trivial changes, the
Elantech-v4 part of pms is the only one that makes full use of the
new MT functions.

Unlike the basic input layer, which I hope is already fairly stable,
the in-kernel touchpad support is in a more experimental stage. If
you have a Synaptics, ALPS, or Elantech-v4 touchpad, you could test
it by adding this xorg.conf to /etc:

Section "InputClass"
    Identifier "wstpad"
    Driver "ws"
    MatchIsTouchPad "true"
EndSection

Only a default configuration will be available with this. It enables
two-finger-scrolling and a lower soft-button area for clickpads, and
two-finger- or edge-scrolling for touchpads (support for tapping and
upper soft-button areas is implemented, but it won't be enabled by
the automatic configuration). If this works, it would also be
interesting for me to know whether the defaults for pointer speed
and acceleration are decent.

Of course I'm not only interested in tests. Questions, comments,
suggestions, and any kind of help would also be welcome.



Index: src/wsconscomm.c
===================================================================
RCS file: /cvs/xenocara/driver/xf86-input-synaptics/src/wsconscomm.c,v
retrieving revision 1.13
diff -u -p -r1.13 wsconscomm.c
--- src/wsconscomm.c    29 Aug 2015 08:48:28 -0000      1.13
+++ src/wsconscomm.c    1 Dec 2015 22:40:15 -0000
@@ -215,45 +215,29 @@ WSConsReadHwState(InputInfoPtr pInfo,
             hw->y = priv->maxy - event->value + priv->miny;
             hw->cumulative_dy = hw->y;
             break;
-        case WSCONS_EVENT_MOUSE_ABSOLUTE_Z:
+        case WSCONS_EVENT_TOUCH_PRESSURE:
             hw->z = event->value;
             break;
-        case WSCONS_EVENT_MOUSE_ABSOLUTE_W:
-            if (priv->model == MODEL_ELANTECH) {
-                /* Elantech touchpads report number of fingers directly. */
-                hw->fingerWidth = 5;
-                hw->numFingers = event->value;
-                break;
-            }
-            /* XXX magic number mapping which is mirrored in pms driver */
 -            switch (event->value) {
-            case 0:
-                hw->fingerWidth = 5;
-                hw->numFingers = 2;
-                break;
-            case 1:
+        case WSCONS_EVENT_TOUCH_FINGERS:
+            hw->numFingers = event->value;
+            if (hw->numFingers == 0)
+                hw->fingerWidth = 0;
+            else if (hw->fingerWidth == 0)
                 hw->fingerWidth = 5;
-                hw->numFingers = 3;
-                break;
-            case 4 ... 5:
-                hw->fingerWidth = event->value;
-                hw->numFingers = 1;
-                break;
-            }
+            break;
+        case WSCONS_EVENT_TOUCH_WIDTH:
+            hw->fingerWidth = event->value;
+            break;
+        case WSCONS_EVENT_TOUCH_UPDATE:
+            /*
+             * The finger count or the active MT-slot has changed.
+             * Suppress pointer motion and two-finger scrolling.
+             */
+            priv->count_packet_finger = 0;
+            priv->vert_scroll_twofinger_on = FALSE;
+            priv->horiz_scroll_twofinger_on = FALSE;
             break;
         case WSCONS_EVENT_SYNC:
-            if (hw->z == 0) {
-                hw->fingerWidth = 0;
-                hw->numFingers = 0;
-            } else if (hw->numFingers == 0) {
-                /*
-                 * Because W may be 0 already, a two-finger touch on a
-                 * Synaptics touchpad doesn't necessarily produce an update
-                 * event for W.
-                 */
-                hw->fingerWidth = 5;
-                hw->numFingers = 2;
-            }
             hw->millis = 1000 * event->time.tv_sec +
                 event->time.tv_nsec / 1000000;
             SynapticsCopyHwState(hwRet, hw);


Index: arch/i386/isa/lms.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/isa/lms.c,v
retrieving revision 1.20
diff -u -p -r1.20 lms.c
--- arch/i386/isa/lms.c 10 Apr 2007 22:37:17 -0000      1.20
+++ arch/i386/isa/lms.c 1 Dec 2015 22:17:41 -0000
@@ -36,6 +36,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 #define        LMS_DATA        0       /* offset for data port, read-only */
 #define        LMS_SIGN        1       /* offset for signature port, 
read-write */
@@ -235,8 +236,7 @@ lmsintr(void *arg)
        sc->oldbuttons = buttons;

        if (dx || dy || changed)
-               wsmouse_input(sc->sc_wsmousedev,
-                             buttons, dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);
+               WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, 0, 0);

        return -1;
 }
Index: arch/i386/isa/mms.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/isa/mms.c,v
retrieving revision 1.19
diff -u -p -r1.19 mms.c
--- arch/i386/isa/mms.c 10 Apr 2007 22:37:17 -0000      1.19
+++ arch/i386/isa/mms.c 1 Dec 2015 22:17:41 -0000
@@ -36,6 +36,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 #define        MMS_ADDR        0       /* offset for register select */
 #define        MMS_DATA        1       /* offset for InPort data */
@@ -229,8 +230,7 @@ mmsintr(void *arg)
        changed = status & 0x38;

        if (dx || dy || changed)
-               wsmouse_input(sc->sc_wsmousedev,
-                             buttons, dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);
+               WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, 0, 0);

        return -1;
 }
Index: arch/luna88k/dev/lunaws.c
===================================================================
RCS file: /cvs/src/sys/arch/luna88k/dev/lunaws.c,v
retrieving revision 1.11
diff -u -p -r1.11 lunaws.c
--- arch/luna88k/dev/lunaws.c   7 Jun 2014 11:55:35 -0000       1.11
+++ arch/luna88k/dev/lunaws.c   1 Dec 2015 22:17:41 -0000
@@ -45,6 +45,7 @@
 #include "wsmouse.h"
 #if NWSMOUSE > 0
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>
 #endif

 #include <luna88k/dev/omkbdmap.h>
@@ -260,9 +261,8 @@ wsintr(int chan)
                        else if (sc->sc_msreport == 2) {
                                sc->dy = (signed char)code;
                                if (sc->sc_wsmousedev != NULL)
-                                       wsmouse_input(sc->sc_wsmousedev,
-                                           sc->buttons, sc->dx, sc->dy, 0, 0,
-                                           WSMOUSE_INPUT_DELTA);
+                                       WSMOUSE_INPUT(sc->sc_wsmousedev,
+                                           sc->buttons, sc->dx, sc->dy, 0, 0);
                                sc->sc_msreport = 0;
                        }
 #else
Index: arch/sgi/hpc/z8530ms.c
===================================================================
RCS file: /cvs/src/sys/arch/sgi/hpc/z8530ms.c,v
retrieving revision 1.1
diff -u -p -r1.1 z8530ms.c
--- arch/sgi/hpc/z8530ms.c      17 Apr 2012 22:06:33 -0000      1.1
+++ arch/sgi/hpc/z8530ms.c      1 Dec 2015 22:17:41 -0000
@@ -43,6 +43,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 #include <machine/autoconf.h>
 #include <mips64/archtype.h>
@@ -296,7 +297,7 @@ zsms_wsmouse_input(struct zsms_softc *sc
        x = (int)sc->packet[ZSMS_PACKET_X1] + (int)sc->packet[ZSMS_PACKET_X2];
        y = (int)sc->packet[ZSMS_PACKET_Y1] + (int)sc->packet[ZSMS_PACKET_Y2];

-       wsmouse_input(sc->wsmousedev, btns, x, y, 0, 0, WSMOUSE_INPUT_DELTA);
+       WSMOUSE_INPUT(sc->sc_wsmousedev, btns, x, y, 0, 0);
 }

 int
Index: arch/vax/dec/vsms_ws.c
===================================================================
RCS file: /cvs/src/sys/arch/vax/dec/vsms_ws.c,v
retrieving revision 1.4
diff -u -p -r1.4 vsms_ws.c
--- arch/vax/dec/vsms_ws.c      26 Aug 2008 19:46:23 -0000      1.4
+++ arch/vax/dec/vsms_ws.c      1 Dec 2015 22:17:42 -0000
@@ -75,6 +75,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 int    lkms_handle_error(struct lkms_softc *, int);
 void   lkms_input_disabled(struct lkms_softc *, int);
@@ -327,8 +328,9 @@ lkms_input_mouse(struct lkms_softc *sc,
                        sc->dy = -data;
                else
                        sc->dy = data;
-               wsmouse_input(sc->sc_wsmousedev, sc->buttons,
-                   sc->dx, sc->dy, 0, 0, WSMOUSE_INPUT_DELTA);
+
+               WSMOUSE_INPUT(sc->sc_wsmousedev,
+                   sc->buttons, sc->dx, sc->dy, 0, 0);

                sc->sc_frametype = 0;
                break;
@@ -380,9 +382,9 @@ lkms_input_tablet(struct lkms_softc *sc,
                break;
        case 4:
                sc->dy |= (data & 0x3f) << 6;
-               wsmouse_input(sc->sc_wsmousedev, sc->buttons,
-                   sc->dx, sc->dy, 0, 0,
-                   WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
+               wsmouse_buttons(sc->sc_wsmousedev, sc->buttons);
+               wsmouse_position(sc->sc_wsmousedev, sc->dx, sc->dy);
+               wsmouse_input_sync(sc->sc_wsmousedev);

                sc->sc_frametype = 0;
                break;
Index: arch/zaurus/dev/zts.c
===================================================================
RCS file: /cvs/src/sys/arch/zaurus/dev/zts.c,v
retrieving revision 1.16
diff -u -p -r1.16 zts.c
--- arch/zaurus/dev/zts.c       29 Mar 2014 18:09:30 -0000      1.16
+++ arch/zaurus/dev/zts.c       1 Dec 2015 22:17:42 -0000
@@ -30,6 +30,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 #include <dev/wscons/wsdisplayvar.h>
 #include <arm/xscale/pxa2x0var.h>
@@ -544,10 +545,9 @@ zts_irq(void *v)
                DPRINTF(("%s: tp.z = %d, tp.x = %d, tp.y = %d\n",
                    sc->sc_dev.dv_xname, tp.z, tp.x, tp.y));

-               wsmouse_input(sc->sc_wsmousedev, down, tp.x, tp.y,
-                   0 /* z */, 0 /* w */,
-                   WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
-                   WSMOUSE_INPUT_ABSOLUTE_Z);
+               wsmouse_buttons(sc->sc_wsmousedev, down);
+               wsmouse_position(sc->sc_wsmousedev, tp.x, tp.y);
+               wsmouse_input_sync(sc->sc_wsmousedev);
                sc->sc_buttons = down;
                sc->sc_oldx = tp.x;
                sc->sc_oldy = tp.y;
Index: dev/adb/ams.c
===================================================================
RCS file: /cvs/src/sys/dev/adb/ams.c,v
retrieving revision 1.6
diff -u -p -r1.6 ams.c
--- dev/adb/ams.c       15 Jun 2011 21:32:05 -0000      1.6
+++ dev/adb/ams.c       1 Dec 2015 22:17:43 -0000
@@ -39,6 +39,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 #include <dev/adb/adb.h>
 #include <dev/adb/amsvar.h>
@@ -501,8 +502,7 @@ ms_processevent(adb_event_t *event, stru
            ((event->bytes[0] & 0x40) ? 64 : 0);

        if (sc->sc_wsmousedev)
-               wsmouse_input(sc->sc_wsmousedev, buttons, dx, -dy, 0, 0,
-                   WSMOUSE_INPUT_DELTA);
+               WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, -dy, 0, 0);
 }

 int
Index: dev/hil/hilms.c
===================================================================
RCS file: /cvs/src/sys/dev/hil/hilms.c,v
retrieving revision 1.5
diff -u -p -r1.5 hilms.c
--- dev/hil/hilms.c     10 Apr 2007 22:37:17 -0000      1.5
+++ dev/hil/hilms.c     1 Dec 2015 22:17:43 -0000
@@ -41,6 +41,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 struct hilms_softc {
        struct hildev_softc sc_hildev;
@@ -333,6 +334,6 @@ hilms_callback(struct hildev_softc *dev,
        }
        
        if (sc->sc_wsmousedev != NULL)
-               wsmouse_input(sc->sc_wsmousedev,
-                   sc->sc_buttonstate, dx, dy, dz, 0, flags);
+               WSMOUSE_INPUT(sc->sc_wsmousedev,
+                   sc->sc_buttonstate, dx, dy, dz, 0);
 }
Index: dev/pckbc/pms.c
===================================================================
RCS file: /cvs/src/sys/dev/pckbc/pms.c,v
retrieving revision 1.67
diff -u -p -r1.67 pms.c
--- dev/pckbc/pms.c     5 Sep 2015 14:02:21 -0000       1.67
+++ dev/pckbc/pms.c     1 Dec 2015 22:17:50 -0000
@@ -39,6 +39,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 #if defined(__i386__) || defined(__amd64__)
 #include "acpi.h"
@@ -95,17 +96,18 @@ struct synaptics_softc {
 #define SYNAPTICS_VALID_NEWABS_FIRST   0x80
 #define SYNAPTICS_VALID_NEWABS_NEXT    0xc0

-       int res_x, res_y;
-       int min_x, min_y;
-       int max_x, max_y;
+       struct wsmousehw hw;
+#define SYNAPTICS_MAX_PRESSURE         256
+#define SYNAPTICS_MAX_FINGERWIDTH      16
+#define SYNAPTICS_MAX_FINGERS          3

        /* Compat mode */
        int wsmode;
-       int old_x, old_y;
-       u_int old_buttons;
        u_int sec_buttons;
 #define SYNAPTICS_SCALE                4
-#define SYNAPTICS_PRESSURE     30
+#define SYNAPTICS_PRESSURE_HI  30
+#define SYNAPTICS_PRESSURE_LO  25
+#define SYNAPTICS_PRESSURE     SYNAPTICS_PRESSURE_HI
 };

 struct alps_softc {
@@ -118,8 +120,9 @@ struct alps_softc {
        int mask;
        int version;

-       int min_x, min_y;
-       int max_x, max_y;
+       struct wsmousehw hw;
+#define ALPS_MAX_PRESSURE      256
+#define ALPS_MAX_FINGERS       1

        u_int gesture;

@@ -127,9 +130,11 @@ struct alps_softc {

        /* Compat mode */
        int wsmode;
-       int old_x, old_y;
-       u_int old_buttons;
-#define ALPS_PRESSURE          40
+       int old_x;
+       int old_y;
+
+#define ALPS_PRESSURE_HI       30
+#define ALPS_PRESSURE_LO       20
 };

 struct elantech_softc {
@@ -141,22 +146,12 @@ struct elantech_softc {
 #define ELANTECH_F_CRC_ENABLED         0x10
        int fw_version;

-       int min_x, min_y;
-       int max_x, max_y;
-       struct {
-               unsigned int x;
-               unsigned int y;
-               unsigned int z;
-       } mt[ELANTECH_MAX_FINGERS];
-       int mt_slots;
-       int mt_count;
-       int mt_filter;
-       int mt_lastid;
-       int mt_lastcount;
-       int mt_buttons;
+       struct wsmousehw hw;

        int width;

+       u_int mt_slots;
+
        u_char parity[256];
        u_char p1, p2, p3;

@@ -325,8 +320,6 @@ int elantech_set_absolute_mode_v2(struct
 int    elantech_set_absolute_mode_v3(struct pms_softc *);
 int    elantech_set_absolute_mode_v4(struct pms_softc *);

-void   elantech_send_mt_input(struct pms_softc *, int);
-
 struct cfattach pms_ca = {
        sizeof(struct pms_softc), pmsprobe, pmsattach, NULL,
        pmsactivate
@@ -641,8 +634,7 @@ pms_proc_mouse(struct pms_softc *sc)
        else
                dz = 0;

-       wsmouse_input(sc->sc_wsmousedev,
-           buttons, dx, dy, dz, 0, WSMOUSE_INPUT_DELTA);
+       WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, dz, 0);
 }

 int
@@ -950,17 +942,31 @@ synaptics_get_hwinfo(struct pms_softc *s
                        return (-1);
        }

+       if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) &&
+           !(syn->ext2_capabilities & SYNAPTICS_EXT2_CAP_BUTTONS_STICK)
+           && mouse_has_softbtn)
+               syn->hw.type = WSMOUSE_TYPE_SYNAP_SBTN;
+       else
+               syn->hw.type = WSMOUSE_TYPE_SYNAPTICS;
+
+       syn->hw.hw_type = (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD)
+           ? WSMHW_CLICKPAD : WSMHW_TOUCHPAD;
+
        if (syn->resolution & SYNAPTICS_RESOLUTION_VALID) {
-               syn->res_x = SYNAPTICS_RESOLUTION_X(syn->resolution);
-               syn->res_y = SYNAPTICS_RESOLUTION_Y(syn->resolution);
+               syn->hw.h_res = SYNAPTICS_RESOLUTION_X(syn->resolution);
+               syn->hw.v_res = SYNAPTICS_RESOLUTION_Y(syn->resolution);
        }
-       syn->min_x = SYNAPTICS_XMIN_BEZEL;
-       syn->min_y = SYNAPTICS_YMIN_BEZEL;
-       syn->max_x = (syn->dimension) ?
+       syn->hw.x_min = SYNAPTICS_XMIN_BEZEL;
+       syn->hw.y_min = SYNAPTICS_YMIN_BEZEL;
+       syn->hw.x_max = (syn->dimension) ?
            SYNAPTICS_DIM_X(syn->dimension) : SYNAPTICS_XMAX_BEZEL;
-       syn->max_y = (syn->dimension) ?
+       syn->hw.y_max = (syn->dimension) ?
            SYNAPTICS_DIM_Y(syn->dimension) : SYNAPTICS_YMAX_BEZEL;

+       syn->hw.pressure_max = SYNAPTICS_MAX_PRESSURE;
+       syn->hw.fingerwidth_max = SYNAPTICS_MAX_FINGERWIDTH;
+       syn->hw.fingers_max = SYNAPTICS_MAX_FINGERS;
+
        syn->sec_buttons = 0;

        if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) > 8)
@@ -997,8 +1003,7 @@ synaptics_sec_proc(struct pms_softc *sc)
        dy = (sc->packet[1] & PMS_PS2_YNEG) ?
            (int)sc->packet[5] - 256 : sc->packet[5];

-       wsmouse_input(sc->sc_sec_wsmousedev,
-           buttons, dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);
+       WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0);
 }

 int
@@ -1018,6 +1023,20 @@ synaptics_knock(struct pms_softc *sc)
 }

 int
+pms_synaptics_configure(struct device *sc_wsmousedev, struct synaptics_softc *syn)
+{
+       if (wsmouse_configure(sc_wsmousedev, 0, &syn->hw))
+               return (-1);
+
+       wsmouse_set_param(sc_wsmousedev,
+           WSMPARAM_PRESSURE_LO, SYNAPTICS_PRESSURE_LO);
+       wsmouse_set_param(sc_wsmousedev,
+           WSMPARAM_PRESSURE_HI, SYNAPTICS_PRESSURE_HI);
+
+       return (0);
+}
+
+int
 pms_enable_synaptics(struct pms_softc *sc)
 {
        struct synaptics_softc *syn = sc->synaptics;
@@ -1071,6 +1090,9 @@ pms_enable_synaptics(struct pms_softc *s

                syn->wsmode = WSMOUSE_COMPAT;

+               if (pms_synaptics_configure(sc->sc_wsmousedev, syn))
+                       goto err;
+
                printf("%s: Synaptics %s, firmware %d.%d\n", DEVNAME(sc),
                    (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD ?
                        "clickpad" : "touchpad"),
@@ -1100,6 +1122,7 @@ err:
        return (0);
 }

+
 int
pms_ioctl_synaptics(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
     struct proc *p)
@@ -1110,27 +1133,23 @@ pms_ioctl_synaptics(struct pms_softc *sc

        switch (cmd) {
        case WSMOUSEIO_GTYPE:
-               if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) &&
-                   !(syn->ext2_capabilities & SYNAPTICS_EXT2_CAP_BUTTONS_STICK)
-                   && mouse_has_softbtn)
-                       *(u_int *)data = WSMOUSE_TYPE_SYNAP_SBTN;
-               else
-                       *(u_int *)data = WSMOUSE_TYPE_SYNAPTICS;
+               *(u_int *)data = syn->hw.type;
                break;
        case WSMOUSEIO_GCALIBCOORDS:
-               wsmc->minx = syn->min_x;
-               wsmc->maxx = syn->max_x;
-               wsmc->miny = syn->min_y;
-               wsmc->maxy = syn->max_y;
+               wsmc->minx = syn->hw.x_min;
+               wsmc->maxx = syn->hw.x_max;
+               wsmc->miny = syn->hw.y_min;
+               wsmc->maxy = syn->hw.y_max;
                wsmc->swapxy = 0;
-               wsmc->resx = syn->res_x;
-               wsmc->resy = syn->res_y;
+               wsmc->resx = syn->hw.h_res;
+               wsmc->resy = syn->hw.v_res;
                break;
        case WSMOUSEIO_SETMODE:
                wsmode = *(u_int *)data;
                if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
                        return (EINVAL);
                syn->wsmode = wsmode;
+               wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
                break;
        default:
                return (-1);
@@ -1162,7 +1181,7 @@ pms_proc_synaptics(struct pms_softc *sc)
 {
        struct synaptics_softc *syn = sc->synaptics;
        u_int buttons;
-       int x, y, z, w, dx, dy;
+       int x, y, z, w, fingerwidth;

        w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) |
            ((sc->packet[3] & 0x04) >> 2);
@@ -1230,8 +1249,9 @@ pms_proc_synaptics(struct pms_softc *sc)
                            (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(3) : 0;
                        syn->sec_buttons |=
                            (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(2) : 0;
-                       wsmouse_input(sc->sc_sec_wsmousedev,
-                           syn->sec_buttons, 0, 0, 0, 0, WSMOUSE_INPUT_DELTA);
+                       wsmouse_buttons(sc->sc_sec_wsmousedev,
+                           syn->sec_buttons);
+                       wsmouse_input_sync(sc->sc_sec_wsmousedev);
                        return;
                }

@@ -1247,32 +1267,15 @@ pms_proc_synaptics(struct pms_softc *sc)
                y &= ~0x0f;
        }

-       /* ignore final events that happen when removing all fingers */
-       if (x <= 1 || y <= 1) {
-               x = syn->old_x;
-               y = syn->old_y;
-       }
-
-       if (syn->wsmode == WSMOUSE_NATIVE) {
-               wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
-                   WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
-                   WSMOUSE_INPUT_ABSOLUTE_Z | WSMOUSE_INPUT_ABSOLUTE_W);
+       if (z) {
+               fingerwidth = max(w, 4);
+               w = (w < 2 ? w + 2 : 1);
        } else {
-               dx = dy = 0;
-               if (z > SYNAPTICS_PRESSURE) {
-                       dx = x - syn->old_x;
-                       dy = y - syn->old_y;
-                       dx /= SYNAPTICS_SCALE;
-                       dy /= SYNAPTICS_SCALE;
-               }
-               if (dx || dy || buttons != syn->old_buttons)
-                       wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
-                           WSMOUSE_INPUT_DELTA);
-               syn->old_buttons = buttons;
+               fingerwidth = 0;
+               w = 0;
        }
-
-       syn->old_x = x;
-       syn->old_y = y;
+       wsmouse_set(sc->sc_wsmousedev, WSMOUSE_FINGERWIDTH, fingerwidth, 0);
+       WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
 }

 void
@@ -1313,8 +1316,7 @@ alps_sec_proc(struct pms_softc *sc)
        dy = (sc->packet[pos] & PMS_PS2_YNEG) ?
            (int)sc->packet[pos + 2] - 256 : sc->packet[pos + 2];

-       wsmouse_input(sc->sc_sec_wsmousedev, alps->sec_buttons,
-           dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);
+       WSMOUSE_INPUT(sc->sc_sec_wsmousedev, alps->sec_buttons, dx, dy, 0, 0);

        return (1);
 }
@@ -1337,6 +1339,16 @@ alps_get_hwinfo(struct pms_softc *sc)

        alps->version = (resp[0] << 8) | (resp[1] << 4) | (resp[2] / 20 + 1);

+       alps->hw.type = WSMOUSE_TYPE_ALPS;
+       alps->hw.hw_type = WSMHW_TOUCHPAD;
+       alps->hw.x_min = ALPS_XMIN_BEZEL;
+       alps->hw.x_max = ALPS_XMAX_BEZEL;
+       alps->hw.y_min = ALPS_YMIN_BEZEL;
+       alps->hw.y_max = ALPS_YMAX_BEZEL;
+       alps->hw.flags |= WSMHW_LR_DOWN;
+       alps->hw.pressure_max = ALPS_MAX_PRESSURE;
+       alps->hw.fingers_max = ALPS_MAX_FINGERS;
+
        for (i = 0; i < nitems(alps_models); i++)
                if (alps->version == alps_models[i].version) {
                        alps->model = alps_models[i].model;
@@ -1348,6 +1360,20 @@ alps_get_hwinfo(struct pms_softc *sc)
 }

 int
+pms_alps_configure(struct device *sc_wsmousedev, struct alps_softc *alps)
+{
+       if (wsmouse_configure(sc_wsmousedev, 0, &alps->hw))
+               return (-1);
+
+       wsmouse_set_param(sc_wsmousedev,
+           WSMPARAM_PRESSURE_LO, ALPS_PRESSURE_LO);
+       wsmouse_set_param(sc_wsmousedev,
+           WSMPARAM_PRESSURE_HI, ALPS_PRESSURE_HI);
+
+       return (0);
+}
+
+int
 pms_enable_alps(struct pms_softc *sc)
 {
        struct alps_softc *alps = sc->alps;
@@ -1383,13 +1409,11 @@ pms_enable_alps(struct pms_softc *sc)
                    (alps->model & ALPS_DUALPOINT ? "Dualpoint" : "Glidepoint"),
                    alps->version);

-               alps->min_x = ALPS_XMIN_BEZEL;
-               alps->min_y = ALPS_YMIN_BEZEL;
-               alps->max_x = ALPS_XMAX_BEZEL;
-               alps->max_y = ALPS_YMAX_BEZEL;
-
                alps->wsmode = WSMOUSE_COMPAT;

+               if (pms_alps_configure(sc->sc_wsmousedev, alps))
+                       goto err;
+
                if (alps->model & ALPS_DUALPOINT) {
                        a.accessops = &pms_sec_accessops;
                        a.accesscookie = sc;
@@ -1398,6 +1422,7 @@ pms_enable_alps(struct pms_softc *sc)
                }
        }

+
        if (alps->model == 0)
                goto err;

@@ -1458,10 +1483,10 @@ pms_ioctl_alps(struct pms_softc *sc, u_l
                *(u_int *)data = WSMOUSE_TYPE_ALPS;
                break;
        case WSMOUSEIO_GCALIBCOORDS:
-               wsmc->minx = alps->min_x;
-               wsmc->maxx = alps->max_x;
-               wsmc->miny = alps->min_y;
-               wsmc->maxy = alps->max_y;
+               wsmc->minx = alps->hw.x_min;
+               wsmc->maxx = alps->hw.x_max;
+               wsmc->miny = alps->hw.y_min;
+               wsmc->maxy = alps->hw.y_max;
                wsmc->swapxy = 0;
                break;
        case WSMOUSEIO_SETMODE:
@@ -1469,6 +1494,7 @@ pms_ioctl_alps(struct pms_softc *sc, u_l
                if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
                        return (EINVAL);
                alps->wsmode = wsmode;
+               wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
                break;
        default:
                return (-1);
@@ -1514,7 +1540,7 @@ void
 pms_proc_alps(struct pms_softc *sc)
 {
        struct alps_softc *alps = sc->alps;
-       int x, y, z, w, dx, dy;
+       int x, y, z, dx, dy;
        u_int buttons, gesture;

        if ((alps->model & ALPS_DUALPOINT) && alps_sec_proc(sc))
@@ -1532,8 +1558,7 @@ pms_proc_alps(struct pms_softc *sc)
                dx = (x > ALPS_XSEC_BEZEL / 2) ? (x - ALPS_XSEC_BEZEL) : x;
                dy = (y > ALPS_YSEC_BEZEL / 2) ? (y - ALPS_YSEC_BEZEL) : y;

-               wsmouse_input(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0,
-                   WSMOUSE_INPUT_DELTA);
+               WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0);

                return;
        }
@@ -1541,72 +1566,29 @@ pms_proc_alps(struct pms_softc *sc)
        if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0)
                return;

-       /*
-        * XXX The Y-axis is in the oposit direction compared to
-        * Synaptics touchpads and PS/2 mouses.
-        * It's why we need to translate the y value here for both
-        * NATIVE and COMPAT modes.
-        */
-       y = ALPS_YMAX_BEZEL - y + ALPS_YMIN_BEZEL;
-
-       if (alps->wsmode == WSMOUSE_NATIVE) {
-               if (alps->gesture == ALPS_TAP) {
-                       /* Report a touch with the tap coordinates. */
-                       wsmouse_input(sc->sc_wsmousedev, buttons,
-                           alps->old_x, alps->old_y, ALPS_PRESSURE, 4,
-                           WSMOUSE_INPUT_ABSOLUTE_X
-                           | WSMOUSE_INPUT_ABSOLUTE_Y
-                           | WSMOUSE_INPUT_ABSOLUTE_Z
-                           | WSMOUSE_INPUT_ABSOLUTE_W);
-                       if (z > 0) {
-                               /*
-                                * The hardware doesn't send a null pressure
-                                * event when dragging starts.
-                                */
-                               wsmouse_input(sc->sc_wsmousedev, buttons,
-                                   alps->old_x, alps->old_y, 0, 0,
-                                   WSMOUSE_INPUT_ABSOLUTE_X
-                                   | WSMOUSE_INPUT_ABSOLUTE_Y
-                                   | WSMOUSE_INPUT_ABSOLUTE_Z
-                                   | WSMOUSE_INPUT_ABSOLUTE_W);
-                       }
-               }
-
-               gesture = sc->packet[2] & 0x03;
-               if (gesture != ALPS_TAP) {
-                       w = z ? 4 : 0;
-                       wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
-                           WSMOUSE_INPUT_ABSOLUTE_X
-                           | WSMOUSE_INPUT_ABSOLUTE_Y
-                           | WSMOUSE_INPUT_ABSOLUTE_Z
-                           | WSMOUSE_INPUT_ABSOLUTE_W);
+       if (alps->gesture == ALPS_TAP) {
+               /* Report a touch with the tap coordinates. */
+               WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,
+                   alps->old_x, alps->old_y, ALPS_PRESSURE_HI, 1);
+               if (z > 0) {
+                       /*
+                        * The hardware doesn't send a null pressure
+                        * event when dragging starts.
+                        */
+                       wsmouse_touch(sc->sc_wsmousedev, 0, 0);
+                       wsmouse_input_sync(sc->sc_wsmousedev);
                }
+       }

-               if (alps->gesture != ALPS_DRAG || gesture != ALPS_TAP)
-                       alps->gesture = gesture;
-
-               alps->old_x = x;
-               alps->old_y = y;
+       gesture = sc->packet[2] & 0x03;
+       if (gesture != ALPS_TAP)
+               WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, z ? 1 : 0);

-       } else {
-               dx = dy = 0;
-               if (z > ALPS_PRESSURE) {
-                       dx = x - alps->old_x;
-                       dy = y - alps->old_y;
-
-                       /* Prevent jump */
-                       dx = abs(dx) > 50 ? 0 : dx;
-                       dy = abs(dy) > 50 ? 0 : dy;
-               }
-
-               if (dx || dy || buttons != alps->old_buttons)
-                       wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
-                           WSMOUSE_INPUT_DELTA);
-
-               alps->old_x = x;
-               alps->old_y = y;
-               alps->old_buttons = buttons;
-       }
+       if (alps->gesture != ALPS_DRAG || gesture != ALPS_TAP)
+               alps->gesture = gesture;
+
+       alps->old_x = x;
+       alps->old_y = y;
 }

 int
@@ -1763,10 +1745,10 @@ elantech_get_hwinfo_v1(struct pms_softc
        if (elantech_set_absolute_mode_v1(sc))
                return (-1);

-       elantech->min_x = ELANTECH_V1_X_MIN;
-       elantech->max_x = ELANTECH_V1_X_MAX;
-       elantech->min_y = ELANTECH_V1_Y_MIN;
-       elantech->max_y = ELANTECH_V1_Y_MAX;
+       elantech->hw.x_min = ELANTECH_V1_X_MIN;
+       elantech->hw.x_max = ELANTECH_V1_X_MAX;
+       elantech->hw.y_min = ELANTECH_V1_Y_MIN;
+       elantech->hw.y_max = ELANTECH_V1_Y_MAX;

        return (0);
 }
@@ -1800,8 +1782,8 @@ elantech_get_hwinfo_v2(struct pms_softc

        if (fw_version == 0x20800 || fw_version == 0x20b00 ||
            fw_version == 0x20030) {
-               elantech->max_x = ELANTECH_V2_X_MAX;
-               elantech->max_y = ELANTECH_V2_Y_MAX;
+               elantech->hw.x_max = ELANTECH_V2_X_MAX;
+               elantech->hw.y_max = ELANTECH_V2_Y_MAX;
        } else {
                if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
                    pms_get_status(sc, resp))
@@ -1812,17 +1794,17 @@ elantech_get_hwinfo_v2(struct pms_softc
                        if (pms_spec_cmd(sc, ELANTECH_QUE_SAMPLE) ||
                            pms_get_status(sc, resp))
                                return (-1);
-                       elantech->max_x = (capabilities[1] - i) * resp[1] / 2;
-                       elantech->max_y = (capabilities[2] - i) * resp[2] / 2;
+                       elantech->hw.x_max = (capabilities[1] - i) * resp[1] / 
2;
+                       elantech->hw.y_max = (capabilities[2] - i) * resp[2] / 
2;
                } else if (fw_version == 0x040216) {
-                       elantech->max_x = 819;
-                       elantech->max_y = 405;
+                       elantech->hw.x_max = 819;
+                       elantech->hw.y_max = 405;
                } else if (fw_version == 0x040219 || fw_version == 0x040215) {
-                       elantech->max_x = 900;
-                       elantech->max_y = 500;
+                       elantech->hw.x_max = 900;
+                       elantech->hw.y_max = 500;
                } else {
-                       elantech->max_x = (capabilities[1] - i) * 64;
-                       elantech->max_y = (capabilities[2] - i) * 64;
+                       elantech->hw.x_max = (capabilities[1] - i) * 64;
+                       elantech->hw.y_max = (capabilities[2] - i) * 64;
                }
        }

@@ -1855,8 +1837,8 @@ elantech_get_hwinfo_v3(struct pms_softc
            pms_get_status(sc, resp))
                return (-1);

-       elantech->max_x = (resp[0] & 0x0f) << 8 | resp[1];
-       elantech->max_y = (resp[0] & 0xf0) << 4 | resp[2];
+       elantech->hw.x_max = (resp[0] & 0x0f) << 8 | resp[1];
+       elantech->hw.y_max = (resp[0] & 0xf0) << 4 | resp[2];

        return (0);
 }
@@ -1890,13 +1872,18 @@ elantech_get_hwinfo_v4(struct pms_softc
            pms_get_status(sc, resp))
                return (-1);

-       elantech->max_x = (resp[0] & 0x0f) << 8 | resp[1];
-       elantech->max_y = (resp[0] & 0xf0) << 4 | resp[2];
+       elantech->hw.x_max = (resp[0] & 0x0f) << 8 | resp[1];
+       elantech->hw.y_max = (resp[0] & 0xf0) << 4 | resp[2];

-       if ((capabilities[1] < 2) || (capabilities[1] > elantech->max_x))
+       if ((capabilities[1] < 2) || (capabilities[1] > elantech->hw.x_max))
                return (-1);

-       elantech->width = elantech->max_x / (capabilities[1] - 1);
+       elantech->hw.type = WSMOUSE_TYPE_ELANTECH;
+       elantech->hw.hw_type = WSMHW_CLICKPAD;
+       elantech->hw.mt_slots = ELANTECH_MAX_FINGERS;
+       elantech->hw.pressure_max = 256;
+
+       elantech->width = elantech->hw.x_max / (capabilities[1] - 1);

        return (0);
 }
@@ -2045,6 +2032,13 @@ err:
 }

 int
+pms_elantech_v4_configure(struct device *sc_wsmousedev,
+    struct elantech_softc *elantech)
+{
+       return wsmouse_configure(sc_wsmousedev, 0, &elantech->hw);
+}
+
+int
 pms_enable_elantech_v4(struct pms_softc *sc)
 {
        struct elantech_softc *elantech = sc->elantech;
@@ -2067,6 +2061,13 @@ pms_enable_elantech_v4(struct pms_softc
                        sc->elantech = NULL;
                        goto err;
                }
+               if (pms_elantech_v4_configure(
+                   sc->sc_wsmousedev, sc->elantech)) {
+                       free(sc->elantech, M_DEVBUF, 0);
+                       sc->elantech = NULL;
+                       printf("%s: input setup failed\n", DEVNAME(sc));
+                       goto err;
+               }

                printf("%s: Elantech Clickpad, version %d, firmware 0x%x\n",
                    DEVNAME(sc), 4, sc->elantech->fw_version);
@@ -2094,10 +2095,10 @@ pms_ioctl_elantech(struct pms_softc *sc,
                *(u_int *)data = WSMOUSE_TYPE_ELANTECH;
                break;
        case WSMOUSEIO_GCALIBCOORDS:
-               wsmc->minx = elantech->min_x;
-               wsmc->maxx = elantech->max_x;
-               wsmc->miny = elantech->min_y;
-               wsmc->maxy = elantech->max_y;
+               wsmc->minx = elantech->hw.x_min;
+               wsmc->maxx = elantech->hw.x_max;
+               wsmc->miny = elantech->hw.y_min;
+               wsmc->maxy = elantech->hw.y_max;
                wsmc->swapxy = 0;
                wsmc->resx = 0;
                wsmc->resy = 0;
@@ -2107,6 +2108,8 @@ pms_ioctl_elantech(struct pms_softc *sc,
                if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
                        return (EINVAL);
                elantech->wsmode = wsmode;
+               if (sc->protocol->type == PMS_ELANTECH_V4)
+                       wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
                break;
        default:
                return (-1);
@@ -2334,7 +2337,8 @@ pms_proc_elantech_v3(struct pms_softc *s

        /* Prevent juming cursor if pad isn't touched or reports garbage. */
        if (w == 0 ||
-           ((x == 0 || y == 0 || x == elantech->max_x || y == elantech->max_y)
+           ((x == 0 || y == 0 || x == elantech->hw.x_max ||
+           y == elantech->hw.y_max)
            && (x != elantech->old_x || y != elantech->old_y))) {
                x = elantech->old_x;
                y = elantech->old_y;
@@ -2352,41 +2356,36 @@ void
 pms_proc_elantech_v4(struct pms_softc *sc)
 {
        struct elantech_softc *elantech = sc->elantech;
-       int n, id, slots, weight, dx, dy;
+       struct device *sc_wsmousedev = sc->sc_wsmousedev;
+       int n, id, weight, x, y, z;
+       u_int buttons, lifted;

-       switch (sc->packet[3] & 0x1f) {
-       case ELANTECH_V4_PKT_STATUS:
-               if (elantech->mt_slots == 0)
-                       elantech->mt_lastid = -1;
-               slots = sc->packet[1] & 0x1f;
-               if (slots == 0 && elantech->mt_lastid > -1)
-                       /* Notify that we lifted. */
-                       elantech_send_input(sc,
-                           elantech->mt[elantech->mt_lastid].x,
-                           elantech->mt[elantech->mt_lastid].y, 0, 0);
-
-               elantech->mt_filter = elantech->mt_slots = slots;
+       buttons = 0;
+       if (sc->packet[0] & 0x01)
+               buttons |= WSMOUSE_BUTTON(1);
+       if (sc->packet[0] & 0x02)
+               buttons |= WSMOUSE_BUTTON(3);

-               for (elantech->mt_count = 0; slots != 0; slots >>= 1)
-                       elantech->mt_count += (1 & slots);
+       wsmouse_buttons(sc_wsmousedev, buttons);

+       switch (sc->packet[3] & 0x1f) {
+       case ELANTECH_V4_PKT_STATUS:
+               lifted = elantech->mt_slots;
+               elantech->mt_slots = sc->packet[1] & 0x1f;
+               lifted &= ~elantech->mt_slots;
+               for (id = 0; lifted; id++, lifted >>= 1)
+                       if (lifted & 1)
+                               wsmouse_mtstate(sc_wsmousedev, id, 0, 0, 0);
                break;

        case ELANTECH_V4_PKT_HEAD:
                id = ((sc->packet[3] & 0xe0) >> 5) - 1;
                if (id > -1 && id < ELANTECH_MAX_FINGERS) {
-                       elantech->mt[id].x =
-                           ((sc->packet[1] & 0x0f) << 8) | sc->packet[2];
-                       elantech->mt[id].y =
-                           ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
-                       elantech->mt[id].z =
-                           (sc->packet[1] & 0xf0)
+                       x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2];
+                       y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
+                       z = (sc->packet[1] & 0xf0)
                            | ((sc->packet[4] & 0xf0) >> 4);
-
-                       if (elantech->mt_filter & (1 << id)) {
-                               elantech_send_mt_input(sc, id);
-                               elantech->mt_filter = (1 << id);
-                       }
+                       wsmouse_mtstate(sc_wsmousedev, id, x, y, z);
                }
                break;

@@ -2396,22 +2395,12 @@ pms_proc_elantech_v4(struct pms_softc *s
                        id = ((sc->packet[n] & 0xe0) >> 5) - 1;
                        if (id < 0 || id >= ELANTECH_MAX_FINGERS)
                                continue;
-                       dx = weight * (signed char)sc->packet[n + 1];
-                       dy = weight * (signed char)sc->packet[n + 2];
-                       elantech->mt[id].x += dx;
-                       elantech->mt[id].y += dy;
-                       elantech->mt[id].z = 1;
-                       if (elantech->mt_filter & (1 << id)) {
-                               if ((dx | dy)
-                                   || elantech->mt_count !=
-                                   elantech->mt_lastcount
-                                   || (sc->packet[0] & 3) !=
-                                   elantech->mt_buttons)
-                                       elantech_send_mt_input(sc, id);
-
-                               elantech->mt_filter = (dx | dy) ?
-                                   (1 << id) : elantech->mt_slots;
-                       }
+                       x = weight * (signed char)sc->packet[n + 1];
+                       y = weight * (signed char)sc->packet[n + 2];
+                       z = WSMOUSE_DEFAULT_PRESSURE;
+                       wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_X, x, id);
+                       wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_Y, y, id);
+                       wsmouse_set(sc_wsmousedev, WSMOUSE_MT_PRESSURE, z, id);
                }

                break;
@@ -2419,45 +2408,18 @@ pms_proc_elantech_v4(struct pms_softc *s
        default:
                printf("%s: unknown packet type 0x%x\n", DEVNAME(sc),
                    sc->packet[3] & 0x1f);
-               return;
+               break;
        }
-}
-
-void
-elantech_send_mt_input(struct pms_softc *sc, int id)
-{
-       struct elantech_softc *elantech = sc->elantech;

-       if (id != elantech->mt_lastid) {
-               /* Correct for compatibility mode, but not useful yet: */
-               elantech->old_x = elantech->mt[id].x;
-               elantech->old_y = elantech->mt[id].y;
-               /*
-                * To avoid a jump of the cursor, simulate a change of the
-                * number of touches (without producing tapping gestures
-                * accidentally). It should suffice to do that only if
-                * mt_count hasn't changed, but we cannot rely on the
-                * synaptics driver, which alters its finger counts when
-                * handling click-and-drag actions (see HandleTapProcessing
-                * and ComputeDeltas in synaptics.c).
-                */
-               if (elantech->mt_lastid > -1)
-                       elantech_send_input(sc,
-                           elantech->mt[id].x, elantech->mt[id].y,
-                           elantech->mt[id].z, ELANTECH_MAX_FINGERS);
-               elantech->mt_lastid = id;
-       }
-       elantech->mt_lastcount = elantech->mt_count;
-       elantech->mt_buttons = sc->packet[0] & 3;
-       elantech_send_input(sc,
-           elantech->mt[id].x, elantech->mt[id].y,
-           elantech->mt[id].z, elantech->mt_count);
+       wsmouse_input_sync(sc_wsmousedev);
 }

+
 void
 elantech_send_input(struct pms_softc *sc, int x, int y, int z, int w)
 {
        struct elantech_softc *elantech = sc->elantech;
+       struct device *sc_wsmousedev = sc->sc_wsmousedev;
        int dx, dy;
        u_int buttons = 0;

@@ -2474,11 +2436,7 @@ elantech_send_input(struct pms_softc *sc
        }

        if (elantech->wsmode == WSMOUSE_NATIVE) {
-               wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
-                   WSMOUSE_INPUT_ABSOLUTE_X |
-                   WSMOUSE_INPUT_ABSOLUTE_Y |
-                   WSMOUSE_INPUT_ABSOLUTE_Z |
-                   WSMOUSE_INPUT_ABSOLUTE_W);
+               WSMOUSE_TOUCH(sc_wsmousedev, buttons, x, y, z, w);
        } else {
                dx = dy = 0;

@@ -2490,8 +2448,7 @@ elantech_send_input(struct pms_softc *sc
                        dy /= SYNAPTICS_SCALE;
                }
                if (dx || dy || buttons != elantech->old_buttons)
-                       wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
-                           WSMOUSE_INPUT_DELTA);
+                       WSMOUSE_INPUT(sc_wsmousedev, buttons, dx, dy, 0, 0);
                elantech->old_buttons = buttons;
        }

Index: dev/sun/sunms.c
===================================================================
RCS file: /cvs/src/sys/dev/sun/sunms.c,v
retrieving revision 1.1
diff -u -p -r1.1 sunms.c
--- dev/sun/sunms.c     20 May 2009 18:22:33 -0000      1.1
+++ dev/sun/sunms.c     1 Dec 2015 22:17:50 -0000
@@ -44,6 +44,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 #include <dev/sun/sunmsvar.h>

@@ -220,8 +221,8 @@ sunms_input(struct sunms_softc *sc, int
        if (sc->sc_byteno == sc->sc_pktlen) {
                timeout_del(&sc->sc_abort_tmo);
                sc->sc_byteno = -1;
-               wsmouse_input(sc->sc_wsmousedev, sc->sc_mb,
-                   sc->sc_dx, sc->sc_dy, 0, 0, WSMOUSE_INPUT_DELTA);
+               WSMOUSE_INPUT(sc->sc_wsmousedev,
+                   sc->sc_mb, sc->sc_dx, sc->sc_dy, 0, 0);
                sc->sc_dx = sc->sc_dy = 0;
        }
 }
Index: dev/usb/hidms.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/hidms.c,v
retrieving revision 1.8
diff -u -p -r1.8 hidms.c
--- dev/usb/hidms.c     5 Nov 2015 15:41:15 -0000       1.8
+++ dev/usb/hidms.c     1 Dec 2015 22:17:50 -0000
@@ -49,6 +49,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 #include <dev/usb/hidmsvar.h>

@@ -341,7 +342,6 @@ hidms_input(struct hidms *ms, uint8_t *d
 {
        int dx, dy, dz, dw;
        u_int32_t buttons = 0;
-       int flags;
        int i, s;

        DPRINTFN(5,("hidms_input: len=%d\n", len));
@@ -368,12 +368,6 @@ hidms_input(struct hidms *ms, uint8_t *d
                        return;
        }

-       flags = WSMOUSE_INPUT_DELTA;
-       if (ms->sc_flags & HIDMS_ABSX)
-               flags |= WSMOUSE_INPUT_ABSOLUTE_X;
-       if (ms->sc_flags & HIDMS_ABSY)
-               flags |= WSMOUSE_INPUT_ABSOLUTE_Y;
-
        dx =  hid_get_data(data, len, &ms->sc_loc_x);
        dy = -hid_get_data(data, len, &ms->sc_loc_y);
        dz =  hid_get_data(data, len, &ms->sc_loc_z);
@@ -413,8 +407,18 @@ hidms_input(struct hidms *ms, uint8_t *d
                ms->sc_buttons = buttons;
                if (ms->sc_wsmousedev != NULL) {
                        s = spltty();
-                       wsmouse_input(ms->sc_wsmousedev, buttons,
-                           dx, dy, dz, dw, flags);
+                       if (ms->sc_flags & HIDMS_ABSX) {
+                               wsmouse_set(ms->sc_wsmousedev,
+                                   WSMOUSE_ABS_X, dx, 0);
+                               dx = 0;
+                       }
+                       if (ms->sc_flags & HIDMS_ABSY) {
+                               wsmouse_set(ms->sc_wsmousedev,
+                                   WSMOUSE_ABS_Y, dy, 0);
+                               dy = 0;
+                       }
+                       WSMOUSE_INPUT(ms->sc_wsmousedev,
+                           buttons, dx, dy, dz, dw);
                        splx(s);
                }
        }
Index: dev/usb/ubcmtp.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/ubcmtp.c,v
retrieving revision 1.10
diff -u -p -r1.10 ubcmtp.c
--- dev/usb/ubcmtp.c    30 Nov 2015 09:30:48 -0000      1.10
+++ dev/usb/ubcmtp.c    1 Dec 2015 22:17:51 -0000
@@ -52,6 +52,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 /* #define UBCMTP_DEBUG */

@@ -615,6 +616,7 @@ ubcmtp_ioctl(void *v, unsigned long cmd,
                    "native"));

                sc->wsmode = wsmode;
+               wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
                break;

        default:
@@ -866,20 +868,13 @@ ubcmtp_tp_intr(struct usbd_xfer *xfer, v
                if (sc->wsmode == WSMOUSE_NATIVE) {
                        DPRINTF("absolute input %d, %d (finger %d, button 
%d)\n",
                            sc->pos[0].x, sc->pos[0].y, finger, sc->btn);
-                       wsmouse_input(sc->sc_wsmousedev, sc->btn, sc->pos[0].x,
-                           sc->pos[0].y,
-                           (finger == 0 ? 0 : 50), /* fake z for now */
-                           finger,
-                           WSMOUSE_INPUT_ABSOLUTE_X |
-                           WSMOUSE_INPUT_ABSOLUTE_Y |
-                           WSMOUSE_INPUT_ABSOLUTE_Z |
-                           WSMOUSE_INPUT_ABSOLUTE_W);
+                       WSMOUSE_TOUCH(sc->sc_wsmousedev, sc->btn, sc->pos[0].x,
+                           sc->pos[0].y, (finger ? 50 : 0), finger);
                } else {
                        DPRINTF("relative input %d, %d (button %d)\n",
                            sc->pos[0].dx, sc->pos[0].dy, sc->btn);
-                       wsmouse_input(sc->sc_wsmousedev, sc->btn,
-                           sc->pos[0].dx, sc->pos[0].dy, 0, 0,
-                           WSMOUSE_INPUT_DELTA);
+                       WSMOUSE_INPUT(sc->sc_wsmousedev, sc->btn,
+                           sc->pos[0].dx, sc->pos[0].dy, 0, 0);
                }
                splx(s);
        }
@@ -916,17 +911,7 @@ ubcmtp_bt_intr(struct usbd_xfer *xfer, v

        if (pkt->button != sc->btn) {
                sc->btn = pkt->button;
-
-               if (sc->wsmode == WSMOUSE_NATIVE)
-                       wsmouse_input(sc->sc_wsmousedev, sc->btn, sc->pos[0].x,
-                           sc->pos[0].y, 50 /* fake z for now */,
-                           1,
-                           WSMOUSE_INPUT_ABSOLUTE_X |
-                           WSMOUSE_INPUT_ABSOLUTE_Y |
-                           WSMOUSE_INPUT_ABSOLUTE_Z |
-                           WSMOUSE_INPUT_ABSOLUTE_W);
-               else
-                       wsmouse_input(sc->sc_wsmousedev, sc->btn,
-                           0, 0, 0, 0, WSMOUSE_INPUT_DELTA);
+               wsmouse_buttons(sc->sc_wsmousedev, sc->btn);
+               wsmouse_input_sync(sc->sc_wsmousedev);
        }
 }
Index: dev/usb/uts.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uts.c,v
retrieving revision 1.36
diff -u -p -r1.36 uts.c
--- dev/usb/uts.c       14 Mar 2015 03:38:50 -0000      1.36
+++ dev/usb/uts.c       1 Dec 2015 22:17:53 -0000
@@ -37,6 +37,7 @@

 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>

 #ifdef UTS_DEBUG
 #define DPRINTF(x)             do { printf x; } while (0)
@@ -476,9 +477,8 @@ uts_intr(struct usbd_xfer *xfer, void *a
        DPRINTF(("%s: tp.down = %d, tp.z = %d, tp.x = %d, tp.y = %d\n",
            sc->sc_dev.dv_xname, tp.down, tp.z, tp.x, tp.y));

-       wsmouse_input(sc->sc_wsmousedev, tp.down, tp.x, tp.y, tp.z, 0,
-           WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
-           WSMOUSE_INPUT_ABSOLUTE_Z);
+       WSMOUSE_TOUCH(sc->sc_wsmousedev,
+           tp.down, tp.x, tp.y, tp.z, (tp.z ? 1 : 0));
        sc->sc_oldy = tp.y;
        sc->sc_oldx = tp.x;

Index: dev/wscons/files.wscons
===================================================================
RCS file: /cvs/src/sys/dev/wscons/files.wscons,v
retrieving revision 1.16
diff -u -p -r1.16 files.wscons
--- dev/wscons/files.wscons     18 Oct 2013 22:06:40 -0000      1.16
+++ dev/wscons/files.wscons     1 Dec 2015 22:17:53 -0000
@@ -28,6 +28,8 @@ file  dev/wscons/wsevent.c                    wsdisplay |
 file   dev/wscons/wskbd.c                      wskbd           needs-flag
 file   dev/wscons/wskbdutil.c                  wskbd
 file   dev/wscons/wsmouse.c                    wsmouse         needs-flag
+file   dev/wscons/wsmouseinput.c               wsmouse
+file   dev/wscons/wstpad.c                     wsmouse

 pseudo-device  wsmux
 file   dev/wscons/wsmux.c                      wsmux           needs-flag
Index: dev/wscons/wsconsio.h
===================================================================
RCS file: /cvs/src/sys/dev/wscons/wsconsio.h,v
retrieving revision 1.72
diff -u -p -r1.72 wsconsio.h
--- dev/wscons/wsconsio.h       30 Aug 2015 10:05:09 -0000      1.72
+++ dev/wscons/wsconsio.h       1 Dec 2015 22:17:53 -0000
@@ -76,10 +76,10 @@ struct wscons_event {
 #define        WSCONS_EVENT_MOUSE_ABSOLUTE_X   8       /* X location */
 #define        WSCONS_EVENT_MOUSE_ABSOLUTE_Y   9       /* Y location */
 #define        WSCONS_EVENT_MOUSE_DELTA_Z      10      /* Z delta amount */
-#define        WSCONS_EVENT_MOUSE_ABSOLUTE_Z   11      /* Z location */
+#define        WSCONS_EVENT_MOUSE_ABSOLUTE_Z   11      /* (legacy, see below) 
*/
                                     /* 12-15, see below */
 #define        WSCONS_EVENT_MOUSE_DELTA_W      16      /* W delta amount */
-#define        WSCONS_EVENT_MOUSE_ABSOLUTE_W   17      /* W location */
+#define        WSCONS_EVENT_MOUSE_ABSOLUTE_W   17      /* (legacy, see below) 
*/
 #define        WSCONS_EVENT_SYNC               18
 /*
* Following events are not real wscons_event but are used as parameters of the
@@ -96,6 +96,22 @@ struct wscons_event {
                               ((type) == WSCONS_EVENT_MOUSE_DOWN))
 #define IS_CTRL_EVENT(type) ((type == WSCONS_EVENT_WSMOUSED_ON) || \
                             (type == WSCONS_EVENT_WSMOUSED_OFF))
+
+
+/*
+ * (Single-) Touch Events
+ *
+ * Touchpads only generate the WIDTH- and UPDATE-events when they are
+ * running in native mode. An UPDATE is signalled whenever a change
+ * of X and Y is coupled with a change of the finger count, or with a
+ * change of the pointer-controlling MT slot.
+ *
+ */
+#define        WSCONS_EVENT_TOUCH_PRESSURE     WSCONS_EVENT_MOUSE_ABSOLUTE_Z
+#define        WSCONS_EVENT_TOUCH_FINGERS      WSCONS_EVENT_MOUSE_ABSOLUTE_W
+
+#define WSCONS_EVENT_TOUCH_WIDTH       24      /* synaptics finger width */
+#define WSCONS_EVENT_TOUCH_UPDATE      25      /* (no value) */

 /*
  * Keyboard ioctls (0 - 31)
Index: dev/wscons/wsmouse.c
===================================================================
RCS file: /cvs/src/sys/dev/wscons/wsmouse.c,v
retrieving revision 1.28
diff -u -p -r1.28 wsmouse.c
--- dev/wscons/wsmouse.c        10 Sep 2015 18:14:52 -0000      1.28
+++ dev/wscons/wsmouse.c        1 Dec 2015 22:17:53 -0000
@@ -92,6 +92,7 @@
 #include <dev/wscons/wscons_features.h>
 #include <dev/wscons/wsconsio.h>
 #include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>
 #include <dev/wscons/wseventvar.h>
 #include <dev/rndvar.h>

@@ -101,6 +102,9 @@

 #include <dev/wscons/wsmuxvar.h>

+#include "wsmouseinput_sc.h"
+#include "wsmouse_sc.h"
+
 #if defined(WSMUX_DEBUG) && NWSMUX > 0
 #define        DPRINTF(x)      if (wsmuxdebug) printf x
 #define        DPRINTFN(n,x)   if (wsmuxdebug > (n)) printf x
@@ -110,32 +114,6 @@ extern int wsmuxdebug;
 #define        DPRINTFN(n,x)
 #endif

-#define        INVALID_X       INT_MAX
-#define        INVALID_Y       INT_MAX
-#define        INVALID_Z       INT_MAX
-#define        INVALID_W       INT_MAX
-
-struct wsmouse_softc {
-       struct wsevsrc  sc_base;
-
-       const struct wsmouse_accessops *sc_accessops;
-       void            *sc_accesscookie;
-
-       u_int           sc_mb;          /* mouse button state */
-       u_int           sc_ub;          /* user button state */
-       int             sc_dx;          /* delta-x */
-       int             sc_dy;          /* delta-y */
-       int             sc_dz;          /* delta-z */
-       int             sc_dw;          /* delta-w */
-       int             sc_x;           /* absolute-x */
-       int             sc_y;           /* absolute-y */
-       int             sc_z;           /* absolute-z */
-       int             sc_w;           /* absolute-w */
-
-       int             sc_refcnt;
-       u_char          sc_dying;       /* device is being detached */
-};
-
 int    wsmouse_match(struct device *, void *, void *);
 void   wsmouse_attach(struct device *, struct device *, void *);
 int    wsmouse_detach(struct device *, int);
@@ -199,6 +177,8 @@ wsmouse_attach(struct device *parent, st
        sc->sc_accessops = ap->accessops;
        sc->sc_accesscookie = ap->accesscookie;

+       wsmouse_input_init(&sc->input, &sc->sc_base.me_evp);
+
 #if NWSMUX > 0
        sc->sc_base.me_ops = &wsmouse_srcops;
        mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux;
@@ -279,201 +259,9 @@ wsmouse_detach(struct device *self, int
        mn = self->dv_unit;
        vdevgone(maj, mn, mn, VCHR);

-       return (0);
-}
-
-void
-wsmouse_input(struct device *wsmousedev, u_int btns, /* 0 is up */
-    int x, int y, int z, int w, u_int flags)
-{
-       struct wsmouse_softc *sc = (struct wsmouse_softc *)wsmousedev;
-       struct wscons_event *ev;
-       struct wseventvar *evar;
-       int mb, ub, d, get, put, any;
-
-       add_mouse_randomness(x ^ y ^ z ^ w ^ btns);
-
-       /*
-        * Discard input if not ready.
-        */
-       evar = sc->sc_base.me_evp;
-       if (evar == NULL)
-               return;
-
-#ifdef DIAGNOSTIC
-       if (evar->q == NULL) {
-               printf("wsmouse_input: evar->q=NULL\n");
-               return;
-       }
-#endif
-
-#if NWSMUX > 0
-       DPRINTFN(5,("wsmouse_input: %s mux=%p, evar=%p\n",
-                   sc->sc_base.me_dv.dv_xname, sc->sc_base.me_parent, evar));
-#endif
-
-       sc->sc_mb = btns;
-       if (!(flags & WSMOUSE_INPUT_ABSOLUTE_X))
-               sc->sc_dx += x;
-       if (!(flags & WSMOUSE_INPUT_ABSOLUTE_Y))
-               sc->sc_dy += y;
-       if (!(flags & WSMOUSE_INPUT_ABSOLUTE_Z))
-               sc->sc_dz += z;
-       if (!(flags & WSMOUSE_INPUT_ABSOLUTE_W))
-               sc->sc_dw += w;
-
-       /*
-        * We have at least one event (mouse button, delta-X, or
-        * delta-Y; possibly all three, and possibly three separate
-        * button events).  Deliver these events until we are out
-        * of changes or out of room.  As events get delivered,
-        * mark them `unchanged'.
-        */
-       ub = sc->sc_ub;
-       any = 0;
-       get = evar->get;
-       put = evar->put;
-       ev = &evar->q[put];
-
-       /* NEXT prepares to put the next event, backing off if necessary */
-#define        NEXT                                                            
\
-       if ((++put) % WSEVENT_QSIZE == get) {                           \
-               put--;                                                  \
-               goto out;                                               \
-       }
-       /* ADVANCE completes the `put' of the event */
-#define        ADVANCE                                                         
\
-       ev++;                                                           \
-       if (put >= WSEVENT_QSIZE) {                                  \
-               put = 0;                                                \
-               ev = &evar->q[0];                                \
-       }                                                               \
-       any = 1
-       /* TIMESTAMP sets `time' field of the event to the current time */
-#define TIMESTAMP                                                      \
-       do {                                                            \
-               getnanotime(&ev->time);                                  \
-       } while (0)
-
-       if (flags & WSMOUSE_INPUT_ABSOLUTE_X) {
-               if (sc->sc_x != x) {
-                       NEXT;
-                       ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_X;
-                       ev->value = x;
-                       TIMESTAMP;
-                       ADVANCE;
-                       sc->sc_x = x;
-               }
-       } else {
-               if (sc->sc_dx) {
-                       NEXT;
-                       ev->type = WSCONS_EVENT_MOUSE_DELTA_X;
-                       ev->value = sc->sc_dx;
-                       TIMESTAMP;
-                       ADVANCE;
-                       sc->sc_dx = 0;
-               }
-       }
-       if (flags & WSMOUSE_INPUT_ABSOLUTE_Y) {
-               if (sc->sc_y != y) {
-                       NEXT;
-                       ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_Y;
-                       ev->value = y;
-                       TIMESTAMP;
-                       ADVANCE;
-                       sc->sc_y = y;
-               }
-       } else {
-               if (sc->sc_dy) {
-                       NEXT;
-                       ev->type = WSCONS_EVENT_MOUSE_DELTA_Y;
-                       ev->value = sc->sc_dy;
-                       TIMESTAMP;
-                       ADVANCE;
-                       sc->sc_dy = 0;
-               }
-       }
-       if (flags & WSMOUSE_INPUT_ABSOLUTE_Z) {
-               if (sc->sc_z != z) {
-                       NEXT;
-                       ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_Z;
-                       ev->value = z;
-                       TIMESTAMP;
-                       ADVANCE;
-                       sc->sc_z = z;
-               }
-       } else {
-               if (sc->sc_dz) {
-                       NEXT;
-                       ev->type = WSCONS_EVENT_MOUSE_DELTA_Z;
-                       ev->value = sc->sc_dz;
-                       TIMESTAMP;
-                       ADVANCE;
-                       sc->sc_dz = 0;
-               }
-       }
-       if (flags & WSMOUSE_INPUT_ABSOLUTE_W) {
-               if (sc->sc_w != w) {
-                       NEXT;
-                       ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_W;
-                       ev->value = w;
-                       TIMESTAMP;
-                       ADVANCE;
-                       sc->sc_w = w;
-               }
-       } else {
-               if (sc->sc_dw) {
-                       NEXT;
-                       ev->type = WSCONS_EVENT_MOUSE_DELTA_W;
-                       ev->value = sc->sc_dw;
-                       TIMESTAMP;
-                       ADVANCE;
-                       sc->sc_dw = 0;
-               }
-       }
-
-       mb = sc->sc_mb;
-       while ((d = mb ^ ub) != 0) {
-               /*
-                * Mouse button change.  Find the first change and drop
-                * it into the event queue.
-                */
-               NEXT;
-               ev->value = ffs(d) - 1;
-
-               KASSERT(ev->value >= 0);
-
-               d = 1 << ev->value;
-               ev->type =
-                   (mb & d) ? WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP;
-               TIMESTAMP;
-               ADVANCE;
-               ub ^= d;
-       }
+       wsmouse_input_cleanup(&sc->input);

-       NEXT;
-       ev->type = WSCONS_EVENT_SYNC;
-       ev->value = 0;
-       TIMESTAMP;
-       ADVANCE;
-
-#undef TIMESTAMP
-#undef ADVANCE
-#undef NEXT
-
-out:
-       if (any) {
-               sc->sc_ub = ub;
-               evar->put = put;
-               WSEVENT_WAKEUP(evar);
-#ifdef HAVE_BURNER_SUPPORT
-               /* wsdisplay_burn(sc->sc_displaydv, WSDISPLAY_BURN_MOUSE); */
-#endif
-#if NWSMUX > 0
-               DPRINTFN(5,("wsmouse_input: %s wakeup evar=%p\n",
-                           sc->sc_base.me_dv.dv_xname, evar));
-#endif
-       }
+       return (0);
 }

 int
@@ -564,10 +352,6 @@ int
 wsmousedoopen(struct wsmouse_softc *sc, struct wseventvar *evp)
 {
        sc->sc_base.me_evp = evp;
-       sc->sc_x = INVALID_X;
-       sc->sc_y = INVALID_Y;
-       sc->sc_z = INVALID_Z;
-       sc->sc_w = INVALID_W;

        /* enable the device, and punt if that's not possible */
        return (*sc->sc_accessops->enable)(sc->sc_accesscookie);
Index: dev/wscons/wsmouse_sc.h
===================================================================
RCS file: dev/wscons/wsmouse_sc.h
diff -N dev/wscons/wsmouse_sc.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/wscons/wsmouse_sc.h     1 Dec 2015 22:17:53 -0000
@@ -0,0 +1,17 @@
+
+#ifndef _WSMOUSE_SC_H_
+#define _WSMOUSE_SC_H_
+
+struct wsmouse_softc {
+       struct wsevsrc  sc_base;
+
+       const struct wsmouse_accessops *sc_accessops;
+       void            *sc_accesscookie;
+
+       int             sc_refcnt;
+       u_char          sc_dying;       /* device is being detached */
+
+       struct wsmouseinput input;
+};
+
+#endif /* _WSMOUSE_SC_H_ */
Index: dev/wscons/wsmouseinput.c
===================================================================
RCS file: dev/wscons/wsmouseinput.c
diff -N dev/wscons/wsmouseinput.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/wscons/wsmouseinput.c   1 Dec 2015 22:17:53 -0000
@@ -0,0 +1,1068 @@
+/*
+ * Copyright (c) 2015 Ulf Brosziewski
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * wsmouse input processing
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/signalvar.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wseventvar.h>
+#include <dev/wscons/wsmouseinput.h>
+#include <dev/rndvar.h>
+
+#include "wsmux.h"
+#include "wsdisplay.h"
+#include "wskbd.h"
+#include <dev/wscons/wsmuxvar.h>
+#include "wsmouseinput_sc.h"
+#include "wsmouse_sc.h"
+
+
+void
+wsmouse_buttons(struct device *sc, u_int buttons)
+{
+       struct btn_state *btn =
+           &((struct wsmouse_softc *) sc)->input.btn;
+
+       if (btn->sync)
+               /* Restore the old state. */
+               btn->buttons ^= btn->sync;
+
+       btn->sync = btn->buttons ^ buttons;
+       btn->buttons = buttons;
+}
+
+void
+wsmouse_motion(struct device *sc, int dx, int dy, int dz, int dw)
+{
+       struct motion_state *motion =
+           &((struct wsmouse_softc *) sc)->input.motion;
+
+       motion->dx = dx;
+       motion->dy = dy;
+       motion->dz = dz;
+       motion->dw = dw;
+       if (dx || dy || dz || dw)
+               motion->sync |= SYNC_DELTAS;
+}
+
+/*
+ * Handle absolute coordinates.
+ *
+ * x_delta/y_delta are used by touchpad code. The values are
+ * only valid if the SYNC-flags are set, and will be cleared
+ * by update- or conversion-functions if a touch shouldn't
+ * trigger pointer motion.
+ */
+void
+wsmouse_position(struct device *sc, int x, int y)
+{
+       struct motion_state *motion =
+           &((struct wsmouse_softc *) sc)->input.motion;
+       int delta;
+
+       delta = x - motion->x;
+       if (delta) {
+               motion->x = x;
+               motion->sync |= SYNC_X;
+               motion->x_delta = delta;
+       }
+       delta = y - motion->y;
+       if (delta) {
+               motion->y = y;
+               motion->sync |= SYNC_Y;
+               motion->y_delta = delta;
+       }
+}
+
+static __inline int
+filter_pressure(struct wsmouseinput *input, int pressure)
+{
+       int limit = imax(input->touch.min_pressure, 1);
+
+       if (pressure >= limit)
+               return pressure;
+       else
+               return (pressure < 0 ? limit : 0);
+}
+
+void
+wsmouse_touch(struct device *sc, int pressure, int fingers)
+{
+       struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+       struct touch_state *touch = &input->touch;
+
+       pressure = filter_pressure(input, pressure);
+       if (pressure == 0 || pressure != touch->pressure) {
+               /*
+                * pressure == 0: Drivers may report arbitrary coordinates
+                * in this case; touch_update() will correct them.
+                */
+               touch->pressure = pressure;
+               touch->sync |= SYNC_PRESSURE;
+       }
+       if (fingers != touch->fingers) {
+               touch->prev_fingers = touch->fingers;
+               touch->fingers = fingers;
+               touch->sync |= SYNC_FINGERS;
+       }
+}
+
+void
+wsmouse_mtstate(struct device *sc, int slot, int x, int y, int pressure)
+{
+       struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+       struct mt_state *mt = &input->mt;
+       struct mt_slot *mts;
+       u_int flag;
+       int initial;
+
+       if (slot < 0 || slot >= mt->num_slots)
+               return;
+
+       flag = (1 << slot);
+       mt->frame |= flag;
+
+       /* Is this a new touch? */
+       initial = ((mt->touches & flag) == (mt->sync[MTS_TOUCH] & flag));
+
+       mts = &mt->slots[slot];
+       if (x != mts->x || initial) {
+               mt->sync[MTS_X] |= flag;
+               mts->x = x;
+       }
+       if (y != mts->y || initial) {
+               mt->sync[MTS_Y] |= flag;
+               mts->y = y;
+       }
+       pressure = filter_pressure(input, pressure);
+       if (pressure != mts->pressure) {
+               mt->sync[MTS_PRESSURE] |= flag;
+               mts->pressure = pressure;
+
+               if (pressure) {
+                       if ((mt->touches & flag) == 0) {
+                               mt->num_touches++;
+                               mt->touches |= flag;
+                               mt->sync[MTS_TOUCH] |= flag;
+                       }
+               } else if (mt->touches & flag) {
+                       mt->num_touches--;
+                       mt->touches ^= flag;
+                       mt->sync[MTS_TOUCH] |= flag;
+               }
+       }
+}
+
+void
+wsmouse_set(struct device *sc, enum wsmouseval type, int value, int aux)
+{
+       struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+       struct mt_slot *mts;
+
+       if (WSMOUSE_IS_MT_CODE(type)) {
+               if (aux < 0 || aux >= input->mt.num_slots)
+                       return;
+               mts = &input->mt.slots[aux];
+       }
+
+       switch (type) {
+       case WSMOUSE_REL_X:
+               value += input->motion.x; /* fall through */
+       case WSMOUSE_ABS_X:
+               wsmouse_position(sc, value, input->motion.y);
+               return;
+       case WSMOUSE_REL_Y:
+               value += input->motion.y;
+       case WSMOUSE_ABS_Y:
+               wsmouse_position(sc, input->motion.x, value);
+               return;
+       case WSMOUSE_PRESSURE:
+               wsmouse_touch(sc, value, input->touch.fingers);
+               return;
+       case WSMOUSE_FINGERS:
+               wsmouse_touch(sc, input->touch.pressure, value);
+               return;
+       case WSMOUSE_FINGERWIDTH:
+               if (value != input->touch.fingerwidth) {
+                       input->touch.fingerwidth = value;
+                       input->touch.sync |= SYNC_FINGERWIDTH;
+               }
+               return;
+       case WSMOUSE_MT_REL_X:
+               value += mts->x; /* fall through */
+       case WSMOUSE_MT_ABS_X:
+               wsmouse_mtstate(sc, aux, value, mts->y, mts->pressure);
+               return;
+       case WSMOUSE_MT_REL_Y:
+               value += mts->y;
+       case WSMOUSE_MT_ABS_Y:
+               wsmouse_mtstate(sc, aux, mts->x, value, mts->pressure);
+               return;
+       case WSMOUSE_MT_PRESSURE:
+               wsmouse_mtstate(sc, aux, mts->x, mts->y, value);
+               return;
+       }
+}
+
+/* Make touch and motion state consistent. */
+void
+wsmouse_touch_update(struct wsmouseinput *input)
+{
+       struct motion_state *motion = &input->motion;
+       struct touch_state *touch = &input->touch;
+
+       if (touch->pressure == 0) {
+               /* Restore valid coordinates. */
+               if (motion->sync & SYNC_X)
+                       motion->x -= motion->x_delta;
+               if (motion->sync & SYNC_Y)
+                       motion->y -= motion->y_delta;
+               /* Don't generate motion/position events. */
+               motion->sync &= ~SYNC_POSITION;
+       }
+       if (touch->sync & SYNC_FINGERS)
+               /* Suppress pointer motion. */
+               motion->x_delta = motion->y_delta = 0;
+
+       if ((touch->sync & SYNC_PRESSURE) && touch->min_pressure) {
+               if (touch->pressure >= input->params.pressure_hi)
+                       touch->min_pressure = input->params.pressure_lo;
+               else if (touch->pressure < input->params.pressure_lo)
+                       touch->min_pressure = input->params.pressure_hi;
+       }
+}
+
+/* Normalize multitouch state. */
+static __inline void
+wsmouse_mt_update(struct wsmouseinput *input)
+{
+       int i;
+
+       /*
+        * The same as above: there may be arbitrary coordinates if
+        * (pressure == 0). Clear the flags for touches that have
+        * been released.
+        */
+       if (input->mt.sync[MTS_TOUCH] & ~input->mt.touches) {
+               for (i = MTS_X; i < MTS_SIZE; i++)
+                       input->mt.sync[i] &= input->mt.touches;
+       }
+}
+
+/*
+ * Select the pointer-controlling MT slot.
+ *
+ * This function assigns pointer-control to slots with non-zero motion
+ * deltas if at least one such slot exists. It doesn't impose any
+ * restrictions on the way drivers use wsmouse_mtstate(), it covers
+ * partial, unordered, and "delta-filtered" input.
+ *
+ * The "cycle" is the set of slots with X/Y updates in previous sync
+ * operations; it will be cleared and rebuilt whenever an updated slot
+ * is already a member. If a cycle ends that doesn't contain the
+ * pointer-controlling slot, a new one will be selected.
+ */
+
+void
+wsmouse_ptr_ctrl(struct mt_state *mt)
+{
+       u_int updates;
+       int select, slot;
+
+       mt->prev_ptr = mt->ptr;
+
+       if (mt->num_touches <= 1) {
+               mt->ptr = mt->touches;
+               mt->ptr_cycle = mt->ptr;
+               return;
+       }
+
+       /*
+        * If there is no pointer-controlling slot or it is inactive,
+        * select a new one.
+        */
+       select = ((mt->ptr & mt->touches) == 0);
+
+       /* Remove slots without X/Y deltas from the cycle. */
+       updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH];
+       mt->ptr_cycle &= ~(mt->frame ^ updates);
+
+       if (mt->ptr_cycle & updates) {
+               select |= ((mt->ptr_cycle & mt->ptr) == 0);
+               mt->ptr_cycle = updates;
+       } else {
+               mt->ptr_cycle |= updates;
+       }
+       if (select) {
+               slot = (mt->ptr_cycle ?
+                   ffs(mt->ptr_cycle) - 1 : ffs(mt->touches) - 1);
+               mt->ptr = (1 << slot);
+       }
+}
+
+/* Derive touch and motion state from MT state. */
+void
+wsmouse_mt_convert(struct device *sc)
+{
+       struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+       struct mt_state *mt = &input->mt;
+       struct mt_slot *mts;
+       int slot, pressure;
+
+       wsmouse_ptr_ctrl(mt);
+
+       if (mt->ptr) {
+               slot = ffs(mt->ptr) - 1;
+               mts = &mt->slots[slot];
+               wsmouse_position(sc, mts->x, mts->y);
+               if (mt->ptr != mt->prev_ptr)
+                       /* Suppress pointer motion. */
+                       input->motion.x_delta = input->motion.y_delta = 0;
+               pressure = mts->pressure;
+       } else
+               pressure = 0;
+
+       wsmouse_touch(sc, pressure, mt->num_touches);
+}
+
+void
+wsmouse_evq_put(struct evq_access *evq, int ev_type, int ev_value)
+{
+       struct wscons_event *ev;
+       int space;
+
+       space = evq->evar->get - evq->put;
+       if (space != 1 && space != 1 - WSEVENT_QSIZE) {
+               ev = &evq->evar->q[evq->put++];
+               evq->put %= WSEVENT_QSIZE;
+               ev->type = ev_type;
+               ev->value = ev_value;
+               timespeccpy(&ev->time, &evq->ts);
+               evq->result |= EVQ_RESULT_SUCCESS;
+       } else
+               evq->result = EVQ_RESULT_OVERFLOW;
+}
+
+
+static __inline void
+wsmouse_btn_sync(struct wsmouseinput *input, struct evq_access *evq)
+{
+       struct btn_state *btn = &input->btn;
+       int button, ev_type;
+       u_int flag;
+
+       while (btn->sync) {
+               button = ffs(btn->sync) - 1;
+               flag = (1 << button);
+               btn->sync ^= flag;
+               ev_type = (btn->buttons & flag) ? BTN_DOWN_EV : BTN_UP_EV;
+               wsmouse_evq_put(evq, ev_type, button);
+       }
+}
+
+/*
+ * Scale with a [*.12] fixed-point factor and a remainder:
+ */
+static __inline int
+scale(int val, int factor, int *rmdr)
+{
+       val = val * factor + *rmdr;
+       if (val >= 0) {
+               *rmdr = val & 0xfff;
+               return (val >> 12);
+       } else {
+               *rmdr = -(-val & 0xfff);
+               return -(-val >> 12);
+       }
+}
+
+void
+wsmouse_motion_sync(struct wsmouseinput *input, struct evq_access *evq)
+{
+       struct motion_state *motion = &input->motion;
+       struct wsmouseparams *params = &input->params;
+       struct axis_filter *fltr;
+       int x, y, dx, dy;
+
+       if (motion->sync & SYNC_DELTAS) {
+               dx = params->x_inv ? -motion->dx : motion->dx;
+               dy = params->y_inv ? -motion->dy : motion->dy;
+               if (input->flags & SCALE_DELTAS) {
+                       fltr = &input->fltr.h;
+                       dx = scale(dx, fltr->scale, &fltr->rmdr);
+                       fltr = &input->fltr.v;
+                       dy = scale(dy, fltr->scale, &fltr->rmdr);
+               }
+               if (dx)
+                       wsmouse_evq_put(evq, DELTA_X_EV(input->flags), dx);
+               if (dy)
+                       wsmouse_evq_put(evq, DELTA_Y_EV(input->flags), dy);
+               if (motion->dz)
+                       wsmouse_evq_put(evq, DELTA_Z_EV, motion->dz);
+               if (motion->dw)
+                       wsmouse_evq_put(evq, DELTA_W_EV, motion->dw);
+       }
+       if (motion->sync & SYNC_POSITION) {
+               if (motion->sync & SYNC_X) {
+                       x = motion->x;
+                       if (params->x_inv)
+                               x = params->x_inv - x;
+                       wsmouse_evq_put(evq, ABS_X_EV(input->flags), x);
+               }
+               if (motion->sync & SYNC_Y) {
+                       y = motion->y;
+                       if (params->y_inv)
+                               y = params->y_inv - y;
+                       wsmouse_evq_put(evq, ABS_Y_EV(input->flags), y);
+               }
+               if (motion->x_delta == 0 && motion->y_delta == 0
+                   && (input->flags & TPAD_NATIVE_MODE))
+                       /* Suppress pointer motion. */
+                       wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_UPDATE, 0);
+       }
+
+       motion->sync = 0;
+}
+
+void
+wsmouse_touch_sync(struct wsmouseinput *input, struct evq_access *evq)
+{
+       struct touch_state *touch = &input->touch;
+
+       if (touch->sync & SYNC_PRESSURE)
+               wsmouse_evq_put(evq, ABS_Z_EV, touch->pressure);
+       if (touch->sync & SYNC_FINGERS)
+               wsmouse_evq_put(evq, ABS_W_EV, touch->fingers);
+       if ((touch->sync & SYNC_FINGERWIDTH)
+           && (input->flags & TPAD_NATIVE_MODE))
+               wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_WIDTH,
+                   touch->fingerwidth);
+
+       touch->sync = 0;
+}
+
+void
+wsmouse_mt_sync(struct wsmouseinput *input, struct evq_access *evq)
+{
+       int i;
+
+       /* We don't generate MT events yet. */
+
+       input->mt.frame = 0;
+       for (i = 0; i < MTS_SIZE; i++)
+               input->mt.sync[i] = 0;
+}
+
+/*
+ * The touchpad-specific motion filters are "hysteresis" and
+ * deceleration.
+ *
+ * With hysteresis, the sum of the output deltas will lag behind the
+ * input deltas within a window bounded by +[hysteresis threshold] and
+ * -[hysteresis threshold]. It may suppress accidental movements when
+ * a touch starts or ends, or when small corrections with changing
+ * directions are made. As in the X synaptics driver, the default
+ * threshold is 0.5 percent of the diagonal of the touchpad surface.
+ * The fields fltr.h.hyst and fltr.v.hyst accumulate the deltas up to
+ * the threshold values.
+ *
+ * The default deceleration options of X don't work convincingly with
+ * touchpads (the synaptics driver installs its own "acceleration
+ * profile" and callback function). As a preliminary workaround, this
+ * filter applies a simple deceleration scheme to small deltas. Based
+ * on an approximation to the distance, it assigns a "magnitude" to a
+ * delta pair. A value of 8 corresponds to the number of units defined
+ * by the 'dclr'-parameter; deltas smaller than 7 will be downscaled by
+ * the factor 2/8, deltas with magnitudes from 7 to 11 by factors ranging
+ * from 3/8 to 7/8.
+ */
+
+#define HYSTERESIS(acc, delta, thr)                            \
+       do {                                                    \
+               (acc) += (delta);                               \
+               if ((acc) > (thr))                           \
+                       (delta) = ((acc) - (thr));              \
+               else if ((acc) < -(thr))                     \
+                       (delta) = ((acc) + (thr));              \
+               else                                            \
+                       (delta) = 0;                            \
+               (acc) -= (delta);                               \
+       } while (0)
+
+
+static __inline void
+compat_filter(struct wsmouseinput *input, int *dx, int *dy)
+{
+       struct wsmouseparams *params = &input->params;
+       int mag, h_mag, v_mag;
+
+       if ((input->touch.pressure == 0)
+           || (params->dx_max && abs(*dx) > params->dx_max)
+           || (params->dy_max && abs(*dy) > params->dy_max)) {
+
+               *dx = *dy = 0;
+       }
+
+       if (input->touch.sync & SYNC_FINGERS)
+               input->fltr.h.hyst = input->fltr.v.hyst = 0;
+
+       HYSTERESIS(input->fltr.h.hyst, *dx, params->h_hysteresis);
+       HYSTERESIS(input->fltr.v.hyst, *dy, params->v_hysteresis);
+
+       if ((*dx || *dy) && (input->flags & DECELERATE)) {
+               h_mag = abs(*dx) * input->fltr.h.mag_scale;
+               v_mag = abs(*dy) * input->fltr.v.mag_scale;
+               mag = (h_mag >= v_mag
+                   ? h_mag + v_mag / 2 : v_mag + h_mag / 2);
+
+               /* Don't change deceleration levels abruptly. */
+               mag = (mag + 7 * input->fltr.mag) / 8;
+               /* Don't use arbitrarily high weights. */
+               input->fltr.mag = imin(mag, 24 << 12);
+
+               mag = imax((mag >> 12) - 4, 2);
+               if (mag < 8) {
+                       *dx = *dx * mag + input->fltr.h.dclr_rmdr;
+                       input->fltr.h.dclr_rmdr =
+                           *dx >= 0 ? *dx & 7 : -(-*dx & 7);
+                       *dx /= 8;
+                       *dy = *dy * mag + input->fltr.v.dclr_rmdr;
+                       input->fltr.v.dclr_rmdr =
+                           *dy >= 0 ? *dy & 7 : -(-*dy & 7);
+                       *dy /= 8;
+               }
+       }
+
+}
+
+/*
+ * Convert absolute touchpad input (compatibility mode).
+ */
+void
+wsmouse_compat_convert(struct device *sc, struct evq_access *evq)
+{
+       struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+       int dx, dy, dz, dw;
+
+       /* 'prev_fingers' is only valid with the sync-flag. */
+       if ((input->touch.sync & SYNC_FINGERS) == 0)
+               input->touch.prev_fingers = input->touch.fingers;
+
+       if (input->touch.pressure == 0) {
+               input->motion.sync &= ~SYNC_POSITION;
+               input->touch.fingers = 0;
+               if (input->touch.prev_fingers)
+                       input->touch.sync |= SYNC_FINGERS;
+               else
+                       input->touch.sync &= ~SYNC_FINGERS;
+       }
+
+       dx = (input->motion.sync & SYNC_X) ? input->motion.x_delta : 0;
+       dy = (input->motion.sync & SYNC_Y) ? input->motion.y_delta : 0;
+       dz = (input->motion.sync & SYNC_DELTAS) ? input->motion.dz : 0;
+       dw = (input->motion.sync & SYNC_DELTAS) ? input->motion.dw : 0;
+
+       compat_filter(input, &dx, &dy);
+       wsmouse_motion(sc, dx, dy, dz, dw);
+
+       if (input->tp != NULL)
+               wstpad_process_input(input, evq);
+
+       input->motion.sync &= ~SYNC_POSITION;
+       input->touch.sync = 0;
+       if (input->mt.frame)
+               wsmouse_mt_sync(input, NULL);
+}
+
+/*
+ * Try to determine the average interval between two updates. Various
+ * conditions are checked in order to ensure that only valid samples
+ * enter into the calculation. However, the approach might be too
+ * simplistic for various reasons. MT devices may need more than one
+ * packet to transmit their state if there are multiple touches. In this
+ * case, the update interval per slot will be higher than for a single
+ * touch. And the change isn't necessarily proportional to the number
+ * of packets that belong to a frame (Elantech-v4 touchpads, for
+ * example, seem to send their packets at a higher frequency when more
+ * than one "motion packet" is needed).
+ */
+void
+wsmouse_track_interval(struct wsmouseinput *input, struct timespec *now)
+{
+       static const struct timespec limit = { 0, 30 * 1000000L };
+       struct timespec ts;
+       int samples;
+
+       if (input->motion.sync == 0
+           || (input->touch.sync & SYNC_FINGERS)
+           || (input->touch.fingers > 1)) {
+               input->interval.track = 0;
+               return;
+       }
+       if (input->interval.track) {
+               timespeccpy(&ts, now);
+               timespecsub(&ts, &input->interval.ts, &ts);
+               if (timespeccmp(&ts, &limit, <)) {
+                       input->interval.sum += ts.tv_nsec >> 12;
+                       samples = ++input->interval.samples;
+                       /*
+                        * First make a quick calculation and later a more
+                        * reliable one:
+                        */
+                       if (samples == 16) {
+                               input->interval.avg = input->interval.sum << 8;
+                               wsmouse_init_deceleration(input);
+                       } else if (samples == 128) {
+                               input->interval.avg = input->interval.sum << 5;
+                               wsmouse_init_deceleration(input);
+                               input->interval.samples = 0;
+                               input->interval.sum = 0;
+                               input->flags &= ~TRACK_INTERVAL;
+                       }
+               }
+       }
+       timespeccpy(&input->interval.ts, now);
+       input->interval.track = 1;
+}
+
+int
+wsmouse_input_sync(struct device *sc)
+{
+       struct wsmouseinput *input =
+            &((struct wsmouse_softc *) sc)->input;
+       struct evq_access evq;
+
+       evq.evar = *input->evar;
+       if (evq.evar == NULL)
+               return (-1);
+       evq.put = evq.evar->put;
+       evq.result = EVQ_RESULT_NONE;
+       getnanotime(&evq.ts);
+
+       add_mouse_randomness(input->btn.buttons ^
+           input->motion.dx ^ input->motion.dy ^
+           input->motion.x ^ input->motion.y ^
+           input->motion.dz ^ input->motion.dw);
+
+       if (input->mt.frame) {
+               wsmouse_mt_update(input);
+               wsmouse_mt_convert(sc);
+       }
+       if (input->touch.sync)
+               wsmouse_touch_update(input);
+
+       if (input->flags & TRACK_INTERVAL)
+               wsmouse_track_interval(input, &evq.ts);
+
+       if (input->flags & TPAD_COMPAT_MODE)
+               wsmouse_compat_convert(sc, &evq);
+
+       if (input->btn.sync)
+               wsmouse_btn_sync(input, &evq);
+       if (input->motion.sync)
+               wsmouse_motion_sync(input, &evq);
+       if (input->touch.sync)
+               wsmouse_touch_sync(input, &evq);
+       if (input->mt.frame)
+               wsmouse_mt_sync(input, &evq);
+
+       if (evq.result == EVQ_RESULT_SUCCESS) {
+               wsmouse_evq_put(&evq, WSCONS_EVENT_SYNC, 0);
+               if (evq.result == EVQ_RESULT_SUCCESS){
+                       evq.evar->put = evq.put;
+                       WSEVENT_WAKEUP(evq.evar);
+               }
+       }
+
+       return (evq.result < 0 ? -1 : 0);
+}
+
+int
+wsmouse_id_to_slot(struct device *sc, int id)
+{
+       struct wsmouseinput *input =
+           &((struct wsmouse_softc *) sc)->input;
+       struct mt_state *mt = &input->mt;
+       int slot;
+
+       FOREACHBIT(mt->touches, slot) {
+               if (mt->slots[slot].id == id)
+                       return slot;
+       }
+       if (mt->num_touches < mt->num_slots) {
+               slot = ffs(~mt->touches) - 1;
+               mt->slots[slot].id = id;
+               return slot;
+       } else
+               return (-1);
+}
+
+/*
+ * Find a minimum weight matching for an m-by-n matrix.
+ *
+ * n must be less than or equal to m. The size of the buffer must be at
+ * least 3m + 3n.
+ *
+ * On return, the first m elements of the buffer contain the row-to-
+ * column mappings, i.e., buffer[i] is the column index for row i, or -1
+ * if there is no assignment for that row (which may happen if n < m).
+ *
+ * The function applies the Dinic-Kronrod algorithm, with the following
+ * modification of its original form: There is no initial search for row
+ * minima, the rows are initially assigned to a "virtual" column with
+ * the index -1 and zero-weights. This permits inputs with n < m
+ * (moreover, tracking the number of assignments per column isn't
+ * necessary because a chain of alternative assignments always ends in
+ * the virtual column).
+ */
+void
+wsmouse_matching(int *matrix, int m, int n, int *buffer) {
+       int i, j, d, e, col, delta, altr, altc;
+       int *row;
+       int *r2c = buffer;      /* row-to-column assignments */
+       int *alt = r2c + m;     /* alternative assignments */
+       int *cv = alt + m;      /* the column vector (row minima within cs) */
+       int *cs = cv + m;       /* the column set */
+       int *red = cs + n;      /* column reduction */
+       int *c2r = red + n;     /* column-to-row assignments in cs */
+       int nc;                 /* the number of columns in cs */
+
+       for (i = 0; i < m; r2c[i++] = -1) {}
+       for (i = 0; i < n; red[i++] = 0) {}
+
+       for (col = 0; col < n; col++) {
+               j = col;
+               nc = 0;
+               for (i = 0; i < m; alt[i++] = -1) {}
+
+               while (1) {
+                       cs[nc++] = j;
+                       altr = -1;
+                       for (i = 0, row = matrix; i < m; i++, row += n) {
+                               if (alt[i] >= 0)
+                                       continue;
+                               d = row[j] - red[j];
+                               if (j == col ||
+                                   d < (e = row[cv[i]] - red[cv[i]]))
+                                       cv[i] = j;
+                               else
+                                       d = e;
+                               if (r2c[i] >= 0)
+                                       d -= row[r2c[i]] - red[r2c[i]];
+                               if (altr == -1 || d < delta) {
+                                       altr = i;
+                                       altc = cv[i];
+                                       delta = d;
+                               }
+                       }
+                       for (i = 0; i < nc; i++)
+                               red[cs[i]] += delta;
+
+                       j = r2c[altr];
+                       if (j >= 0) {
+                               alt[altr] = altc;
+                               c2r[j] = altr;
+                       } else
+                               break;
+               }
+               while ((r2c[altr] = altc) != col) {
+                       altr = c2r[altc];
+                       altc = alt[altr];
+               }
+       }
+}
+
+void
+wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int size)
+{
+       struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+       struct mt_state *mt = &input->mt;
+       int i, j, slot, dx, dy;
+       int *p, *map;
+
+       if (mt->num_slots == 0 || mt->matrix == NULL)
+               return;
+
+       size = imin(size, mt->num_slots);
+       p = mt->matrix;
+       if (mt->num_touches >= size) {
+               FOREACHBIT(mt->touches, slot)
+                       for (j = 0; j < size; j++) {
+                               dx = pt[j].x - mt->slots[slot].x;
+                               dy = pt[j].y - mt->slots[slot].y;
+                               *p++ = dx * dx + dy * dy;
+                       }
+               wsmouse_matching(mt->matrix, mt->num_touches, size, p);
+               /* TODO: Check the distances in the matching. */
+               FOREACHBIT(mt->touches, slot)
+                       if ((j = *p++) >= 0) {
+                               wsmouse_mtstate(sc, slot,
+                                   pt[j].x, pt[j].y, pt[j].pressure);
+                               pt[j].slot = slot;
+                       } else
+                               wsmouse_mtstate(sc, slot, 0, 0, 0);
+       } else {
+               for (j = 0; j < size; j++)
+                       FOREACHBIT(mt->touches, slot) {
+                               dx = pt[j].x - mt->slots[slot].x;
+                               dy = pt[j].y - mt->slots[slot].y;
+                               *p++ = dx * dx + dy * dy;
+                       }
+               wsmouse_matching(mt->matrix, size, mt->num_touches, p);
+               map = p + size;
+               i = 0;
+               FOREACHBIT(mt->touches, slot)
+                       map[i++] = slot;
+               /* TODO: Check the distances in the matching. */
+               for (j = 0; j < size; j++) {
+                       if ((i = *p++) >= 0)
+                               slot = map[i];
+                       else
+                               slot = ffs(~mt->touches) - 1;
+                       wsmouse_mtstate(sc, slot,
+                           pt[j].x, pt[j].y, pt[j].pressure);
+                       pt[j].slot = slot;
+               }
+       }
+}
+
+/* wsmouse_matching needs six additional rows. */
+#define MATRIX_SIZE(n) (((n) + 6) * (n) * sizeof(int))
+
+static __inline void
+free_mt_slots(struct wsmouseinput *input)
+{
+       int n, size;
+
+       if ((n = input->mt.num_slots)) {
+               size = n * sizeof(struct mt_slot);
+               if (input->hw.flags & WSMHW_MT_TRACKING)
+                       size += MATRIX_SIZE(n);
+               free(input->mt.slots, M_DEVBUF, size);
+               input->mt.num_slots = 0;
+               input->mt.slots = NULL;
+               input->mt.matrix = NULL;
+       }
+}
+
+/* Allocate the MT slots and, if necessary, the buffers for MT tracking. */
+static __inline int
+init_mt_slots(struct wsmouseinput *input)
+{
+       int n, size;
+
+       n = imin(input->hw.mt_slots, WSMOUSE_MT_SLOTS_MAX);
+       if (n != input->mt.num_slots) {
+               free_mt_slots(input);
+               if (n) {
+                       size = n * sizeof(struct mt_slot);
+                       if (input->hw.flags & MT_TRACKING)
+                               size += MATRIX_SIZE(n);
+                       input->mt.slots = malloc(size,
+                           M_DEVBUF, M_WAITOK | M_ZERO);
+                       if (input->mt.slots != NULL) {
+                               input->mt.num_slots = n;
+                               if (input->flags & MT_TRACKING)
+                                       input->mt.matrix = (int *)
+                                           (input->mt.slots + n);
+                       }
+               }
+       }
+       return -(n != input->mt.num_slots);
+}
+
+#undef MATRIX_SIZE
+
+void
+wsmouse_init_scaling(struct wsmouseinput *input)
+{
+       struct wsmouseparams *params = &input->params;
+       int m, n;
+
+       if (params->dx_mul || params->dx_div
+           || params->dy_mul || params->dy_div) {
+               /*
+                * The scale factors for the output deltas
+                * have a [*.12] fixed point format.
+                */
+               m = (params->dx_mul ? abs(params->dx_mul) : 1);
+               n = (params->dx_div ? abs(params->dx_div) : 1);
+               input->fltr.h.scale = (m << 12) / n;
+               input->fltr.h.rmdr = 0;
+               m = (params->dy_mul ? abs(params->dy_mul) : 1);
+               n = (params->dy_div ? abs(params->dy_div): 1);
+               input->fltr.v.scale = (m << 12) / n;
+               input->fltr.v.rmdr = 0;
+               input->flags |= SCALE_DELTAS;
+       } else
+               input->flags &= ~SCALE_DELTAS;
+}
+
+void
+wsmouse_init_deceleration(struct wsmouseinput *input)
+{
+       int dclr = input->params.dclr;
+       int n;
+
+       if (dclr == 0) {
+               input->flags &= ~DECELERATE;
+               return;
+       }
+       dclr = imax(dclr, 4);
+
+       /*
+        * For a standard update rate of 80Hz, (dclr) units will be
+        * mapped to a magnitude of 8.
+        */
+       if (input->interval.avg == 0)
+               n = 8;
+       else
+               n = 8 * 13000000 / lmax(lmin(
+                   input->interval.avg, 26000000), 3250000);
+       input->fltr.h.mag_scale = (n << 12) / dclr;
+       input->fltr.v.mag_scale = n * input->fltr.ratio / dclr;
+       input->fltr.h.dclr_rmdr = 0;
+       input->fltr.v.dclr_rmdr = 0;
+       input->flags |= DECELERATE;
+}
+
+
+int
+wsmouse_configure(struct device *sc, u_int opts, struct wsmousehw *hw)
+{
+       struct wsmouseinput *input =
+            &((struct wsmouse_softc *) sc)->input;
+
+       if (hw != NULL)
+               memcpy(&input->hw, hw, sizeof(struct wsmousehw));
+
+       if (input->hw.x_max && input->hw.y_max) {
+               if (input->hw.flags & WSMHW_LR_DOWN)
+                       input->params.y_inv =
+                           input->hw.y_max + input->hw.y_min;
+       }
+
+       if (init_mt_slots(input)) {
+               printf("wsmouseinput: MT initialization failed.\n");
+               return (-1);
+       }
+
+       input->fltr.ratio = 1 << 12;
+       if (imax(input->hw.h_res, 0) && imax(input->hw.v_res, 0)) {
+               input->fltr.ratio *= input->hw.h_res;
+               input->fltr.ratio /= input->hw.v_res;
+       }
+
+       input->options = opts;
+
+       if (input->hw.hw_type == WSMHW_TOUCHPAD
+           || input->hw.hw_type == WSMHW_CLICKPAD) {
+
+               input->flags &= ~TPAD_NATIVE_MODE;
+               input->flags |= TPAD_COMPAT_MODE;
+               if (wstpad_configure_filter(input)
+                   || wstpad_configure(input))
+                       return (-1);
+       }
+
+       return (0);
+}
+
+void
+wsmouse_set_param(struct device *sc, size_t param, int value)
+{
+       struct wsmouseinput *input =
+           &((struct wsmouse_softc *) sc)->input;
+       struct wsmouseparams *params = &input->params;
+       int *p;
+
+       if (param < 0 || param > WSMPARAM_LASTFIELD) {
+               printf("wsmouse_set_param: invalid parameter type\n");
+               return;
+       }
+
+       p = (int *) (((void *) params) + param);
+       *p = value;
+
+       if (IS_WSMFLTR_PARAM(param)) {
+               wsmouse_init_scaling(input);
+               wsmouse_init_deceleration(input);
+       } else if (param == WSMPARAM_SWAPXY) {
+               if (value)
+                       input->flags |= SWAPXY;
+               else
+                       input->flags &= ~SWAPXY;
+       } else if (param == WSMPARAM_PRESSURE_LO) {
+               params->pressure_hi =
+                   imax(params->pressure_lo, params->pressure_hi);
+               input->touch.min_pressure = params->pressure_hi;
+       } else if (param == WSMPARAM_PRESSURE_HI
+           && params->pressure_lo == 0) {
+               params->pressure_lo = input->params.pressure_hi;
+               input->touch.min_pressure = input->params.pressure_hi;
+       }
+}
+
+int
+wsmouse_set_mode(struct device *sc, int mode)
+{
+       struct wsmouseinput *input =
+           &((struct wsmouse_softc *) sc)->input;
+       int res = -1;
+
+       if (input->flags & TPAD_COMPAT_MODE) {
+               if (mode == WSMOUSE_COMPAT)
+                       res = 0;
+               else if (mode == WSMOUSE_NATIVE) {
+                       input->flags &= ~TPAD_COMPAT_MODE;
+                       input->flags |= TPAD_NATIVE_MODE;
+                       wstpad_destroy(input);
+                       res = 0;
+               }
+       } else if (input->flags & TPAD_NATIVE_MODE) {
+               if (mode == WSMOUSE_NATIVE)
+                       res = 0;
+               else if (mode == WSMOUSE_COMPAT)
+                       input->flags &= ~TPAD_NATIVE_MODE;
+                       input->flags |= TPAD_COMPAT_MODE;
+                       res = wstpad_configure(input);
+       }
+       return (res);
+}
+
+void
+wsmouse_input_init(struct wsmouseinput *input, struct wseventvar **evar)
+{
+       input->evar = evar;
+}
+
+void
+wsmouse_input_cleanup(struct wsmouseinput *input)
+{
+       wstpad_destroy(input);
+       free_mt_slots(input);
+}
Index: dev/wscons/wsmouseinput.h
===================================================================
RCS file: dev/wscons/wsmouseinput.h
diff -N dev/wscons/wsmouseinput.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/wscons/wsmouseinput.h   1 Dec 2015 22:17:53 -0000
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2015 Ulf Brosziewski
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef _WSMOUSEINPUT_H_
+#define _WSMOUSEINPUT_H_
+
+/* Process standard mouse input. */
+#define WSMOUSE_INPUT(sc_wsmousedev, btns, dx, dy, dz, dw)             \
+       do {                                                            \
+               wsmouse_buttons((sc_wsmousedev), (btns));               \
+               wsmouse_motion((sc_wsmousedev), (dx), (dy), (dz), (dw));\
+               wsmouse_input_sync(sc_wsmousedev);                      \
+       } while (0)
+
+
+/* Process standard touchpad input. */
+#define WSMOUSE_TOUCH(sc_wsmousedev, btns, x, y, pressure, fingers)    \
+       do {                                                            \
+               wsmouse_buttons((sc_wsmousedev), (btns));               \
+               wsmouse_position((sc_wsmousedev), (x), (y));            \
+               wsmouse_touch((sc_wsmousedev), (pressure), (fingers));  \
+               wsmouse_input_sync(sc_wsmousedev);                      \
+       } while (0)
+
+
+/*
+ * Drivers for touchpads that don't report pressure values can pass
+ * WSMOUSE_DEFAULT_PRESSURE to wsmouse_touch/wsmouse_mtstate.
+ *
+ * A pressure value of 0 signals that a touch has been released (coordinates
+ * or finger count and width will be ignored).
+ */
+#define WSMOUSE_DEFAULT_PRESSURE       -1
+
+
+struct device;
+enum wsmouseval;
+struct wsmousehw;
+struct mtpoint;
+
+
+/* Report button state. */
+void wsmouse_buttons(struct device *, u_int);
+
+/* Report motion deltas (dx, dy, dz, dw). */
+void wsmouse_motion(struct device *, int, int, int, int);
+
+/* Report absolute coordinates (x, y). */
+void wsmouse_position(struct device *, int, int);
+
+/* Report (single-) touch input (pressure, fingers). */
+void wsmouse_touch(struct device *, int, int);
+
+/* Report slot-based multitouch input (slot, x, y, pressure). */
+void wsmouse_mtstate(struct device *, int, int, int, int);
+
+/* Report multitouch input (mtpoints, size). */
+void wsmouse_mtframe(struct device *, struct mtpoint *, int);
+
+/* Report a single value (type, value, aux). */
+void wsmouse_set(struct device *, enum wsmouseval, int, int);
+
+/* Map a tracking ID to a slot (id). */
+int wsmouse_id_to_slot(struct device *, int);
+
+
+/* Synchronize (generate wscons events) */
+int wsmouse_input_sync(struct device *);
+
+
+/*
+ * Configure or reconfigure the input context (options, hw).
+ *
+ * Currently this is only useful for touchpads. The 'options' value
+ * contains flags for the touchpad extensions (soft-buttons, two-
+ * finger-scrolling, tapping etc.). The 'hw' pointer must not be NULL
+ * in the first call.
+ */
+int wsmouse_configure(struct device *, u_int, struct wsmousehw *);
+
+/* Set a filter/transformation value (param type, value). */
+void wsmouse_set_param(struct device *, size_t, int);
+
+/* Switch between compatibility mode and native mode. */
+int wsmouse_set_mode(struct device *, int);
+
+
+/*
+ * Type codes for wsmouse_set. REL_X/Y, MT_REL_X/Y, and FINGERWIDTH
+ * can't be reported by other functions. Please note that REL_X/Y
+ * values are deltas to be applied to the absolute coordinates and
+ * don't represent "pure" relative motion.
+ */
+enum wsmouseval {
+       WSMOUSE_REL_X,
+       WSMOUSE_ABS_X,
+       WSMOUSE_REL_Y,
+       WSMOUSE_ABS_Y,
+       WSMOUSE_PRESSURE,
+       WSMOUSE_FINGERS,
+       WSMOUSE_FINGERWIDTH,
+       WSMOUSE_MT_REL_X,
+       WSMOUSE_MT_ABS_X,
+       WSMOUSE_MT_REL_Y,
+       WSMOUSE_MT_ABS_Y,
+       WSMOUSE_MT_PRESSURE
+};
+
+#define WSMOUSE_IS_MT_CODE(code) \
+    ((code) >= WSMOUSE_MT_REL_X && (code) <= WSMOUSE_MT_PRESSURE)
+
+
+enum wsmhw_type {
+       WSMHW_RAW,
+       WSMHW_MOUSE,
+       WSMHW_TOUCHPAD,
+       WSMHW_CLICKPAD,
+};
+
+/*
+ * wsmousehw.flags
+ */
+/* Invert Y-coordinates */
+#define WSMHW_LR_DOWN          (1 << 0)
+/* Allocate the buffers for wsmouse_mtframe(). */
+#define WSMHW_MT_TRACKING      (1 << 1)
+
+
+/*
+ * The more or less minimal device description for the default
+ * configuration.
+ *
+ * Drivers that report coordinates with a downward orientation
+ * must set the flag WSMHW_LR_DOWN.
+ * Drivers for MT hardware must provide the number of slots. If
+ * they use wsmouse_mtframe(), WSMHW_MT_TRACKING must be set.
+ *
+ * The resolution values are optional, as well as the limits for
+ * pressure, finger width, and finger count.
+ */
+struct wsmousehw {
+       int type;               /* WSMOUSE_TYPE_*, cf. wsconsio.h */
+       enum wsmhw_type hw_type;
+       int x_min;
+       int x_max;
+       int y_min;
+       int y_max;
+       int h_res;
+       int v_res;
+
+       int flags;
+       int mt_slots;
+
+       int pressure_max;
+       int fingerwidth_max;
+       int fingers_max;        /* inclusive (not needed for MT touchpads) */
+};
+
+struct mtpoint {
+       int x;
+       int y;
+       int pressure;
+       int slot;
+};
+
+
+struct wsmouseparams {
+       /* General output transformations: */
+       int x_inv;
+       int y_inv;
+       int dx_mul;             /* delta scaling */
+       int dx_div;
+       int dy_mul;
+       int dy_div;
+       int swapxy;
+
+       /* Filters applying to touchpads in compatibility mode: */
+       int pressure_lo;
+       int pressure_hi;
+       int dx_max;
+       int dy_max;
+       int h_hysteresis;       /* horizontal/vertical hysteresis */
+       int v_hysteresis;
+       int dclr;               /* threshold for deceleration */
+
+       /* wstpad: */
+       int fingerwidth_lo;
+       int fingerwidth_palm;
+       int pressure_palm;
+};
+
+#define WSMPARAM_X_INV         offsetof(struct wsmouseparams, x_inv)
+#define WSMPARAM_Y_INV         offsetof(struct wsmouseparams, y_inv)
+#define WSMPARAM_DX_MUL                offsetof(struct wsmouseparams, dx_mul)
+#define WSMPARAM_DX_DIV                offsetof(struct wsmouseparams, dx_div)
+#define WSMPARAM_DY_MUL                offsetof(struct wsmouseparams, dy_mul)
+#define WSMPARAM_DY_DIV                offsetof(struct wsmouseparams, dy_div)
+#define WSMPARAM_SWAPXY                offsetof(struct wsmouseparams, swapxy)
+#define WSMPARAM_PRESSURE_LO   offsetof(struct wsmouseparams, pressure_lo)
+#define WSMPARAM_PRESSURE_HI   offsetof(struct wsmouseparams, pressure_hi)
+#define WSMPARAM_DX_MAX                offsetof(struct wsmouseparams, dx_max)
+#define WSMPARAM_DY_MAX                offsetof(struct wsmouseparams, dy_max)
+#define WSMPARAM_H_HYST                offsetof(struct wsmouseparams, h_hyst)
+#define WSMPARAM_V_HYST                offsetof(struct wsmouseparams, v_hyst)
+#define WSMPARAM_DCLR          offsetof(struct wsmouseparams, dclr)
+#define WSMPARAM_FWIDTH_LO     offsetof(struct wsmouseparams, fingerwidth_lo)
+#define WSMPARAM_FWIDTH_PALM \
+    offsetof(struct wsmouseparams, fingerwidth_palm)
+#define WSMPARAM_PRESSURE_PALM \
+    offsetof(struct wsmouseparams, pressure_palm)
+
+#define WSMPARAM_LASTFIELD     WSMPARAM_PRESSURE_PALM
+
+#define IS_WSMFLTR_PARAM(param) ((param) == WSMPARAM_DCLR || \
+    ((param) >= WSMPARAM_DX_MUL && (param) <= WSMPARAM_DY_DIV))
+
+
+
+/* wsmouse_configure/options */
+#define WSMCFG_DEFAULTS                0
+
+/*
+ * Touchpad features - Hardware drivers can override the default sets of
+ * wstpad features (see below) by adding these flags to the "configure"
+ * options.
+ */
+#define WSTPAD_SOFTBUTTONS     (1 << 0)
+#define WSTPAD_TOPBUTTONS      (1 << 1)
+#define WSTPAD_F2CLICK         (1 << 2)
+#define WSTPAD_PALM            (1 << 3)
+#define WSTPAD_F2SCROLL                (1 << 4)
+#define WSTPAD_EDGESCROLL      (1 << 5)
+#define WSTPAD_TAPPING         (1 << 6)
+#define WSTPAD_SWAP_SIDES      (1 << 7)
+
+#define WSTPAD_TOUCHPAD_DEFAULTS (WSTPAD_F2SCROLL)
+#define WSTPAD_CLICKPAD_DEFAULTS (WSTPAD_SOFTBUTTONS | WSTPAD_F2SCROLL)
+
+
+#define WSMOUSE_MT_SLOTS_MAX   10
+
+#endif /* _WSMOUSEINPUT_H_ */
Index: dev/wscons/wsmouseinput_sc.h
===================================================================
RCS file: dev/wscons/wsmouseinput_sc.h
diff -N dev/wscons/wsmouseinput_sc.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/wscons/wsmouseinput_sc.h        1 Dec 2015 22:17:53 -0000
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2015 Ulf Brosziewski
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * wsmouse input processing - private header (wsmouse, wsmouseinput, wstpad)
+ */
+
+#ifndef _WSMOUSEINPUT_SC_H_
+#define _WSMOUSEINPUT_SC_H_
+
+
+struct btn_state {
+       u_int buttons;
+       u_int sync;
+};
+
+#define SYNC_DELTAS            (1 << 0)
+#define SYNC_X                 (1 << 1)
+#define SYNC_Y                 (1 << 2)
+#define SYNC_POSITION          (SYNC_X | SYNC_Y)
+
+struct motion_state {
+       int dx;
+       int dy;
+       int dz;
+       int dw;
+       int x;
+       int y;
+       u_int sync;
+
+       /* Deltas of absolute coordinates. */
+       int x_delta;
+       int y_delta;
+};
+
+#define SYNC_PRESSURE          (1 << 0)
+#define SYNC_FINGERS           (1 << 1)
+#define SYNC_FINGERWIDTH       (1 << 2)
+
+struct touch_state {
+       int pressure;
+       int fingers;
+       int fingerwidth;
+       u_int sync;
+
+       int prev_fingers;
+       int min_pressure;
+};
+
+/*
+ * XXX: Do we need more or a dynamic number of "axes"?
+ */
+#define MTS_TOUCH      0
+#define MTS_X          1
+#define MTS_Y          2
+#define MTS_PRESSURE   3
+#define MTS_SIZE       4
+
+struct mt_slot {
+       int x;
+       int y;
+       int pressure;
+       int id; /* tracking ID */
+};
+
+struct mt_state {
+       /* The set of slots with active touches */
+       u_int touches;
+       /* The set of slots with unsynchronized state */
+       u_int frame;
+
+       int num_slots;
+       struct mt_slot *slots;
+       /* The sets of changes per slot axis */
+       u_int sync[MTS_SIZE];
+
+       int num_touches;
+
+       /* pointer control */
+       u_int ptr;
+       u_int ptr_cycle;
+       u_int prev_ptr;
+
+       /* A buffer for slot assignment. */
+       int *matrix;
+};
+
+
+struct axis_filter {
+       /* Scale factor in [*.12] fixed-point format. */
+       int scale;
+       int rmdr;
+       /* Accumulated deltas, bounded by the hysteresis thresholds. */
+       int hyst;
+       /* A [*.12] coefficient for "magnitudes", used for deceleration. */
+       int mag_scale;
+       int dclr_rmdr;
+};
+
+struct wstpad;
+
+/* wsmouseinput.flags */
+#define TPAD_COMPAT_MODE       (1 << 0)
+#define TPAD_NATIVE_MODE       (1 << 1)
+#define SCALE_DELTAS           (1 << 2)
+#define DECELERATE             (1 << 3)
+#define SWAPXY                 (1 << 4)
+#define TRACK_INTERVAL         (1 << 5)
+#define MT_TRACKING            (1 << 6)
+
+#define DELTA_X_EV(flags) (((flags) & SWAPXY) ? \
+    WSCONS_EVENT_MOUSE_DELTA_Y : WSCONS_EVENT_MOUSE_DELTA_X)
+#define DELTA_Y_EV(flags) (((flags) & SWAPXY) ? \
+    WSCONS_EVENT_MOUSE_DELTA_X : WSCONS_EVENT_MOUSE_DELTA_Y)
+#define ABS_X_EV(flags) (((flags) & SWAPXY) ? \
+    WSCONS_EVENT_MOUSE_ABSOLUTE_Y : WSCONS_EVENT_MOUSE_ABSOLUTE_X)
+#define ABS_Y_EV(flags) (((flags) & SWAPXY) ? \
+    WSCONS_EVENT_MOUSE_ABSOLUTE_X : WSCONS_EVENT_MOUSE_ABSOLUTE_Y)
+
+#define DELTA_Z_EV     WSCONS_EVENT_MOUSE_DELTA_Z
+#define DELTA_W_EV     WSCONS_EVENT_MOUSE_DELTA_W
+#define ABS_Z_EV       WSCONS_EVENT_TOUCH_PRESSURE
+#define ABS_W_EV       WSCONS_EVENT_TOUCH_FINGERS
+#define BTN_DOWN_EV    WSCONS_EVENT_MOUSE_DOWN
+#define BTN_UP_EV      WSCONS_EVENT_MOUSE_UP
+#define SYNC_EV                WSCONS_EVENT_SYNC
+
+
+struct wsmouseinput {
+       u_int flags;
+
+       /* input states */
+       struct btn_state btn;
+       struct motion_state motion;
+       struct touch_state touch;
+       struct mt_state mt;
+
+       struct wsmousehw hw;
+       struct wsmouseparams params;
+       struct {
+               struct axis_filter h;
+               struct axis_filter v;
+               int mag;
+               int ratio;      /* X/Y ratio in [*.12] fixed-point format. */
+       } fltr;
+
+       u_int options;          /* configuration flags */
+       struct wstpad *tp;      /* touchpad extensions */
+       struct {
+               long avg;       /* average update interval in nanoseconds */
+               long sum;
+               int samples;
+               struct timespec ts;
+               int track;
+       } interval;
+
+       struct wseventvar **evar;
+};
+
+
+#define EVQ_RESULT_OVERFLOW    -1
+#define EVQ_RESULT_NONE                0
+#define EVQ_RESULT_SUCCESS     1
+
+struct evq_access {
+       struct wseventvar *evar;
+       struct timespec ts;
+       int put;
+       int result;
+};
+
+void wsmouse_evq_put(struct evq_access *, int, int);
+void wsmouse_init_scaling(struct wsmouseinput *);
+void wsmouse_init_deceleration(struct wsmouseinput *);
+
+void wsmouse_input_init(struct wsmouseinput *, struct wseventvar **);
+void wsmouse_input_cleanup(struct wsmouseinput *);
+
+/*
+ * touchpad extensions
+ */
+int wstpad_configure_filter(struct wsmouseinput *);
+int wstpad_configure(struct wsmouseinput *);
+void wstpad_destroy(struct wsmouseinput *);
+void wstpad_process_input(struct wsmouseinput *, struct evq_access *);
+
+
+static __inline__ void
+timespeccpy(struct timespec *dst, struct timespec *src)
+{
+       dst->tv_sec = src->tv_sec;
+       dst->tv_nsec = src->tv_nsec;
+}
+
+#define FOREACHBIT(v, i) \
+    for ((i) = ffs(v) - 1; (i) != -1; (i) = ffs((v) & (~1 << (i))) - 1)
+
+#endif /* _WSMOUSEINPUT_SC_H_ */
Index: dev/wscons/wsmousevar.h
===================================================================
RCS file: /cvs/src/sys/dev/wscons/wsmousevar.h,v
retrieving revision 1.8
diff -u -p -r1.8 wsmousevar.h
--- dev/wscons/wsmousevar.h     21 Dec 2014 18:16:07 -0000      1.8
+++ dev/wscons/wsmousevar.h     1 Dec 2015 22:17:53 -0000
@@ -48,13 +48,14 @@ struct wsmouse_accessops {
        void    (*disable)(void *);
 };

+
 /*
  * Attachment information provided by wsmousedev devices when attaching
  * wsmouse units.
  */
 struct wsmousedev_attach_args {
-       const struct wsmouse_accessops *accessops;      /* access ops */
-       void    *accesscookie;                          /* access cookie */
+       const struct wsmouse_accessops *accessops;
+       void *accesscookie;
 };

 #define        wsmousedevcf_mux        cf_loc[WSMOUSEDEVCF_MUX]
@@ -63,15 +64,3 @@ struct wsmousedev_attach_args {
  * Autoconfiguration helper functions.
  */
 int    wsmousedevprint(void *, const char *);
-
-/*
- * Callbacks from the mouse driver to the wsmouse interface driver.
- */
-#define WSMOUSE_INPUT_DELTA            0
-#define WSMOUSE_INPUT_ABSOLUTE_X       (1<<0)
-#define WSMOUSE_INPUT_ABSOLUTE_Y       (1<<1)
-#define WSMOUSE_INPUT_ABSOLUTE_Z       (1<<2)
-#define WSMOUSE_INPUT_ABSOLUTE_W       (1<<3)
-
-void   wsmouse_input(struct device *kbddev, u_int btns,
-                          int x, int y, int z, int w, u_int flags);
Index: dev/wscons/wstpad.c
===================================================================
RCS file: dev/wscons/wstpad.c
diff -N dev/wscons/wstpad.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/wscons/wstpad.c 1 Dec 2015 22:17:53 -0000
@@ -0,0 +1,1192 @@
+/*
+ * Copyright (c) 2015 Ulf Brosziewski
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * wsmouse input processing, touchpad extensions
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/signalvar.h>
+#include <sys/timeout.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>
+#include <dev/wscons/wseventvar.h>
+
+#include "wsmouseinput_sc.h"
+
+
+#define LEFTBTN                        (1 << 0)
+#define MIDDLEBTN              (1 << 1)
+#define RIGHTBTN               (1 << 2)
+#define PRIMARYBTN LEFTBTN
+
+#define PRIMARYBTN_CLICKED(tp) ((tp)->btns_sync & PRIMARYBTN & (tp)->btns)
+#define PRIMARYBTN_RELEASED(tp) ((tp)->btns_sync & PRIMARYBTN & ~(tp)->btns)
+
+#define IS_MT(tp) ((tp)->flags & TP_IS_MT)
+
+#define TAP_DEFAULT_TAPTIME    180
+#define TAP_DEFAULT_WAITTIME   180
+#define TAP_DEFAULT_LOCKTIME   1500
+#define PALM_WAIT              400
+
+#define TAP_DOUBLECLK_DELAY    20
+
+enum tpad_touch_state {
+       TOUCH_NONE,
+       TOUCH_BEGIN,
+       TOUCH_UPDATE,
+       TOUCH_END,
+};
+
+enum tpad_handlers {
+       PALM_HDLR,
+       CLICK_HDLR,
+       SOFTBUTTON_HDLR,
+       TOPBUTTON_HDLR,
+       F2CLICK_HDLR,
+       TAP_HDLR,
+       F2SCROLL_HDLR,
+       EDGESCROLL_HDLR,
+};
+
+enum tap_state {
+       TAP_DETECT,
+       TAP_IGNORE,
+       TAP_LIFTED,
+       TAP_2ND_TOUCH,
+       TAP_LOCKED,
+       TAP_NTH_TOUCH,
+};
+
+enum tpad_cmd {
+       CLEAR_MOTION_DELTAS,
+       SOFTBUTTON_UP,
+       SOFTBUTTON_DOWN,
+       TAPBUTTON_UP,
+       TAPBUTTON_DOWN,
+       TAPBUTTON_DOUBLECLK,
+       TIMEOUT_TAP_RELEASE,
+       VSCROLL,
+       HSCROLL,
+};
+
+/*
+ * tpad.touch flags: Palm detection or clicks in the main area of a
+ * clickpad may stop pointer motion, either for a short period of time
+ * or for the complete duration of a touch (PALM_FROZEN).
+ */
+#define PALM_FREEZE            (1 << 0)
+#define PALM_FROZEN            (1 << 1)
+#define CLICK_FREEZE           (1 << 2)
+#define FREEZE (PALM_FREEZE | PALM_FROZEN | CLICK_FREEZE)
+
+struct tpad_touch {
+       int x;
+       int y;
+       enum tpad_touch_state state;
+       u_int flags;
+       struct {
+               int val;
+               int matches;
+       } direction;
+       struct {
+               int x;
+               int y;
+               struct timespec time;
+       } orig;
+};
+
+/* wstpad.flags */
+#define TP_IS_MT               (1 << 0)
+#define TP_DISABLE             (1 << 1)
+
+
+struct wstpad {
+       u_int features;
+       u_int handlers;
+
+       struct {
+               int left;
+               int right;
+               int bottom;
+               int top;
+               int center;
+               int center_left;
+               int center_right;
+               int middle;
+               int hscroll;
+               int vscroll;
+       } edge;
+
+       /*
+        * t always points into the tpad_touches array, which has at
+        * least one element. If there is more than one, t references
+        * the pointer-controlling touch.
+        */
+       struct tpad_touch *t;
+       struct tpad_touch *tpad_touches;
+
+       u_int flags;
+       struct timespec now;
+
+       /* convenience copies */
+       int dx;
+       int dy;
+       int fingers;
+       int prev_fingers;
+       u_int btns;
+       u_int btns_sync;
+
+       /* Handler state and configuration: */
+
+       struct timeout timeout;
+       u_int timeout_cmd;
+
+       u_int softbutton;
+       u_int sbtnmsk;
+
+       struct {
+               enum tap_state state;
+               int fingers;
+               u_int button;
+               int hlimit;
+               int vlimit;
+               struct timespec taptime;
+               int waittime_ms;
+               int drag_enabled;
+               int lock_enabled;
+               int locktime_ms;
+       } tap;
+
+       struct {
+               struct timespec wait;
+       } palm;
+
+       struct {
+               int acc_dx;
+               int acc_dy;
+               int dz;
+               int dw;
+               int h_dist;
+               int v_dist;
+       } scroll;
+};
+
+void wstpad_events(struct wsmouseinput *, struct evq_access *, u_int);
+
+/*
+ * wstpad works with "normalized" device coordinates. The orientation
+ * is left-to-right and upward.
+ */
+
+static __inline int
+normalize_x(struct wsmouseparams *params, int x)
+{
+       return (params->x_inv ? params->x_inv - x : x);
+}
+
+static __inline int
+normalize_y(struct wsmouseparams *params, int x)
+{
+       return (params->x_inv ? params->x_inv - x : x);
+}
+
+static __inline int
+normalize_dx(struct wsmouseparams *params, int dx)
+{
+       return (params->x_inv ? -dx : dx);
+}
+
+static __inline int
+normalize_dy(struct wsmouseparams *params, int dy)
+{
+       return (params->y_inv ? -dy : dy);
+}
+
+/*
+ * Directions of motion are represented by numbers in the range 0 - 11,
+ * corresponding to clockwise counted circle sectors:
+ *
+ *              11 | 0
+ *           10    |    1
+ *          9      |      2
+ *          -------+-------
+ *          8      |      3
+ *            7    |    4
+ *               6 | 5
+ *
+ * Two directions "match" each other if they are equal or adjacent.
+ * Some handlers require that a motion is "stable" and check the number
+ * of matches.
+ *
+ * Like the X/Y ratio, the tangent constants have a [*.12] fixed-point
+ * format: (int) round(tan(M_PI / ...) * 4096)
+ */
+#define TAN_DEG_60 7094
+#define TAN_DEG_30 2365
+
+#define DIR_MIN_MATCHES        4
+
+#define NORTH(d) ((d) == 0 || (d) == 11)
+#define SOUTH(d) ((d) == 5 || (d) == 6)
+#define EAST(d) ((d) == 2 || (d) == 3)
+#define WEST(d) ((d) == 8 || (d) == 9)
+
+static __inline int
+get_direction(int dx, int dy, int ratio)
+{
+       int rdy, dir = -1;
+
+       if (dx || dy) {
+               rdy = abs(dy) * ratio;
+               if (abs(dx) * TAN_DEG_60 < rdy)
+                       dir = 0;
+               else if (abs(dx) * TAN_DEG_30 < rdy)
+                       dir = 1;
+               else
+                       dir = 2;
+               if ((dx < 0) != (dy < 0))
+                       dir = 5 - dir;
+               if (dx < 0)
+                       dir += 6;
+       }
+       return dir;
+}
+
+static __inline int
+dircmp(int dir1, int dir2)
+{
+       int diff = abs(dir1 - dir2);
+       return (diff <= 6 ? diff : 12 - diff);
+}
+
+/* Check whether two directions are equal or adjacent. */
+static __inline int
+match_directions(int dir1, int dir2)
+{
+       return (dir1 >= 0 && dir2 >= 0 && dircmp(dir1, dir2) <= 1);
+}
+
+void
+wstpad_set_direction(struct wsmouseinput *input,
+    struct tpad_touch *t, int dx, int dy)
+{
+       int dir;
+
+       if (t->state != TOUCH_UPDATE) {
+               t->direction.val = -1;
+               t->direction.matches = 0;
+       } else {
+               dir = get_direction(dx, dy, input->fltr.ratio);
+               if (match_directions(t->direction.val, dir))
+                       t->direction.matches++;
+               else
+                       t->direction.matches = 1;
+               t->direction.val = dir;
+       }
+}
+
+static __inline int
+start_timeout(struct wstpad *tp, u_int cmd, int ms)
+{
+       if (tp->timeout_cmd == 0) {
+               tp->timeout_cmd = cmd;
+               timeout_add_msec(&tp->timeout, ms);
+               return (0);
+       }
+       return (-1);
+}
+
+static __inline int
+cancel_timeout(struct wstpad *tp, u_int expected)
+{
+       if (tp->timeout_cmd == expected) {
+               timeout_del(&tp->timeout);
+               tp->timeout_cmd = 0;
+               return (0);
+       }
+       return (-1);
+}
+
+static __inline struct tpad_touch *
+get_2nd_touch(struct wsmouseinput *input)
+{
+       struct wstpad *tp = input->tp;
+       int slot;
+
+       if (IS_MT(tp)) {
+               slot = ffs(input->mt.touches & ~input->mt.ptr);
+               if (slot--)
+                       return &tp->tpad_touches[slot];
+       }
+       return NULL;
+}
+
+static __inline int
+is_tap(struct wstpad *tp)
+{
+       struct tpad_touch *t = tp->t;
+       struct timespec tsp;
+       int dx, dy;
+
+       if ((t->flags & FREEZE) && t->y >= tp->edge.middle)
+               /* No tapping in the upper halves of the palm areas. */
+               return (0);
+
+       dx = abs(t->x - t->orig.x);
+       dy = abs(t->y - t->orig.y);
+       if (dx <= tp->tap.hlimit && dy <= tp->tap.vlimit) {
+               timespeccpy(&tsp, &tp->now);
+               timespecsub(&tsp, &t->orig.time, &tsp);
+               return (timespeccmp(&tsp, &tp->tap.taptime, <));
+       }
+       return (0);
+}
+
+static __inline void
+set_tap_button(struct wstpad *tp)
+{
+       tp->tap.button = (tp->tap.fingers == 1 ? LEFTBTN :
+           (tp->tap.fingers == 2 ? RIGHTBTN : MIDDLEBTN));
+}
+
+/*
+ * This handler supports one-, two-, and three-finger-taps, which
+ * are mapped to left-button, right-button and middle-button events,
+ * respectively; moreover, it supports tap-and-drag operations with
+ * "locked" drags, which are finished by a timeout or a "tap-to-end"
+ * gesture.
+ */
+void
+wstpad_tap(struct wsmouseinput *input, u_int *cmds)
+{
+       struct wstpad *tp = input->tp;
+       struct tpad_touch *t = tp->t;
+       int err = 0;
+
+       if (tp->btns) {
+               /*
+                * Don't process tapping while hardware buttons are being
+                * pressed. If the handler is not in its initial state,
+                * the "tap button" will be released.
+                */
+               if (tp->tap.state > TAP_IGNORE) {
+                       cancel_timeout(tp, 1 << TIMEOUT_TAP_RELEASE);
+                       *cmds |= 1 << TAPBUTTON_UP;
+               }
+               /*
+                * It might be possible to produce a click within the tap
+                * timeout; ignore the current touch.
+                */
+               tp->tap.state = TAP_IGNORE;
+       }
+
+       switch (tp->tap.state) {
+       case TAP_DETECT:
+               if (t->state == TOUCH_END) {
+                       if (is_tap(tp)) {
+                               set_tap_button(tp);
+                               *cmds |= 1 << TAPBUTTON_DOWN;
+                               tp->tap.state = TAP_LIFTED;
+                               err = start_timeout(tp,
+                                   1 << TIMEOUT_TAP_RELEASE,
+                                   tp->tap.waittime_ms);
+                       }
+                       tp->tap.fingers = 0;
+               } else if (tp->fingers > tp->tap.fingers) {
+                       tp->tap.fingers = imin(tp->fingers, 3);
+               } else if (tp->fingers < tp->tap.fingers) {
+                       if (!is_tap(tp))
+                               tp->tap.state = TAP_IGNORE;
+               }
+               break;
+       case TAP_IGNORE:
+               if (t->state == TOUCH_END) {
+                       tp->tap.state = TAP_DETECT;
+                       tp->tap.fingers = 0;
+               }
+               break;
+       case TAP_LIFTED:
+               if (t->state >= TOUCH_BEGIN) {
+                       cancel_timeout(tp, 1 << TIMEOUT_TAP_RELEASE);
+                       if (tp->tap.drag_enabled &&
+                           tp->tap.button == PRIMARYBTN) {
+                               tp->tap.state = TAP_2ND_TOUCH;
+                       } else {
+                               *cmds |= 1 << TAPBUTTON_UP;
+                               tp->tap.state = TAP_DETECT;
+                       }
+               }
+               break;
+       case TAP_2ND_TOUCH:
+               if (t->state == TOUCH_END) {
+                       if (is_tap(tp)) {
+                               *cmds |= 1 << TAPBUTTON_DOUBLECLK;
+                               tp->tap.state = TAP_LIFTED;
+                               err = start_timeout(tp,
+                                   1 << TIMEOUT_TAP_RELEASE,
+                                   TAP_DOUBLECLK_DELAY);
+                       } else if (!tp->tap.lock_enabled) {
+                               *cmds |= 1 << TAPBUTTON_UP;
+                               tp->tap.state = TAP_DETECT;
+                       } else {
+                               tp->tap.state = TAP_LOCKED;
+                               err = start_timeout(tp,
+                                   1 << TIMEOUT_TAP_RELEASE,
+                                   tp->tap.locktime_ms);
+                       }
+               } else if (tp->fingers > 1) {
+                       *cmds |= 1 << TAPBUTTON_UP;
+                       tp->tap.state = TAP_DETECT;
+               }
+               break;
+       case TAP_LOCKED:
+               if (t->state >= TOUCH_BEGIN) {
+                       cancel_timeout(tp, 1 << TIMEOUT_TAP_RELEASE);
+                       tp->tap.state = TAP_NTH_TOUCH;
+               }
+               break;
+       case TAP_NTH_TOUCH:
+               if (t->state == TOUCH_END) {
+                       if (is_tap(tp)) { /* "tap-to-end" */
+                               *cmds |= 1 << TAPBUTTON_UP;
+                               tp->tap.state = TAP_DETECT;
+                       } else {
+                               tp->tap.state = TAP_LOCKED;
+                               err = start_timeout(tp,
+                                   1 << TIMEOUT_TAP_RELEASE,
+                                   tp->tap.locktime_ms);
+                       }
+               } else if (tp->fingers > 1) {
+                       *cmds |= 1 << TAPBUTTON_UP;
+                       tp->tap.state = TAP_DETECT;
+               }
+               break;
+       }
+
+       if (err) { /* Did start_timeout() fail? */
+               if (tp->tap.state == TAP_LIFTED)
+                       *cmds &= ~(1 << TAPBUTTON_DOWN);
+               else
+                       *cmds |= 1 << TAPBUTTON_UP;
+
+               tp->tap.state = TAP_DETECT;
+       }
+}
+
+void
+wstpad_timeout(void *p)
+{
+       struct wsmouseinput *input = p;
+       struct evq_access evq;
+       int s, cmd;
+
+       s = spltty();
+       evq.evar = *input->evar;
+       cmd = input->tp->timeout_cmd;
+       input->tp->timeout_cmd = 0;
+       if (evq.evar == NULL || cmd == 0)
+               goto done;
+       evq.put = evq.evar->put;
+       evq.result = EVQ_RESULT_NONE;
+
+       wstpad_events(input, &evq, cmd);
+
+       if (evq.result == EVQ_RESULT_SUCCESS) {
+               evq.evar->put = evq.put;
+               WSEVENT_WAKEUP(evq.evar);
+       }
+
+done:
+       splx(s);
+}
+
+/* Return TRUE if f2-/edge-scrolling would be valid. */
+static __inline int
+chk_scroll_state(struct wstpad *tp)
+{
+       if (tp->fingers != tp->prev_fingers || tp->btns_sync) {
+               tp->scroll.acc_dx = 0;
+               tp->scroll.acc_dy = 0;
+               return (0);
+       }
+       if (tp->fingers == 0 || tp->btns || (tp->t->flags & FREEZE)
+           || tp->t->direction.matches < DIR_MIN_MATCHES)
+               return (0);
+
+       return (tp->dx || tp->dy);
+}
+
+void
+wstpad_scroll(struct wsmouseinput *input, int dx, int dy, u_int *cmds)
+{
+       struct wstpad *tp = input->tp;
+
+       /* Scrolling is either horizontal or vertical, but not both. */
+       if (dy) {
+               tp->scroll.acc_dy += dy;
+               if (abs(tp->scroll.acc_dy) > tp->scroll.v_dist) {
+                       if (tp->scroll.acc_dy < 0) {
+                               tp->scroll.acc_dy += tp->scroll.v_dist;
+                               tp->scroll.dz = 1;
+                       } else {
+                               tp->scroll.acc_dy -= tp->scroll.v_dist;
+                               tp->scroll.dz = -1;
+                       }
+                       *cmds |= 1 << VSCROLL;
+               }
+       } else if (dx) {
+               tp->scroll.acc_dx += dx;
+               if (abs(tp->scroll.acc_dx) > tp->scroll.h_dist) {
+                       if (tp->scroll.acc_dx < 0) {
+                               tp->scroll.acc_dx += tp->scroll.h_dist;
+                               tp->scroll.dw = -1;
+                       } else {
+                               tp->scroll.acc_dx -= tp->scroll.h_dist;
+                               tp->scroll.dw = 1;
+                       }
+                       *cmds |= 1 << HSCROLL;
+               }
+       }
+}
+
+void
+wstpad_f2scroll(struct wsmouseinput *input, u_int *cmds)
+{
+       struct wstpad *tp = input->tp;
+       struct tpad_touch *t2;
+       int dir, dx, dy;
+
+       if (!chk_scroll_state(tp) || tp->fingers != 2)
+               return;
+
+       dir = tp->t->direction.val;
+       dy = NORTH(dir) || SOUTH(dir) ? tp->dy : 0;
+       dx = EAST(dir) || WEST(dir) ? tp->dx : 0;
+
+       if ((dx || dy) && IS_MT(tp)) {
+               t2 = get_2nd_touch(input);
+               if (t2 == NULL || t2->direction.matches < DIR_MIN_MATCHES)
+                       return;
+               dir = t2->direction.val;
+               if ((dy > 0 && !NORTH(dir)) || (dy < 0 && !SOUTH(dir)))
+                       return;
+               if ((dx > 0 && !EAST(dir)) || (dx < 0 && !WEST(dir)))
+                       return;
+       }
+
+       wstpad_scroll(input, dx, dy, cmds);
+}
+
+void
+wstpad_edgescroll(struct wsmouseinput *input, u_int *cmds)
+{
+       struct wstpad *tp = input->tp;
+       struct tpad_touch *t = tp->t;
+
+       if (!chk_scroll_state(tp) || tp->fingers != 1)
+               return;
+
+       if (t->orig.x >= tp->edge.vscroll && t->x >= tp->edge.vscroll)
+               wstpad_scroll(input, 0, tp->dy, cmds);
+       else if (t->orig.y < tp->edge.hscroll && t->y < tp->edge.hscroll)
+               wstpad_scroll(input, tp->dx, 0, cmds);
+}
+
+static __inline u_int
+sbtn(struct wstpad *tp, int x, int y)
+{
+        return (y >= tp->edge.bottom ? 0
+           : (x < tp->edge.center ? LEFTBTN : RIGHTBTN) ^ tp->sbtnmsk);
+}
+
+static __inline u_int
+top_sbtn(struct wstpad *tp, int x, int y)
+{
+       if (y < tp->edge.top)
+               return (0);
+       if (x < tp->edge.center_left)
+               return (LEFTBTN ^ tp->sbtnmsk);
+       return (x > tp->edge.center_right
+           ? (RIGHTBTN ^ tp->sbtnmsk) : MIDDLEBTN);
+}
+
+u_int
+wstpad_get_sbtn(struct wsmouseinput *input, int top)
+{
+       struct wstpad *tp = input->tp;
+       struct tpad_touch *t = tp->t;
+       u_int btn, touches;
+       int slot;
+
+       btn = 0;
+       if (tp->fingers) {
+               btn = top ? top_sbtn(tp, t->x, t->y)
+                   : sbtn(tp, t->x, t->y);
+               if (IS_MT(tp) && tp->fingers > 1 && !top) {
+                       touches = input->mt.touches & ~input->mt.ptr;
+                       FOREACHBIT(touches, slot) {
+                               t = &tp->tpad_touches[slot];
+                               btn |= sbtn(tp, t->x, t->y);
+                       }
+                       if (btn == (LEFTBTN | RIGHTBTN))
+                               btn = MIDDLEBTN;
+               }
+       }
+       return (btn);
+}
+
+void
+wstpad_softbuttons(struct wsmouseinput *input, u_int *cmds, int hdlr)
+{
+       struct wstpad *tp = input->tp;
+       int top = (hdlr == TOPBUTTON_HDLR);
+
+       if (tp->softbutton && PRIMARYBTN_RELEASED(tp)) {
+               *cmds |= 1 << SOFTBUTTON_UP;
+               return;
+       }
+
+       if (tp->softbutton == 0 && PRIMARYBTN_CLICKED(tp)) {
+               /* Did the touch start in the button area? */
+               if ((!top && tp->t->orig.y < tp->edge.bottom)
+                   || (top && tp->t->orig.y >= tp->edge.top)) {
+
+                       tp->softbutton = wstpad_get_sbtn(input, top);
+                       if (tp->softbutton)
+                               *cmds |= 1 << SOFTBUTTON_DOWN;
+               }
+       }
+}
+
+/*
+ * If a touch starts in the left or right edge area, it is marked as
+ * an accidental touch. However, if it leaves that area within a short
+ * period of time and with a "nearly" horizontal movement, it will be
+ * treated as normal touch.
+ *
+ * XXX: Make pressure/finger width values available and check them here.
+ */
+void
+wstpad_palm(struct wsmouseinput *input, u_int *cmds)
+{
+       struct wstpad *tp = input->tp;
+       struct tpad_touch *t = tp->t;
+       struct timespec tsp;
+       int dx, dy;
+
+       if (t->state == TOUCH_BEGIN) {
+               if (t->x < tp->edge.left || t->x >= tp->edge.right)
+                       t->flags |= PALM_FREEZE;
+
+       } else if (t->flags & PALM_FREEZE) {
+               if (t->x >= tp->edge.left && t->x < tp->edge.right) {
+                       dx = t->x - t->orig.x;
+                       dy = t->y - t->orig.y;
+                       if ((abs(dx) << 12) > abs(dy) * input->fltr.ratio) {
+                               timespeccpy(&tsp, &tp->now);
+                               timespecsub(&tsp, &t->orig.time, &tsp);
+                               if (timespeccmp(&tsp, &tp->palm.wait, <))
+                                       t->flags &= ~PALM_FREEZE;
+                               else
+                                       t->flags ^=
+                                           (PALM_FREEZE | PALM_FROZEN);
+                       }
+               }
+       }
+}
+
+/*
+ * The basic delta- and motion-handling is done by wsmouseinput. This
+ * function only checks whether pointer movement would be valid.
+ */
+static __inline void
+wstpad_motion(struct wstpad *tp, u_int *cmds)
+{
+       struct tpad_touch *t = tp->t;
+
+       if ((tp->dx || tp->dy) &&
+           ((tp->flags & TP_DISABLE) || (t->flags & FREEZE)
+           /* Is the touch in one of the edge zones? */
+           || t->x < tp->edge.left || t->x >= tp->edge.right
+           || t->y < tp->edge.bottom || t->y >= tp->edge.top
+           /* Is there more than one finger, and no click-and-drag? */
+           || tp->fingers > 2
+           || (tp->fingers == 2 && (tp->btns & PRIMARYBTN) == 0))) {
+
+                       *cmds |= 1 << CLEAR_MOTION_DELTAS;
+       }
+}
+
+/*
+ * Translate the "command" bits into wscons events, either directly or
+ * indirectly by updating the sync-state of wsmouseinput.
+ */
+void
+wstpad_events(struct wsmouseinput *input, struct evq_access *evq, u_int cmds)
+{
+       struct wstpad *tp = input->tp;
+       u_int btn;
+       int n;
+
+       FOREACHBIT(cmds, n) {
+               switch (n) {
+               case CLEAR_MOTION_DELTAS:
+                       input->motion.dx = input->motion.dy = 0;
+                       if (input->motion.dz == 0 && input->motion.dw == 0)
+                               input->motion.sync &= ~SYNC_DELTAS;
+                       continue;
+               case SOFTBUTTON_DOWN:
+                       input->btn.sync &= ~PRIMARYBTN;
+                       btn = ffs(tp->softbutton) - 1;
+                       wsmouse_evq_put(evq, BTN_DOWN_EV, btn);
+                       continue;
+               case SOFTBUTTON_UP:
+                       input->btn.sync &= ~PRIMARYBTN;
+                       btn = ffs(tp->softbutton) - 1;
+                       wsmouse_evq_put(evq, BTN_UP_EV, btn);
+                       tp->softbutton = 0;
+                       continue;
+               case TAPBUTTON_DOWN:
+                       btn = ffs(tp->tap.button) - 1;
+                       wsmouse_evq_put(evq, BTN_DOWN_EV, btn);
+                       continue;
+               case TAPBUTTON_UP:
+                       /*
+                        * This operation shouldn't interfere with
+                        * the state of the hardware buttons.
+                        */
+                       if ((input->btn.buttons & tp->tap.button) == 0) {
+                               btn = ffs(tp->tap.button) - 1;
+                               wsmouse_evq_put(evq, BTN_UP_EV, btn);
+                       }
+                       input->btn.sync &= ~tp->tap.button;
+                       continue;
+               case TAPBUTTON_DOUBLECLK:
+                       /*
+                        * A double tap can't be distinguished from a
+                        * tap-and-drag operation until the second tap
+                        * is finished. NB: We cannot add the final
+                        * BTN_UP event here. It seems that X doesn't
+                        * recognize double clicks if there is no delay.
+                        * This is the reason why the tap handler returns
+                        * from the 2ND_TOUCH state into the LIFTED state
+                        * with a very short timeout (TAP_DOUBLECLK_DELAY).
+                        */
+                       btn = ffs(PRIMARYBTN) - 1;
+                       wsmouse_evq_put(evq, BTN_UP_EV, btn);
+                       wsmouse_evq_put(evq, SYNC_EV, 0);
+                       wsmouse_evq_put(evq, BTN_DOWN_EV, btn);
+                       continue;
+               case TIMEOUT_TAP_RELEASE:
+                       if (tp->tap.state == TAP_LIFTED
+                           || tp->tap.state == TAP_LOCKED) {
+                               tp->tap.state = TAP_DETECT;
+                               btn = ffs(tp->tap.button) - 1;
+                               wsmouse_evq_put(evq, BTN_UP_EV, btn);
+                               wsmouse_evq_put(evq, SYNC_EV, 0);
+                       }
+                       continue;
+               case HSCROLL:
+                       input->motion.dw = tp->scroll.dw;
+                       input->motion.sync |= SYNC_DELTAS;
+                       continue;
+               case VSCROLL:
+                       input->motion.dz = tp->scroll.dz;
+                       input->motion.sync |= SYNC_DELTAS;
+                       continue;
+               default:
+                       printf("[wstpad] invalid cmd %d\n", n);
+                       break;
+               }
+       }
+}
+
+/*
+ * Set the touch_state of touches that have ended. TOUCH_END is a
+ * transitional state and will be overidden with TOUCH_NONE before
+ * process_input() returns.
+ */
+static __inline void
+clear_touches(struct wsmouseinput *input, enum tpad_touch_state state)
+{
+       u_int touches;
+       int slot;
+
+       touches = input->mt.sync[MTS_TOUCH] & ~input->mt.touches;
+       FOREACHBIT(touches, slot)
+               input->tp->tpad_touches[slot].state = state;
+}
+
+void
+wstpad_mt_inputs(struct wsmouseinput *input)
+{
+       struct wstpad *tp = input->tp;
+       struct tpad_touch *t;
+       struct mt_slot *mts;
+       int slot, dx, dy;
+       u_int touches;
+
+       /* TOUCH_BEGIN */
+       touches = input->mt.touches & input->mt.sync[MTS_TOUCH];
+       FOREACHBIT(touches, slot) {
+               t = &tp->tpad_touches[slot];
+               t->state = TOUCH_BEGIN;
+               mts = &input->mt.slots[slot];
+               t->x = normalize_x(&input->params, mts->x);
+               t->y = normalize_y(&input->params, mts->y);
+               t->orig.x = t->x;
+               t->orig.y = t->y;
+               timespeccpy(&t->orig.time, &tp->now);
+               t->flags = 0;
+               wstpad_set_direction(input, t, 0, 0);
+       }
+
+       /* TOUCH_UPDATE */
+       touches = input->mt.touches & ~input->mt.sync[MTS_TOUCH];
+       FOREACHBIT(touches, slot) {
+               t = &tp->tpad_touches[slot];
+               t->state = TOUCH_UPDATE;
+               mts = &input->mt.slots[slot];
+               dx = normalize_x(&input->params, mts->x) - t->x;
+               t->x += dx;
+               dy = normalize_y(&input->params, mts->y) - t->y;
+               t->y += dy;
+               wstpad_set_direction(input, t, dx, dy);
+       }
+
+       clear_touches(input, TOUCH_END);
+}
+
+void
+wstpad_touch_inputs(struct wsmouseinput *input)
+{
+       struct wstpad *tp = input->tp;
+       struct tpad_touch *t;
+       int slot;
+
+       tp->dx = normalize_dx(&input->params, input->motion.dx);
+       tp->dy = normalize_dy(&input->params, input->motion.dy);
+       tp->fingers = input->touch.fingers;
+       tp->prev_fingers = input->touch.prev_fingers;
+       tp->btns = input->btn.buttons;
+       tp->btns_sync = input->btn.sync;
+
+       if (IS_MT(tp)) {
+               wstpad_mt_inputs(input);
+               if (input->mt.ptr) {
+                       slot = ffs(input->mt.ptr) - 1;
+                       tp->t = &tp->tpad_touches[slot];
+               }
+       } else {
+               t = tp->t;
+               t->x = normalize_x(&input->params, input->motion.x);
+               t->y = normalize_y(&input->params, input->motion.y);
+               if (tp->fingers)
+                       t->state = tp->prev_fingers ?
+                           TOUCH_UPDATE : TOUCH_BEGIN;
+               else
+                       t->state = tp->prev_fingers ?
+                           TOUCH_END : TOUCH_NONE;
+
+               if (t->state == TOUCH_BEGIN) {
+                       t->orig.x = t->x;
+                       t->orig.y = t->y;
+                       timespeccpy(&t->orig.time, &tp->now);
+                       t->flags = 0;
+               }
+               /* Use unfiltered deltas for determining directions: */
+               wstpad_set_direction(input, t,
+                   normalize_dx(&input->params, input->motion.x_delta),
+                   normalize_dy(&input->params, input->motion.y_delta));
+       }
+}
+
+/*
+ * This is the entry point for input processing. It is called from
+ * wsmouse_input_sync when its state updates and compat-mode conversions
+ * are finished, and before the internal 'sync' functions will be called.
+ */
+void
+wstpad_process_input(struct wsmouseinput *input, struct evq_access *evq)
+{
+       struct wstpad *tp = input->tp;
+       u_int handlers, hdlr, cmds;
+
+       timespeccpy(&tp->now, &evq->ts);
+       wstpad_touch_inputs(input);
+       handlers = tp->handlers;
+       cmds = 0;
+       if (tp->flags & TP_DISABLE)
+               handlers &= ~(1 << TOPBUTTON_HDLR);
+
+       FOREACHBIT(handlers, hdlr) {
+               switch (hdlr) {
+               case PALM_HDLR:
+                       wstpad_palm(input, &cmds);
+                       continue;
+               case CLICK_HDLR:
+                       /* wstpad_click(input, &cmds); */
+                       continue;
+               case SOFTBUTTON_HDLR:
+               case TOPBUTTON_HDLR:
+                       wstpad_softbuttons(input, &cmds, hdlr);
+                       continue;
+               case F2CLICK_HDLR:
+                       //wstpad_f2click(input, &cmds);
+                       continue;
+               case TAP_HDLR:
+                       wstpad_tap(input, &cmds);
+                       continue;
+               case F2SCROLL_HDLR:
+                       wstpad_f2scroll(input, &cmds);
+                       continue;
+               case EDGESCROLL_HDLR:
+                       wstpad_edgescroll(input, &cmds);
+                       continue;
+               }
+       }
+
+       if (tp->dx || tp->dy)
+               wstpad_motion(tp, &cmds);
+
+       wstpad_events(input, evq, cmds);
+
+       clear_touches(input, TOUCH_NONE);
+}
+
+/* Integer square root (John Halleck's method) */
+static __inline int
+isqrt(int n)
+{
+       int root, sqbit;
+
+       root = 0;
+       sqbit = 1 << (sizeof(int) * 8 - 2);
+       while (sqbit) {
+               if (n >= (sqbit | root)) {
+                       n -= (sqbit | root);
+                       root = (root >> 1) | sqbit;
+               } else {
+                       root >>= 1;
+               }
+               sqbit >>= 2;
+       }
+       return (root);
+}
+
+/*
+ * TODO: The default configurations don't use the resolution values yet.
+ * The parameters are derived from the length of the diagonal in device
+ * units, with some magic constants which are partly adapted from libinput
+ * or synaptics code, or are based on tests and guess work.
+ */
+
+void
+wstpad_dimensions(struct wsmouseinput *input,
+    int *width, int *height, int *diag, int *h_unit, int *v_unit)
+{
+       int w, h;
+
+       w = abs(input->hw.x_max - input->hw.x_min);
+       h = abs(input->hw.y_max - input->hw.y_min);
+       *width = w;
+       *height = h;
+       *diag = isqrt(w * w + h * h);
+       /*
+        * Various parameters are derived from h_unit and v_unit. The
+        * untransformed values are used as default hysteresis threshold.
+        */
+       *h_unit = imax(*diag / 280, 3);
+       *v_unit = *h_unit;
+}
+
+int
+wstpad_configure_filter(struct wsmouseinput *input)
+{
+       struct wsmouseparams *params = &input->params;
+       int w, h, diag, h_unit, v_unit;
+
+       wstpad_dimensions(input,
+           &w, &h, &diag, &h_unit, &v_unit);
+       if (w == 0 || h == 0)
+               return (-1);
+
+       params->h_hysteresis = h_unit;
+       params->v_hysteresis = v_unit;
+       params->dclr = h_unit - h_unit / 4;
+
+       params->dx_mul = params->dy_mul = imin(1200, diag);
+       params->dx_div = params->dy_div = diag;
+
+       if (input->hw.flags & WSMHW_LR_DOWN)
+               params->y_inv = input->hw.y_max + input->hw.y_min;
+
+       wsmouse_init_scaling(input);
+       wsmouse_init_deceleration(input);
+       input->flags |= TRACK_INTERVAL;
+
+       return (0);
+}
+
+void
+wstpad_configure_edges(struct wsmouseinput *input)
+{
+       struct wstpad *tp = input->tp;
+       int w, h;
+
+       w = abs(input->hw.x_max - input->hw.x_min);
+       h = abs(input->hw.y_max - input->hw.y_min);
+
+       tp->edge.left = input->hw.x_min;
+       tp->edge.right = input->hw.x_max;
+       tp->edge.bottom = input->hw.y_min;
+       tp->edge.top = input->hw.y_max;
+       tp->edge.center = (tp->edge.left + tp->edge.right) / 2;
+       tp->edge.center_left = tp->edge.center - (w / 10);
+       tp->edge.center_right = tp->edge.center + (w / 10);
+       tp->edge.middle = (tp->edge.top - tp->edge.bottom) / 2;
+       tp->edge.hscroll = tp->edge.bottom;
+       tp->edge.vscroll = tp->edge.right;
+
+       if (tp->features & WSTPAD_SOFTBUTTONS)
+               tp->edge.bottom += (h / 16);
+       if (tp->features & WSTPAD_TOPBUTTONS)
+               tp->edge.top -= (h / 16);
+
+       /*
+        * libinput enables palm detection for touchpads with a
+        * width > 70mm and sets palm edges to 5% of the width.
+        */
+       if (tp->features & WSTPAD_PALM) {
+               tp->edge.left += (w / 20);
+               tp->edge.right -= (w / 20);
+       }
+       if (tp->features & WSTPAD_EDGESCROLL) {
+               /* Reset the palm edge if necessary. */
+               tp->edge.right = input->hw.x_max;
+               tp->edge.vscroll -= (w / 20);
+       }
+}
+
+
+int
+wstpad_configure(struct wsmouseinput *input)
+{
+       struct wstpad *tp;
+       int w, h, diag, h_unit, v_unit;
+       int slots;
+
+       wstpad_dimensions(input,
+           &w, &h, &diag, &h_unit, &v_unit);
+       if (w == 0 || h == 0)
+               return (-1);    /* We can't do anything. */
+
+       tp = input->tp;
+       if (tp == NULL) {
+               input->tp = tp = malloc(sizeof(struct wstpad),
+                   M_DEVBUF, M_WAITOK | M_ZERO);
+               if (tp == NULL)
+                       return (-1);
+
+               slots = imax(input->mt.num_slots, 1);
+               tp->tpad_touches = malloc(
+                   slots * sizeof(struct tpad_touch),
+                   M_DEVBUF, M_WAITOK | M_ZERO);
+               if (tp->tpad_touches == NULL) {
+                       free(tp, M_DEVBUF, sizeof(struct wstpad));
+                       return (-1);
+               }
+
+               tp->t = &tp->tpad_touches[0];
+               if (input->mt.num_slots)
+                       tp->flags |= TP_IS_MT;
+       }
+
+       tp->features = input->options;
+       if (tp->features == 0) {
+               tp->features = (input->hw.hw_type == WSMHW_TOUCHPAD ?
+                   WSTPAD_TOUCHPAD_DEFAULTS : WSTPAD_CLICKPAD_DEFAULTS);
+               if (input->hw.fingers_max == 1) {
+                       tp->features &= ~WSTPAD_F2SCROLL;
+                       tp->features |= WSTPAD_EDGESCROLL;
+               }
+       }
+
+       wstpad_configure_edges(input);
+
+       timeout_set(&tp->timeout, wstpad_timeout, input);
+
+       tp->handlers = 0;
+
+       if (tp->features & WSTPAD_TAPPING) {
+               tp->tap.hlimit = 3 * h_unit;
+               tp->tap.vlimit = 3 * v_unit;
+               tp->tap.taptime.tv_nsec = TAP_DEFAULT_TAPTIME * 1000000;
+               tp->tap.waittime_ms = TAP_DEFAULT_WAITTIME;
+               tp->tap.drag_enabled = 1;
+               tp->tap.locktime_ms = TAP_DEFAULT_LOCKTIME;
+               tp->tap.lock_enabled = 1;
+
+               tp->handlers |= 1 << TAP_HDLR;
+       }
+       if (tp->features & WSTPAD_PALM) {
+               tp->palm.wait.tv_nsec = PALM_WAIT * 1000000;
+               tp->handlers |= 1 << PALM_HDLR;
+       }
+       if (tp->features & WSTPAD_F2SCROLL) {
+               tp->scroll.h_dist = 4 * h_unit;
+               tp->scroll.v_dist = 4 * v_unit;
+               tp->handlers |= 1 << F2SCROLL_HDLR;
+       }
+       if (tp->features & WSTPAD_EDGESCROLL)
+               tp->handlers |= 1 << EDGESCROLL_HDLR;
+
+       if (tp->features & WSTPAD_SOFTBUTTONS)
+               tp->handlers |= 1 << SOFTBUTTON_HDLR;
+       if (tp->features & WSTPAD_TOPBUTTONS)
+               tp->handlers |= 1 << TOPBUTTON_HDLR;
+       if (tp->features & WSTPAD_SWAP_SIDES)
+               tp->sbtnmsk = (LEFTBTN | RIGHTBTN);
+
+       return (0);
+}
+
+void
+wstpad_destroy(struct wsmouseinput *input)
+{
+       struct wstpad *tp = input->tp;
+
+       if (tp == NULL)
+               return;
+
+       input->tp = NULL;
+
+       timeout_del(&tp->timeout);
+       free(tp->tpad_touches, M_DEVBUF,
+           imax(input->mt.num_slots, 1) * sizeof(struct tpad_touch));
+
+       free(tp, M_DEVBUF, sizeof(struct wstpad));
+}

Reply via email to