This diff is an extension of Tobias Heider's proposal, which aims at
providing "Apple-like" button inputs on clickpads.  I have added some
things in order to approximate the behaviour of other input drivers.

It's a quick shot, and I have no idea whether it is sufficient in
practice, it certainly needs thorough testing.

The wsconsctl part doesn't provide a named field yet.  With a
recompiled wsconsctl and kernel, the command

    # wsconsctl mouse.param=72:1

activates the feature, if it is available (see below).

The patch contains a simple filter for distinguishing the two-finger
inputs that should trigger right-button events from the ones that
shouldn't:  If the distance between two contacts is small, the driver
generates a right-button event; if it is greater than some threshold
value, the second contact will be ignored.

When a touch is resting in the bottom area, it will be ignored, and no
further filtering applies to the other touches.

You can inspect the threshold value with

    # wsconsctl mouse.param=143

and change it with

    # wsconsctl mouse.param=143:<new value>

The value is given in device units.  If the driver for your touchpad is
imt(4), the default should correspond, roughly, to a distance of 35mm.
The threshold is reduced by one third if a two-finger click involves a
touch in the bottom area.  (On medium-sized touchpads, this may be
necessary to leave enough room for left-button clicks performed by the
thumb while the pointer-controlling touch remains on the touchpad.)

The feature won't work decently on small touchpads, and it cannot work
on touchpads without MT-support in our kernel.  wsmouse checks whether
a touchpad
    1) has MT support,
    2) is a clickpad,
    3) its resolution is reported to wsmouse,
    4) it reports a horizontal size greater than 100mm, and
    5) a vertical size greater than 60mm.

If these conditions aren't met, wsmouse sets the distance limit to -1,
which blocks the MTBUTTONS feature.  I think only imt(4) touchpads can
meet these criteria; however, the value can be overridden manually or
programmatically, and ubcmtp and aplms do this on initialization.
These drivers don't report resolution values; the distance limit will
be set to a fourth of the length of the touchpad diagonal.  That's a
workaround based on a wild guess, and I couldn't test it with Apple
hardware.  If you want to apply it to an Elantech-v4 touchpad run by
pms(4), try

    # wsconsctl mouse.param=143:0,72:1

(A change from -1 to 0 will trigger the workaround.)


diff --git a/sbin/wsconsctl/mousecfg.c b/sbin/wsconsctl/mousecfg.c
index 76a9984bd86..d6609218372 100644
--- a/sbin/wsconsctl/mousecfg.c
+++ b/sbin/wsconsctl/mousecfg.c
@@ -40,9 +40,9 @@
 #define TP_FILTER_FIRST                WSMOUSECFG_DX_MAX
 #define TP_FILTER_LAST         WSMOUSECFG_SMOOTHING
 #define TP_FEATURES_FIRST      WSMOUSECFG_SOFTBUTTONS
-#define TP_FEATURES_LAST       WSMOUSECFG_DISABLE
+#define TP_FEATURES_LAST       WSMOUSECFG_MTBUTTONS
 #define TP_SETUP_FIRST         WSMOUSECFG_LEFT_EDGE
-#define TP_SETUP_LAST          WSMOUSECFG_TAP_THREE_BTNMAP
+#define TP_SETUP_LAST          WSMOUSECFG_MTBTN_MAXDIST
 #define LOG_FIRST              WSMOUSECFG_LOG_INPUT
 #define LOG_LAST               WSMOUSECFG_LOG_EVENTS

diff --git a/sys/arch/arm64/dev/aplhidev.c b/sys/arch/arm64/dev/aplhidev.c
index 265c5196168..b3bf4838fe8 100644
--- a/sys/arch/arm64/dev/aplhidev.c
+++ b/sys/arch/arm64/dev/aplhidev.c
@@ -680,6 +680,10 @@ struct ubcmtp_finger {
 /* Use a constant, synaptics-compatible pressure value for now. */
 #define DEFAULT_PRESSURE       40

+static struct wsmouse_param aplms_wsmousecfg[] = {
+       { WSMOUSECFG_MTBTN_MAXDIST, 0 }, /* 0: Compute a default value. */
+};
+
 struct aplms_softc {
        struct device   sc_dev;
        struct device   *sc_wsmousedev;
@@ -759,7 +763,8 @@ aplms_configure(struct aplms_softc *sc)
        hw->mt_slots = UBCMTP_MAX_FINGERS;
        hw->flags = WSMOUSEHW_MT_TRACKING;

-       return wsmouse_configure(sc->sc_wsmousedev, NULL, 0);
+       return wsmouse_configure(sc->sc_wsmousedev,
+           aplms_wsmousecfg, nitems(aplms_wsmousecfg));
 }

 void
diff --git a/sys/dev/hid/hidmt.c b/sys/dev/hid/hidmt.c
index 62b500a4f44..9e01fe597bf 100644
--- a/sys/dev/hid/hidmt.c
+++ b/sys/dev/hid/hidmt.c
@@ -103,7 +103,7 @@ hidmt_get_resolution(struct hid_item *h)
                phy_extent *= 10;
        }

-       return (log_extent / phy_extent);
+       return ((log_extent + phy_extent / 2) / phy_extent);
 }

 int
diff --git a/sys/dev/usb/ubcmtp.c b/sys/dev/usb/ubcmtp.c
index d86883bd6c2..b5acdadef46 100644
--- a/sys/dev/usb/ubcmtp.c
+++ b/sys/dev/usb/ubcmtp.c
@@ -309,6 +309,10 @@ static const struct ubcmtp_dev ubcmtp_devices[] = {
        },
 };

+static struct wsmouse_param ubcmtp_wsmousecfg[] = {
+       { WSMOUSECFG_MTBTN_MAXDIST, 0 }, /* 0: Compute a default value. */
+};
+
 struct ubcmtp_softc {
        struct device           sc_dev;         /* base device */

@@ -529,7 +533,8 @@ ubcmtp_configure(struct ubcmtp_softc *sc)
        hw->mt_slots = UBCMTP_MAX_FINGERS;
        hw->flags = WSMOUSEHW_MT_TRACKING;

-       return wsmouse_configure(sc->sc_wsmousedev, NULL, 0);
+       return wsmouse_configure(sc->sc_wsmousedev,
+           ubcmtp_wsmousecfg, nitems(ubcmtp_wsmousecfg));
 }

 int
diff --git a/sys/dev/wscons/wsconsio.h b/sys/dev/wscons/wsconsio.h
index de483493360..7de7c356e9a 100644
--- a/sys/dev/wscons/wsconsio.h
+++ b/sys/dev/wscons/wsconsio.h
@@ -319,6 +319,7 @@ enum wsmousecfg {
        WSMOUSECFG_SWAPSIDES,           /* invert soft-button/scroll areas */
        WSMOUSECFG_DISABLE,             /* disable all output except for
                                           clicks in the top-button area */
+       WSMOUSECFG_MTBUTTONS,           /* multi-touch buttons */

        /*
         * Touchpad options
@@ -340,6 +341,8 @@ enum wsmousecfg {
        WSMOUSECFG_TAP_ONE_BTNMAP,      /* one-finger tap button mapping */
        WSMOUSECFG_TAP_TWO_BTNMAP,      /* two-finger tap button mapping */
        WSMOUSECFG_TAP_THREE_BTNMAP,    /* three-finger tap button mapping */
+       WSMOUSECFG_MTBTN_MAXDIST,       /* MTBUTTONS: distance limit for
+                                          two-finger clicks */

        /*
         * Enable/Disable debug output.
@@ -347,7 +350,7 @@ enum wsmousecfg {
        WSMOUSECFG_LOG_INPUT = 256,
        WSMOUSECFG_LOG_EVENTS,
 };
-#define WSMOUSECFG_MAX 41      /* max size of param array per ioctl */
+#define WSMOUSECFG_MAX 43      /* max size of param array per ioctl */

 struct wsmouse_param {
        enum wsmousecfg key;
diff --git a/sys/dev/wscons/wstpad.c b/sys/dev/wscons/wstpad.c
index be074b89fb8..ea32242a9a9 100644
--- a/sys/dev/wscons/wstpad.c
+++ b/sys/dev/wscons/wstpad.c
@@ -149,6 +149,7 @@ struct tpad_touch {
 #define WSTPAD_HORIZSCROLL     (1 << 5)
 #define WSTPAD_SWAPSIDES       (1 << 6)
 #define WSTPAD_DISABLE         (1 << 7)
+#define WSTPAD_MTBUTTONS       (1 << 8)

 #define WSTPAD_MT              (1 << 31)

@@ -201,6 +202,8 @@ struct wstpad {
                /* two-finger contacts */
                int f2pressure;
                int f2width;
+               /* MTBUTTONS: distance limit for two-finger clicks */
+               int mtbtn_maxdist;
        } params;

        /* handler state and configuration: */
@@ -634,6 +637,37 @@ wstpad_get_sbtn(struct wsmouseinput *input, int top)
        return (btn != PRIMARYBTN ? btn : 0);
 }

+int
+wstpad_mtbtn_contacts(struct wsmouseinput *input)
+{
+       struct wstpad *tp = input->tp;
+       struct tpad_touch *t;
+       int dx, dy, dist, limit;
+
+       if (tp->ignore != 0)
+               return (tp->contacts - 1);
+
+       if (tp->contacts == 2 && (t = get_2nd_touch(input)) != NULL) {
+               dx = abs(t->x - tp->t->x) << 12;
+               dy = abs(t->y - tp->t->y) * tp->ratio;
+               dist = (dx >= dy ? dx + 3 * dy / 8 : dy + 3 * dx / 8);
+               limit = tp->params.mtbtn_maxdist << 12;
+               if (input->mt.ptr_mask != 0)
+                       limit = limit * 2 / 3;
+               if (dist > limit)
+                       return (1);
+       }
+       return (tp->contacts);
+}
+
+u_int
+wstpad_get_mtbtn(struct wsmouseinput *input)
+{
+       int contacts = wstpad_mtbtn_contacts(input);
+       return (contacts == 2 ? RIGHTBTN : (contacts == 3 ? MIDDLEBTN : 0));
+}
+
+
 void
 wstpad_softbuttons(struct wsmouseinput *input, u_int *cmds, int hdlr)
 {
@@ -646,7 +680,8 @@ wstpad_softbuttons(struct wsmouseinput *input, u_int *cmds, 
int hdlr)
        }

        if (tp->softbutton == 0 && PRIMARYBTN_CLICKED(tp)) {
-               tp->softbutton = wstpad_get_sbtn(input, top);
+               tp->softbutton = ((tp->features & WSTPAD_MTBUTTONS)
+                   ? wstpad_get_mtbtn(input) : wstpad_get_sbtn(input, top));
                if (tp->softbutton)
                        *cmds |= 1 << SOFTBUTTON_DOWN;
        }
@@ -1599,6 +1634,15 @@ wstpad_configure(struct wsmouseinput *input)
                tp->scroll.hdist = 4 * h_unit;
                tp->scroll.vdist = 4 * v_unit;
                tp->tap.maxdist = 4 * h_unit;
+
+               if (IS_MT(tp) && h_res > 1 && v_res > 1 &&
+                   input->hw.hw_type == WSMOUSEHW_CLICKPAD &&
+                   (width + h_res / 2) / h_res > 100 &&
+                   (height + v_res / 2) / v_res > 60) {
+                       tp->params.mtbtn_maxdist = h_res * 35;
+               } else {
+                       tp->params.mtbtn_maxdist = -1; /* not available */
+               }
        }

        /* A touch with a flag set in this mask does not move the pointer. */
@@ -1621,7 +1665,7 @@ wstpad_configure(struct wsmouseinput *input)

        tp->handlers = 0;

-       if (tp->features & WSTPAD_SOFTBUTTONS)
+       if (tp->features & (WSTPAD_SOFTBUTTONS | WSTPAD_MTBUTTONS))
                tp->handlers |= 1 << SOFTBUTTON_HDLR;
        if (tp->features & WSTPAD_TOPBUTTONS)
                tp->handlers |= 1 << TOPBUTTON_HDLR;
@@ -1685,13 +1729,14 @@ int
 wstpad_set_param(struct wsmouseinput *input, int key, int val)
 {
        struct wstpad *tp = input->tp;
+       int w, h;
        u_int flag;

        if (tp == NULL)
                return (EINVAL);

        switch (key) {
-       case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_DISABLE:
+       case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_MTBUTTONS:
                switch (key) {
                case WSMOUSECFG_SOFTBUTTONS:
                        flag = WSTPAD_SOFTBUTTONS;
@@ -1717,6 +1762,9 @@ wstpad_set_param(struct wsmouseinput *input, int key, int 
val)
                case WSMOUSECFG_DISABLE:
                        flag = WSTPAD_DISABLE;
                        break;
+               case WSMOUSECFG_MTBUTTONS:
+                       flag = (tp->params.mtbtn_maxdist >= 0 ? 
WSTPAD_MTBUTTONS : 0);
+                       break;
                }
                if (val)
                        tp->features |= flag;
@@ -1768,6 +1816,16 @@ wstpad_set_param(struct wsmouseinput *input, int key, 
int val)
        case WSMOUSECFG_TAP_THREE_BTNMAP:
                tp->tap.btnmap[2] = BTNMASK(val);
                break;
+       case WSMOUSECFG_MTBTN_MAXDIST:
+               if (IS_MT(tp)) {
+                       if (tp->params.mtbtn_maxdist == -1 && val == 0) {
+                               w = abs(input->hw.x_max - input->hw.x_min);
+                               h = abs(input->hw.y_max - input->hw.y_min);
+                               val = isqrt(w * w + h * h) / 4;
+                       }
+                       tp->params.mtbtn_maxdist = val;
+               }
+               break;
        default:
                return (ENOTSUP);
        }
@@ -1785,7 +1843,7 @@ wstpad_get_param(struct wsmouseinput *input, int key, int 
*pval)
                return (EINVAL);

        switch (key) {
-       case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_DISABLE:
+       case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_MTBUTTONS:
                switch (key) {
                case WSMOUSECFG_SOFTBUTTONS:
                        flag = WSTPAD_SOFTBUTTONS;
@@ -1811,6 +1869,9 @@ wstpad_get_param(struct wsmouseinput *input, int key, int 
*pval)
                case WSMOUSECFG_DISABLE:
                        flag = WSTPAD_DISABLE;
                        break;
+               case WSMOUSECFG_MTBUTTONS:
+                       flag = WSTPAD_MTBUTTONS;
+                       break;
                }
                *pval = !!(tp->features & flag);
                break;
@@ -1859,6 +1920,9 @@ wstpad_get_param(struct wsmouseinput *input, int key, int 
*pval)
        case WSMOUSECFG_TAP_THREE_BTNMAP:
                *pval = ffs(tp->tap.btnmap[2]);
                break;
+       case WSMOUSECFG_MTBTN_MAXDIST:
+               *pval = tp->params.mtbtn_maxdist;
+               break;
        default:
                return (ENOTSUP);
        }

Reply via email to