summaryrefslogtreecommitdiff
path: root/sys/dev/wscons
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/wscons')
-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
4 files changed, 1194 insertions, 5 deletions
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