Again, my mail client insisted on the "flowed" text format,
sorry. Here is a clean version of the diff.


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