This version of the diff adds a wsconsctl field, named "mouse.tp.
mtbuttons", and an update to the wsmouse.4 page.  Apart from that, it
contains only stylistic changes.

The new wsconsctl field is just a boolean, I don't think that it would
make sense to include the distance filter in the configuration options.
However, if a default value is derived from the length of the touchpad
diagonal, it might be too high for over-sized touchpads.  If that turns
out to be a problem, the proper place to fix it is the hardware driver.

OK?

On 2/21/23 20:10, Ulf Brosziewski wrote:
> 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.
> 
> [...]>
> 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.)
> 
> 
> [...]
Index: src/sbin/wsconsctl/mouse.c
===================================================================
RCS file: /cvs/src/sbin/wsconsctl/mouse.c,v
retrieving revision 1.20
diff -u -p -r1.20 mouse.c
--- src/sbin/wsconsctl/mouse.c  19 Aug 2019 21:42:33 -0000      1.20
+++ src/sbin/wsconsctl/mouse.c  27 Jun 2023 18:54:31 -0000
@@ -57,6 +57,7 @@ struct field mouse_field_tab[] = {
     { "reverse_scrolling",     &cfg_revscroll, FMT_CFG,        FLG_NORDBACK },
     /* touchpad-specific options: */
     { "tp.tapping",            &cfg_tapping,   FMT_CFG,        FLG_NORDBACK },
+    { "tp.mtbuttons",          &cfg_mtbuttons, FMT_CFG,        FLG_NORDBACK },
     { "tp.scaling",            &cfg_scaling,   FMT_CFG,        FLG_NORDBACK },
     { "tp.swapsides",          &cfg_swapsides, FMT_CFG,        FLG_NORDBACK },
     { "tp.disable",            &cfg_disable,   FMT_CFG,        FLG_NORDBACK },
@@ -69,6 +70,10 @@ struct field mouse_field_tab[] = {

 static int dev_index = -1;

+static struct wsmouse_parameters mtbtn_maxdist = {
+    (struct wsmouse_param[]) { { WSMOUSECFG_MTBTN_MAXDIST, 0 } }, 1
+};
+

 void
 mouse_init(int devfd, int devidx) {
@@ -91,6 +96,12 @@ mouse_init(int devfd, int devidx) {
                        if (f->format == FMT_CFG) {
                                f->flags &= ~FLG_DEAD;
                        }
+               /* Hide the 'mtbuttons' field if the feature is unavailable. */
+               if (mousecfg_get_field(&mtbtn_maxdist) ||
+                   mtbtn_maxdist.params[0].value < 0) {
+                       f = field_by_value(mouse_field_tab, &cfg_mtbuttons);
+                       f->flags |= FLG_DEAD;
+               }
        } else {
                for (f = mouse_field_tab; f->name != NULL; f++)
                        if (f->format == FMT_CFG) {
Index: src/sbin/wsconsctl/mousecfg.c
===================================================================
RCS file: /cvs/src/sbin/wsconsctl/mousecfg.c,v
retrieving revision 1.9
diff -u -p -r1.9 mousecfg.c
--- src/sbin/wsconsctl/mousecfg.c       3 Mar 2021 19:44:37 -0000       1.9
+++ src/sbin/wsconsctl/mousecfg.c       27 Jun 2023 18:54:31 -0000
@@ -35,30 +35,15 @@
 #define nitems(_a)       (sizeof((_a)) / sizeof((_a)[0]))
 #endif

-#define BASE_FIRST             WSMOUSECFG_DX_SCALE
-#define BASE_LAST              WSMOUSECFG_REVERSE_SCROLLING
-#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_SETUP_FIRST         WSMOUSECFG_LEFT_EDGE
-#define TP_SETUP_LAST          WSMOUSECFG_TAP_THREE_BTNMAP
-#define LOG_FIRST              WSMOUSECFG_LOG_INPUT
-#define LOG_LAST               WSMOUSECFG_LOG_EVENTS
-
-#define BASESIZE ((BASE_LAST - BASE_FIRST + 1) + (LOG_LAST - LOG_FIRST + 1))
-
-#define BUFSIZE (BASESIZE \
-    + (TP_FILTER_LAST - TP_FILTER_FIRST + 1) \
-    + (TP_FEATURES_LAST - TP_FEATURES_FIRST + 1) \
-    + (TP_SETUP_LAST - TP_SETUP_FIRST + 1))
+#define BASESIZE ((WSMOUSECFG__FILTERS - WSMOUSECFG_DX_SCALE) \
+    + (WSMOUSECFG__DEBUG - WSMOUSECFG_LOG_INPUT))

 static const int range[][2] = {
-       { BASE_FIRST, BASE_LAST },
-       { LOG_FIRST, LOG_LAST },
-       { TP_FILTER_FIRST, TP_FILTER_LAST },
-       { TP_FEATURES_FIRST, TP_FEATURES_LAST },
-       { TP_SETUP_FIRST, TP_SETUP_LAST },
+       { WSMOUSECFG_DX_SCALE, WSMOUSECFG__FILTERS - 1 },
+       { WSMOUSECFG_LOG_INPUT, WSMOUSECFG__DEBUG - 1 },
+       { WSMOUSECFG_DX_MAX, WSMOUSECFG__TPFILTERS - 1 },
+       { WSMOUSECFG_SOFTBUTTONS, WSMOUSECFG__TPFEATURES - 1 },
+       { WSMOUSECFG_LEFT_EDGE, WSMOUSECFG__TPSETUP - 1 },
 };

 static const int touchpad_types[] = {
@@ -77,6 +62,12 @@ struct wsmouse_parameters cfg_tapping =
        3
 };

+struct wsmouse_parameters cfg_mtbuttons = {
+       (struct wsmouse_param[]) {
+           { WSMOUSECFG_MTBUTTONS, 0 }, },
+       1
+};
+
 struct wsmouse_parameters cfg_scaling = {
        (struct wsmouse_param[]) {
            { WSMOUSECFG_DX_SCALE, 0 },
@@ -124,7 +115,7 @@ int cfg_touchpad;

 static int cfg_horiz_res;
 static int cfg_vert_res;
-static struct wsmouse_param cfg_buffer[BUFSIZE];
+static struct wsmouse_param cfg_buffer[WSMOUSECFG_MAX];


 int
@@ -171,7 +162,7 @@ mousecfg_init(int dev_fd, const char **e
        }
        if (cfg_touchpad) {
                parameters.params = cfg_buffer + BASESIZE;
-               parameters.nparams = BUFSIZE - BASESIZE;
+               parameters.nparams = WSMOUSECFG_MAX - BASESIZE;
                if (ioctl(dev_fd, WSMOUSEIO_GETPARAMS, &parameters))
                        cfg_touchpad = 0;
        }
Index: src/sbin/wsconsctl/mousecfg.h
===================================================================
RCS file: /cvs/src/sbin/wsconsctl/mousecfg.h,v
retrieving revision 1.4
diff -u -p -r1.4 mousecfg.h
--- src/sbin/wsconsctl/mousecfg.h       19 Aug 2019 21:42:33 -0000      1.4
+++ src/sbin/wsconsctl/mousecfg.h       27 Jun 2023 18:54:31 -0000
@@ -17,6 +17,7 @@
  */

 extern struct wsmouse_parameters cfg_tapping;
+extern struct wsmouse_parameters cfg_mtbuttons;
 extern struct wsmouse_parameters cfg_scaling;
 extern struct wsmouse_parameters cfg_edges;
 extern struct wsmouse_parameters cfg_swapsides;
Index: src/share/man/man4/wsmouse.4
===================================================================
RCS file: /cvs/src/share/man/man4/wsmouse.4,v
retrieving revision 1.22
diff -u -p -r1.22 wsmouse.4
--- src/share/man/man4/wsmouse.4        4 Mar 2021 17:03:42 -0000       1.22
+++ src/share/man/man4/wsmouse.4        27 Jun 2023 18:54:31 -0000
@@ -106,6 +106,10 @@ If, within a short time interval, a seco
 mapped to a left-button click, the button-up event is not issued
 until that touch ends
 .Pq Dq tap-and-drag .
+.It Cm mouse.tp.mtbuttons
+This feature is supported from some clickpads.
+If enabled, two-finger clicks - with the fingers side by side - generate
+left-button events, three-finger clicks generate middle-button events.
 .It Cm mouse.tp.scaling
 The value is a scale coefficient that is applied to the relative
 coordinates.
Index: sys/arch/arm64/dev/aplhidev.c
===================================================================
RCS file: /cvs/src/sys/arch/arm64/dev/aplhidev.c,v
retrieving revision 1.11
diff -u -p -r1.11 aplhidev.c
--- sys/arch/arm64/dev/aplhidev.c       10 Apr 2023 15:14:04 -0000      1.11
+++ sys/arch/arm64/dev/aplhidev.c       27 Jun 2023 19:02:23 -0000
@@ -683,6 +683,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;
@@ -762,7 +766,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
Index: sys/dev/hid/hidmt.c
===================================================================
RCS file: /cvs/src/sys/dev/hid/hidmt.c,v
retrieving revision 1.13
diff -u -p -r1.13 hidmt.c
--- sys/dev/hid/hidmt.c 16 Oct 2022 20:17:08 -0000      1.13
+++ sys/dev/hid/hidmt.c 27 Jun 2023 19:02:24 -0000
@@ -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
Index: sys/dev/usb/ubcmtp.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/ubcmtp.c,v
retrieving revision 1.24
diff -u -p -r1.24 ubcmtp.c
--- sys/dev/usb/ubcmtp.c        26 Oct 2022 16:07:28 -0000      1.24
+++ sys/dev/usb/ubcmtp.c        27 Jun 2023 19:02:32 -0000
@@ -309,6 +309,10 @@ static const struct ubcmtp_dev ubcmtp_de
        },
 };

+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
Index: sys/dev/wscons/wsconsio.h
===================================================================
RCS file: /cvs/src/sys/dev/wscons/wsconsio.h,v
retrieving revision 1.99
diff -u -p -r1.99 wsconsio.h
--- sys/dev/wscons/wsconsio.h   20 Apr 2023 19:28:31 -0000      1.99
+++ sys/dev/wscons/wsconsio.h   27 Jun 2023 19:02:32 -0000
@@ -279,6 +279,9 @@ struct wsmouse_calibcoords {
  * WSMOUSEIO_SETPARAMS calls. Arbitrary subsets can be passed, provided
  * that all keys are valid and that the number of key/value pairs doesn't
  * exceed WSMOUSECFG_MAX.
+ *
+ * The keys are divided into various groups, which end with marker entries
+ * of the form WSMOUSECFG__*.
  */
 enum wsmousecfg {
        /*
@@ -295,6 +298,8 @@ enum wsmousecfg {
        WSMOUSECFG_REVERSE_SCROLLING,
                                /* reverse scroll directions */

+        WSMOUSECFG__FILTERS,
+
        /*
         * Coordinate handling, applying only in WSMOUSE_COMPAT  mode.
         */
@@ -307,6 +312,8 @@ enum wsmousecfg {
                                           ture is not supported anymore. */
        WSMOUSECFG_SMOOTHING,   /* smoothing factor (0-7) */

+       WSMOUSECFG__TPFILTERS,
+
        /*
         * Touchpad features
         */
@@ -319,6 +326,9 @@ 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 */
+
+       WSMOUSECFG__TPFEATURES,

        /*
         * Touchpad options
@@ -340,14 +350,25 @@ 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 */
+
+       WSMOUSECFG__TPSETUP,

        /*
         * Enable/Disable debug output.
         */
        WSMOUSECFG_LOG_INPUT = 256,
        WSMOUSECFG_LOG_EVENTS,
+
+       WSMOUSECFG__DEBUG,
 };
-#define WSMOUSECFG_MAX 41      /* max size of param array per ioctl */
+
+#define WSMOUSECFG_MAX ((WSMOUSECFG__FILTERS - WSMOUSECFG_DX_SCALE)    \
+    + (WSMOUSECFG__TPFILTERS - WSMOUSECFG_DX_MAX)                      \
+    + (WSMOUSECFG__TPFEATURES - WSMOUSECFG_SOFTBUTTONS)                        
\
+    + (WSMOUSECFG__TPSETUP - WSMOUSECFG_LEFT_EDGE)                     \
+    + (WSMOUSECFG__DEBUG - WSMOUSECFG_LOG_INPUT))

 struct wsmouse_param {
        enum wsmousecfg key;
Index: sys/dev/wscons/wstpad.c
===================================================================
RCS file: /cvs/src/sys/dev/wscons/wstpad.c,v
retrieving revision 1.31
diff -u -p -r1.31 wstpad.c
--- sys/dev/wscons/wstpad.c     9 Jun 2022 22:17:18 -0000       1.31
+++ sys/dev/wscons/wstpad.c     27 Jun 2023 19:02:32 -0000
@@ -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 *inp
        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 *
        }

        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 *in
                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. */
@@ -1619,13 +1663,24 @@ wstpad_configure(struct wsmouseinput *in
        tp->edge.center_left = tp->edge.center - offset;
        tp->edge.center_right = tp->edge.center + offset;

+       /*
+        * Make the MTBUTTONS configuration consistent.  A non-negative 
'maxdist'
+        * value makes the feature visible in wsconsctl.  0-values are replaced
+        * by a default (one fourth of the length of the touchpad diagonal).
+        */
+       if (tp->params.mtbtn_maxdist < 0) {
+               tp->features &= ~WSTPAD_MTBUTTONS;
+       } else if (tp->params.mtbtn_maxdist == 0) {
+               diag = isqrt(width * width + height * height);
+               tp->params.mtbtn_maxdist = diag / 4;
+       }
+
        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;
-
        if (tp->features & WSTPAD_TWOFINGERSCROLL)
                tp->handlers |= 1 << F2SCROLL_HDLR;
        else if (tp->features & WSTPAD_EDGESCROLL)
@@ -1691,7 +1746,7 @@ wstpad_set_param(struct wsmouseinput *in
                return (EINVAL);

        switch (key) {
-       case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_DISABLE:
+       case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_MTBUTTONS:
                switch (key) {
                case WSMOUSECFG_SOFTBUTTONS:
                        flag = WSTPAD_SOFTBUTTONS;
@@ -1717,6 +1772,9 @@ wstpad_set_param(struct wsmouseinput *in
                case WSMOUSECFG_DISABLE:
                        flag = WSTPAD_DISABLE;
                        break;
+               case WSMOUSECFG_MTBUTTONS:
+                       flag = WSTPAD_MTBUTTONS;
+                       break;
                }
                if (val)
                        tp->features |= flag;
@@ -1768,6 +1826,10 @@ wstpad_set_param(struct wsmouseinput *in
        case WSMOUSECFG_TAP_THREE_BTNMAP:
                tp->tap.btnmap[2] = BTNMASK(val);
                break;
+       case WSMOUSECFG_MTBTN_MAXDIST:
+               if (IS_MT(tp))
+                       tp->params.mtbtn_maxdist = val;
+               break;
        default:
                return (ENOTSUP);
        }
@@ -1785,7 +1847,7 @@ wstpad_get_param(struct wsmouseinput *in
                return (EINVAL);

        switch (key) {
-       case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_DISABLE:
+       case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_MTBUTTONS:
                switch (key) {
                case WSMOUSECFG_SOFTBUTTONS:
                        flag = WSTPAD_SOFTBUTTONS;
@@ -1811,6 +1873,9 @@ wstpad_get_param(struct wsmouseinput *in
                case WSMOUSECFG_DISABLE:
                        flag = WSTPAD_DISABLE;
                        break;
+               case WSMOUSECFG_MTBUTTONS:
+                       flag = WSTPAD_MTBUTTONS;
+                       break;
                }
                *pval = !!(tp->features & flag);
                break;
@@ -1858,6 +1923,9 @@ wstpad_get_param(struct wsmouseinput *in
                break;
        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