summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorUlf Brosziewski <bru@cvs.openbsd.org>2016-03-30 23:34:13 +0000
committerUlf Brosziewski <bru@cvs.openbsd.org>2016-03-30 23:34:13 +0000
commitbb8a088342dbb8ce495fcdc306d2e2aafba86ce3 (patch)
treec6734c2df6aff4e3b257bec7d01b73f1a42d02bd /sys
parent5a710a5f10a7c160b6aa76c992df11c4f29c2846 (diff)
Add support for multitouch input to wsmouse.
This change adds new input-processing functions to wsmouse and adapts the touchpad drivers. ok mpi@, shadchin@
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/hid/hidmt.c15
-rw-r--r--sys/dev/pckbc/pms.c206
-rw-r--r--sys/dev/usb/ubcmtp.c31
-rw-r--r--sys/dev/wscons/wsconsio.h20
-rw-r--r--sys/dev/wscons/wsmouse.c843
-rw-r--r--sys/dev/wscons/wsmouseinput.h169
-rw-r--r--sys/dev/wscons/wsmousevar.h167
7 files changed, 1285 insertions, 166 deletions
diff --git a/sys/dev/hid/hidmt.c b/sys/dev/hid/hidmt.c
index db74eef90c0..bb7287a0046 100644
--- a/sys/dev/hid/hidmt.c
+++ b/sys/dev/hid/hidmt.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: hidmt.c,v 1.1 2016/01/20 01:26:00 jcs Exp $ */
+/* $OpenBSD: hidmt.c,v 1.2 2016/03/30 23:34:12 bru Exp $ */
/*
* HID multitouch driver for devices conforming to Windows Precision Touchpad
* standard
@@ -329,20 +329,15 @@ hidmt_input(struct hidmt *mt, uint8_t *data, u_int len)
width = 50;
}
- wsmouse_input(mt->sc_wsmousedev, mt->sc_button,
+ WSMOUSE_TOUCH(mt->sc_wsmousedev, mt->sc_button,
(mt->last_x = mt->sc_contacts[i].x),
(mt->last_y = mt->sc_contacts[i].y),
- width, tips,
- WSMOUSE_INPUT_ABSOLUTE_X |
- WSMOUSE_INPUT_ABSOLUTE_Y |
- WSMOUSE_INPUT_ABSOLUTE_Z |
- WSMOUSE_INPUT_ABSOLUTE_W);
+ width, tips);
} else {
- wsmouse_input(mt->sc_wsmousedev, mt->sc_button,
+ WSMOUSE_INPUT(mt->sc_wsmousedev, mt->sc_button,
(mt->last_x - mt->sc_contacts[i].x),
(mt->last_y - mt->sc_contacts[i].y),
- 0, 0,
- WSMOUSE_INPUT_DELTA);
+ 0, 0);
mt->last_x = mt->sc_contacts[i].x;
mt->last_y = mt->sc_contacts[i].y;
}
diff --git a/sys/dev/pckbc/pms.c b/sys/dev/pckbc/pms.c
index a4abfb20283..2bf2806dda8 100644
--- a/sys/dev/pckbc/pms.c
+++ b/sys/dev/pckbc/pms.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pms.c,v 1.68 2016/02/27 22:01:58 mmcc Exp $ */
+/* $OpenBSD: pms.c,v 1.69 2016/03/30 23:34:12 bru Exp $ */
/* $NetBSD: psm.c,v 1.11 2000/06/05 22:20:57 sommerfeld Exp $ */
/*-
@@ -143,17 +143,8 @@ struct elantech_softc {
int min_x, min_y;
int max_x, max_y;
- struct {
- unsigned int x;
- unsigned int y;
- unsigned int z;
- } mt[ELANTECH_MAX_FINGERS];
- int mt_slots;
- int mt_count;
- int mt_filter;
- int mt_lastid;
- int mt_lastcount;
- int mt_buttons;
+
+ u_int mt_slots;
int width;
@@ -997,8 +988,7 @@ synaptics_sec_proc(struct pms_softc *sc)
dy = (sc->packet[1] & PMS_PS2_YNEG) ?
(int)sc->packet[5] - 256 : sc->packet[5];
- wsmouse_input(sc->sc_sec_wsmousedev,
- buttons, dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);
+ WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0);
}
int
@@ -1162,7 +1152,7 @@ pms_proc_synaptics(struct pms_softc *sc)
{
struct synaptics_softc *syn = sc->synaptics;
u_int buttons;
- int x, y, z, w, dx, dy;
+ int x, y, z, w, dx, dy, width;
w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) |
((sc->packet[3] & 0x04) >> 2);
@@ -1230,8 +1220,9 @@ pms_proc_synaptics(struct pms_softc *sc)
(sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(3) : 0;
syn->sec_buttons |=
(sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(2) : 0;
- wsmouse_input(sc->sc_sec_wsmousedev,
- syn->sec_buttons, 0, 0, 0, 0, WSMOUSE_INPUT_DELTA);
+ wsmouse_buttons(
+ sc->sc_sec_wsmousedev, syn->sec_buttons);
+ wsmouse_input_sync(sc->sc_sec_wsmousedev);
return;
}
@@ -1254,9 +1245,14 @@ pms_proc_synaptics(struct pms_softc *sc)
}
if (syn->wsmode == WSMOUSE_NATIVE) {
- wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
- WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
- WSMOUSE_INPUT_ABSOLUTE_Z | WSMOUSE_INPUT_ABSOLUTE_W);
+ if (z) {
+ width = imax(w, 4);
+ w = (w < 2 ? w + 2 : 1);
+ } else {
+ width = w = 0;
+ }
+ wsmouse_set(sc->sc_wsmousedev, WSMOUSE_TOUCH_WIDTH, width, 0);
+ WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
} else {
dx = dy = 0;
if (z > SYNAPTICS_PRESSURE) {
@@ -1266,8 +1262,7 @@ pms_proc_synaptics(struct pms_softc *sc)
dy /= SYNAPTICS_SCALE;
}
if (dx || dy || buttons != syn->old_buttons)
- wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
- WSMOUSE_INPUT_DELTA);
+ WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, 0, 0);
syn->old_buttons = buttons;
}
@@ -1313,8 +1308,7 @@ alps_sec_proc(struct pms_softc *sc)
dy = (sc->packet[pos] & PMS_PS2_YNEG) ?
(int)sc->packet[pos + 2] - 256 : sc->packet[pos + 2];
- wsmouse_input(sc->sc_sec_wsmousedev, alps->sec_buttons,
- dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);
+ WSMOUSE_INPUT(sc->sc_sec_wsmousedev, alps->sec_buttons, dx, dy, 0, 0);
return (1);
}
@@ -1514,7 +1508,7 @@ void
pms_proc_alps(struct pms_softc *sc)
{
struct alps_softc *alps = sc->alps;
- int x, y, z, w, dx, dy;
+ int x, y, z, dx, dy;
u_int buttons, gesture;
if ((alps->model & ALPS_DUALPOINT) && alps_sec_proc(sc))
@@ -1532,8 +1526,7 @@ pms_proc_alps(struct pms_softc *sc)
dx = (x > ALPS_XSEC_BEZEL / 2) ? (x - ALPS_XSEC_BEZEL) : x;
dy = (y > ALPS_YSEC_BEZEL / 2) ? (y - ALPS_YSEC_BEZEL) : y;
- wsmouse_input(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0,
- WSMOUSE_INPUT_DELTA);
+ WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0);
return;
}
@@ -1552,35 +1545,21 @@ pms_proc_alps(struct pms_softc *sc)
if (alps->wsmode == WSMOUSE_NATIVE) {
if (alps->gesture == ALPS_TAP) {
/* Report a touch with the tap coordinates. */
- wsmouse_input(sc->sc_wsmousedev, buttons,
- alps->old_x, alps->old_y, ALPS_PRESSURE, 4,
- WSMOUSE_INPUT_ABSOLUTE_X
- | WSMOUSE_INPUT_ABSOLUTE_Y
- | WSMOUSE_INPUT_ABSOLUTE_Z
- | WSMOUSE_INPUT_ABSOLUTE_W);
+ WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,
+ alps->old_x, alps->old_y, ALPS_PRESSURE, 0);
if (z > 0) {
/*
* The hardware doesn't send a null pressure
* event when dragging starts.
*/
- wsmouse_input(sc->sc_wsmousedev, buttons,
- alps->old_x, alps->old_y, 0, 0,
- WSMOUSE_INPUT_ABSOLUTE_X
- | WSMOUSE_INPUT_ABSOLUTE_Y
- | WSMOUSE_INPUT_ABSOLUTE_Z
- | WSMOUSE_INPUT_ABSOLUTE_W);
+ WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,
+ alps->old_x, alps->old_y, 0, 0);
}
}
gesture = sc->packet[2] & 0x03;
- if (gesture != ALPS_TAP) {
- w = z ? 4 : 0;
- wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
- WSMOUSE_INPUT_ABSOLUTE_X
- | WSMOUSE_INPUT_ABSOLUTE_Y
- | WSMOUSE_INPUT_ABSOLUTE_Z
- | WSMOUSE_INPUT_ABSOLUTE_W);
- }
+ if (gesture != ALPS_TAP)
+ WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, 0);
if (alps->gesture != ALPS_DRAG || gesture != ALPS_TAP)
alps->gesture = gesture;
@@ -1600,8 +1579,7 @@ pms_proc_alps(struct pms_softc *sc)
}
if (dx || dy || buttons != alps->old_buttons)
- wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
- WSMOUSE_INPUT_DELTA);
+ WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, 0, 0);
alps->old_x = x;
alps->old_y = y;
@@ -2045,6 +2023,18 @@ err:
}
int
+pms_elantech_v4_configure(struct device *sc_wsmousedev,
+ struct elantech_softc *elantech)
+{
+ if (wsmouse_mt_init(sc_wsmousedev, ELANTECH_MAX_FINGERS, 0))
+ return (-1);
+
+ wsmouse_set_param(sc_wsmousedev, WSMPARAM_DX_DIV, SYNAPTICS_SCALE);
+ wsmouse_set_param(sc_wsmousedev, WSMPARAM_DY_DIV, SYNAPTICS_SCALE);
+ return (0);
+}
+
+int
pms_enable_elantech_v4(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
@@ -2068,6 +2058,15 @@ pms_enable_elantech_v4(struct pms_softc *sc)
goto err;
}
+ if (pms_elantech_v4_configure(
+ sc->sc_wsmousedev, sc->elantech)) {
+ free(sc->elantech, M_DEVBUF, 0);
+ sc->elantech = NULL;
+ printf("%s: setup failed\n", DEVNAME(sc));
+ goto err;
+ }
+ wsmouse_set_mode(sc->sc_wsmousedev, WSMOUSE_COMPAT);
+
printf("%s: Elantech Clickpad, version %d, firmware 0x%x\n",
DEVNAME(sc), 4, sc->elantech->fw_version);
} else if (elantech_set_absolute_mode_v4(sc))
@@ -2107,6 +2106,8 @@ pms_ioctl_elantech(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
return (EINVAL);
elantech->wsmode = wsmode;
+ if (sc->protocol->type == PMS_ELANTECH_V4)
+ wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
break;
default:
return (-1);
@@ -2352,41 +2353,29 @@ void
pms_proc_elantech_v4(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
- int n, id, slots, weight, dx, dy;
+ struct device *sc_wsmousedev = sc->sc_wsmousedev;
+ int id, weight, n, x, y, z;
+ u_int buttons, slots;
switch (sc->packet[3] & 0x1f) {
case ELANTECH_V4_PKT_STATUS:
- if (elantech->mt_slots == 0)
- elantech->mt_lastid = -1;
- slots = sc->packet[1] & 0x1f;
- if (slots == 0 && elantech->mt_lastid > -1)
- /* Notify that we lifted. */
- elantech_send_input(sc,
- elantech->mt[elantech->mt_lastid].x,
- elantech->mt[elantech->mt_lastid].y, 0, 0);
-
- elantech->mt_filter = elantech->mt_slots = slots;
-
- for (elantech->mt_count = 0; slots != 0; slots >>= 1)
- elantech->mt_count += (1 & slots);
-
+ slots = elantech->mt_slots;
+ elantech->mt_slots = sc->packet[1] & 0x1f;
+ slots &= ~elantech->mt_slots;
+ for (id = 0; slots; id++, slots >>= 1) {
+ if (slots & 1)
+ wsmouse_mtstate(sc_wsmousedev, id, 0, 0, 0);
+ }
break;
case ELANTECH_V4_PKT_HEAD:
id = ((sc->packet[3] & 0xe0) >> 5) - 1;
if (id > -1 && id < ELANTECH_MAX_FINGERS) {
- elantech->mt[id].x =
- ((sc->packet[1] & 0x0f) << 8) | sc->packet[2];
- elantech->mt[id].y =
- ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
- elantech->mt[id].z =
- (sc->packet[1] & 0xf0)
+ x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2];
+ y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
+ z = (sc->packet[1] & 0xf0)
| ((sc->packet[4] & 0xf0) >> 4);
-
- if (elantech->mt_filter & (1 << id)) {
- elantech_send_mt_input(sc, id);
- elantech->mt_filter = (1 << id);
- }
+ wsmouse_mtstate(sc_wsmousedev, id, x, y, z);
}
break;
@@ -2396,22 +2385,12 @@ pms_proc_elantech_v4(struct pms_softc *sc)
id = ((sc->packet[n] & 0xe0) >> 5) - 1;
if (id < 0 || id >= ELANTECH_MAX_FINGERS)
continue;
- dx = weight * (signed char)sc->packet[n + 1];
- dy = weight * (signed char)sc->packet[n + 2];
- elantech->mt[id].x += dx;
- elantech->mt[id].y += dy;
- elantech->mt[id].z = 1;
- if (elantech->mt_filter & (1 << id)) {
- if ((dx | dy)
- || elantech->mt_count !=
- elantech->mt_lastcount
- || (sc->packet[0] & 3) !=
- elantech->mt_buttons)
- elantech_send_mt_input(sc, id);
-
- elantech->mt_filter = (dx | dy) ?
- (1 << id) : elantech->mt_slots;
- }
+ x = weight * (signed char)sc->packet[n + 1];
+ y = weight * (signed char)sc->packet[n + 2];
+ z = WSMOUSE_DEFAULT_PRESSURE;
+ wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_X, x, id);
+ wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_Y, y, id);
+ wsmouse_set(sc_wsmousedev, WSMOUSE_MT_PRESSURE, z, id);
}
break;
@@ -2421,37 +2400,15 @@ pms_proc_elantech_v4(struct pms_softc *sc)
sc->packet[3] & 0x1f);
return;
}
-}
-void
-elantech_send_mt_input(struct pms_softc *sc, int id)
-{
- struct elantech_softc *elantech = sc->elantech;
+ buttons = 0;
+ if (sc->packet[0] & 0x01)
+ buttons |= WSMOUSE_BUTTON(1);
+ if (sc->packet[0] & 0x02)
+ buttons |= WSMOUSE_BUTTON(3);
+ wsmouse_buttons(sc_wsmousedev, buttons);
- if (id != elantech->mt_lastid) {
- /* Correct for compatibility mode, but not useful yet: */
- elantech->old_x = elantech->mt[id].x;
- elantech->old_y = elantech->mt[id].y;
- /*
- * To avoid a jump of the cursor, simulate a change of the
- * number of touches (without producing tapping gestures
- * accidentally). It should suffice to do that only if
- * mt_count hasn't changed, but we cannot rely on the
- * synaptics driver, which alters its finger counts when
- * handling click-and-drag actions (see HandleTapProcessing
- * and ComputeDeltas in synaptics.c).
- */
- if (elantech->mt_lastid > -1)
- elantech_send_input(sc,
- elantech->mt[id].x, elantech->mt[id].y,
- elantech->mt[id].z, ELANTECH_MAX_FINGERS);
- elantech->mt_lastid = id;
- }
- elantech->mt_lastcount = elantech->mt_count;
- elantech->mt_buttons = sc->packet[0] & 3;
- elantech_send_input(sc,
- elantech->mt[id].x, elantech->mt[id].y,
- elantech->mt[id].z, elantech->mt_count);
+ wsmouse_input_sync(sc_wsmousedev);
}
void
@@ -2474,11 +2431,7 @@ elantech_send_input(struct pms_softc *sc, int x, int y, int z, int w)
}
if (elantech->wsmode == WSMOUSE_NATIVE) {
- wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
- WSMOUSE_INPUT_ABSOLUTE_X |
- WSMOUSE_INPUT_ABSOLUTE_Y |
- WSMOUSE_INPUT_ABSOLUTE_Z |
- WSMOUSE_INPUT_ABSOLUTE_W);
+ WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
} else {
dx = dy = 0;
@@ -2490,8 +2443,7 @@ elantech_send_input(struct pms_softc *sc, int x, int y, int z, int w)
dy /= SYNAPTICS_SCALE;
}
if (dx || dy || buttons != elantech->old_buttons)
- wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
- WSMOUSE_INPUT_DELTA);
+ WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, 0, 0);
elantech->old_buttons = buttons;
}
diff --git a/sys/dev/usb/ubcmtp.c b/sys/dev/usb/ubcmtp.c
index 27898e3488c..4f091965436 100644
--- a/sys/dev/usb/ubcmtp.c
+++ b/sys/dev/usb/ubcmtp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ubcmtp.c,v 1.11 2015/12/04 16:22:27 kettenis Exp $ */
+/* $OpenBSD: ubcmtp.c,v 1.12 2016/03/30 23:34:12 bru Exp $ */
/*
* Copyright (c) 2013-2014, joshua stein <jcs@openbsd.org>
@@ -865,20 +865,13 @@ ubcmtp_tp_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
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_input(sc->sc_wsmousedev, sc->btn, sc->pos[0].x,
- sc->pos[0].y,
- (finger == 0 ? 0 : 50), /* fake z for now */
- finger,
- WSMOUSE_INPUT_ABSOLUTE_X |
- WSMOUSE_INPUT_ABSOLUTE_Y |
- WSMOUSE_INPUT_ABSOLUTE_Z |
- WSMOUSE_INPUT_ABSOLUTE_W);
+ 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_INPUT_DELTA);
+ WSMOUSE_INPUT(sc->sc_wsmousedev, sc->btn,
+ sc->pos[0].dx, sc->pos[0].dy, 0, 0);
}
splx(s);
}
@@ -915,17 +908,7 @@ ubcmtp_bt_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
if (pkt->button != sc->btn) {
sc->btn = pkt->button;
-
- if (sc->wsmode == WSMOUSE_NATIVE)
- wsmouse_input(sc->sc_wsmousedev, sc->btn, sc->pos[0].x,
- sc->pos[0].y, 50 /* fake z for now */,
- 1,
- WSMOUSE_INPUT_ABSOLUTE_X |
- WSMOUSE_INPUT_ABSOLUTE_Y |
- WSMOUSE_INPUT_ABSOLUTE_Z |
- WSMOUSE_INPUT_ABSOLUTE_W);
- else
- wsmouse_input(sc->sc_wsmousedev, sc->btn,
- 0, 0, 0, 0, WSMOUSE_INPUT_DELTA);
+ wsmouse_buttons(sc->sc_wsmousedev, sc->btn);
+ wsmouse_input_sync(sc->sc_wsmousedev);
}
}
diff --git a/sys/dev/wscons/wsconsio.h b/sys/dev/wscons/wsconsio.h
index e6705b702d6..32d2eef6962 100644
--- a/sys/dev/wscons/wsconsio.h
+++ b/sys/dev/wscons/wsconsio.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsconsio.h,v 1.73 2015/12/12 12:30:18 jung Exp $ */
+/* $OpenBSD: wsconsio.h,v 1.74 2016/03/30 23:34:12 bru Exp $ */
/* $NetBSD: wsconsio.h,v 1.74 2005/04/28 07:15:44 martin Exp $ */
/*
@@ -76,10 +76,10 @@ struct wscons_event {
#define WSCONS_EVENT_MOUSE_ABSOLUTE_X 8 /* X location */
#define WSCONS_EVENT_MOUSE_ABSOLUTE_Y 9 /* Y location */
#define WSCONS_EVENT_MOUSE_DELTA_Z 10 /* Z delta amount */
-#define WSCONS_EVENT_MOUSE_ABSOLUTE_Z 11 /* Z location */
+#define WSCONS_EVENT_MOUSE_ABSOLUTE_Z 11 /* (legacy, see below) */
/* 12-15, see below */
#define WSCONS_EVENT_MOUSE_DELTA_W 16 /* W delta amount */
-#define WSCONS_EVENT_MOUSE_ABSOLUTE_W 17 /* W location */
+#define WSCONS_EVENT_MOUSE_ABSOLUTE_W 17 /* (legacy, see below) */
#define WSCONS_EVENT_SYNC 18
/*
* Following events are not real wscons_event but are used as parameters of the
@@ -97,6 +97,20 @@ struct wscons_event {
#define IS_CTRL_EVENT(type) ((type == WSCONS_EVENT_WSMOUSED_ON) || \
(type == WSCONS_EVENT_WSMOUSED_OFF))
+
+/*
+ * (Single-) Touch Events
+ *
+ * A RESET event will be generated whenever a change of X and Y is
+ * coupled with a change of the contact count, or with a change of
+ * the pointer-controlling MT slot.
+ */
+#define WSCONS_EVENT_TOUCH_PRESSURE WSCONS_EVENT_MOUSE_ABSOLUTE_Z
+#define WSCONS_EVENT_TOUCH_CONTACTS WSCONS_EVENT_MOUSE_ABSOLUTE_W
+
+#define WSCONS_EVENT_TOUCH_WIDTH 24 /* contact width */
+#define WSCONS_EVENT_TOUCH_RESET 25 /* (no value) */
+
/*
* Keyboard ioctls (0 - 31)
*/
diff --git a/sys/dev/wscons/wsmouse.c b/sys/dev/wscons/wsmouse.c
index c3b907ae1df..59887143923 100644
--- a/sys/dev/wscons/wsmouse.c
+++ b/sys/dev/wscons/wsmouse.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsmouse.c,v 1.28 2015/09/10 18:14:52 mpi Exp $ */
+/* $OpenBSD: wsmouse.c,v 1.29 2016/03/30 23:34:12 bru Exp $ */
/* $NetBSD: wsmouse.c,v 1.35 2005/02/27 00:27:52 perry Exp $ */
/*
@@ -88,10 +88,12 @@
#include <sys/device.h>
#include <sys/vnode.h>
#include <sys/poll.h>
+#include <sys/malloc.h>
#include <dev/wscons/wscons_features.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wsmouseinput.h>
#include <dev/wscons/wseventvar.h>
#include <dev/rndvar.h>
@@ -132,6 +134,8 @@ struct wsmouse_softc {
int sc_z; /* absolute-z */
int sc_w; /* absolute-w */
+ struct wsmouseinput input;
+
int sc_refcnt;
u_char sc_dying; /* device is being detached */
};
@@ -199,6 +203,8 @@ wsmouse_attach(struct device *parent, struct device *self, void *aux)
sc->sc_accessops = ap->accessops;
sc->sc_accesscookie = ap->accesscookie;
+ wsmouse_input_init(&sc->input, &sc->sc_base.me_evp);
+
#if NWSMUX > 0
sc->sc_base.me_ops = &wsmouse_srcops;
mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux;
@@ -279,6 +285,8 @@ wsmouse_detach(struct device *self, int flags)
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
+ wsmouse_input_cleanup(&sc->input);
+
return (0);
}
@@ -734,3 +742,836 @@ wsmouse_add_mux(int unit, struct wsmux_softc *muxsc)
return (wsmux_attach_sc(muxsc, &sc->sc_base));
}
#endif /* NWSMUX > 0 */
+
+
+void
+wsmouse_buttons(struct device *sc, u_int buttons)
+{
+ struct btn_state *btn =
+ &((struct wsmouse_softc *) sc)->input.btn;
+
+ if (btn->sync)
+ /* Restore the old state. */
+ btn->buttons ^= btn->sync;
+
+ btn->sync = btn->buttons ^ buttons;
+ btn->buttons = buttons;
+}
+
+void
+wsmouse_motion(struct device *sc, int dx, int dy, int dz, int dw)
+{
+ struct motion_state *motion =
+ &((struct wsmouse_softc *) sc)->input.motion;
+
+ motion->dx = dx;
+ motion->dy = dy;
+ motion->dz = dz;
+ motion->dw = dw;
+ if (dx || dy || dz || dw)
+ motion->sync |= SYNC_DELTAS;
+}
+
+/*
+ * Handle absolute coordinates.
+ *
+ * x_delta/y_delta are used by touchpad code. The values are only
+ * valid if the SYNC-flags are set, and will be cleared by update- or
+ * conversion-functions if a touch shouldn't trigger pointer motion.
+ */
+void
+wsmouse_position(struct device *sc, int x, int y)
+{
+ struct motion_state *motion =
+ &((struct wsmouse_softc *) sc)->input.motion;
+ int delta;
+
+ delta = x - motion->x;
+ if (delta) {
+ motion->x = x;
+ motion->sync |= SYNC_X;
+ motion->x_delta = delta;
+ }
+ delta = y - motion->y;
+ if (delta) {
+ motion->y = y;
+ motion->sync |= SYNC_Y;
+ motion->y_delta = delta;
+ }
+}
+
+static __inline int
+normalized_pressure(struct wsmouseinput *input, int pressure)
+{
+ int limit = imax(input->touch.min_pressure, 1);
+
+ if (pressure >= limit)
+ return pressure;
+ else
+ return (pressure < 0 ? limit : 0);
+}
+
+void
+wsmouse_touch(struct device *sc, int pressure, int contacts)
+{
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct touch_state *touch = &input->touch;
+
+ pressure = normalized_pressure(input, pressure);
+ contacts = (pressure ? imax(contacts, 1) : 0);
+
+ if (pressure == 0 || pressure != touch->pressure) {
+ /*
+ * pressure == 0: Drivers may report possibly arbitrary
+ * coordinates in this case; touch_update will correct them.
+ */
+ touch->pressure = pressure;
+ touch->sync |= SYNC_PRESSURE;
+ }
+ if (contacts != touch->contacts) {
+ touch->contacts = contacts;
+ touch->sync |= SYNC_CONTACTS;
+ }
+}
+
+void
+wsmouse_mtstate(struct device *sc, int slot, int x, int y, int pressure)
+{
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct mt_state *mt = &input->mt;
+ struct mt_slot *mts;
+ u_int bit;
+ int initial;
+
+ if (slot < 0 || slot >= mt->num_slots)
+ return;
+
+ bit = (1 << slot);
+ mt->frame |= bit;
+
+ /* Is this a new touch? */
+ initial = ((mt->touches & bit) == (mt->sync[MTS_TOUCH] & bit));
+
+ mts = &mt->slots[slot];
+ if (x != mts->x || initial) {
+ mts->x = x;
+ mt->sync[MTS_X] |= bit;
+ }
+ if (y != mts->y || initial) {
+ mts->y = y;
+ mt->sync[MTS_Y] |= bit;
+ }
+ pressure = normalized_pressure(input, pressure);
+ if (pressure != mts->pressure || initial) {
+ mts->pressure = pressure;
+ mt->sync[MTS_PRESSURE] |= bit;
+
+ if (pressure) {
+ if ((mt->touches & bit) == 0) {
+ mt->num_touches++;
+ mt->touches |= bit;
+ mt->sync[MTS_TOUCH] |= bit;
+ }
+ } else if (mt->touches & bit) {
+ mt->num_touches--;
+ mt->touches ^= bit;
+ mt->sync[MTS_TOUCH] |= bit;
+ }
+ }
+}
+
+void
+wsmouse_set(struct device *sc, enum wsmouseval type, int value, int aux)
+{
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct mt_slot *mts;
+
+ if (WSMOUSE_IS_MT_CODE(type)) {
+ if (aux < 0 || aux >= input->mt.num_slots)
+ return;
+ mts = &input->mt.slots[aux];
+ }
+
+ switch (type) {
+ case WSMOUSE_REL_X:
+ value += input->motion.x; /* fall through */
+ case WSMOUSE_ABS_X:
+ wsmouse_position(sc, value, input->motion.y);
+ return;
+ case WSMOUSE_REL_Y:
+ value += input->motion.y;
+ case WSMOUSE_ABS_Y:
+ wsmouse_position(sc, input->motion.x, value);
+ return;
+ case WSMOUSE_PRESSURE:
+ wsmouse_touch(sc, value, input->touch.contacts);
+ return;
+ case WSMOUSE_CONTACTS:
+ /* Contact counts can be overridden by wsmouse_touch. */
+ if (value != input->touch.contacts) {
+ input->touch.contacts = value;
+ input->touch.sync |= SYNC_CONTACTS;
+ }
+ return;
+ case WSMOUSE_TOUCH_WIDTH:
+ if (value != input->touch.width) {
+ input->touch.width = value;
+ input->touch.sync |= SYNC_TOUCH_WIDTH;
+ }
+ return;
+ case WSMOUSE_MT_REL_X:
+ value += mts->x; /* fall through */
+ case WSMOUSE_MT_ABS_X:
+ wsmouse_mtstate(sc, aux, value, mts->y, mts->pressure);
+ return;
+ case WSMOUSE_MT_REL_Y:
+ value += mts->y;
+ case WSMOUSE_MT_ABS_Y:
+ wsmouse_mtstate(sc, aux, mts->x, value, mts->pressure);
+ return;
+ case WSMOUSE_MT_PRESSURE:
+ wsmouse_mtstate(sc, aux, mts->x, mts->y, value);
+ return;
+ }
+}
+
+/* Make touch and motion state consistent. */
+void
+wsmouse_touch_update(struct wsmouseinput *input)
+{
+ struct motion_state *motion = &input->motion;
+ struct touch_state *touch = &input->touch;
+
+ if (touch->pressure == 0) {
+ /* Restore valid coordinates. */
+ if (motion->sync & SYNC_X)
+ motion->x -= motion->x_delta;
+ if (motion->sync & SYNC_Y)
+ motion->y -= motion->y_delta;
+ /* Don't generate motion/position events. */
+ motion->sync &= ~SYNC_POSITION;
+ }
+ if (touch->sync & SYNC_CONTACTS)
+ /* Suppress pointer motion. */
+ motion->x_delta = motion->y_delta = 0;
+
+ if ((touch->sync & SYNC_PRESSURE) && touch->min_pressure) {
+ if (touch->pressure >= input->params.pressure_hi)
+ touch->min_pressure = input->params.pressure_lo;
+ else if (touch->pressure < input->params.pressure_lo)
+ touch->min_pressure = input->params.pressure_hi;
+ }
+}
+
+/* Normalize multitouch state. */
+void
+wsmouse_mt_update(struct wsmouseinput *input)
+{
+ int i;
+
+ /*
+ * The same as above: There may be arbitrary coordinates if
+ * (pressure == 0). Clear the sync flags for touches that have
+ * been released.
+ */
+ if (input->mt.sync[MTS_TOUCH] & ~input->mt.touches) {
+ for (i = MTS_X; i < MTS_SIZE; i++)
+ input->mt.sync[i] &= input->mt.touches;
+ }
+}
+
+/*
+ * Select the pointer-controlling MT slot.
+ *
+ * Pointer-control is assigned to slots with non-zero motion deltas if
+ * at least one such slot exists. This function doesn't impose any
+ * restrictions on the way drivers use wsmouse_mtstate(), it covers
+ * partial, unordered, and "delta-filtered" input.
+ *
+ * The "cycle" is the set of slots with X/Y updates in previous sync
+ * operations; it will be cleared and rebuilt whenever a slot that is
+ * being updated is already a member. If a cycle ends that doesn't
+ * contain the pointer-controlling slot, a new slot will be selected.
+ */
+void
+wsmouse_ptr_ctrl(struct mt_state *mt)
+{
+ u_int updates;
+ int select, slot;
+
+ mt->prev_ptr = mt->ptr;
+
+ if (mt->num_touches <= 1) {
+ mt->ptr = mt->touches;
+ mt->ptr_cycle = mt->ptr;
+ return;
+ }
+
+ /*
+ * If there is no pointer-controlling slot or it is inactive,
+ * select a new one.
+ */
+ select = ((mt->ptr & mt->touches) == 0);
+
+ /* Remove slots without X/Y deltas from the cycle. */
+ updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH];
+ mt->ptr_cycle &= ~(mt->frame ^ updates);
+
+ if (mt->ptr_cycle & updates) {
+ select |= ((mt->ptr_cycle & mt->ptr) == 0);
+ mt->ptr_cycle = updates;
+ } else {
+ mt->ptr_cycle |= updates;
+ }
+ if (select) {
+ slot = (mt->ptr_cycle
+ ? ffs(mt->ptr_cycle) - 1 : ffs(mt->touches) - 1);
+ mt->ptr = (1 << slot);
+ }
+}
+
+/* Derive touch and motion state from MT state. */
+void
+wsmouse_mt_convert(struct device *sc)
+{
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct mt_state *mt = &input->mt;
+ struct mt_slot *mts;
+ int slot, pressure;
+
+ wsmouse_ptr_ctrl(mt);
+
+ if (mt->ptr) {
+ slot = ffs(mt->ptr) - 1;
+ mts = &mt->slots[slot];
+ wsmouse_position(sc, mts->x, mts->y);
+ if (mt->ptr != mt->prev_ptr)
+ /* Suppress pointer motion. */
+ input->motion.x_delta = input->motion.y_delta = 0;
+ pressure = mts->pressure;
+ } else {
+ pressure = 0;
+ }
+
+ wsmouse_touch(sc, pressure, mt->num_touches);
+}
+
+void
+wsmouse_evq_put(struct evq_access *evq, int ev_type, int ev_value)
+{
+ struct wscons_event *ev;
+ int space;
+
+ space = evq->evar->get - evq->put;
+ if (space != 1 && space != 1 - WSEVENT_QSIZE) {
+ ev = &evq->evar->q[evq->put++];
+ evq->put %= WSEVENT_QSIZE;
+ ev->type = ev_type;
+ ev->value = ev_value;
+ memcpy(&ev->time, &evq->ts, sizeof(struct timespec));
+ evq->result |= EVQ_RESULT_SUCCESS;
+ } else {
+ evq->result = EVQ_RESULT_OVERFLOW;
+ }
+}
+
+
+void
+wsmouse_btn_sync(struct wsmouseinput *input, struct evq_access *evq)
+{
+ struct btn_state *btn = &input->btn;
+ int button, ev_type;
+ u_int bit, sync;
+
+ for (sync = btn->sync; sync; sync ^= bit) {
+ button = ffs(sync) - 1;
+ bit = (1 << button);
+ ev_type = (btn->buttons & bit) ? BTN_DOWN_EV : BTN_UP_EV;
+ wsmouse_evq_put(evq, ev_type, button);
+ }
+}
+
+/*
+ * Scale with a [*.12] fixed-point factor and a remainder:
+ */
+static __inline int
+scale(int val, int factor, int *rmdr)
+{
+ val = val * factor + *rmdr;
+ if (val >= 0) {
+ *rmdr = val & 0xfff;
+ return (val >> 12);
+ } else {
+ *rmdr = -(-val & 0xfff);
+ return -(-val >> 12);
+ }
+}
+
+void
+wsmouse_motion_sync(struct wsmouseinput *input, struct evq_access *evq)
+{
+ struct motion_state *motion = &input->motion;
+ struct wsmouseparams *params = &input->params;
+ struct axis_filter *fltr;
+ int x, y, dx, dy;
+
+ if (motion->sync & SYNC_DELTAS) {
+ dx = params->x_inv ? -motion->dx : motion->dx;
+ dy = params->y_inv ? -motion->dy : motion->dy;
+ if (input->flags & SCALE_DELTAS) {
+ fltr = &input->fltr.h;
+ dx = scale(dx, fltr->scale, &fltr->rmdr);
+ fltr = &input->fltr.v;
+ dy = scale(dy, fltr->scale, &fltr->rmdr);
+ }
+ if (dx)
+ wsmouse_evq_put(evq, DELTA_X_EV(input->flags), dx);
+ if (dy)
+ wsmouse_evq_put(evq, DELTA_Y_EV(input->flags), dy);
+ if (motion->dz)
+ wsmouse_evq_put(evq, DELTA_Z_EV, motion->dz);
+ if (motion->dw)
+ wsmouse_evq_put(evq, DELTA_W_EV, motion->dw);
+ }
+ if (motion->sync & SYNC_POSITION) {
+ if (motion->sync & SYNC_X) {
+ x = (params->x_inv
+ ? params->x_inv - motion->x : motion->x);
+ wsmouse_evq_put(evq, ABS_X_EV(input->flags), x);
+ }
+ if (motion->sync & SYNC_Y) {
+ y = (params->y_inv
+ ? params->y_inv - motion->y : motion->y);
+ wsmouse_evq_put(evq, ABS_Y_EV(input->flags), y);
+ }
+ if (motion->x_delta == 0 && motion->y_delta == 0
+ && (input->flags & TPAD_NATIVE_MODE))
+ /* Suppress pointer motion. */
+ wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_RESET, 0);
+ }
+}
+
+void
+wsmouse_touch_sync(struct wsmouseinput *input, struct evq_access *evq)
+{
+ struct touch_state *touch = &input->touch;
+
+ if (touch->sync & SYNC_PRESSURE)
+ wsmouse_evq_put(evq, ABS_Z_EV, touch->pressure);
+ if (touch->sync & SYNC_CONTACTS)
+ wsmouse_evq_put(evq, ABS_W_EV, touch->contacts);
+ if ((touch->sync & SYNC_TOUCH_WIDTH)
+ && (input->flags & TPAD_NATIVE_MODE))
+ wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_WIDTH, touch->width);
+}
+
+/*
+ * Convert absolute touchpad input (compatibility mode).
+ */
+void
+wsmouse_compat_convert(struct device *sc, struct evq_access *evq)
+{
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseparams *params = &input->params;
+ int dx, dy, dz, dw;
+
+ dx = (input->motion.sync & SYNC_X) ? input->motion.x_delta : 0;
+ dy = (input->motion.sync & SYNC_Y) ? input->motion.y_delta : 0;
+ dz = (input->motion.sync & SYNC_DELTAS) ? input->motion.dz : 0;
+ dw = (input->motion.sync & SYNC_DELTAS) ? input->motion.dw : 0;
+
+ if ((params->dx_max && abs(dx) > params->dx_max)
+ || (params->dy_max && abs(dy) > params->dy_max)) {
+
+ dx = dy = 0;
+ }
+
+ wsmouse_motion(sc, dx, dy, dz, dw);
+
+ input->motion.sync &= ~SYNC_POSITION;
+ input->touch.sync = 0;
+}
+
+static __inline void
+clear_sync_flags(struct wsmouseinput *input)
+{
+ int i;
+
+ input->btn.sync = 0;
+ input->motion.sync = 0;
+ input->touch.sync = 0;
+ if (input->mt.frame) {
+ input->mt.frame = 0;
+ for (i = 0; i < MTS_SIZE; i++)
+ input->mt.sync[i] = 0;
+ }
+}
+
+void
+wsmouse_input_sync(struct device *sc)
+{
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct evq_access evq;
+
+ evq.evar = *input->evar;
+ if (evq.evar == NULL)
+ return;
+ evq.put = evq.evar->put;
+ evq.result = EVQ_RESULT_NONE;
+ getnanotime(&evq.ts);
+
+ add_mouse_randomness(input->btn.buttons
+ ^ input->motion.dx ^ input->motion.dy
+ ^ input->motion.x ^ input->motion.y
+ ^ input->motion.dz ^ input->motion.dw);
+
+ if (input->mt.frame) {
+ wsmouse_mt_update(input);
+ wsmouse_mt_convert(sc);
+ }
+ if (input->touch.sync)
+ wsmouse_touch_update(input);
+
+ if (input->flags & TPAD_COMPAT_MODE)
+ wsmouse_compat_convert(sc, &evq);
+
+ if (input->flags & RESYNC) {
+ input->flags &= ~RESYNC;
+ input->motion.sync &= SYNC_POSITION;
+ input->motion.x_delta = input->motion.y_delta = 0;
+ }
+
+ if (input->btn.sync)
+ wsmouse_btn_sync(input, &evq);
+ if (input->motion.sync)
+ wsmouse_motion_sync(input, &evq);
+ if (input->touch.sync)
+ wsmouse_touch_sync(input, &evq);
+ /* No MT events are generated yet. */
+
+ if (evq.result == EVQ_RESULT_SUCCESS) {
+ wsmouse_evq_put(&evq, WSCONS_EVENT_SYNC, 0);
+ if (evq.result == EVQ_RESULT_SUCCESS) {
+ evq.evar->put = evq.put;
+ WSEVENT_WAKEUP(evq.evar);
+ }
+ }
+
+ if (evq.result != EVQ_RESULT_OVERFLOW)
+ clear_sync_flags(input);
+ else
+ input->flags |= RESYNC;
+}
+
+int
+wsmouse_id_to_slot(struct device *sc, int id)
+{
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct mt_state *mt = &input->mt;
+ int slot;
+
+ if (mt->num_slots == 0)
+ return (-1);
+
+ FOREACHBIT(mt->touches, slot) {
+ if (mt->slots[slot].id == id)
+ return slot;
+ }
+ slot = ffs(~(mt->touches | mt->frame)) - 1;
+ if (slot >= 0 && slot < mt->num_slots) {
+ mt->frame |= 1 << slot;
+ mt->slots[slot].id = id;
+ return (slot);
+ } else {
+ return (-1);
+ }
+}
+
+/*
+ * Find a minimum-weight matching for an m-by-n matrix.
+ *
+ * m must be greater than or equal to n. The size of the buffer must be
+ * at least 4m + 3n.
+ *
+ * On return, the first m elements of the buffer contain the row-to-
+ * column mappings, i.e., buffer[i] is the column index for row i, or -1
+ * if there is no assignment for that row (which may happen if n < m).
+ *
+ * Wrong results because of overflows will not occur with input values
+ * in the range of 0 to INT_MAX / 2 inclusive.
+ *
+ * The function applies the Dinic-Kronrod algorithm. It is not modern or
+ * popular, but it seems to be a good choice for small matrices at least.
+ * The original form of the algorithm is modified as follows: There is no
+ * initial search for row minima, the initial assignments are in a
+ * "virtual" column with the index -1 and zero values. This permits inputs
+ * with n < m, and it simplifies the reassignments.
+ */
+void
+wsmouse_matching(int *matrix, int m, int n, int *buffer)
+{
+ int i, j, k, d, e, row, col, delta;
+ int *p;
+ int *r2c = buffer; /* row-to-column assignments */
+ int *red = r2c + m; /* reduced values of the assignments */
+ int *alt = red + m; /* alternative assignments */
+ int *mc = alt + m; /* row-wise minimal elements of cs */
+ int *cs = mc + m; /* the column set */
+ int *c2r = cs + n; /* column-to-row assignments in cs */
+ int *cd = c2r + n; /* column deltas (reduction) */
+
+ for (p = r2c; p < red; *p++ = -1) {}
+ for (; p < alt; *p++ = 0) {}
+ for (col = 0; col < n; col++) {
+ delta = INT_MAX;
+ for (i = 0, p = matrix + col; i < m; i++, p += n)
+ if ((d = *p - red[i]) <= delta) {
+ delta = d;
+ row = i;
+ }
+ cd[col] = delta;
+ if (r2c[row] < 0) {
+ r2c[row] = col;
+ continue;
+ }
+ for (p = alt; p < mc; *p++ = -1) {}
+ for (; p < cs; *p++ = col) {}
+ for (k = 0; (j = r2c[row]) >= 0;) {
+ cs[k++] = j;
+ c2r[j] = row;
+ alt[row] = mc[row];
+ delta = INT_MAX;
+ for (i = 0, p = matrix; i < m; i++, p += n)
+ if (alt[i] < 0) {
+ d = p[mc[i]] - cd[mc[i]];
+ e = p[j] - cd[j];
+ if (e < d) {
+ d = e;
+ mc[i] = j;
+ }
+ d -= red[i];
+ if (d <= delta) {
+ delta = d;
+ row = i;
+ }
+ }
+ cd[col] += delta;
+ for (i = 0; i < k; i++) {
+ cd[cs[i]] += delta;
+ red[c2r[cs[i]]] -= delta;
+ }
+ }
+ for (j = mc[row]; (r2c[row] = j) != col;) {
+ row = c2r[j];
+ j = alt[row];
+ }
+ }
+}
+
+void
+wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int size)
+{
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct mt_state *mt = &input->mt;
+ int i, j, m, n, dx, dy, slot, maxdist;
+ int *p, *r2c, *c2r;
+ u_int touches;
+
+ if (mt->num_slots == 0 || mt->matrix == NULL)
+ return;
+
+ size = imax(0, imin(size, mt->num_slots));
+ p = mt->matrix;
+ touches = mt->touches;
+ if (mt->num_touches >= size) {
+ FOREACHBIT(touches, slot)
+ for (i = 0; i < size; i++) {
+ dx = pt[i].x - mt->slots[slot].x;
+ dy = pt[i].y - mt->slots[slot].y;
+ *p++ = dx * dx + dy * dy;
+ }
+ m = mt->num_touches;
+ n = size;
+ } else {
+ for (i = 0; i < size; i++)
+ FOREACHBIT(touches, slot) {
+ dx = pt[i].x - mt->slots[slot].x;
+ dy = pt[i].y - mt->slots[slot].y;
+ *p++ = dx * dx + dy * dy;
+ }
+ m = size;
+ n = mt->num_touches;
+ }
+ wsmouse_matching(mt->matrix, m, n, p);
+
+ r2c = p;
+ c2r = p + m;
+ maxdist = input->params.tracking_maxdist;
+ maxdist = (maxdist ? maxdist * maxdist : INT_MAX);
+ for (i = 0, p = mt->matrix; i < m; i++, p += n)
+ if ((j = r2c[i]) >= 0) {
+ if (p[j] <= maxdist)
+ c2r[j] = i;
+ else
+ c2r[j] = r2c[i] = -1;
+ }
+
+ p = (n == size ? c2r : r2c);
+ for (i = 0; i < size; i++)
+ if (*p++ < 0) {
+ slot = ffs(~(mt->touches | mt->frame)) - 1;
+ if (slot < 0 || slot >= mt->num_slots)
+ break;
+ wsmouse_mtstate(sc, slot,
+ pt[i].x, pt[i].y, pt[i].pressure);
+ pt[i].slot = slot;
+ }
+
+ p = (n == size ? r2c : c2r);
+ FOREACHBIT(touches, slot)
+ if ((i = *p++) >= 0) {
+ wsmouse_mtstate(sc, slot,
+ pt[i].x, pt[i].y, pt[i].pressure);
+ pt[i].slot = slot;
+ } else {
+ wsmouse_mtstate(sc, slot, 0, 0, 0);
+ }
+}
+
+static __inline void
+free_mt_slots(struct wsmouseinput *input)
+{
+ int n, size;
+
+ if ((n = input->mt.num_slots)) {
+ size = n * sizeof(struct mt_slot);
+ if (input->flags & MT_TRACKING)
+ size += MATRIX_SIZE(n);
+ input->mt.num_slots = 0;
+ free(input->mt.slots, M_DEVBUF, size);
+ input->mt.slots = NULL;
+ input->mt.matrix = NULL;
+ }
+}
+
+/* Allocate the MT slots and, if necessary, the buffers for MT tracking. */
+int
+wsmouse_mt_init(struct device *sc, int num_slots, int tracking)
+{
+ struct wsmouseinput *input =
+ &((struct wsmouse_softc *) sc)->input;
+ int n, size;
+
+ if (num_slots == input->mt.num_slots
+ && (!tracking == ((input->flags & MT_TRACKING) == 0)))
+ return (0);
+
+ free_mt_slots(input);
+
+ if (tracking)
+ input->flags |= MT_TRACKING;
+ else
+ input->flags &= ~MT_TRACKING;
+ n = imin(imax(num_slots, 0), WSMOUSE_MT_SLOTS_MAX);
+ if (n) {
+ size = n * sizeof(struct mt_slot);
+ if (input->flags & MT_TRACKING)
+ size += MATRIX_SIZE(n);
+ input->mt.slots = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+ if (input->mt.slots != NULL) {
+ if (input->flags & MT_TRACKING)
+ input->mt.matrix = (int *)
+ (input->mt.slots + n);
+ input->mt.num_slots = n;
+ return (0);
+ }
+ }
+ return (-1);
+}
+
+void
+wsmouse_init_scaling(struct wsmouseinput *input)
+{
+ struct wsmouseparams *params = &input->params;
+ int m, n;
+
+ if (params->dx_mul || params->dx_div
+ || params->dy_mul || params->dy_div) {
+ /* Scale factors have a [*.12] fixed point format. */
+ m = (params->dx_mul ? abs(params->dx_mul) : 1);
+ n = (params->dx_div ? abs(params->dx_div) : 1);
+ input->fltr.h.scale = (m << 12) / n;
+ input->fltr.h.rmdr = 0;
+ m = (params->dy_mul ? abs(params->dy_mul) : 1);
+ n = (params->dy_div ? abs(params->dy_div): 1);
+ input->fltr.v.scale = (m << 12) / n;
+ input->fltr.v.rmdr = 0;
+ input->flags |= SCALE_DELTAS;
+ } else {
+ input->flags &= ~SCALE_DELTAS;
+ }
+}
+
+void
+wsmouse_set_param(struct device *sc, size_t param, int value)
+{
+ struct wsmouseinput *input =
+ &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseparams *params = &input->params;
+ int *p;
+
+ if (param < 0 || param > WSMPARAM_LASTFIELD) {
+ printf("wsmouse_set_param: invalid parameter type\n");
+ return;
+ }
+
+ p = (int *) (((void *) params) + param);
+ *p = value;
+
+ if (IS_WSMFLTR_PARAM(param)) {
+ wsmouse_init_scaling(input);
+ } else if (param == WSMPARAM_SWAPXY) {
+ if (value)
+ input->flags |= SWAPXY;
+ else
+ input->flags &= ~SWAPXY;
+ } else if (param == WSMPARAM_PRESSURE_LO) {
+ params->pressure_hi =
+ imax(params->pressure_lo, params->pressure_hi);
+ input->touch.min_pressure = params->pressure_hi;
+ } else if (param == WSMPARAM_PRESSURE_HI
+ && params->pressure_lo == 0) {
+ params->pressure_lo = params->pressure_hi;
+ input->touch.min_pressure = params->pressure_hi;
+ }
+}
+
+int
+wsmouse_set_mode(struct device *sc, int mode)
+{
+ struct wsmouseinput *input =
+ &((struct wsmouse_softc *) sc)->input;
+
+ if (mode == WSMOUSE_COMPAT) {
+ input->flags &= ~TPAD_NATIVE_MODE;
+ input->flags |= TPAD_COMPAT_MODE;
+ return (0);
+ } else if (mode == WSMOUSE_NATIVE) {
+ input->flags &= ~TPAD_COMPAT_MODE;
+ input->flags |= TPAD_NATIVE_MODE;
+ return (0);
+ }
+ return (-1);
+}
+
+void
+wsmouse_input_init(struct wsmouseinput *input, struct wseventvar **evar)
+{
+ input->evar = evar;
+}
+
+void
+wsmouse_input_cleanup(struct wsmouseinput *input)
+{
+ free_mt_slots(input);
+}
diff --git a/sys/dev/wscons/wsmouseinput.h b/sys/dev/wscons/wsmouseinput.h
new file mode 100644
index 00000000000..0b377b405cc
--- /dev/null
+++ b/sys/dev/wscons/wsmouseinput.h
@@ -0,0 +1,169 @@
+/* $OpenBSD: wsmouseinput.h,v 1.1 2016/03/30 23:34:12 bru Exp $ */
+
+/*
+ * Copyright (c) 2015, 2016 Ulf Brosziewski
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * wsmouse input processing - private header
+ */
+
+#ifndef _WSMOUSEINPUT_H_
+#define _WSMOUSEINPUT_H_
+
+
+struct btn_state {
+ u_int buttons;
+ u_int sync;
+};
+
+struct motion_state {
+ int dx;
+ int dy;
+ int dz;
+ int dw;
+ int x;
+ int y;
+ u_int sync;
+
+ /* deltas of absolute coordinates */
+ int x_delta;
+ int y_delta;
+};
+#define SYNC_DELTAS (1 << 0)
+#define SYNC_X (1 << 1)
+#define SYNC_Y (1 << 2)
+#define SYNC_POSITION (SYNC_X | SYNC_Y)
+
+struct touch_state {
+ int pressure;
+ int contacts;
+ int width;
+ u_int sync;
+
+ int min_pressure;
+};
+#define SYNC_PRESSURE (1 << 0)
+#define SYNC_CONTACTS (1 << 1)
+#define SYNC_TOUCH_WIDTH (1 << 2)
+
+struct mt_slot {
+ int x;
+ int y;
+ int pressure;
+ int id; /* tracking ID */
+};
+#define MTS_TOUCH 0
+#define MTS_X 1
+#define MTS_Y 2
+#define MTS_PRESSURE 3
+
+#define MTS_SIZE 4
+
+struct mt_state {
+ /* the set of slots with active touches */
+ u_int touches;
+ /* the set of slots with unsynchronized state */
+ u_int frame;
+
+ int num_slots;
+ struct mt_slot *slots;
+ /* the sets of changes per slot axis */
+ u_int sync[MTS_SIZE];
+
+ int num_touches;
+
+ /* pointer control */
+ u_int ptr;
+ u_int ptr_cycle;
+ u_int prev_ptr;
+
+ /* a buffer for the MT tracking function */
+ int *matrix;
+};
+
+
+struct axis_filter {
+ /* scale factor in [*.12] fixed-point format */
+ int scale;
+ int rmdr;
+};
+
+struct wsmouseinput {
+ u_int flags;
+
+ struct btn_state btn;
+ struct motion_state motion;
+ struct touch_state touch;
+ struct mt_state mt;
+
+ struct wsmouseparams params;
+ struct {
+ struct axis_filter h;
+ struct axis_filter v;
+ } fltr;
+
+ struct wseventvar **evar;
+};
+/* wsmouseinput.flags */
+#define TPAD_COMPAT_MODE (1 << 0)
+#define TPAD_NATIVE_MODE (1 << 1)
+#define SCALE_DELTAS (1 << 2)
+#define MT_TRACKING (1 << 3)
+#define SWAPXY (1 << 4)
+#define RESYNC (1 << 16)
+
+struct evq_access {
+ struct wseventvar *evar;
+ struct timespec ts;
+ int put;
+ int result;
+};
+#define EVQ_RESULT_OVERFLOW -1
+#define EVQ_RESULT_NONE 0
+#define EVQ_RESULT_SUCCESS 1
+
+
+void wsmouse_evq_put(struct evq_access *, int, int);
+void wsmouse_init_scaling(struct wsmouseinput *);
+
+void wsmouse_input_init(struct wsmouseinput *, struct wseventvar **);
+void wsmouse_input_cleanup(struct wsmouseinput *);
+
+
+#define FOREACHBIT(v, i) \
+ for ((i) = ffs(v) - 1; (i) != -1; (i) = ffs((v) & (~1 << (i))) - 1)
+
+
+#define DELTA_X_EV(flags) (((flags) & SWAPXY) ? \
+ WSCONS_EVENT_MOUSE_DELTA_Y : WSCONS_EVENT_MOUSE_DELTA_X)
+#define DELTA_Y_EV(flags) (((flags) & SWAPXY) ? \
+ WSCONS_EVENT_MOUSE_DELTA_X : WSCONS_EVENT_MOUSE_DELTA_Y)
+#define ABS_X_EV(flags) (((flags) & SWAPXY) ? \
+ WSCONS_EVENT_MOUSE_ABSOLUTE_Y : WSCONS_EVENT_MOUSE_ABSOLUTE_X)
+#define ABS_Y_EV(flags) (((flags) & SWAPXY) ? \
+ WSCONS_EVENT_MOUSE_ABSOLUTE_X : WSCONS_EVENT_MOUSE_ABSOLUTE_Y)
+#define DELTA_Z_EV WSCONS_EVENT_MOUSE_DELTA_Z
+#define DELTA_W_EV WSCONS_EVENT_MOUSE_DELTA_W
+#define ABS_Z_EV WSCONS_EVENT_TOUCH_PRESSURE
+#define ABS_W_EV WSCONS_EVENT_TOUCH_CONTACTS
+#define BTN_DOWN_EV WSCONS_EVENT_MOUSE_DOWN
+#define BTN_UP_EV WSCONS_EVENT_MOUSE_UP
+#define SYNC_EV WSCONS_EVENT_SYNC
+
+/* buffer size for wsmouse_matching */
+#define MATRIX_SIZE(slots) (((slots) + 7) * (slots) * sizeof(int))
+
+#endif /* _WSMOUSEINPUT_H_ */
diff --git a/sys/dev/wscons/wsmousevar.h b/sys/dev/wscons/wsmousevar.h
index fa5fa04d09f..3c5a5a8176d 100644
--- a/sys/dev/wscons/wsmousevar.h
+++ b/sys/dev/wscons/wsmousevar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsmousevar.h,v 1.8 2014/12/21 18:16:07 shadchin Exp $ */
+/* $OpenBSD: wsmousevar.h,v 1.9 2016/03/30 23:34:12 bru Exp $ */
/* $NetBSD: wsmousevar.h,v 1.4 2000/01/08 02:57:24 takemura Exp $ */
/*
@@ -32,6 +32,22 @@
*/
/*
+ * Copyright (c) 2015, 2016 Ulf Brosziewski
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
* WSMOUSE interfaces.
*/
@@ -75,3 +91,152 @@ int wsmousedevprint(void *, const char *);
void wsmouse_input(struct device *kbddev, u_int btns,
int x, int y, int z, int w, u_int flags);
+
+
+/* Process standard mouse input. */
+#define WSMOUSE_INPUT(sc_wsmousedev, btns, dx, dy, dz, dw) \
+ do { \
+ wsmouse_buttons((sc_wsmousedev), (btns)); \
+ wsmouse_motion((sc_wsmousedev), (dx), (dy), (dz), (dw));\
+ wsmouse_input_sync(sc_wsmousedev); \
+ } while (0)
+
+
+/* Process standard touchpad input. */
+#define WSMOUSE_TOUCH(sc_wsmousedev, btns, x, y, pressure, contacts) \
+ do { \
+ wsmouse_buttons((sc_wsmousedev), (btns)); \
+ wsmouse_position((sc_wsmousedev), (x), (y)); \
+ wsmouse_touch((sc_wsmousedev), (pressure), (contacts)); \
+ wsmouse_input_sync(sc_wsmousedev); \
+ } while (0)
+
+
+/*
+ * Drivers for touchpads that don't report pressure values can pass
+ * WSMOUSE_DEFAULT_PRESSURE to wsmouse_touch or wsmouse_mtstate.
+ *
+ * A pressure value of 0 signals that a touch has been released (coordinates
+ * will be ignored). Based on its pressure argument, wsmouse_touch will
+ * normalize the contact count (drivers for touch devices that don't
+ * recognize multiple contacts can always pass 0 as contact count to
+ * wsmouse_touch).
+ */
+#define WSMOUSE_DEFAULT_PRESSURE -1
+
+
+struct device;
+enum wsmouseval;
+struct mtpoint;
+
+
+/* Report button state. */
+void wsmouse_buttons(struct device *, u_int);
+
+/* Report motion deltas (dx, dy, dz, dw). */
+void wsmouse_motion(struct device *, int, int, int, int);
+
+/* Report absolute coordinates (x, y). */
+void wsmouse_position(struct device *, int, int);
+
+/* Report (single-)touch input (pressure, contacts). */
+void wsmouse_touch(struct device *, int, int);
+
+/* Report slot-based multitouch input (slot, x, y, pressure). */
+void wsmouse_mtstate(struct device *, int, int, int, int);
+
+/* Report multitouch input (mtpoints, size). */
+void wsmouse_mtframe(struct device *, struct mtpoint *, int);
+
+/* Report a single value (type, value, aux). */
+void wsmouse_set(struct device *, enum wsmouseval, int, int);
+
+/* Assign or look up a slot number for a tracking ID (id). */
+int wsmouse_id_to_slot(struct device *, int);
+
+
+/* Synchronize (generate wscons events) */
+void wsmouse_input_sync(struct device *);
+
+
+/* Initialize MT structures (num_slots, tracking). */
+int wsmouse_mt_init(struct device *, int, int);
+
+/* Set a filter/transformation value (param type, value). */
+void wsmouse_set_param(struct device *, size_t, int);
+
+/* Switch between compatibility mode and native mode. */
+int wsmouse_set_mode(struct device *, int);
+
+
+/*
+ * Type codes for wsmouse_set. REL_X/Y, MT_REL_X/Y, and TOUCH_WIDTH
+ * cannot be reported by other functions. Please note that REL_X/Y
+ * values are deltas to be applied to the absolute coordinates and
+ * don't represent "pure" relative motion.
+ */
+enum wsmouseval {
+ WSMOUSE_REL_X,
+ WSMOUSE_ABS_X,
+ WSMOUSE_REL_Y,
+ WSMOUSE_ABS_Y,
+ WSMOUSE_PRESSURE,
+ WSMOUSE_CONTACTS,
+ WSMOUSE_TOUCH_WIDTH,
+ WSMOUSE_MT_REL_X,
+ WSMOUSE_MT_ABS_X,
+ WSMOUSE_MT_REL_Y,
+ WSMOUSE_MT_ABS_Y,
+ WSMOUSE_MT_PRESSURE
+};
+
+#define WSMOUSE_IS_MT_CODE(code) \
+ ((code) >= WSMOUSE_MT_REL_X && (code) <= WSMOUSE_MT_PRESSURE)
+
+
+struct mtpoint {
+ int x;
+ int y;
+ int pressure;
+ int slot; /* An output field, set by wsmouse_mtframe. */
+};
+
+
+struct wsmouseparams {
+ int x_inv;
+ int y_inv;
+
+ int dx_mul; /* delta scaling */
+ int dx_div;
+ int dy_mul;
+ int dy_div;
+
+ int swapxy;
+
+ int pressure_lo;
+ int pressure_hi;
+
+ int dx_max; /* (compat mode) */
+ int dy_max;
+
+ int tracking_maxdist;
+};
+
+#define WSMPARAM_X_INV offsetof(struct wsmouseparams, x_inv)
+#define WSMPARAM_Y_INV offsetof(struct wsmouseparams, y_inv)
+#define WSMPARAM_DX_MUL offsetof(struct wsmouseparams, dx_mul)
+#define WSMPARAM_DX_DIV offsetof(struct wsmouseparams, dx_div)
+#define WSMPARAM_DY_MUL offsetof(struct wsmouseparams, dy_mul)
+#define WSMPARAM_DY_DIV offsetof(struct wsmouseparams, dy_div)
+#define WSMPARAM_SWAPXY offsetof(struct wsmouseparams, swapxy)
+#define WSMPARAM_PRESSURE_LO offsetof(struct wsmouseparams, pressure_lo)
+#define WSMPARAM_PRESSURE_HI offsetof(struct wsmouseparams, pressure_hi)
+#define WSMPARAM_DX_MAX offsetof(struct wsmouseparams, dx_max)
+#define WSMPARAM_DY_MAX offsetof(struct wsmouseparams, dy_max)
+
+#define WSMPARAM_LASTFIELD WSMPARAM_DY_MAX
+
+#define IS_WSMFLTR_PARAM(param) \
+ ((param) >= WSMPARAM_DX_MUL && (param) <= WSMPARAM_DY_DIV)
+
+#define WSMOUSE_MT_SLOTS_MAX 10