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);