I have been asked to prepare a less invasive version of
the MT diffs I posted a week ago. Here it is. It doesn't
delete wsmouse_input(). The old and the new infrastructure
coexist in wsmouse, and only the touchpad drivers have been
changed to use the new one. Except for a stylistic detail
(s/timespeccpy/memcpy/), nothing else has changed, and the
descriptions I added last week apply to this version as
well (the synaptics diff, which needs the modified wsconsio.h,
is the first one here, the rest is for the kernel).

Tests have been made with an Elantech-v4 touchpad, an ALPS
Glidepoint, and an old Synaptics touchpad. I couldn't test
the older Elantech models, more recent Synaptics models, or
models that run with hidmt or ubcmtp. (The December version
had more tests, but this isn't exactly the same code anymore.)


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    11 Mar 2016 20:46:30 -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_CONTACTS:
+            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_RESET:
+            /*
+             * The contact 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: dev/hid/hidmt.c
===================================================================
RCS file: /cvs/src/sys/dev/hid/hidmt.c,v
retrieving revision 1.1
diff -u -p -r1.1 hidmt.c
--- dev/hid/hidmt.c     20 Jan 2016 01:26:00 -0000      1.1
+++ dev/hid/hidmt.c     20 Mar 2016 14:22:49 -0000
@@ -329,20 +329,15 @@ hidmt_input(struct hidmt *mt, uint8_t *d
                                        width = 50;
                        }

-                       wsmouse_input(mt->sc_wsmousedev, mt->sc_button,
+                       WSMOUSE_TOUCH(mt->sc_wsmousedev, mt->sc_button,
                            (mt->last_x = mt->sc_contacts[i].x),
                            (mt->last_y = mt->sc_contacts[i].y),
-                           width, tips,
-                           WSMOUSE_INPUT_ABSOLUTE_X |
-                           WSMOUSE_INPUT_ABSOLUTE_Y |
-                           WSMOUSE_INPUT_ABSOLUTE_Z |
-                           WSMOUSE_INPUT_ABSOLUTE_W);
+                           width, tips);
                } else {
-                       wsmouse_input(mt->sc_wsmousedev, mt->sc_button,
+                       WSMOUSE_INPUT(mt->sc_wsmousedev, mt->sc_button,
                            (mt->last_x - mt->sc_contacts[i].x),
                            (mt->last_y - mt->sc_contacts[i].y),
-                           0, 0,
-                           WSMOUSE_INPUT_DELTA);
+                           0, 0);
                        mt->last_x = mt->sc_contacts[i].x;
                        mt->last_y = mt->sc_contacts[i].y;
                }
Index: dev/pckbc/pms.c
===================================================================
RCS file: /cvs/src/sys/dev/pckbc/pms.c,v
retrieving revision 1.68
diff -u -p -r1.68 pms.c
--- dev/pckbc/pms.c     27 Feb 2016 22:01:58 -0000      1.68
+++ dev/pckbc/pms.c     20 Mar 2016 14:22:52 -0000
@@ -143,17 +143,8 @@ struct elantech_softc {

        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;
+
+       u_int mt_slots;

        int width;

@@ -997,8 +988,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
@@ -1162,7 +1152,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, dx, dy, width;

        w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) |
            ((sc->packet[3] & 0x04) >> 2);
@@ -1230,8 +1220,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;
                }

@@ -1254,9 +1245,14 @@ pms_proc_synaptics(struct pms_softc *sc)
        }

        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) {
+                       width = imax(w, 4);
+                       w = (w < 2 ? w + 2 : 1);
+               } else {
+                       width = w = 0;
+               }
+               wsmouse_set(sc->sc_wsmousedev, WSMOUSE_TOUCH_WIDTH, width, 0);
+               WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
        } else {
                dx = dy = 0;
                if (z > SYNAPTICS_PRESSURE) {
@@ -1266,8 +1262,7 @@ pms_proc_synaptics(struct pms_softc *sc)
                        dy /= SYNAPTICS_SCALE;
                }
                if (dx || dy || buttons != syn->old_buttons)
-                       wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
-                           WSMOUSE_INPUT_DELTA);
+                       WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, 0, 0);
                syn->old_buttons = buttons;
        }

@@ -1313,8 +1308,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);
 }
@@ -1514,7 +1508,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 +1526,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;
        }
@@ -1552,35 +1545,21 @@ pms_proc_alps(struct pms_softc *sc)
        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);
+                       WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,
+                           alps->old_x, alps->old_y, ALPS_PRESSURE, 0);
                        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);
+                               WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,
+                                   alps->old_x, alps->old_y, 0, 0);
                        }
                }

                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 (gesture != ALPS_TAP)
+                       WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, 0);

                if (alps->gesture != ALPS_DRAG || gesture != ALPS_TAP)
                        alps->gesture = gesture;
@@ -1600,8 +1579,7 @@ pms_proc_alps(struct pms_softc *sc)
                }

                if (dx || dy || buttons != alps->old_buttons)
-                       wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
-                           WSMOUSE_INPUT_DELTA);
+                       WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, 0, 0);

                alps->old_x = x;
                alps->old_y = y;
@@ -2045,6 +2023,18 @@ err:
 }

 int
+pms_elantech_v4_configure(struct device *sc_wsmousedev,
+    struct elantech_softc *elantech)
+{
+       if (wsmouse_mt_init(sc_wsmousedev, ELANTECH_MAX_FINGERS, 0))
+               return (-1);
+
+       wsmouse_set_param(sc_wsmousedev, WSMPARAM_DX_DIV, SYNAPTICS_SCALE);
+       wsmouse_set_param(sc_wsmousedev, WSMPARAM_DY_DIV, SYNAPTICS_SCALE);
+       return (0);
+}
+
+int
 pms_enable_elantech_v4(struct pms_softc *sc)
 {
        struct elantech_softc *elantech = sc->elantech;
@@ -2068,6 +2058,15 @@ pms_enable_elantech_v4(struct pms_softc
                        goto err;
                }

+               if (pms_elantech_v4_configure(
+                   sc->sc_wsmousedev, sc->elantech)) {
+                       free(sc->elantech, M_DEVBUF, 0);
+                       sc->elantech = NULL;
+                       printf("%s: setup failed\n", DEVNAME(sc));
+                       goto err;
+               }
+               wsmouse_set_mode(sc->sc_wsmousedev, WSMOUSE_COMPAT);
+
                printf("%s: Elantech Clickpad, version %d, firmware 0x%x\n",
                    DEVNAME(sc), 4, sc->elantech->fw_version);
        } else if (elantech_set_absolute_mode_v4(sc))
@@ -2107,6 +2106,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);
@@ -2352,41 +2353,29 @@ 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 id, weight, n, x, y, z;
+       u_int buttons, slots;

        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;
-
-               for (elantech->mt_count = 0; slots != 0; slots >>= 1)
-                       elantech->mt_count += (1 & slots);
-
+               slots = elantech->mt_slots;
+               elantech->mt_slots = sc->packet[1] & 0x1f;
+               slots &= ~elantech->mt_slots;
+               for (id = 0; slots; id++, slots >>= 1) {
+                       if (slots & 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 +2385,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;
@@ -2421,37 +2400,15 @@ pms_proc_elantech_v4(struct pms_softc *s
                    sc->packet[3] & 0x1f);
                return;
        }
-}

-void
-elantech_send_mt_input(struct pms_softc *sc, int id)
-{
-       struct elantech_softc *elantech = sc->elantech;
+       buttons = 0;
+       if (sc->packet[0] & 0x01)
+               buttons |= WSMOUSE_BUTTON(1);
+       if (sc->packet[0] & 0x02)
+               buttons |= WSMOUSE_BUTTON(3);
+       wsmouse_buttons(sc_wsmousedev, buttons);

-       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
@@ -2474,11 +2431,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->sc_wsmousedev, buttons, x, y, z, w);
        } else {
                dx = dy = 0;

@@ -2490,8 +2443,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->sc_wsmousedev, buttons, dx, dy, 0, 0);
                elantech->old_buttons = buttons;
        }

Index: dev/usb/ubcmtp.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/ubcmtp.c,v
retrieving revision 1.11
diff -u -p -r1.11 ubcmtp.c
--- dev/usb/ubcmtp.c    4 Dec 2015 16:22:27 -0000       1.11
+++ dev/usb/ubcmtp.c    20 Mar 2016 14:22:52 -0000
@@ -865,20 +865,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);
        }
@@ -915,17 +908,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/wscons/wsconsio.h
===================================================================
RCS file: /cvs/src/sys/dev/wscons/wsconsio.h,v
retrieving revision 1.73
diff -u -p -r1.73 wsconsio.h
--- dev/wscons/wsconsio.h       12 Dec 2015 12:30:18 -0000      1.73
+++ dev/wscons/wsconsio.h       20 Mar 2016 14:22:52 -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,20 @@ 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
+ *
+ * A RESET event will be generated whenever a change of X and Y is
+ * coupled with a change of the contact 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_CONTACTS     WSCONS_EVENT_MOUSE_ABSOLUTE_W
+
+#define        WSCONS_EVENT_TOUCH_WIDTH        24      /* contact width */
+#define        WSCONS_EVENT_TOUCH_RESET        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        20 Mar 2016 14:22:52 -0000
@@ -88,10 +88,12 @@
 #include <sys/device.h>
 #include <sys/vnode.h>
 #include <sys/poll.h>
+#include <sys/malloc.h>

 #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>

@@ -132,6 +134,8 @@ struct wsmouse_softc {
        int             sc_z;           /* absolute-z */
        int             sc_w;           /* absolute-w */

+       struct wsmouseinput input;
+
        int             sc_refcnt;
        u_char          sc_dying;       /* device is being detached */
 };
@@ -199,6 +203,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,6 +285,8 @@ wsmouse_detach(struct device *self, int
        mn = self->dv_unit;
        vdevgone(maj, mn, mn, VCHR);

+       wsmouse_input_cleanup(&sc->input);
+
        return (0);
 }

@@ -734,3 +742,836 @@ wsmouse_add_mux(int unit, struct wsmux_s
        return (wsmux_attach_sc(muxsc, &sc->sc_base));
 }
 #endif /* NWSMUX > 0 */
+
+
+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
+normalized_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 contacts)
+{
+       struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+       struct touch_state *touch = &input->touch;
+
+       pressure = normalized_pressure(input, pressure);
+       contacts = (pressure ? imax(contacts, 1) : 0);
+
+       if (pressure == 0 || pressure != touch->pressure) {
+               /*
+                * pressure == 0: Drivers may report possibly arbitrary
+                * coordinates in this case; touch_update will correct them.
+                */
+               touch->pressure = pressure;
+               touch->sync |= SYNC_PRESSURE;
+       }
+       if (contacts != touch->contacts) {
+               touch->contacts = contacts;
+               touch->sync |= SYNC_CONTACTS;
+       }
+}
+
+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 bit;
+       int initial;
+
+       if (slot < 0 || slot >= mt->num_slots)
+               return;
+
+       bit = (1 << slot);
+       mt->frame |= bit;
+
+       /* Is this a new touch? */
+       initial = ((mt->touches & bit) == (mt->sync[MTS_TOUCH] & bit));
+
+       mts = &mt->slots[slot];
+       if (x != mts->x || initial) {
+               mts->x = x;
+               mt->sync[MTS_X] |= bit;
+       }
+       if (y != mts->y || initial) {
+               mts->y = y;
+               mt->sync[MTS_Y] |= bit;
+       }
+       pressure = normalized_pressure(input, pressure);
+       if (pressure != mts->pressure || initial) {
+               mts->pressure = pressure;
+               mt->sync[MTS_PRESSURE] |= bit;
+
+               if (pressure) {
+                       if ((mt->touches & bit) == 0) {
+                               mt->num_touches++;
+                               mt->touches |= bit;
+                               mt->sync[MTS_TOUCH] |= bit;
+                       }
+               } else if (mt->touches & bit) {
+                       mt->num_touches--;
+                       mt->touches ^= bit;
+                       mt->sync[MTS_TOUCH] |= bit;
+               }
+       }
+}
+
+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.contacts);
+               return;
+       case WSMOUSE_CONTACTS:
+               /* Contact counts can be overridden by wsmouse_touch. */
+               if (value != input->touch.contacts) {
+                       input->touch.contacts = value;
+                       input->touch.sync |= SYNC_CONTACTS;
+               }
+               return;
+       case WSMOUSE_TOUCH_WIDTH:
+               if (value != input->touch.width) {
+                       input->touch.width = value;
+                       input->touch.sync |= SYNC_TOUCH_WIDTH;
+               }
+               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_CONTACTS)
+               /* 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. */
+void
+wsmouse_mt_update(struct wsmouseinput *input)
+{
+       int i;
+
+       /*
+        * The same as above: There may be arbitrary coordinates if
+        * (pressure == 0). Clear the sync 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.
+ *
+ * Pointer-control is assigned to slots with non-zero motion deltas if
+ * at least one such slot exists. This function 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 a slot that is
+ * being updated is already a member. If a cycle ends that doesn't
+ * contain the pointer-controlling slot, a new slot 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;
+               memcpy(&ev->time, &evq->ts, sizeof(struct timespec));
+               evq->result |= EVQ_RESULT_SUCCESS;
+       } else {
+               evq->result = EVQ_RESULT_OVERFLOW;
+       }
+}
+
+
+void
+wsmouse_btn_sync(struct wsmouseinput *input, struct evq_access *evq)
+{
+       struct btn_state *btn = &input->btn;
+       int button, ev_type;
+       u_int bit, sync;
+
+       for (sync = btn->sync; sync; sync ^= bit) {
+               button = ffs(sync) - 1;
+               bit = (1 << button);
+               ev_type = (btn->buttons & bit) ? 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 = (params->x_inv
+                           ? params->x_inv - motion->x : motion->x);
+                       wsmouse_evq_put(evq, ABS_X_EV(input->flags), x);
+               }
+               if (motion->sync & SYNC_Y) {
+                       y = (params->y_inv
+                           ? params->y_inv - motion->y : motion->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_RESET, 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_CONTACTS)
+               wsmouse_evq_put(evq, ABS_W_EV, touch->contacts);
+       if ((touch->sync & SYNC_TOUCH_WIDTH)
+           && (input->flags & TPAD_NATIVE_MODE))
+               wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_WIDTH, touch->width);
+}
+
+/*
+ * 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;
+       struct wsmouseparams *params = &input->params;
+       int dx, dy, dz, dw;
+
+       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;
+
+       if ((params->dx_max && abs(dx) > params->dx_max)
+           || (params->dy_max && abs(dy) > params->dy_max)) {
+
+               dx = dy = 0;
+       }
+
+       wsmouse_motion(sc, dx, dy, dz, dw);
+
+       input->motion.sync &= ~SYNC_POSITION;
+       input->touch.sync = 0;
+}
+
+static __inline void
+clear_sync_flags(struct wsmouseinput *input)
+{
+       int i;
+
+       input->btn.sync = 0;
+       input->motion.sync = 0;
+       input->touch.sync = 0;
+       if (input->mt.frame) {
+               input->mt.frame = 0;
+               for (i = 0; i < MTS_SIZE; i++)
+                       input->mt.sync[i] = 0;
+       }
+}
+
+void
+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;
+       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 & TPAD_COMPAT_MODE)
+               wsmouse_compat_convert(sc, &evq);
+
+       if (input->flags & RESYNC) {
+               input->flags &= ~RESYNC;
+               input->motion.sync &= SYNC_POSITION;
+               input->motion.x_delta = input->motion.y_delta = 0;
+       }
+
+       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);
+       /* No MT events are generated yet. */
+
+       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);
+               }
+       }
+
+       if (evq.result != EVQ_RESULT_OVERFLOW)
+               clear_sync_flags(input);
+       else
+               input->flags |= RESYNC;
+}
+
+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;
+
+       if (mt->num_slots == 0)
+               return (-1);
+
+       FOREACHBIT(mt->touches, slot) {
+               if (mt->slots[slot].id == id)
+                       return slot;
+       }
+       slot = ffs(~(mt->touches | mt->frame)) - 1;
+       if (slot >= 0 && slot < mt->num_slots) {
+               mt->frame |= 1 << slot;
+               mt->slots[slot].id = id;
+               return (slot);
+       } else {
+               return (-1);
+       }
+}
+
+/*
+ * Find a minimum-weight matching for an m-by-n matrix.
+ *
+ * m must be greater than or equal to n. The size of the buffer must be
+ * at least 4m + 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).
+ *
+ * Wrong results because of overflows will not occur with input values
+ * in the range of 0 to INT_MAX / 2 inclusive.
+ *
+ * The function applies the Dinic-Kronrod algorithm. It is not modern or
+ * popular, but it seems to be a good choice for small matrices at least.
+ * The original form of the algorithm is modified as follows: There is no
+ * initial search for row minima, the initial assignments are in a
+ * "virtual" column with the index -1 and zero values. This permits inputs
+ * with n < m, and it simplifies the reassignments.
+ */
+void
+wsmouse_matching(int *matrix, int m, int n, int *buffer)
+{
+       int i, j, k, d, e, row, col, delta;
+       int *p;
+       int *r2c = buffer;      /* row-to-column assignments */
+       int *red = r2c + m;     /* reduced values of the assignments */
+       int *alt = red + m;     /* alternative assignments */
+       int *mc = alt + m;      /* row-wise minimal elements of cs */
+       int *cs = mc + m;       /* the column set */
+       int *c2r = cs + n;      /* column-to-row assignments in cs */
+       int *cd = c2r + n;      /* column deltas (reduction) */
+
+       for (p = r2c; p < red; *p++ = -1) {}
+       for (; p < alt; *p++ = 0) {}
+       for (col = 0; col < n; col++) {
+               delta = INT_MAX;
+               for (i = 0, p = matrix + col; i < m; i++, p += n)
+                       if ((d = *p - red[i]) <= delta) {
+                               delta = d;
+                               row = i;
+                       }
+               cd[col] = delta;
+               if (r2c[row] < 0) {
+                       r2c[row] = col;
+                       continue;
+               }
+               for (p = alt; p < mc; *p++ = -1) {}
+               for (; p < cs; *p++ = col) {}
+               for (k = 0; (j = r2c[row]) >= 0;) {
+                       cs[k++] = j;
+                       c2r[j] = row;
+                       alt[row] = mc[row];
+                       delta = INT_MAX;
+                       for (i = 0, p = matrix; i < m; i++, p += n)
+                               if (alt[i] < 0) {
+                                       d = p[mc[i]] - cd[mc[i]];
+                                       e = p[j] - cd[j];
+                                       if (e < d) {
+                                               d = e;
+                                               mc[i] = j;
+                                       }
+                                       d -= red[i];
+                                       if (d <= delta) {
+                                               delta = d;
+                                               row = i;
+                                       }
+                               }
+                       cd[col] += delta;
+                       for (i = 0; i < k; i++) {
+                               cd[cs[i]] += delta;
+                               red[c2r[cs[i]]] -= delta;
+                       }
+               }
+               for (j = mc[row]; (r2c[row] = j) != col;) {
+                       row = c2r[j];
+                       j = alt[row];
+               }
+       }
+}
+
+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, m, n, dx, dy, slot, maxdist;
+       int *p, *r2c, *c2r;
+       u_int touches;
+
+       if (mt->num_slots == 0 || mt->matrix == NULL)
+               return;
+
+       size = imax(0, imin(size, mt->num_slots));
+       p = mt->matrix;
+       touches = mt->touches;
+       if (mt->num_touches >= size) {
+               FOREACHBIT(touches, slot)
+                       for (i = 0; i < size; i++) {
+                               dx = pt[i].x - mt->slots[slot].x;
+                               dy = pt[i].y - mt->slots[slot].y;
+                               *p++ = dx * dx + dy * dy;
+                       }
+               m = mt->num_touches;
+               n = size;
+       } else {
+               for (i = 0; i < size; i++)
+                       FOREACHBIT(touches, slot) {
+                               dx = pt[i].x - mt->slots[slot].x;
+                               dy = pt[i].y - mt->slots[slot].y;
+                               *p++ = dx * dx + dy * dy;
+                       }
+               m = size;
+               n = mt->num_touches;
+       }
+       wsmouse_matching(mt->matrix, m, n, p);
+
+       r2c = p;
+       c2r = p + m;
+       maxdist = input->params.tracking_maxdist;
+       maxdist = (maxdist ? maxdist * maxdist : INT_MAX);
+       for (i = 0, p = mt->matrix; i < m; i++, p += n)
+               if ((j = r2c[i]) >= 0) {
+                       if (p[j] <= maxdist)
+                               c2r[j] = i;
+                       else
+                               c2r[j] = r2c[i] = -1;
+               }
+
+       p = (n == size ? c2r : r2c);
+       for (i = 0; i < size; i++)
+               if (*p++ < 0) {
+                       slot = ffs(~(mt->touches | mt->frame)) - 1;
+                       if (slot < 0 || slot >= mt->num_slots)
+                               break;
+                       wsmouse_mtstate(sc, slot,
+                           pt[i].x, pt[i].y, pt[i].pressure);
+                       pt[i].slot = slot;
+               }
+
+       p = (n == size ? r2c : c2r);
+       FOREACHBIT(touches, slot)
+               if ((i = *p++) >= 0) {
+                       wsmouse_mtstate(sc, slot,
+                           pt[i].x, pt[i].y, pt[i].pressure);
+                       pt[i].slot = slot;
+               } else {
+                       wsmouse_mtstate(sc, slot, 0, 0, 0);
+               }
+}
+
+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->flags & MT_TRACKING)
+                       size += MATRIX_SIZE(n);
+               input->mt.num_slots = 0;
+               free(input->mt.slots, M_DEVBUF, size);
+               input->mt.slots = NULL;
+               input->mt.matrix = NULL;
+       }
+}
+
+/* Allocate the MT slots and, if necessary, the buffers for MT tracking. */
+int
+wsmouse_mt_init(struct device *sc, int num_slots, int tracking)
+{
+       struct wsmouseinput *input =
+           &((struct wsmouse_softc *) sc)->input;
+       int n, size;
+
+       if (num_slots == input->mt.num_slots
+           && (!tracking == ((input->flags & MT_TRACKING) == 0)))
+               return (0);
+
+       free_mt_slots(input);
+
+       if (tracking)
+               input->flags |= MT_TRACKING;
+       else
+               input->flags &= ~MT_TRACKING;
+       n = imin(imax(num_slots, 0), WSMOUSE_MT_SLOTS_MAX);
+       if (n) {
+               size = n * sizeof(struct mt_slot);
+               if (input->flags & MT_TRACKING)
+                       size += MATRIX_SIZE(n);
+               input->mt.slots = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+               if (input->mt.slots != NULL) {
+                       if (input->flags & MT_TRACKING)
+                               input->mt.matrix = (int *)
+                                   (input->mt.slots + n);
+                       input->mt.num_slots = n;
+                       return (0);
+               }
+       }
+       return (-1);
+}
+
+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) {
+               /* Scale factors 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_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);
+       } 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 = params->pressure_hi;
+               input->touch.min_pressure = params->pressure_hi;
+       }
+}
+
+int
+wsmouse_set_mode(struct device *sc, int mode)
+{
+       struct wsmouseinput *input =
+           &((struct wsmouse_softc *) sc)->input;
+
+       if (mode == WSMOUSE_COMPAT) {
+               input->flags &= ~TPAD_NATIVE_MODE;
+               input->flags |= TPAD_COMPAT_MODE;
+               return (0);
+       } else if (mode == WSMOUSE_NATIVE) {
+               input->flags &= ~TPAD_COMPAT_MODE;
+               input->flags |= TPAD_NATIVE_MODE;
+               return (0);
+       }
+       return (-1);
+}
+
+void
+wsmouse_input_init(struct wsmouseinput *input, struct wseventvar **evar)
+{
+       input->evar = evar;
+}
+
+void
+wsmouse_input_cleanup(struct wsmouseinput *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   20 Mar 2016 14:22:52 -0000
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2015, 2016 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
+ */
+
+#ifndef _WSMOUSEINPUT_H_
+#define _WSMOUSEINPUT_H_
+
+
+struct btn_state {
+       u_int buttons;
+       u_int sync;
+};
+
+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_DELTAS            (1 << 0)
+#define SYNC_X                 (1 << 1)
+#define SYNC_Y                 (1 << 2)
+#define SYNC_POSITION          (SYNC_X | SYNC_Y)
+
+struct touch_state {
+       int pressure;
+       int contacts;
+       int width;
+       u_int sync;
+
+       int min_pressure;
+};
+#define SYNC_PRESSURE          (1 << 0)
+#define SYNC_CONTACTS          (1 << 1)
+#define SYNC_TOUCH_WIDTH       (1 << 2)
+
+struct mt_slot {
+       int x;
+       int y;
+       int pressure;
+       int id;         /* tracking ID */
+};
+#define MTS_TOUCH      0
+#define MTS_X          1
+#define MTS_Y          2
+#define MTS_PRESSURE   3
+
+#define MTS_SIZE       4
+
+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 the MT tracking function */
+       int *matrix;
+};
+
+
+struct axis_filter {
+       /* scale factor in [*.12] fixed-point format */
+       int scale;
+       int rmdr;
+};
+
+struct wsmouseinput {
+       u_int flags;
+
+       struct btn_state btn;
+       struct motion_state motion;
+       struct touch_state touch;
+       struct mt_state mt;
+
+       struct wsmouseparams params;
+       struct {
+               struct axis_filter h;
+               struct axis_filter v;
+       } fltr;
+
+       struct wseventvar **evar;
+};
+/* wsmouseinput.flags */
+#define TPAD_COMPAT_MODE       (1 << 0)
+#define TPAD_NATIVE_MODE       (1 << 1)
+#define SCALE_DELTAS           (1 << 2)
+#define MT_TRACKING            (1 << 3)
+#define SWAPXY                 (1 << 4)
+#define RESYNC                 (1 << 16)
+
+struct evq_access {
+       struct wseventvar *evar;
+       struct timespec ts;
+       int put;
+       int result;
+};
+#define EVQ_RESULT_OVERFLOW    -1
+#define EVQ_RESULT_NONE                0
+#define EVQ_RESULT_SUCCESS     1
+
+
+void wsmouse_evq_put(struct evq_access *, int, int);
+void wsmouse_init_scaling(struct wsmouseinput *);
+
+void wsmouse_input_init(struct wsmouseinput *, struct wseventvar **);
+void wsmouse_input_cleanup(struct wsmouseinput *);
+
+
+#define FOREACHBIT(v, i) \
+    for ((i) = ffs(v) - 1; (i) != -1; (i) = ffs((v) & (~1 << (i))) - 1)
+
+
+#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_CONTACTS
+#define BTN_DOWN_EV    WSCONS_EVENT_MOUSE_DOWN
+#define BTN_UP_EV      WSCONS_EVENT_MOUSE_UP
+#define SYNC_EV                WSCONS_EVENT_SYNC
+
+/* buffer size for wsmouse_matching */
+#define MATRIX_SIZE(slots) (((slots) + 7) * (slots) * sizeof(int))
+
+#endif /* _WSMOUSEINPUT_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     20 Mar 2016 14:22:52 -0000
@@ -32,6 +32,22 @@
  */

 /*
+ * Copyright (c) 2015, 2016 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 interfaces.
  */

@@ -75,3 +91,152 @@ int wsmousedevprint(void *, const char *

 void   wsmouse_input(struct device *kbddev, u_int btns,
                           int x, int y, int z, int w, u_int flags);
+
+
+/* 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, contacts)   \
+       do {                                                            \
+               wsmouse_buttons((sc_wsmousedev), (btns));               \
+               wsmouse_position((sc_wsmousedev), (x), (y));            \
+               wsmouse_touch((sc_wsmousedev), (pressure), (contacts)); \
+               wsmouse_input_sync(sc_wsmousedev);                      \
+       } while (0)
+
+
+/*
+ * Drivers for touchpads that don't report pressure values can pass
+ * WSMOUSE_DEFAULT_PRESSURE to wsmouse_touch or wsmouse_mtstate.
+ *
+ * A pressure value of 0 signals that a touch has been released (coordinates
+ * will be ignored). Based on its pressure argument, wsmouse_touch will
+ * normalize the contact count (drivers for touch devices that don't
+ * recognize multiple contacts can always pass 0 as contact count to
+ * wsmouse_touch).
+ */
+#define WSMOUSE_DEFAULT_PRESSURE       -1
+
+
+struct device;
+enum wsmouseval;
+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, contacts). */
+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);
+
+/* Assign or look up a slot number for a tracking ID (id). */
+int wsmouse_id_to_slot(struct device *, int);
+
+
+/* Synchronize (generate wscons events) */
+void wsmouse_input_sync(struct device *);
+
+
+/* Initialize MT structures (num_slots, tracking). */
+int wsmouse_mt_init(struct device *, int, int);
+
+/* 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 TOUCH_WIDTH
+ * cannot 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_CONTACTS,
+       WSMOUSE_TOUCH_WIDTH,
+       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)
+
+
+struct mtpoint {
+       int x;
+       int y;
+       int pressure;
+       int slot;               /* An output field, set by wsmouse_mtframe. */
+};
+
+
+struct wsmouseparams {
+       int x_inv;
+       int y_inv;
+
+       int dx_mul;             /* delta scaling */
+       int dx_div;
+       int dy_mul;
+       int dy_div;
+
+       int swapxy;
+
+       int pressure_lo;
+       int pressure_hi;
+
+       int dx_max;             /* (compat mode) */
+       int dy_max;
+
+       int tracking_maxdist;
+};
+
+#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_LASTFIELD     WSMPARAM_DY_MAX
+
+#define IS_WSMFLTR_PARAM(param) \
+    ((param) >= WSMPARAM_DX_MUL && (param) <= WSMPARAM_DY_DIV)
+
+#define WSMOUSE_MT_SLOTS_MAX   10

Reply via email to