This patch for ubcmtp makes it use the multitouch-input functions of wsmouse. It's the first driver that would apply the "tracking" variant (wsmouse_mtframe).
No wonders will result from the change, but the two-finger gestures that involve movement - scrolling and click-and-drag with two fingers on a clickpad - should work without flaws. A quick way to check whether all touch positions are available and the selection of the active touch works properly is two-finger-scrolling, performed with only one finger moving, then switching to the other one. Please note that click-and-drag will still require that the "ClickPad" attribute is set in the synaptics(4) configuration. The patch has been tested on a MacBookPro 8,2 (from 2011). It would be nice to learn that it doesn't introduce regressions on older or newer models. If you don't use a brand-new version of the synaptics driver, you may encounter the following bug: If the X cursor is in a window with scrollable content and you put two finger on the touchpad without moving them, the window content may quickly scroll up and down by a small distance. This bug is fixed in the latest version. Index: dev/usb/ubcmtp.c =================================================================== RCS file: /cvs/src/sys/dev/usb/ubcmtp.c,v retrieving revision 1.12 diff -u -p -r1.12 ubcmtp.c --- dev/usb/ubcmtp.c 30 Mar 2016 23:34:12 -0000 1.12 +++ dev/usb/ubcmtp.c 9 Mar 2017 22:17:50 -0000 @@ -125,6 +125,12 @@ struct ubcmtp_finger { #define UBCMTP_SN_COORD 250 #define UBCMTP_SN_ORIENT 10 +/* Identify clickpads in ubcmtp_configure. */ +#define IS_CLICKPAD(ubcmtp_type) (ubcmtp_type != UBCMTP_TYPE1) + +/* Use a constant, synaptics-compatible pressure value for now. */ +#define DEFAULT_PRESSURE 40 + struct ubcmtp_limit { int limit; int min; @@ -316,17 +322,6 @@ static struct ubcmtp_dev ubcmtp_devices[ }, }; -/* coordinates for each tracked finger */ -struct ubcmtp_pos { - int down; - int x; - int y; - int z; - int w; - int dx; - int dy; -}; - struct ubcmtp_softc { struct device sc_dev; /* base device */ @@ -355,7 +350,8 @@ struct ubcmtp_softc { uint32_t sc_status; #define UBCMTP_ENABLED 1 - struct ubcmtp_pos pos[UBCMTP_MAX_FINGERS]; + struct mtpoint frame[UBCMTP_MAX_FINGERS]; + int contacts; int btn; }; @@ -519,6 +515,24 @@ ubcmtp_activate(struct device *self, int } int +ubcmtp_configure(struct ubcmtp_softc *sc) +{ + struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev); + + hw->type = WSMOUSE_TYPE_ELANTECH; /* see ubcmtp_ioctl */ + hw->hw_type = (IS_CLICKPAD(sc->dev_type->type) + ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD); + hw->x_min = sc->dev_type->l_x.min; + hw->x_max = sc->dev_type->l_x.max; + hw->y_min = sc->dev_type->l_y.min; + hw->y_max = sc->dev_type->l_y.max; + hw->mt_slots = UBCMTP_MAX_FINGERS; + hw->flags = WSMOUSEHW_MT_TRACKING; + + return wsmouse_configure(sc->sc_wsmousedev, NULL, 0); +} + +int ubcmtp_enable(void *v) { struct ubcmtp_softc *sc = v; @@ -534,6 +548,9 @@ ubcmtp_enable(void *v) return (1); } + if (ubcmtp_configure(sc)) + return (1); + if (ubcmtp_setup_pipes(sc) == 0) { sc->sc_status |= UBCMTP_ENABLED; return (0); @@ -608,6 +625,7 @@ ubcmtp_ioctl(void *v, unsigned long cmd, wsmode); return (EINVAL); } + wsmouse_set_mode(sc->sc_wsmousedev, wsmode); DPRINTF("%s: changing mode to %s\n", sc->sc_dev.dv_xname, (wsmode == WSMOUSE_COMPAT ? "compat" : @@ -779,7 +797,7 @@ ubcmtp_tp_intr(struct usbd_xfer *xfer, v struct ubcmtp_softc *sc = priv; struct ubcmtp_finger *pkt; u_int32_t pktlen; - int i, diff = 0, finger = 0, fingers = 0, s, t; + int i, s, btn, contacts, fingers; if (usbd_is_dying(sc->sc_udev) || !(sc->sc_status & UBCMTP_ENABLED)) return; @@ -803,76 +821,30 @@ ubcmtp_tp_intr(struct usbd_xfer *xfer, v pkt = (struct ubcmtp_finger *)(sc->tp_pkt + sc->tp_offset); fingers = (pktlen - sc->tp_offset) / sizeof(struct ubcmtp_finger); + contacts = 0; for (i = 0; i < fingers; i++) { - if ((int16_t)letoh16(pkt[i].touch_major) == 0) { - /* finger lifted */ - sc->pos[i].down = 0; - diff = 1; - continue; - } - - if (!sc->pos[finger].down) { - sc->pos[finger].down = 1; - diff = 1; - } - - if ((t = (int16_t)letoh16(pkt[i].abs_x)) != sc->pos[finger].x) { - sc->pos[finger].x = t; - diff = 1; - } - - if ((t = (int16_t)letoh16(pkt[i].abs_y)) != sc->pos[finger].y) { - sc->pos[finger].y = t; - diff = 1; - } - - if ((t = (int16_t)letoh16(pkt[i].rel_x)) != sc->pos[finger].dx) { - sc->pos[finger].dx = t; - diff = 1; - } - - if ((t = (int16_t)letoh16(pkt[i].rel_y)) != sc->pos[finger].dy) { - sc->pos[finger].dy = t; - diff = 1; - } - - finger++; - } - - if (sc->dev_type->type == UBCMTP_TYPE2 || - sc->dev_type->type == UBCMTP_TYPE3 || - sc->dev_type->type == UBCMTP_TYPE4) { - if (sc->dev_type->type == UBCMTP_TYPE2) - t = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE2_BTOFF])); - else if (sc->dev_type->type == UBCMTP_TYPE3) - t = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE3_BTOFF])); - else if (sc->dev_type->type == UBCMTP_TYPE4) - t = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE4_BTOFF])); - - if (sc->btn != t) { - sc->btn = t; - diff = 1; - - DPRINTF("%s: [button]\n", sc->sc_dev.dv_xname); - } - } + if ((int16_t)letoh16(pkt[i].touch_major) == 0) + continue; /* finger lifted */ + sc->frame[contacts].x = (int16_t)letoh16(pkt[i].abs_x); + sc->frame[contacts].y = (int16_t)letoh16(pkt[i].abs_y); + sc->frame[contacts].pressure = DEFAULT_PRESSURE; + contacts++; + } + + btn = sc->btn; + if (sc->dev_type->type == UBCMTP_TYPE2) + sc->btn = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE2_BTOFF])); + else if (sc->dev_type->type == UBCMTP_TYPE3) + sc->btn = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE3_BTOFF])); + else if (sc->dev_type->type == UBCMTP_TYPE4) + sc->btn = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE4_BTOFF])); - if (diff) { + if (contacts || sc->contacts || sc->btn != btn) { + sc->contacts = contacts; s = spltty(); - - DPRINTF("%s: ", sc->sc_dev.dv_xname); - - 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_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_buttons(sc->sc_wsmousedev, sc->btn); + wsmouse_mtframe(sc->sc_wsmousedev, sc->frame, contacts); + wsmouse_input_sync(sc->sc_wsmousedev); splx(s); } }