The diffs below are a rewrite of the input-processing part of wsmouse. It
adds support for multitouch input.
I have split the set of diffs into three parts and I will post part 2 and 3
in separate messages. Part 1 below contains all patches for wscons, part 2
is for the hardware drivers, part 3 is a patch for the synaptics driver in
xenocara (compiling that driver will require the modified version of
wsconsio.h in /usr/include/dev/wscons).
For those who had a look at the test version from December: The code for
the internal touchpad driver ("wstpad") is not included here, I intend
to add and present it again later. Apart from that there are only minor
changes. I have moved the input-processing functions into wsmouse.c,
renamed various things, and improved the tracking functions and the
handling of overflows of the event queue.
The wsmouse_input function has been removed. Hardware drivers make one
or more calls to the new "state-reporting" functions of wsmouse (see
wsmousevar.h), and signal that a frame is complete by calling
wsmouse_input_sync, which generates the wscons events. Roughly, the set
of functions corresponds to the different types of state: button state,
relative motion, absolute positions, single-touch state, and (MT) slot
state. "Standard" combinations of calls are covered by the macros
WSMOUSE_INPUT and WSMOUSE_TOUCH. There is also a more generic function
which can be used for less common inputs or requirements (wsmouse_set).
I assume that support for multi-user touch devices is not required, the
maximal number of MT slots is defined in wsmousevar.h as 10. The technical
limit is sizeof(u_int) * 8.
The MT structures in wsmouse must be initialized with a call to
wsmouse_mt_init. Drivers for MT devices that associate touches with
"tracking IDs" can use wsmouse_id_to_slot() to assign or to look up slot
numbers, and call wsmouse_mtstate subsequently. hidmt may be a driver that
needs it. Drivers for MT devices that don't provide "MT tracking" - ubcmtp,
for example - must collect the contact points in an array of mtpoint
structures and pass it to wsmouse_mtframe, which computes a matching with
the points of the last frame and calls wsmouse_mtstate internally.
Currently MT state is always converted into a single-touch representation,
and wscons events are generated accordingly. No MT events have been defined
yet. However, minor changes in the set of events are necessary to make the
new code work cleanly with the synaptics driver (see 3/3).
I will comment on some more details in the next messages.
And, yes, this is a request for OKs.
Index: dev/wscons/wsmousevar.h
===================================================================
RCS file: /cvs/src/sys/dev/wscons/wsmousevar.h,v
retrieving revision 1.8
diff -u -p -r1.8 wsmousevar.h
--- dev/wscons/wsmousevar.h 21 Dec 2014 18:16:07 -0000 1.8
+++ dev/wscons/wsmousevar.h 12 Mar 2016 20:42:48 -0000
@@ -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.
*/
@@ -64,14 +80,151 @@ struct wsmousedev_attach_args {
*/
int wsmousedevprint(void *, const char *);
+
+/* 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);
+
+
/*
- * Callbacks from the mouse driver to the wsmouse interface driver.
+ * 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.
*/
-#define WSMOUSE_INPUT_DELTA 0
-#define WSMOUSE_INPUT_ABSOLUTE_X (1<<0)
-#define WSMOUSE_INPUT_ABSOLUTE_Y (1<<1)
-#define WSMOUSE_INPUT_ABSOLUTE_Z (1<<2)
-#define WSMOUSE_INPUT_ABSOLUTE_W (1<<3)
+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)
-void wsmouse_input(struct device *kbddev, u_int btns,
- int x, int y, int z, int w, u_int flags);
+#define WSMOUSE_MT_SLOTS_MAX 10
Index: dev/wscons/wsmouseinput.h
===================================================================
RCS file: dev/wscons/wsmouseinput.h
diff -N dev/wscons/wsmouseinput.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ dev/wscons/wsmouseinput.h 12 Mar 2016 20:42:48 -0000
@@ -0,0 +1,173 @@
+/*
+ * 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 *);
+
+static __inline__ void
+timespeccpy(struct timespec *dst, struct timespec *src)
+{
+ dst->tv_sec = src->tv_sec;
+ dst->tv_nsec = src->tv_nsec;
+}
+
+#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_ */
Index: dev/wscons/wsconsio.h
===================================================================
RCS file: /cvs/src/sys/dev/wscons/wsconsio.h,v
retrieving revision 1.73
diff -u -p -r1.73 wsconsio.h
--- dev/wscons/wsconsio.h 12 Dec 2015 12:30:18 -0000 1.73
+++ dev/wscons/wsconsio.h 12 Mar 2016 20:42:48 -0000
@@ -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
@@ -96,6 +96,20 @@ struct wscons_event {
((type) == WSCONS_EVENT_MOUSE_DOWN))
#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)
Index: dev/wscons/wsmouse.c
===================================================================
RCS file: /cvs/src/sys/dev/wscons/wsmouse.c,v
retrieving revision 1.28
diff -u -p -r1.28 wsmouse.c
--- dev/wscons/wsmouse.c 10 Sep 2015 18:14:52 -0000 1.28
+++ dev/wscons/wsmouse.c 12 Mar 2016 20:42:48 -0000
@@ -72,6 +72,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.
+ */
+
+/*
* Mouse driver.
*/
@@ -88,10 +104,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>
@@ -110,30 +128,16 @@ extern int wsmuxdebug;
#define DPRINTFN(n,x)
#endif
-#define INVALID_X INT_MAX
-#define INVALID_Y INT_MAX
-#define INVALID_Z INT_MAX
-#define INVALID_W INT_MAX
-
struct wsmouse_softc {
struct wsevsrc sc_base;
const struct wsmouse_accessops *sc_accessops;
void *sc_accesscookie;
- u_int sc_mb; /* mouse button state */
- u_int sc_ub; /* user button state */
- int sc_dx; /* delta-x */
- int sc_dy; /* delta-y */
- int sc_dz; /* delta-z */
- int sc_dw; /* delta-w */
- int sc_x; /* absolute-x */
- int sc_y; /* absolute-y */
- int sc_z; /* absolute-z */
- int sc_w; /* absolute-w */
-
int sc_refcnt;
u_char sc_dying; /* device is being detached */
+
+ struct wsmouseinput input;
};
int wsmouse_match(struct device *, void *, void *);
@@ -199,6 +203,8 @@ wsmouse_attach(struct device *parent, st
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,201 +285,9 @@ wsmouse_detach(struct device *self, int
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
- return (0);
-}
-
-void
-wsmouse_input(struct device *wsmousedev, u_int btns, /* 0 is up */
- int x, int y, int z, int w, u_int flags)
-{
- struct wsmouse_softc *sc = (struct wsmouse_softc *)wsmousedev;
- struct wscons_event *ev;
- struct wseventvar *evar;
- int mb, ub, d, get, put, any;
-
- add_mouse_randomness(x ^ y ^ z ^ w ^ btns);
-
- /*
- * Discard input if not ready.
- */
- evar = sc->sc_base.me_evp;
- if (evar == NULL)
- return;
-
-#ifdef DIAGNOSTIC
- if (evar->q == NULL) {
- printf("wsmouse_input: evar->q=NULL\n");
- return;
- }
-#endif
-
-#if NWSMUX > 0
- DPRINTFN(5,("wsmouse_input: %s mux=%p, evar=%p\n",
- sc->sc_base.me_dv.dv_xname, sc->sc_base.me_parent, evar));
-#endif
-
- sc->sc_mb = btns;
- if (!(flags & WSMOUSE_INPUT_ABSOLUTE_X))
- sc->sc_dx += x;
- if (!(flags & WSMOUSE_INPUT_ABSOLUTE_Y))
- sc->sc_dy += y;
- if (!(flags & WSMOUSE_INPUT_ABSOLUTE_Z))
- sc->sc_dz += z;
- if (!(flags & WSMOUSE_INPUT_ABSOLUTE_W))
- sc->sc_dw += w;
-
- /*
- * We have at least one event (mouse button, delta-X, or
- * delta-Y; possibly all three, and possibly three separate
- * button events). Deliver these events until we are out
- * of changes or out of room. As events get delivered,
- * mark them `unchanged'.
- */
- ub = sc->sc_ub;
- any = 0;
- get = evar->get;
- put = evar->put;
- ev = &evar->q[put];
-
- /* NEXT prepares to put the next event, backing off if necessary */
-#define NEXT
\
- if ((++put) % WSEVENT_QSIZE == get) { \
- put--; \
- goto out; \
- }
- /* ADVANCE completes the `put' of the event */
-#define ADVANCE
\
- ev++; \
- if (put >= WSEVENT_QSIZE) { \
- put = 0; \
- ev = &evar->q[0]; \
- } \
- any = 1
- /* TIMESTAMP sets `time' field of the event to the current time */
-#define TIMESTAMP \
- do { \
- getnanotime(&ev->time); \
- } while (0)
-
- if (flags & WSMOUSE_INPUT_ABSOLUTE_X) {
- if (sc->sc_x != x) {
- NEXT;
- ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_X;
- ev->value = x;
- TIMESTAMP;
- ADVANCE;
- sc->sc_x = x;
- }
- } else {
- if (sc->sc_dx) {
- NEXT;
- ev->type = WSCONS_EVENT_MOUSE_DELTA_X;
- ev->value = sc->sc_dx;
- TIMESTAMP;
- ADVANCE;
- sc->sc_dx = 0;
- }
- }
- if (flags & WSMOUSE_INPUT_ABSOLUTE_Y) {
- if (sc->sc_y != y) {
- NEXT;
- ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_Y;
- ev->value = y;
- TIMESTAMP;
- ADVANCE;
- sc->sc_y = y;
- }
- } else {
- if (sc->sc_dy) {
- NEXT;
- ev->type = WSCONS_EVENT_MOUSE_DELTA_Y;
- ev->value = sc->sc_dy;
- TIMESTAMP;
- ADVANCE;
- sc->sc_dy = 0;
- }
- }
- if (flags & WSMOUSE_INPUT_ABSOLUTE_Z) {
- if (sc->sc_z != z) {
- NEXT;
- ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_Z;
- ev->value = z;
- TIMESTAMP;
- ADVANCE;
- sc->sc_z = z;
- }
- } else {
- if (sc->sc_dz) {
- NEXT;
- ev->type = WSCONS_EVENT_MOUSE_DELTA_Z;
- ev->value = sc->sc_dz;
- TIMESTAMP;
- ADVANCE;
- sc->sc_dz = 0;
- }
- }
- if (flags & WSMOUSE_INPUT_ABSOLUTE_W) {
- if (sc->sc_w != w) {
- NEXT;
- ev->type = WSCONS_EVENT_MOUSE_ABSOLUTE_W;
- ev->value = w;
- TIMESTAMP;
- ADVANCE;
- sc->sc_w = w;
- }
- } else {
- if (sc->sc_dw) {
- NEXT;
- ev->type = WSCONS_EVENT_MOUSE_DELTA_W;
- ev->value = sc->sc_dw;
- TIMESTAMP;
- ADVANCE;
- sc->sc_dw = 0;
- }
- }
-
- mb = sc->sc_mb;
- while ((d = mb ^ ub) != 0) {
- /*
- * Mouse button change. Find the first change and drop
- * it into the event queue.
- */
- NEXT;
- ev->value = ffs(d) - 1;
+ wsmouse_input_cleanup(&sc->input);
- KASSERT(ev->value >= 0);
-
- d = 1 << ev->value;
- ev->type =
- (mb & d) ? WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP;
- TIMESTAMP;
- ADVANCE;
- ub ^= d;
- }
-
- NEXT;
- ev->type = WSCONS_EVENT_SYNC;
- ev->value = 0;
- TIMESTAMP;
- ADVANCE;
-
-#undef TIMESTAMP
-#undef ADVANCE
-#undef NEXT
-
-out:
- if (any) {
- sc->sc_ub = ub;
- evar->put = put;
- WSEVENT_WAKEUP(evar);
-#ifdef HAVE_BURNER_SUPPORT
- /* wsdisplay_burn(sc->sc_displaydv, WSDISPLAY_BURN_MOUSE); */
-#endif
-#if NWSMUX > 0
- DPRINTFN(5,("wsmouse_input: %s wakeup evar=%p\n",
- sc->sc_base.me_dv.dv_xname, evar));
-#endif
- }
+ return (0);
}
int
@@ -564,10 +378,6 @@ int
wsmousedoopen(struct wsmouse_softc *sc, struct wseventvar *evp)
{
sc->sc_base.me_evp = evp;
- sc->sc_x = INVALID_X;
- sc->sc_y = INVALID_Y;
- sc->sc_z = INVALID_Z;
- sc->sc_w = INVALID_W;
/* enable the device, and punt if that's not possible */
return (*sc->sc_accessops->enable)(sc->sc_accesscookie);
@@ -734,3 +544,836 @@ wsmouse_add_mux(int unit, struct wsmux_s
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;
+ timespeccpy(&ev->time, &evq->ts);
+ 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);
+}