diff options
author | Ulf Brosziewski <bru@cvs.openbsd.org> | 2016-03-30 23:34:13 +0000 |
---|---|---|
committer | Ulf Brosziewski <bru@cvs.openbsd.org> | 2016-03-30 23:34:13 +0000 |
commit | bb8a088342dbb8ce495fcdc306d2e2aafba86ce3 (patch) | |
tree | c6734c2df6aff4e3b257bec7d01b73f1a42d02bd /sys | |
parent | 5a710a5f10a7c160b6aa76c992df11c4f29c2846 (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.c | 15 | ||||
-rw-r--r-- | sys/dev/pckbc/pms.c | 206 | ||||
-rw-r--r-- | sys/dev/usb/ubcmtp.c | 31 | ||||
-rw-r--r-- | sys/dev/wscons/wsconsio.h | 20 | ||||
-rw-r--r-- | sys/dev/wscons/wsmouse.c | 843 | ||||
-rw-r--r-- | sys/dev/wscons/wsmouseinput.h | 169 | ||||
-rw-r--r-- | sys/dev/wscons/wsmousevar.h | 167 |
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 |