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, ¶meters)) 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);