summaryrefslogtreecommitdiff
path: root/sys/dev/wscons
diff options
context:
space:
mode:
authorUlf Brosziewski <bru@cvs.openbsd.org>2017-02-27 15:59:57 +0000
committerUlf Brosziewski <bru@cvs.openbsd.org>2017-02-27 15:59:57 +0000
commit74f221919dd8f5bfe860ec00fc4265f2fead240a (patch)
tree2149b14ceab7372ecc547961ef39156cf9d549f9 /sys/dev/wscons
parent8052f6641ab3f87995bf1c9a600febcff7342382 (diff)
Handle touchpad input in wsmouse.
The wstpad file contains the core of a touchpad-input driver that is coupled with wsmouse. It is active in compat-mode if wsmouse has been configured for it. ok @matthieu @stsp @mpi
Diffstat (limited to 'sys/dev/wscons')
-rw-r--r--sys/dev/wscons/files.wscons3
-rw-r--r--sys/dev/wscons/wsconsio.h57
-rw-r--r--sys/dev/wscons/wsmouse.c243
-rw-r--r--sys/dev/wscons/wsmouseinput.h72
-rw-r--r--sys/dev/wscons/wsmousevar.h52
-rw-r--r--sys/dev/wscons/wstpad.c1158
6 files changed, 1458 insertions, 127 deletions
diff --git a/sys/dev/wscons/files.wscons b/sys/dev/wscons/files.wscons
index 9893665ea92..920f0032986 100644
--- a/sys/dev/wscons/files.wscons
+++ b/sys/dev/wscons/files.wscons
@@ -1,4 +1,4 @@
-# $OpenBSD: files.wscons,v 1.16 2013/10/18 22:06:40 miod Exp $
+# $OpenBSD: files.wscons,v 1.17 2017/02/27 15:59:56 bru Exp $
# $NetBSD: files.wscons,v 1.34 2005/05/04 01:52:16 augustss Exp $
#
@@ -28,6 +28,7 @@ file dev/wscons/wsevent.c wsdisplay | wskbd |
file dev/wscons/wskbd.c wskbd needs-flag
file dev/wscons/wskbdutil.c wskbd
file dev/wscons/wsmouse.c wsmouse needs-flag
+file dev/wscons/wstpad.c wsmouse
pseudo-device wsmux
file dev/wscons/wsmux.c wsmux needs-flag
diff --git a/sys/dev/wscons/wsconsio.h b/sys/dev/wscons/wsconsio.h
index 26d9eda8eaa..4120af32edf 100644
--- a/sys/dev/wscons/wsconsio.h
+++ b/sys/dev/wscons/wsconsio.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsconsio.h,v 1.77 2016/10/23 22:59:19 bru Exp $ */
+/* $OpenBSD: wsconsio.h,v 1.78 2017/02/27 15:59:56 bru Exp $ */
/* $NetBSD: wsconsio.h,v 1.74 2005/04/28 07:15:44 martin Exp $ */
/*
@@ -282,6 +282,10 @@ struct wsmouse_calibcoords {
* that all keys are valid and that the number of key/value pairs doesn't
* exceed the enum size.
*
+ * Most parameters in the "FILTER" group concern coordinate handling.
+ * DX_MAX, DY_MAX, HYSTERESIS, and DECELERATION only apply to touchpads in
+ * WSMOUSE_COMPAT mode.
+ *
* WSMOUSECFG_DX_SCALE, WSMOUSECFG_DY_SCALE:
* Scale factors in [*.12] fixed-point format.
* WSMOUSECFG_PRESSURE_LO, WSMOUSECFG_PRESSURE_HI:
@@ -294,8 +298,25 @@ struct wsmouse_calibcoords {
* WSMOUSECFG_X_INV, WSMOUSECFG_Y_INV:
* Map an absolute coordinate C to (INV - C), negate relative coordinates.
* WSMOUSECFG_DX_MAX, WSMOUSECFG_DY_MAX:
- * Ignore deltas that are greater than these limits (for touchpads in
- * WSMOUSE_COMPAT mode only).
+ * Ignore deltas that are greater than these limits.
+ * WSMOUSECFG_X_HYSTERESIS, WSMOUSECFG_Y_HYSTERESIS:
+ * If these values are non-zero, changes of output coordinates will lag
+ * behind the input by [+hysteresis] units, or [-hysteresis] units,
+ * respectively.
+ * WSMOUSECFG_DECELERATION:
+ * A distance defining the thresholds for deceleration, see
+ * wstpad_decelerate().
+ *
+ * The "TP_OPTS" group consists exclusively of flags that control touchpad
+ * behaviour. Softbuttons at the bottom edge of a touchpad (SOFTBUTTONS)
+ * will not include a middle-button area if SOFTMBTN is not set. Likewise,
+ * if edge scrolling is active, HORIZSCROLL must be set in addition in order
+ * to enable horizontal scrolling.
+ *
+ * The first parameters in the "TP" group (*_EDGE) determine the size of the
+ * edge areas, and the width of the middle-button area (CENTERWIDTH). The
+ * values are ratios to the width or height of the touchpad surface and have
+ * a [*.12] fixed-point format.
*/
#define wsmousecfg_group(group) \
WSMOUSECFG_##group##_MAX, \
@@ -312,8 +333,34 @@ enum wsmousecfg {
WSMOUSECFG_Y_INV,
WSMOUSECFG_DX_MAX,
WSMOUSECFG_DY_MAX,
-
- wsmousecfg_group(FLTR),
+ WSMOUSECFG_X_HYSTERESIS,
+ WSMOUSECFG_Y_HYSTERESIS,
+ WSMOUSECFG_DECELERATION,
+
+ wsmousecfg_group(FILTER),
+
+ WSMOUSECFG_SOFTBUTTONS,
+ WSMOUSECFG_SOFTMBTN,
+ WSMOUSECFG_TOPBUTTONS,
+ WSMOUSECFG_TWOFINGERSCROLL,
+ WSMOUSECFG_EDGESCROLL,
+ WSMOUSECFG_HORIZSCROLL,
+ WSMOUSECFG_SWAPSIDES,
+ WSMOUSECFG_DISABLE,
+
+ wsmousecfg_group(TP_OPTS),
+
+ WSMOUSECFG_LEFT_EDGE,
+ WSMOUSECFG_RIGHT_EDGE,
+ WSMOUSECFG_TOP_EDGE,
+ WSMOUSECFG_BOTTOM_EDGE,
+ WSMOUSECFG_CENTERWIDTH,
+ WSMOUSECFG_HORIZSCROLLDIST,
+ WSMOUSECFG_VERTSCROLLDIST,
+ WSMOUSECFG_F2WIDTH,
+ WSMOUSECFG_F2PRESSURE,
+
+ wsmousecfg_group(TP),
};
#undef wsmousecfg_group
diff --git a/sys/dev/wscons/wsmouse.c b/sys/dev/wscons/wsmouse.c
index 0bf9f6e4dec..dd401d9b741 100644
--- a/sys/dev/wscons/wsmouse.c
+++ b/sys/dev/wscons/wsmouse.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsmouse.c,v 1.35 2016/10/23 22:59:19 bru Exp $ */
+/* $OpenBSD: wsmouse.c,v 1.36 2017/02/27 15:59:56 bru Exp $ */
/* $NetBSD: wsmouse.c,v 1.35 2005/02/27 00:27:52 perry Exp $ */
/*
@@ -109,8 +109,8 @@
#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/wscons/wsmouseinput.h>
#include <dev/rndvar.h>
#include "wsmux.h"
@@ -134,7 +134,7 @@ struct wsmouse_softc {
const struct wsmouse_accessops *sc_accessops;
void *sc_accesscookie;
- struct wsmouseinput input;
+ struct wsmouseinput sc_input;
int sc_refcnt;
u_char sc_dying; /* device is being detached */
@@ -173,29 +173,35 @@ struct wssrcops wsmouse_srcops = {
};
#endif
-static const size_t cfg_fltr[] = {
+static const size_t cfg_filter[] = {
[WSMOUSECFG_DX_SCALE & 0xff] =
- offsetof(struct wsmouseinput, fltr.h.scale),
+ offsetof(struct wsmouseinput, filter.h.scale),
[WSMOUSECFG_DY_SCALE & 0xff] =
- offsetof(struct wsmouseinput, fltr.v.scale),
+ offsetof(struct wsmouseinput, filter.v.scale),
[WSMOUSECFG_PRESSURE_LO & 0xff] =
- offsetof(struct wsmouseinput, fltr.pressure_lo),
+ offsetof(struct wsmouseinput, filter.pressure_lo),
[WSMOUSECFG_PRESSURE_HI & 0xff] =
- offsetof(struct wsmouseinput, fltr.pressure_hi),
+ offsetof(struct wsmouseinput, filter.pressure_hi),
[WSMOUSECFG_TRKMAXDIST & 0xff] =
- offsetof(struct wsmouseinput, fltr.tracking_maxdist),
+ offsetof(struct wsmouseinput, filter.tracking_maxdist),
[WSMOUSECFG_SWAPXY & 0xff] =
- offsetof(struct wsmouseinput, fltr.swapxy),
+ offsetof(struct wsmouseinput, filter.swapxy),
[WSMOUSECFG_X_INV & 0xff] =
- offsetof(struct wsmouseinput, fltr.h.inv),
+ offsetof(struct wsmouseinput, filter.h.inv),
[WSMOUSECFG_Y_INV & 0xff] =
- offsetof(struct wsmouseinput, fltr.v.inv),
+ offsetof(struct wsmouseinput, filter.v.inv),
[WSMOUSECFG_DX_MAX & 0xff] =
- offsetof(struct wsmouseinput, fltr.h.dmax),
+ offsetof(struct wsmouseinput, filter.h.dmax),
[WSMOUSECFG_DY_MAX & 0xff] =
- offsetof(struct wsmouseinput, fltr.v.dmax),
-
- [WSMOUSECFG_FLTR_MAX & 0xff] = 0
+ offsetof(struct wsmouseinput, filter.v.dmax),
+ [WSMOUSECFG_X_HYSTERESIS & 0xff] =
+ offsetof(struct wsmouseinput, filter.h.hysteresis),
+ [WSMOUSECFG_Y_HYSTERESIS & 0xff] =
+ offsetof(struct wsmouseinput, filter.v.hysteresis),
+ [WSMOUSECFG_DECELERATION & 0xff] =
+ offsetof(struct wsmouseinput, filter.dclr),
+
+ [WSMOUSECFG_FILTER_MAX & 0xff] = 0
};
/*
@@ -228,7 +234,7 @@ 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);
+ sc->sc_input.evar = &sc->sc_base.me_evp;
#if NWSMUX > 0
sc->sc_base.me_ops = &wsmouse_srcops;
@@ -310,7 +316,7 @@ wsmouse_detach(struct device *self, int flags)
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
- wsmouse_input_cleanup(&sc->input);
+ wsmouse_input_cleanup(&sc->sc_input);
return (0);
}
@@ -404,7 +410,7 @@ wsmousedoopen(struct wsmouse_softc *sc, struct wseventvar *evp)
{
sc->sc_base.me_evp = evp;
- wsmouse_input_reset(&sc->input);
+ wsmouse_input_reset(&sc->sc_input);
/* enable the device, and punt if that's not possible */
return (*sc->sc_accessops->enable)(sc->sc_accesscookie);
@@ -615,8 +621,7 @@ wsmouse_add_mux(int unit, struct wsmux_softc *muxsc)
void
wsmouse_buttons(struct device *sc, u_int buttons)
{
- struct btn_state *btn =
- &((struct wsmouse_softc *) sc)->input.btn;
+ struct btn_state *btn = &((struct wsmouse_softc *) sc)->sc_input.btn;
if (btn->sync)
/* Restore the old state. */
@@ -630,7 +635,7 @@ void
wsmouse_motion(struct device *sc, int dx, int dy, int dz, int dw)
{
struct motion_state *motion =
- &((struct wsmouse_softc *) sc)->input.motion;
+ &((struct wsmouse_softc *) sc)->sc_input.motion;
motion->dx = dx;
motion->dy = dy;
@@ -651,7 +656,7 @@ void
wsmouse_position(struct device *sc, int x, int y)
{
struct motion_state *motion =
- &((struct wsmouse_softc *) sc)->input.motion;
+ &((struct wsmouse_softc *) sc)->sc_input.motion;
int delta;
delta = x - motion->x;
@@ -682,7 +687,7 @@ normalized_pressure(struct wsmouseinput *input, int pressure)
void
wsmouse_touch(struct device *sc, int pressure, int contacts)
{
- struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct touch_state *touch = &input->touch;
pressure = normalized_pressure(input, pressure);
@@ -696,6 +701,7 @@ wsmouse_touch(struct device *sc, int pressure, int contacts)
touch->pressure = pressure;
touch->sync |= SYNC_PRESSURE;
}
+ touch->prev_contacts = touch->contacts;
if (contacts != touch->contacts) {
touch->contacts = contacts;
touch->sync |= SYNC_CONTACTS;
@@ -705,7 +711,7 @@ wsmouse_touch(struct device *sc, int pressure, int contacts)
void
wsmouse_mtstate(struct device *sc, int slot, int x, int y, int pressure)
{
- struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct mt_state *mt = &input->mt;
struct mt_slot *mts;
u_int bit;
@@ -751,7 +757,7 @@ wsmouse_mtstate(struct device *sc, int slot, int x, int y, int pressure)
void
wsmouse_set(struct device *sc, enum wsmouseval type, int value, int aux)
{
- struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct mt_slot *mts;
if (WSMOUSE_IS_MT_CODE(type)) {
@@ -824,10 +830,10 @@ wsmouse_touch_update(struct wsmouseinput *input)
motion->x_delta = motion->y_delta = 0;
if ((touch->sync & SYNC_PRESSURE) && touch->min_pressure) {
- if (touch->pressure >= input->fltr.pressure_hi)
- touch->min_pressure = input->fltr.pressure_lo;
- else if (touch->pressure < input->fltr.pressure_lo)
- touch->min_pressure = input->fltr.pressure_hi;
+ if (touch->pressure >= input->filter.pressure_hi)
+ touch->min_pressure = input->filter.pressure_lo;
+ else if (touch->pressure < input->filter.pressure_lo)
+ touch->min_pressure = input->filter.pressure_hi;
}
}
@@ -902,7 +908,7 @@ wsmouse_ptr_ctrl(struct mt_state *mt)
void
wsmouse_mt_convert(struct device *sc)
{
- struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct mt_state *mt = &input->mt;
struct mt_slot *mts;
int slot, pressure;
@@ -945,9 +951,8 @@ wsmouse_evq_put(struct evq_access *evq, int ev_type, int ev_value)
void
-wsmouse_btn_sync(struct wsmouseinput *input, struct evq_access *evq)
+wsmouse_btn_sync(struct btn_state *btn, struct evq_access *evq)
{
- struct btn_state *btn = &input->btn;
int button, ev_type;
u_int bit, sync;
@@ -979,8 +984,8 @@ void
wsmouse_motion_sync(struct wsmouseinput *input, struct evq_access *evq)
{
struct motion_state *motion = &input->motion;
- struct axis_filter *h = &input->fltr.h;
- struct axis_filter *v = &input->fltr.v;
+ struct axis_filter *h = &input->filter.h;
+ struct axis_filter *v = &input->filter.v;
int x, y, dx, dy;
if (motion->sync & SYNC_DELTAS) {
@@ -1029,38 +1034,13 @@ wsmouse_touch_sync(struct wsmouseinput *input, struct evq_access *evq)
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 axis_filter *h = &input->fltr.h;
- struct axis_filter *v = &input->fltr.v;
- 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 ((h->dmax && (abs(dx) > h->dmax))
- || (v->dmax && (abs(dy) > v->dmax)))
- 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->sbtn.sync = 0;
input->motion.sync = 0;
input->touch.sync = 0;
if (input->mt.frame) {
@@ -1073,7 +1053,7 @@ clear_sync_flags(struct wsmouseinput *input)
void
wsmouse_input_sync(struct device *sc)
{
- struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct evq_access evq;
evq.evar = *input->evar;
@@ -1096,7 +1076,7 @@ wsmouse_input_sync(struct device *sc)
wsmouse_touch_update(input);
if (input->flags & TPAD_COMPAT_MODE)
- wsmouse_compat_convert(sc, &evq);
+ wstpad_compat_convert(input, &evq);
if (input->flags & RESYNC) {
input->flags &= ~RESYNC;
@@ -1105,7 +1085,9 @@ wsmouse_input_sync(struct device *sc)
}
if (input->btn.sync)
- wsmouse_btn_sync(input, &evq);
+ wsmouse_btn_sync(&input->btn, &evq);
+ if (input->sbtn.sync)
+ wsmouse_btn_sync(&input->sbtn, &evq);
if (input->motion.sync)
wsmouse_motion_sync(input, &evq);
if (input->touch.sync)
@@ -1129,7 +1111,7 @@ wsmouse_input_sync(struct device *sc)
int
wsmouse_id_to_slot(struct device *sc, int id)
{
- struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct mt_state *mt = &input->mt;
int slot;
@@ -1154,7 +1136,7 @@ wsmouse_id_to_slot(struct device *sc, int id)
* 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.
+ * at least 3m + 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
@@ -1177,14 +1159,13 @@ wsmouse_matching(int *matrix, int m, int n, int *buffer)
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 *mc = red + 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 (; p < mc; *p++ = 0) {}
for (col = 0; col < n; col++) {
delta = INT_MAX;
for (i = 0, p = matrix + col; i < m; i++, p += n) {
@@ -1199,15 +1180,14 @@ wsmouse_matching(int *matrix, int m, int n, int *buffer)
r2c[row] = col;
continue;
}
- for (p = alt; p < mc; *p++ = -1) {}
- for (; p < cs; *p++ = col) {}
+ for (p = mc; p < cs; *p++ = col) {}
for (k = 0; (j = r2c[row]) >= 0;) {
cs[k++] = j;
c2r[j] = row;
- alt[row] = mc[row];
+ mc[row] -= n;
delta = INT_MAX;
for (i = 0, p = matrix; i < m; i++, p += n)
- if (alt[i] < 0) {
+ if (mc[i] >= 0) {
d = p[mc[i]] - cd[mc[i]];
e = p[j] - cd[j];
if (e < d) {
@@ -1229,7 +1209,7 @@ wsmouse_matching(int *matrix, int m, int n, int *buffer)
}
for (j = mc[row]; (r2c[row] = j) != col;) {
row = c2r[j];
- j = alt[row];
+ j = mc[row] + n;
}
}
}
@@ -1237,7 +1217,7 @@ wsmouse_matching(int *matrix, int m, int n, int *buffer)
void
wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int size)
{
- struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct mt_state *mt = &input->mt;
int i, j, m, n, dx, dy, slot, maxdist;
int *p, *r2c, *c2r;
@@ -1272,7 +1252,7 @@ wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int size)
r2c = p;
c2r = p + m;
- maxdist = input->fltr.tracking_maxdist;
+ maxdist = input->filter.tracking_maxdist;
maxdist = (maxdist ? maxdist * maxdist : INT_MAX);
for (i = 0, p = mt->matrix; i < m; i++, p += n)
if ((j = r2c[i]) >= 0) {
@@ -1324,8 +1304,7 @@ free_mt_slots(struct wsmouseinput *input)
int
wsmouse_mt_init(struct device *sc, int num_slots, int tracking)
{
- struct wsmouseinput *input =
- &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
int n, size;
if (num_slots == input->mt.num_slots
@@ -1376,8 +1355,7 @@ int
wsmouse_get_params(struct device *sc,
struct wsmouse_param *params, u_int nparams)
{
- struct wsmouseinput *input =
- &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
int i, key, delegate = 0;
void *p;
@@ -1386,9 +1364,9 @@ wsmouse_get_params(struct device *sc,
for (i = 0; i < nparams; i++) {
key = params[i].key;
- if (WSMOUSECFG_MATCH(key, FLTR)) {
+ if (WSMOUSECFG_MATCH(key, FILTER)) {
p = input;
- p += cfg_fltr[key & 0xff];
+ p += cfg_filter[key & 0xff];
if (p != input)
params[i].value = *((int *) p);
else
@@ -1399,7 +1377,7 @@ wsmouse_get_params(struct device *sc,
}
}
if (delegate)
- return (-1); /* not yet */
+ return (wstpad_get_params(input, params, nparams));
return (0);
}
@@ -1408,8 +1386,7 @@ int
wsmouse_set_params(struct device *sc,
const struct wsmouse_param *params, u_int nparams)
{
- struct wsmouseinput *input =
- &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
int i, key, val, delegate = 0;
void *p;
@@ -1418,28 +1395,40 @@ wsmouse_set_params(struct device *sc,
for (i = 0; i < nparams; i++) {
key = params[i].key;
- if (!(WSMOUSECFG_MATCH(key, FLTR))) {
+ if (!(WSMOUSECFG_MATCH(key, FILTER))) {
delegate = 1;
continue;
}
val = params[i].value;
switch (key) {
case WSMOUSECFG_PRESSURE_LO:
- input->fltr.pressure_lo = val;
- if (val > input->fltr.pressure_hi)
- input->fltr.pressure_hi = val;
+ input->filter.pressure_lo = val;
+ if (val > input->filter.pressure_hi)
+ input->filter.pressure_hi = val;
input->touch.min_pressure =
- input->fltr.pressure_hi;
+ input->filter.pressure_hi;
continue;
case WSMOUSECFG_PRESSURE_HI:
- input->fltr.pressure_hi = val;
- if (val < input->fltr.pressure_lo)
- input->fltr.pressure_lo = val;
+ input->filter.pressure_hi = val;
+ if (val < input->filter.pressure_lo)
+ input->filter.pressure_lo = val;
input->touch.min_pressure = val;
continue;
+ case WSMOUSECFG_X_HYSTERESIS:
+ input->filter.h.hysteresis = val;
+ input->filter.h.acc = 0;
+ continue;
+ case WSMOUSECFG_Y_HYSTERESIS:
+ input->filter.v.hysteresis = val;
+ input->filter.v.acc = 0;
+ continue;
+ case WSMOUSECFG_DECELERATION:
+ input->filter.dclr = val;
+ wstpad_init_deceleration(input);
+ continue;
default:
p = input;
- p += cfg_fltr[key & 0xff];
+ p += cfg_filter[key & 0xff];
if (p != input)
*((int *) p) = val;
else
@@ -1449,7 +1438,7 @@ wsmouse_set_params(struct device *sc,
}
}
if (delegate)
- return (-1); /* not yet */
+ return (wstpad_set_params(input, params, nparams));
return (0);
}
@@ -1457,8 +1446,7 @@ wsmouse_set_params(struct device *sc,
int
wsmouse_set_mode(struct device *sc, int mode)
{
- struct wsmouseinput *input =
- &((struct wsmouse_softc *) sc)->input;
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
if (mode == WSMOUSE_COMPAT) {
input->flags &= ~TPAD_NATIVE_MODE;
@@ -1472,6 +1460,57 @@ wsmouse_set_mode(struct device *sc, int mode)
return (-1);
}
+struct wsmousehw *wsmouse_get_hw(struct device *sc)
+{
+ return &((struct wsmouse_softc *) sc)->sc_input.hw;
+}
+
+/*
+ * Create a default configuration based on the hardware infos in the 'hw'
+ * fields. The 'params' argument is optional, hardware drivers can use it
+ * to modify the generic defaults. Up to now this function is only useful
+ * for touchpads.
+ */
+int
+wsmouse_configure(struct device *sc,
+ struct wsmouse_param *params, u_int nparams)
+{
+ struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
+
+ if (!(input->flags & CONFIGURED)) {
+ if (input->hw.x_max && input->hw.y_max) {
+ if (input->hw.flags & WSMOUSEHW_LR_DOWN) {
+ input->filter.v.inv =
+ input->hw.y_max + input->hw.y_min;
+ }
+ }
+ input->filter.ratio = 1 << 12;
+ if (input->hw.h_res > 0 && input->hw.v_res > 0) {
+ input->filter.ratio *= input->hw.h_res;
+ input->filter.ratio /= input->hw.v_res;
+ }
+ if (wsmouse_mt_init(sc, input->hw.mt_slots,
+ (input->hw.flags & WSMOUSEHW_MT_TRACKING))) {
+ printf("wsmouse_configure: "
+ "MT initialization failed.\n");
+ return (-1);
+ }
+ if (IS_TOUCHPAD(input) && wstpad_configure(input)) {
+ printf("wstpad_configure: "
+ "Initialization failed.\n");
+ return (-1);
+ }
+ if (params != NULL)
+ wsmouse_set_params(sc, params, nparams);
+ input->flags |= CONFIGURED;
+ }
+ if (IS_TOUCHPAD(input))
+ wsmouse_set_mode(sc, WSMOUSE_COMPAT);
+
+ return (0);
+}
+
+
void
wsmouse_input_reset(struct wsmouseinput *input)
{
@@ -1481,7 +1520,7 @@ wsmouse_input_reset(struct wsmouseinput *input)
memset(&input->btn, 0, sizeof(struct btn_state));
memset(&input->motion, 0, sizeof(struct motion_state));
memset(&input->touch, 0, sizeof(struct touch_state));
- input->touch.min_pressure = input->fltr.pressure_hi;
+ input->touch.min_pressure = input->filter.pressure_hi;
if ((num_slots = input->mt.num_slots)) {
slots = input->mt.slots;
matrix = input->mt.matrix;
@@ -1491,12 +1530,8 @@ wsmouse_input_reset(struct wsmouseinput *input)
input->mt.slots = slots;
input->mt.matrix = matrix;
}
-}
-
-void
-wsmouse_input_init(struct wsmouseinput *input, struct wseventvar **evar)
-{
- input->evar = evar;
+ if (input->tp != NULL)
+ wstpad_reset(input);
}
void
diff --git a/sys/dev/wscons/wsmouseinput.h b/sys/dev/wscons/wsmouseinput.h
index 46c40f2ef6f..a65cf21a4c4 100644
--- a/sys/dev/wscons/wsmouseinput.h
+++ b/sys/dev/wscons/wsmouseinput.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsmouseinput.h,v 1.3 2016/10/23 22:59:19 bru Exp $ */
+/* $OpenBSD: wsmouseinput.h,v 1.4 2017/02/27 15:59:56 bru Exp $ */
/*
* Copyright (c) 2015, 2016 Ulf Brosziewski
@@ -54,6 +54,7 @@ struct touch_state {
u_int sync;
int min_pressure;
+ int prev_contacts;
};
#define SYNC_PRESSURE (1 << 0)
#define SYNC_CONTACTS (1 << 1)
@@ -101,14 +102,29 @@ struct axis_filter {
int rmdr;
/* Invert coordinates. */
int inv;
+ /* Hysteresis limit and accumulated deltas. */
+ int hysteresis;
+ int acc;
+ /* A [*.12] coefficient for "magnitudes", used for deceleration. */
+ int mag_scale;
+ int dclr_rmdr;
/* Ignore deltas that are greater than this limit. */
int dmax;
};
+struct interval {
+ long avg; /* average update interval in nanoseconds */
+ long sum;
+ int samples;
+ struct timespec ts;
+ int track;
+};
+
struct wsmouseinput {
u_int flags;
struct btn_state btn;
+ struct btn_state sbtn; /* softbuttons */
struct motion_state motion;
struct touch_state touch;
struct mt_state mt;
@@ -117,11 +133,20 @@ struct wsmouseinput {
struct axis_filter h;
struct axis_filter v;
+ int dclr; /* deceleration threshold */
+ int mag; /* weighted average of delta magnitudes */
+
+ int ratio; /* X/Y ratio */
+
int swapxy;
int tracking_maxdist;
int pressure_lo;
int pressure_hi;
- } fltr;
+ } filter;
+
+ struct wsmousehw hw;
+ struct interval intv;
+ struct wstpad *tp;
struct wseventvar **evar;
};
@@ -130,6 +155,8 @@ struct wsmouseinput {
#define TPAD_NATIVE_MODE (1 << 1)
#define MT_TRACKING (1 << 2)
#define RESYNC (1 << 16)
+#define TRACK_INTERVAL (1 << 17)
+#define CONFIGURED (1 << 18)
struct evq_access {
struct wseventvar *evar;
@@ -143,24 +170,30 @@ struct evq_access {
void wsmouse_evq_put(struct evq_access *, int, int);
-void wsmouse_init_scaling(struct wsmouseinput *);
-
void wsmouse_input_reset(struct wsmouseinput *);
-void wsmouse_input_init(struct wsmouseinput *, struct wseventvar **);
void wsmouse_input_cleanup(struct wsmouseinput *);
+void wstpad_compat_convert(struct wsmouseinput *, struct evq_access *);
+void wstpad_init_deceleration(struct wsmouseinput *);
+int wstpad_configure(struct wsmouseinput *);
+void wstpad_reset(struct wsmouseinput *);
+
+int wstpad_get_params(struct wsmouseinput *,
+ struct wsmouse_param *, u_int);
+int wstpad_set_params(struct wsmouseinput *,
+ const struct wsmouse_param *, u_int);
+
#define FOREACHBIT(v, i) \
for ((i) = ffs(v) - 1; (i) != -1; (i) = ffs((v) & (~1 << (i))) - 1)
-
-#define DELTA_X_EV(input) ((input)->fltr.swapxy ? \
+#define DELTA_X_EV(input) ((input)->filter.swapxy ? \
WSCONS_EVENT_MOUSE_DELTA_Y : WSCONS_EVENT_MOUSE_DELTA_X)
-#define DELTA_Y_EV(input) ((input)->fltr.swapxy ? \
+#define DELTA_Y_EV(input) ((input)->filter.swapxy ? \
WSCONS_EVENT_MOUSE_DELTA_X : WSCONS_EVENT_MOUSE_DELTA_Y)
-#define ABS_X_EV(input) ((input)->fltr.swapxy ? \
+#define ABS_X_EV(input) ((input)->filter.swapxy ? \
WSCONS_EVENT_MOUSE_ABSOLUTE_Y : WSCONS_EVENT_MOUSE_ABSOLUTE_X)
-#define ABS_Y_EV(input) ((input)->fltr.swapxy ? \
+#define ABS_Y_EV(input) ((input)->filter.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
@@ -170,18 +203,25 @@ void wsmouse_input_cleanup(struct wsmouseinput *);
#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))
+/* matrix size + buffer size for wsmouse_matching */
+#define MATRIX_SIZE(slots) (((slots) + 6) * (slots) * sizeof(int))
+#define IS_TOUCHPAD(input) \
+ ((input)->hw.hw_type == WSMOUSEHW_TOUCHPAD \
+ || (input)->hw.hw_type == WSMOUSEHW_CLICKPAD)
#define WSMOUSECFG_MATCH(key, group) \
((key) < WSMOUSECFG_##group##_MAX && \
(key) >= (WSMOUSECFG_##group##_MAX & ~0xff))
-#define IS_WSMOUSECFG_KEY(key) \
- WSMOUSECFG_MATCH((key), FLTR)
+#define IS_WSMOUSECFG_KEY(key) \
+ (WSMOUSECFG_MATCH((key), FILTER) \
+ || WSMOUSECFG_MATCH((key), TP_OPTS) \
+ || WSMOUSECFG_MATCH((key), TP))
-#define WSMOUSECFG_SIZE \
- (WSMOUSECFG_FLTR_MAX & 0xff)
+#define WSMOUSECFG_SIZE \
+ ((WSMOUSECFG_FILTER_MAX & 0xff) \
+ + (WSMOUSECFG_TP_OPTS_MAX & 0xff) \
+ + (WSMOUSECFG_TP_MAX & 0xff))
#endif /* _WSMOUSEINPUT_H_ */
diff --git a/sys/dev/wscons/wsmousevar.h b/sys/dev/wscons/wsmousevar.h
index 1cc60ba0908..411bfac50c6 100644
--- a/sys/dev/wscons/wsmousevar.h
+++ b/sys/dev/wscons/wsmousevar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsmousevar.h,v 1.12 2016/10/23 22:59:19 bru Exp $ */
+/* $OpenBSD: wsmousevar.h,v 1.13 2017/02/27 15:59:56 bru Exp $ */
/* $NetBSD: wsmousevar.h,v 1.4 2000/01/08 02:57:24 takemura Exp $ */
/*
@@ -187,3 +187,53 @@ int wsmouse_set_mode(struct device *, int);
/* Read/Set parameter values. */
int wsmouse_get_params(struct device *, struct wsmouse_param *, u_int);
int wsmouse_set_params(struct device *, const struct wsmouse_param *, u_int);
+
+
+enum wsmousehw_type {
+ WSMOUSEHW_RAW,
+ WSMOUSEHW_MOUSE,
+ WSMOUSEHW_TOUCHPAD,
+ WSMOUSEHW_CLICKPAD,
+ WSMOUSEHW_TPANEL,
+};
+
+/*
+ * wsmousehw.flags
+ */
+/* Invert Y-coordinates */
+#define WSMOUSEHW_LR_DOWN (1 << 0)
+/* Allocate the buffers for wsmouse_mtframe(). */
+#define WSMOUSEHW_MT_TRACKING (1 << 1)
+
+
+/*
+ * The more or less minimal hardware description for the default
+ * configuration.
+ *
+ * Drivers that report coordinates with a downward orientation
+ * must set the flag WSMOUSEHW_LR_DOWN. Drivers for MT hardware
+ * must provide the number of slots. If they use wsmouse_mtframe(),
+ * WSMOUSEHW_MT_TRACKING must be set.
+ *
+ * The resolution values are optional.
+ */
+struct wsmousehw {
+ int type; /* WSMOUSE_TYPE_*, cf. wsconsio.h */
+ enum wsmousehw_type hw_type;
+ int x_min;
+ int x_max;
+ int y_min;
+ int y_max;
+ int h_res;
+ int v_res;
+
+ int flags;
+ int mt_slots;
+
+ int contacts_max; /* inclusive (not needed for MT touchpads) */
+};
+
+struct wsmousehw *wsmouse_get_hw(struct device*);
+
+/* Configure the input context. */
+int wsmouse_configure(struct device *, struct wsmouse_param *, u_int);
diff --git a/sys/dev/wscons/wstpad.c b/sys/dev/wscons/wstpad.c
new file mode 100644
index 00000000000..9b5d1ae7bd6
--- /dev/null
+++ b/sys/dev/wscons/wstpad.c
@@ -0,0 +1,1158 @@
+/*
+ * 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.
+ */
+
+/*
+ * touchpad input processing
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/wseventvar.h>
+#include <dev/wscons/wsmouseinput.h>
+
+#define LEFTBTN (1 << 0)
+#define MIDDLEBTN (1 << 1)
+#define RIGHTBTN (1 << 2)
+
+#define PRIMARYBTN LEFTBTN
+
+#define PRIMARYBTN_CLICKED(tp) ((tp)->btns_sync & PRIMARYBTN & (tp)->btns)
+#define PRIMARYBTN_RELEASED(tp) ((tp)->btns_sync & PRIMARYBTN & ~(tp)->btns)
+
+#define IS_MT(tp) ((tp)->features & WSTPAD_MT)
+#define DISABLE(tp) ((tp)->features & WSTPAD_DISABLE)
+
+#define EDGERATIO_DEFAULT (4096 / 16)
+#define CENTERWIDTH_DEFAULT (4096 / 8)
+
+#define FREEZE_MS 100
+
+enum tpad_handlers {
+ SOFTBUTTON_HDLR,
+ TOPBUTTON_HDLR,
+ F2SCROLL_HDLR,
+ EDGESCROLL_HDLR,
+ CLICK_HDLR,
+};
+
+enum tpad_cmd {
+ CLEAR_MOTION_DELTAS,
+ SOFTBUTTON_DOWN,
+ SOFTBUTTON_UP,
+ VSCROLL,
+ HSCROLL,
+};
+
+/*
+ * tpad_touch.flags:
+ */
+#define L_EDGE (1 << 0)
+#define R_EDGE (1 << 1)
+#define T_EDGE (1 << 2)
+#define B_EDGE (1 << 3)
+
+#define EDGES (L_EDGE | R_EDGE | T_EDGE | B_EDGE)
+
+enum touchstates {
+ TOUCH_NONE,
+ TOUCH_BEGIN,
+ TOUCH_UPDATE,
+ TOUCH_END,
+};
+
+struct tpad_touch {
+ u_int flags;
+ enum touchstates state;
+ int x;
+ int y;
+ int dir;
+ int matches;
+ struct {
+ int x;
+ int y;
+ struct timespec time;
+ } orig;
+};
+
+/*
+ * wstpad.features
+ */
+#define WSTPAD_SOFTBUTTONS (1 << (WSMOUSECFG_SOFTBUTTONS & 0xff))
+#define WSTPAD_SOFTMBTN (1 << (WSMOUSECFG_SOFTMBTN & 0xff))
+#define WSTPAD_TOPBUTTONS (1 << (WSMOUSECFG_TOPBUTTONS & 0xff))
+#define WSTPAD_F2SCROLL (1 << (WSMOUSECFG_TWOFINGERSCROLL & 0xff))
+#define WSTPAD_EDGESCROLL (1 << (WSMOUSECFG_EDGESCROLL & 0xff))
+#define WSTPAD_HORIZSCROLL (1 << (WSMOUSECFG_HORIZSCROLL & 0xff))
+#define WSTPAD_SWAPSIDES (1 << (WSMOUSECFG_SWAPSIDES & 0xff))
+#define WSTPAD_DISABLE (1 << (WSMOUSECFG_DISABLE & 0xff))
+
+#define WSTPAD_MT (1 << 31)
+
+
+#define WSTPAD_TOUCHPAD_DEFAULTS (WSTPAD_F2SCROLL)
+#define WSTPAD_CLICKPAD_DEFAULTS \
+ (WSTPAD_SOFTBUTTONS | WSTPAD_F2SCROLL)
+
+
+struct wstpad {
+ u_int features;
+ u_int handlers;
+
+ /*
+ * t always points into the tpad_touches array, which has at
+ * least one element. If there is more than one, t selects
+ * the pointer-controlling touch.
+ */
+ struct tpad_touch *t;
+ struct tpad_touch *tpad_touches;
+
+ u_int mtcycle;
+
+ int dx;
+ int dy;
+ int contacts;
+ int prev_contacts;
+ u_int btns;
+ u_int btns_sync;
+ int ratio;
+
+ struct timespec time;
+
+ u_int freeze;
+ struct timespec freeze_ts;
+
+ /* edge coordinates */
+ struct {
+ int left;
+ int right;
+ int top;
+ int bottom;
+ int center;
+ int center_left;
+ int center_right;
+ int middle;
+ } edge;
+
+ struct {
+ /* ratios to the surface width or height */
+ int left_edge;
+ int right_edge;
+ int top_edge;
+ int bottom_edge;
+ int center_width;
+ /* two-finger contacts */
+ int f2pressure;
+ int f2width;
+ } params;
+
+ /* handler state and configuration: */
+
+ u_int softbutton;
+ u_int sbtnswap;
+
+ struct {
+ int acc_dx;
+ int acc_dy;
+ int dz;
+ int dw;
+ int hdist;
+ int vdist;
+ } scroll;
+};
+
+static const size_t cfg_tp[] = {
+ [WSMOUSECFG_LEFT_EDGE & 0xff] =
+ offsetof(struct wstpad, params.left_edge),
+ [WSMOUSECFG_RIGHT_EDGE & 0xff] =
+ offsetof(struct wstpad, params.right_edge),
+ [WSMOUSECFG_TOP_EDGE & 0xff] =
+ offsetof(struct wstpad, params.top_edge),
+ [WSMOUSECFG_BOTTOM_EDGE & 0xff] =
+ offsetof(struct wstpad, params.bottom_edge),
+ [WSMOUSECFG_CENTERWIDTH & 0xff] =
+ offsetof(struct wstpad, params.center_width),
+ [WSMOUSECFG_HORIZSCROLLDIST & 0xff] =
+ offsetof(struct wstpad, scroll.hdist),
+ [WSMOUSECFG_VERTSCROLLDIST & 0xff] =
+ offsetof(struct wstpad, scroll.vdist),
+ [WSMOUSECFG_F2WIDTH & 0xff] =
+ offsetof(struct wstpad, params.f2width),
+ [WSMOUSECFG_F2PRESSURE & 0xff] =
+ offsetof(struct wstpad, params.f2pressure),
+
+ [WSMOUSECFG_TP_MAX & 0xff] = 0
+};
+
+
+/*
+ * Coordinates in the wstpad struct are "normalized" device coordinates,
+ * the orientation is left-to-right and upward.
+ */
+static __inline int
+normalize_abs(struct axis_filter *filter, int val)
+{
+ return (filter->inv ? filter->inv - val : val);
+}
+
+static __inline int
+normalize_rel(struct axis_filter *filter, int val)
+{
+ return (filter->inv ? -val : val);
+}
+
+/*
+ * Directions of motion are represented by numbers in the range 0 - 11,
+ * corresponding to clockwise counted circle sectors:
+ *
+ * 11 | 0
+ * 10 | 1
+ * 9 | 2
+ * -------+-------
+ * 8 | 3
+ * 7 | 4
+ * 6 | 5
+ *
+ * Two direction values "match" each other if they are equal or adjacent in
+ * this ring. Some handlers require that a movement is "stable" and check
+ * the number of matches.
+ */
+/* Tangent constants in [*.12] fixed-point format: */
+#define TAN_DEG_60 7094
+#define TAN_DEG_30 2365
+
+#define STABLE 3
+
+#define NORTH(d) ((d) == 0 || (d) == 11)
+#define SOUTH(d) ((d) == 5 || (d) == 6)
+#define EAST(d) ((d) == 2 || (d) == 3)
+#define WEST(d) ((d) == 8 || (d) == 9)
+
+static __inline int
+direction(int dx, int dy, int ratio)
+{
+ int rdy, dir = -1;
+
+ if (dx || dy) {
+ rdy = abs(dy) * ratio;
+ if (abs(dx) * TAN_DEG_60 < rdy)
+ dir = 0;
+ else if (abs(dx) * TAN_DEG_30 < rdy)
+ dir = 1;
+ else
+ dir = 2;
+ if ((dx < 0) != (dy < 0))
+ dir = 5 - dir;
+ if (dx < 0)
+ dir += 6;
+ }
+ return dir;
+}
+
+static __inline int
+dircmp(int dir1, int dir2)
+{
+ int diff = abs(dir1 - dir2);
+ return (diff <= 6 ? diff : 12 - diff);
+}
+
+void
+wstpad_set_direction(struct tpad_touch *t, int dx, int dy, int ratio)
+{
+ int dir;
+
+ if (t->state != TOUCH_UPDATE) {
+ t->dir = -1;
+ t->matches = 0;
+ } else {
+ dir = direction(dx, dy, ratio);
+ if (t->dir >= 0 && dir >= 0 && dircmp(t->dir, dir) <= 1)
+ t->matches++;
+ else
+ t->matches = 1;
+ t->dir = dir;
+ }
+}
+
+/*
+ * If a touch starts in an edge area that is configured for softbuttons,
+ * scrolling, or palm detection, pointer motion will be suppressed as long
+ * as it stays in that area.
+ */
+static __inline u_int
+edge_flags(struct wstpad *tp, int x, int y)
+{
+ u_int flags = 0;
+
+ if (x < tp->edge.left)
+ flags |= L_EDGE;
+ else if (x >= tp->edge.right)
+ flags |= R_EDGE;
+ if (y < tp->edge.bottom)
+ flags |= B_EDGE;
+ else if (y >= tp->edge.top)
+ flags |= T_EDGE;
+
+ return (flags);
+}
+
+static __inline struct tpad_touch *
+get_2nd_touch(struct wsmouseinput *input)
+{
+ struct wstpad *tp = input->tp;
+ int slot;
+
+ if (IS_MT(tp)) {
+ slot = ffs(input->mt.touches & ~input->mt.ptr);
+ if (slot)
+ return &tp->tpad_touches[--slot];
+ }
+ return NULL;
+}
+
+/* Suppress pointer motion for a short period of time. */
+static __inline void
+set_freeze_ts(struct wstpad *tp, int sec, int ms)
+{
+ tp->freeze_ts.tv_sec = sec;
+ tp->freeze_ts.tv_nsec = ms * 1000000;
+ timespecadd(&tp->time, &tp->freeze_ts, &tp->freeze_ts);
+}
+
+
+/* Return TRUE if f2-/edge-scrolling would be valid. */
+static __inline int
+chk_scroll_state(struct wstpad *tp)
+{
+ if (tp->contacts != tp->prev_contacts || tp->btns || tp->btns_sync) {
+ tp->scroll.acc_dx = 0;
+ tp->scroll.acc_dy = 0;
+ return (0);
+ }
+ return ((tp->dx || tp->dy) && tp->t->matches >= STABLE);
+}
+
+void
+wstpad_scroll(struct wstpad *tp, int dx, int dy, u_int *cmds)
+{
+ int sign = 0;
+
+ /* Scrolling is either horizontal or vertical, but not both. */
+ if (dy) {
+ tp->scroll.acc_dy += dy;
+ if (tp->scroll.acc_dy <= -tp->scroll.vdist)
+ sign = 1;
+ else if (tp->scroll.acc_dy >= tp->scroll.vdist)
+ sign = -1;
+ if (sign) {
+ tp->scroll.acc_dy += sign * tp->scroll.vdist;
+ tp->scroll.dz = sign;
+ *cmds |= 1 << VSCROLL;
+ }
+ } else if (dx) {
+ tp->scroll.acc_dx += dx;
+ if (tp->scroll.acc_dx <= -tp->scroll.hdist)
+ sign = -1;
+ else if (tp->scroll.acc_dx >= tp->scroll.hdist)
+ sign = 1;
+ if (sign) {
+ tp->scroll.acc_dx -= sign * tp->scroll.hdist;
+ tp->scroll.dw = sign;
+ *cmds |= 1 << HSCROLL;
+ }
+ }
+}
+
+void
+wstpad_f2scroll(struct wsmouseinput *input, u_int *cmds)
+{
+ struct wstpad *tp = input->tp;
+ struct tpad_touch *t2;
+ int dir, dx, dy;
+
+ if (tp->contacts != 2 || !chk_scroll_state(tp))
+ return;
+
+ dir = tp->t->dir;
+ dy = NORTH(dir) || SOUTH(dir) ? tp->dy : 0;
+ dx = EAST(dir) || WEST(dir) ? tp->dx : 0;
+
+ if ((dx || dy) && IS_MT(tp)) {
+ t2 = get_2nd_touch(input);
+ if (t2 == NULL || t2->matches < STABLE)
+ return;
+ dir = t2->dir;
+ if ((dy > 0 && !NORTH(dir)) || (dy < 0 && !SOUTH(dir)))
+ return;
+ if ((dx > 0 && !EAST(dir)) || (dx < 0 && !WEST(dir)))
+ return;
+ }
+
+ wstpad_scroll(tp, dx, dy, cmds);
+ set_freeze_ts(tp, 0, FREEZE_MS);
+}
+
+void
+wstpad_edgescroll(struct wsmouseinput *input, u_int *cmds)
+{
+ struct wstpad *tp = input->tp;
+ struct tpad_touch *t = tp->t;
+ int dx = 0, dy = 0;
+
+ if (tp->contacts != 1 || !chk_scroll_state(tp))
+ return;
+
+ if (tp->features & WSTPAD_SWAPSIDES) {
+ if (t->flags & L_EDGE)
+ dy = tp->dy;
+ } else if (t->flags & R_EDGE) {
+ dy = tp->dy;
+ } else if ((t->flags & B_EDGE) &&
+ (tp->features & WSTPAD_HORIZSCROLL)) {
+ dx = tp->dx;
+ }
+ if (dx || dy)
+ wstpad_scroll(tp, dx, dy, cmds);
+}
+
+static __inline u_int
+sbtn(struct wstpad *tp, int x, int y)
+{
+ if (y >= tp->edge.bottom)
+ return (0);
+ if ((tp->features & WSTPAD_SOFTMBTN)
+ && x >= tp->edge.center_left
+ && x < tp->edge.center_right)
+ return (MIDDLEBTN);
+ return ((x < tp->edge.center ? LEFTBTN : RIGHTBTN) ^ tp->sbtnswap);
+}
+
+static __inline u_int
+top_sbtn(struct wstpad *tp, int x, int y)
+{
+ if (y < tp->edge.top)
+ return (0);
+ if (x < tp->edge.center_left)
+ return (LEFTBTN ^ tp->sbtnswap);
+ return (x > tp->edge.center_right
+ ? (RIGHTBTN ^ tp->sbtnswap) : MIDDLEBTN);
+}
+
+u_int
+wstpad_get_sbtn(struct wsmouseinput *input, int top)
+{
+ struct wstpad *tp = input->tp;
+ struct tpad_touch *t = tp->t;
+ u_int btn;
+
+ btn = 0;
+ if (tp->contacts) {
+ btn = top ? top_sbtn(tp, t->x, t->y) : sbtn(tp, t->x, t->y);
+ /*
+ * If there is no middle-button area, but contacts in both
+ * halves of the edge zone, generate a middle-button event:
+ */
+ if (btn && IS_MT(tp) && tp->contacts == 2
+ && !top && !(tp->features & WSTPAD_SOFTMBTN)) {
+ if ((t = get_2nd_touch(input)) != NULL)
+ btn |= sbtn(tp, t->x, t->y);
+ if (btn == (LEFTBTN | RIGHTBTN))
+ btn = MIDDLEBTN;
+ }
+ }
+ return (btn != PRIMARYBTN ? btn : 0);
+}
+
+void
+wstpad_softbuttons(struct wsmouseinput *input, u_int *cmds, int hdlr)
+{
+ struct wstpad *tp = input->tp;
+ int top = (hdlr == TOPBUTTON_HDLR);
+
+ if (tp->softbutton && PRIMARYBTN_RELEASED(tp)) {
+ *cmds |= 1 << SOFTBUTTON_UP;
+ return;
+ }
+
+ if (tp->softbutton == 0 && PRIMARYBTN_CLICKED(tp)) {
+ tp->softbutton = wstpad_get_sbtn(input, top);
+ if (tp->softbutton)
+ *cmds |= 1 << SOFTBUTTON_DOWN;
+ }
+}
+
+/*
+ * Suppress accidental pointer movements after a click on a clickpad.
+ */
+void
+wstpad_click(struct wsmouseinput *input)
+{
+ struct wstpad *tp = input->tp;
+
+ if (tp->contacts == 1 &&
+ (PRIMARYBTN_CLICKED(tp) || PRIMARYBTN_RELEASED(tp)))
+ set_freeze_ts(tp, 0, FREEZE_MS);
+}
+
+/*
+ * Translate the "command" bits into the sync-state of wsmouse, or into
+ * wscons events.
+ */
+void
+wstpad_cmds(struct wsmouseinput *input, struct evq_access *evq, u_int cmds)
+{
+ struct wstpad *tp = input->tp;
+ u_int sbtns_dn = 0, sbtns_up = 0;
+ int n;
+
+ FOREACHBIT(cmds, n) {
+ switch (n) {
+ case CLEAR_MOTION_DELTAS:
+ input->motion.dx = input->motion.dy = 0;
+ if (input->motion.dz == 0 && input->motion.dw == 0)
+ input->motion.sync &= ~SYNC_DELTAS;
+ continue;
+ case SOFTBUTTON_DOWN:
+ input->btn.sync &= ~PRIMARYBTN;
+ sbtns_dn |= tp->softbutton;
+ continue;
+ case SOFTBUTTON_UP:
+ input->btn.sync &= ~PRIMARYBTN;
+ sbtns_up |= tp->softbutton;
+ tp->softbutton = 0;
+ continue;
+ case HSCROLL:
+ input->motion.dw = tp->scroll.dw;
+ input->motion.sync |= SYNC_DELTAS;
+ continue;
+ case VSCROLL:
+ input->motion.dz = tp->scroll.dz;
+ input->motion.sync |= SYNC_DELTAS;
+ continue;
+ default:
+ printf("[wstpad] invalid cmd %d\n", n);
+ break;
+ }
+ }
+ if (sbtns_dn || sbtns_up) {
+ input->sbtn.buttons |= sbtns_dn;
+ input->sbtn.buttons &= ~sbtns_up;
+ input->sbtn.sync |= (sbtns_dn | sbtns_up);
+ }
+}
+
+
+/*
+ * Set the state of touches that have ended. TOUCH_END is a transitional
+ * state and will be changed to TOUCH_NONE before process_input() returns.
+ */
+static __inline void
+clear_touchstates(struct wsmouseinput *input, enum touchstates state)
+{
+ u_int touches;
+ int slot;
+
+ touches = input->mt.sync[MTS_TOUCH] & ~input->mt.touches;
+ FOREACHBIT(touches, slot)
+ input->tp->tpad_touches[slot].state = state;
+}
+
+void
+wstpad_mt_inputs(struct wsmouseinput *input)
+{
+ struct wstpad *tp = input->tp;
+ struct tpad_touch *t;
+ struct mt_slot *mts;
+ int slot, dx, dy;
+ u_int touches, inactive;
+
+ /* TOUCH_BEGIN */
+ touches = input->mt.touches & input->mt.sync[MTS_TOUCH];
+ FOREACHBIT(touches, slot) {
+ t = &tp->tpad_touches[slot];
+ t->state = TOUCH_BEGIN;
+ mts = &input->mt.slots[slot];
+ t->x = normalize_abs(&input->filter.h, mts->x);
+ t->y = normalize_abs(&input->filter.v, mts->y);
+ t->orig.x = t->x;
+ t->orig.y = t->y;
+ memcpy(&t->orig.time, &tp->time, sizeof(struct timespec));
+ t->flags = edge_flags(tp, t->x, t->y);
+ wstpad_set_direction(t, 0, 0, tp->ratio);
+ }
+
+ /* TOUCH_UPDATE */
+ touches = input->mt.touches & input->mt.frame;
+ if (touches & tp->mtcycle) {
+ /*
+ * Slot data may be synchronized separately, in any order,
+ * or not at all if they are "inactive" and there is no delta.
+ */
+ inactive = input->mt.touches & ~tp->mtcycle;
+ tp->mtcycle = touches;
+ } else {
+ inactive = 0;
+ tp->mtcycle |= touches;
+ }
+ touches = input->mt.touches & ~input->mt.sync[MTS_TOUCH];
+ FOREACHBIT(touches, slot) {
+ t = &tp->tpad_touches[slot];
+ t->state = TOUCH_UPDATE;
+ if ((1 << slot) & input->mt.frame) {
+ mts = &input->mt.slots[slot];
+ dx = normalize_abs(&input->filter.h, mts->x) - t->x;
+ t->x += dx;
+ dy = normalize_abs(&input->filter.v, mts->y) - t->y;
+ t->y += dy;
+ t->flags &= (~EDGES | edge_flags(tp, t->x, t->y));
+ wstpad_set_direction(t, dx, dy, tp->ratio);
+ } else if ((1 << slot) & inactive) {
+ wstpad_set_direction(t, 0, 0, tp->ratio);
+ }
+ }
+
+ clear_touchstates(input, TOUCH_END);
+}
+
+void
+wstpad_touch_inputs(struct wsmouseinput *input)
+{
+ struct wstpad *tp = input->tp;
+ struct tpad_touch *t;
+ int slot;
+
+ tp->dx = normalize_rel(&input->filter.h, input->motion.dx);
+ tp->dy = normalize_rel(&input->filter.v, input->motion.dy);
+ tp->btns = input->btn.buttons;
+ tp->btns_sync = input->btn.sync;
+
+ tp->prev_contacts = tp->contacts;
+ tp->contacts = input->touch.contacts;
+
+ if (tp->contacts == 1 &&
+ ((tp->params.f2width &&
+ input->touch.width >= tp->params.f2width)
+ || (tp->params.f2pressure &&
+ input->touch.pressure >= tp->params.f2pressure)))
+ tp->contacts = 2;
+
+ if (IS_MT(tp)) {
+ wstpad_mt_inputs(input);
+ if (input->mt.ptr) {
+ slot = ffs(input->mt.ptr) - 1;
+ tp->t = &tp->tpad_touches[slot];
+ }
+ } else {
+ t = tp->t;
+ t->x = normalize_abs(&input->filter.h, input->motion.x);
+ t->y = normalize_abs(&input->filter.v, input->motion.y);
+ if (tp->contacts)
+ t->state = (tp->prev_contacts ?
+ TOUCH_UPDATE : TOUCH_BEGIN);
+ else
+ t->state = (tp->prev_contacts ?
+ TOUCH_END : TOUCH_NONE);
+
+ if (t->state == TOUCH_BEGIN) {
+ t->orig.x = t->x;
+ t->orig.y = t->y;
+ memcpy(&t->orig.time, &tp->time,
+ sizeof(struct timespec));
+ t->flags = edge_flags(tp, t->x, t->y);
+ } else {
+ t->flags &= (~EDGES | edge_flags(tp, t->x, t->y));
+ }
+
+ /* Use unfiltered deltas for determining directions: */
+ wstpad_set_direction(t,
+ normalize_rel(&input->filter.h, input->motion.x_delta),
+ normalize_rel(&input->filter.v, input->motion.y_delta),
+ input->filter.ratio);
+ }
+}
+
+void
+wstpad_process_input(struct wsmouseinput *input, struct evq_access *evq)
+{
+ struct wstpad *tp = input->tp;
+ u_int handlers, hdlr, cmds;
+
+ memcpy(&tp->time, &evq->ts, sizeof(struct timespec));
+ wstpad_touch_inputs(input);
+
+ cmds = 0;
+ handlers = tp->handlers;
+ if (DISABLE(tp))
+ handlers &= (1 << TOPBUTTON_HDLR);
+
+ FOREACHBIT(handlers, hdlr) {
+ switch (hdlr) {
+ case SOFTBUTTON_HDLR:
+ case TOPBUTTON_HDLR:
+ wstpad_softbuttons(input, &cmds, hdlr);
+ continue;
+ case F2SCROLL_HDLR:
+ wstpad_f2scroll(input, &cmds);
+ continue;
+ case EDGESCROLL_HDLR:
+ wstpad_edgescroll(input, &cmds);
+ continue;
+ case CLICK_HDLR:
+ wstpad_click(input);
+ continue;
+ }
+ }
+
+ /* Check whether the motion deltas should be cleared. */
+ if (tp->dx || tp->dy) {
+ if (DISABLE(tp)
+ || (tp->t->flags & tp->freeze)
+ || timespeccmp(&tp->time, &tp->freeze_ts, <)
+ /* Is there more than one contact, and no click-and-drag? */
+ || tp->contacts > 2
+ || (tp->contacts == 2 && !(tp->btns & PRIMARYBTN))) {
+
+ cmds |= 1 << CLEAR_MOTION_DELTAS;
+ }
+ }
+
+ wstpad_cmds(input, evq, cmds);
+
+ if (IS_MT(tp))
+ clear_touchstates(input, TOUCH_NONE);
+}
+
+/*
+ * Try to determine the average interval between two updates. Various
+ * conditions are checked in order to ensure that only valid samples enter
+ * into the calculation. Above all, it is restricted to motion events
+ * occuring when there is only one contact. MT devices may need more than
+ * one packet to transmit their state if there are multiple touches, and
+ * the update frequency may be higher in this case.
+ */
+void
+wstpad_track_interval(struct wsmouseinput *input, struct timespec *time)
+{
+ static const struct timespec limit = { 0, 30 * 1000000L };
+ struct timespec ts;
+ int samples;
+
+ if (input->motion.sync == 0
+ || (input->touch.sync & SYNC_CONTACTS)
+ || (input->touch.contacts > 1)) {
+ input->intv.track = 0;
+ return;
+ }
+ if (input->intv.track) {
+ timespecsub(time, &input->intv.ts, &ts);
+ if (timespeccmp(&ts, &limit, <)) {
+ /* The unit of the sum is 4096 nanoseconds. */
+ input->intv.sum += ts.tv_nsec >> 12;
+ samples = ++input->intv.samples;
+ /*
+ * Make the first calculation quickly and later
+ * a more reliable one:
+ */
+ if (samples == 8) {
+ input->intv.avg = input->intv.sum << 9;
+ wstpad_init_deceleration(input);
+ } else if (samples == 128) {
+ input->intv.avg = input->intv.sum << 5;
+ wstpad_init_deceleration(input);
+ input->intv.samples = 0;
+ input->intv.sum = 0;
+ input->flags &= ~TRACK_INTERVAL;
+ }
+ }
+ }
+ memcpy(&input->intv.ts, time, sizeof(struct timespec));
+ input->intv.track = 1;
+}
+
+/*
+ * If hysteresis values are set, the sum of the output motion deltas will
+ * lag behind the input deltas within a window bounded by +[hysteresis]
+ * and -[hysteresis]. It may suppress accidental movements when a touch
+ * starts or ends, or when small corrections with changing directions are
+ * being made. In the synaptics driver the default threshold is 0.5 percent
+ * of the diagonal of the touchpad surface, the wstpad default is about
+ * 0.36 percent. The fields filter.h.acc and filter.v.acc accumulate the
+ * deltas up to the threshold values.
+ */
+void
+wstpad_hysteresis(int *acc, int *delta, int hysteresis)
+{
+ *acc += *delta;
+ if (*acc > hysteresis)
+ *delta = *acc - hysteresis;
+ else if (*acc < -hysteresis)
+ *delta = *acc + hysteresis;
+ else
+ *delta = 0;
+ *acc -= *delta;
+}
+
+/*
+ * The default acceleration options of X don't work convincingly with
+ * touchpads (the synaptics driver installs its own "acceleration
+ * profile" and callback function). As a preliminary workaround, this
+ * filter applies a simple deceleration scheme to small deltas. Based
+ * on an "alpha-max-plus-beta-min" approximation to the distance, it
+ * assigns a "magnitude" to a delta pair. A value of 8 corresponds,
+ * roughly, to a speed of (filter.dclr / 12.5) device units per milli-
+ * second. If its magnitude is smaller than 7 a delta will be downscaled
+ * by the factor 2/8, deltas with magnitudes from 7 to 11 by factors
+ * ranging from 3/8 to 7/8.
+ */
+void
+wstpad_decelerate(struct wsmouseinput *input, int *dx, int *dy)
+{
+ int h = abs(*dx) * input->filter.h.mag_scale;
+ int v = abs(*dy) * input->filter.v.mag_scale;
+ int mag = (h >= v ? h + 3 * v / 8 : v + 3 * h / 8);
+ int n;
+
+ /* Don't change deceleration levels abruptly. */
+ mag = (mag + 7 * input->filter.mag) / 8;
+ /* Don't use arbitrarily high values. */
+ input->filter.mag = imin(mag, 24 << 12);
+
+ n = imax((mag >> 12) - 4, 2);
+ if (n < 8) {
+ /* Scale by (n / 8). */
+ h = *dx * n + input->filter.h.dclr_rmdr;
+ v = *dy * n + input->filter.v.dclr_rmdr;
+ input->filter.h.dclr_rmdr = (h >= 0 ? h & 7 : -(-h & 7));
+ input->filter.v.dclr_rmdr = (v >= 0 ? v & 7 : -(-v & 7));
+ *dx = h / 8;
+ *dy = v / 8;
+ }
+}
+
+/*
+ * Compatibility-mode conversions. This function transforms and filters
+ * the coordinate inputs, extended functionality is provided by
+ * wstpad_process_input.
+ */
+void
+wstpad_compat_convert(struct wsmouseinput *input, struct evq_access *evq)
+{
+ struct axis_filter *h = &input->filter.h;
+ struct axis_filter *v = &input->filter.v;
+ int dx, dy;
+
+ if (input->flags & TRACK_INTERVAL)
+ wstpad_track_interval(input, &evq->ts);
+
+ dx = (input->motion.sync & SYNC_X) ? input->motion.x_delta : 0;
+ dy = (input->motion.sync & SYNC_Y) ? input->motion.y_delta : 0;
+
+ if ((h->dmax && (abs(dx) > h->dmax))
+ || (v->dmax && (abs(dy) > v->dmax)))
+ dx = dy = 0;
+
+ if (dx || dy) {
+ if (input->touch.sync & SYNC_CONTACTS)
+ h->acc = v->acc = 0;
+ if (h->hysteresis)
+ wstpad_hysteresis(&h->acc, &dx, h->hysteresis);
+ if (v->hysteresis)
+ wstpad_hysteresis(&v->acc, &dy, v->hysteresis);
+ if (input->filter.dclr)
+ wstpad_decelerate(input, &dx, &dy);
+ input->motion.dx = dx;
+ input->motion.dy = dy;
+ if ((dx || dy) && !(input->motion.sync & SYNC_DELTAS)) {
+ input->motion.dz = input->motion.dw = 0;
+ input->motion.sync |= SYNC_DELTAS;
+ }
+ }
+
+ if (input->tp != NULL)
+ wstpad_process_input(input, evq);
+
+ input->motion.sync &= ~SYNC_POSITION;
+ input->touch.sync = 0;
+}
+
+int
+wstpad_init(struct wsmouseinput *input)
+{
+ struct wstpad *tp = input->tp;
+ int slots;
+
+ if (tp != NULL)
+ return (0);
+
+ input->tp = tp = malloc(sizeof(struct wstpad),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ if (tp == NULL)
+ return (-1);
+
+ slots = imax(input->mt.num_slots, 1);
+ tp->tpad_touches = malloc(slots * sizeof(struct tpad_touch),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ if (tp->tpad_touches == NULL) {
+ free(tp, M_DEVBUF, sizeof(struct wstpad));
+ return (-1);
+ }
+
+ tp->t = &tp->tpad_touches[0];
+ if (input->mt.num_slots)
+ tp->features |= WSTPAD_MT;
+
+ tp->ratio = input->filter.ratio;
+
+ return (0);
+}
+
+/*
+ * Integer square root (Halleck's method)
+ *
+ * An adaption of code from John B. Halleck (from
+ * http://www.cc.utah.edu/~nahaj/factoring/code.html). This version is
+ * used and published under the OpenBSD license terms with his permission.
+ *
+ * Cf. also Martin Guy's "Square root by abacus" method.
+ */
+static __inline u_int
+isqrt(u_int n)
+{
+ u_int root, sqbit;
+
+ root = 0;
+ sqbit = 1 << (sizeof(u_int) * 8 - 2);
+ while (sqbit) {
+ if (n >= (sqbit | root)) {
+ n -= (sqbit | root);
+ root = (root >> 1) | sqbit;
+ } else {
+ root >>= 1;
+ }
+ sqbit >>= 2;
+ }
+ return (root);
+}
+
+void
+wstpad_init_deceleration(struct wsmouseinput *input)
+{
+ int n, dclr;
+
+ if ((dclr = input->filter.dclr) == 0)
+ return;
+
+ dclr = imax(dclr, 4);
+
+ /*
+ * For a standard update rate of about 80Hz, (dclr) units
+ * will be mapped to a magnitude of 8. If the average rate
+ * is significantly higher or lower, adjust the coefficient
+ * accordingly:
+ */
+ if (input->intv.avg == 0) {
+ n = 8;
+ } else {
+ n = 8 * 13000000 / input->intv.avg;
+ n = imax(imin(n, 32), 4);
+ }
+ input->filter.h.mag_scale = (n << 12) / dclr;
+ input->filter.v.mag_scale = (input->filter.ratio ?
+ n * input->filter.ratio : n << 12) / dclr;
+ input->filter.h.dclr_rmdr = 0;
+ input->filter.v.dclr_rmdr = 0;
+ input->flags |= TRACK_INTERVAL;
+}
+
+int
+wstpad_configure(struct wsmouseinput *input)
+{
+ struct wstpad *tp;
+ int width, height, diag, h_unit, v_unit;
+
+ /*
+ * TODO: The default configurations don't use the resolution values
+ * and the X/Y ratio yet. The parameters are derived from the length
+ * of the diagonal in device units, with some magic constants which
+ * are partly adapted from libinput or synaptics code, or are based
+ * on tests and guess work.
+ */
+ width = abs(input->hw.x_max - input->hw.x_min);
+ height = abs(input->hw.y_max - input->hw.y_min);
+ if (width == 0 || height == 0)
+ return (-1); /* We can't do anything. */
+
+ diag = isqrt(width * width + height * height);
+ h_unit = v_unit = imax(diag / 280, 3);
+
+ if (input->tp == NULL && wstpad_init(input))
+ return (-1);
+ tp = input->tp;
+
+ if (!(input->flags & CONFIGURED)) {
+ input->filter.h.scale = (imin(920, diag) << 12) / diag;
+ input->filter.v.scale = input->filter.h.scale;
+ input->filter.h.hysteresis = h_unit;
+ input->filter.v.hysteresis = v_unit;
+ input->filter.dclr = h_unit - h_unit / 5;
+ wstpad_init_deceleration(input);
+
+ tp->params.top_edge = EDGERATIO_DEFAULT;
+ tp->params.bottom_edge = EDGERATIO_DEFAULT;
+ tp->params.left_edge = EDGERATIO_DEFAULT;
+ tp->params.right_edge = EDGERATIO_DEFAULT;
+ tp->params.center_width = CENTERWIDTH_DEFAULT;
+
+ tp->features &= (WSTPAD_MT | WSTPAD_DISABLE);
+ if (input->hw.hw_type == WSMOUSEHW_TOUCHPAD)
+ tp->features |= WSTPAD_TOUCHPAD_DEFAULTS;
+ else
+ tp->features |= WSTPAD_CLICKPAD_DEFAULTS;
+ if (input->hw.contacts_max == 1) {
+ tp->features &= ~WSTPAD_F2SCROLL;
+ tp->features |= WSTPAD_EDGESCROLL;
+ }
+ tp->scroll.hdist = 5 * h_unit;
+ tp->scroll.vdist = 5 * v_unit;
+ }
+
+ tp->edge.left = input->hw.x_min +
+ width * tp->params.left_edge / 4096;
+ tp->edge.right = input->hw.x_max -
+ width * tp->params.right_edge / 4096;
+ tp->edge.bottom = input->hw.y_min +
+ height * tp->params.bottom_edge / 4096;
+ tp->edge.top = input->hw.y_max -
+ height * tp->params.top_edge / 4096;
+ tp->edge.center = (input->hw.x_min + input->hw.x_max) / 2;
+ tp->edge.center_left = tp->edge.center -
+ width * tp->params.center_width / 8192;
+ tp->edge.center_right = tp->edge.center +
+ width * tp->params.center_width / 8192;
+ tp->edge.middle = (input->hw.y_max - input->hw.y_min) / 2;
+
+ tp->handlers = 0;
+ tp->freeze = 0;
+
+ if (tp->features & WSTPAD_SOFTBUTTONS) {
+ tp->handlers |= 1 << SOFTBUTTON_HDLR;
+ tp->freeze |= B_EDGE;
+ }
+ if (tp->features & WSTPAD_TOPBUTTONS) {
+ tp->handlers |= 1 << TOPBUTTON_HDLR;
+ tp->freeze |= T_EDGE;
+ }
+
+ if (tp->features & WSTPAD_F2SCROLL) {
+ tp->handlers |= 1 << F2SCROLL_HDLR;
+ } else if (tp->features & WSTPAD_EDGESCROLL) {
+ tp->handlers |= 1 << EDGESCROLL_HDLR;
+ tp->freeze |=
+ ((tp->features & WSTPAD_SWAPSIDES) ? L_EDGE : R_EDGE);
+ }
+
+ if (input->hw.hw_type == WSMOUSEHW_CLICKPAD)
+ tp->handlers |= 1 << CLICK_HDLR;
+
+ tp->sbtnswap = ((tp->features & WSTPAD_SWAPSIDES)
+ ? (LEFTBTN | RIGHTBTN) : 0);
+
+ return (0);
+}
+
+void
+wstpad_reset(struct wsmouseinput *input)
+{
+ struct wstpad *tp = input->tp;
+
+ if (tp == NULL)
+ return;
+ if (input->sbtn.buttons) {
+ input->sbtn.sync = input->sbtn.buttons;
+ input->sbtn.buttons = 0;
+ }
+}
+
+int
+wstpad_set_params(struct wsmouseinput *input,
+ const struct wsmouse_param *params, u_int nparams)
+{
+ struct wstpad *tp = input->tp;
+ int i, key, val;
+ u_int flag;
+ void *p;
+
+ if (tp == NULL)
+ return (-1);
+
+ for (i = 0; i < nparams; i++) {
+ key = params[i].key;
+ val = params[i].value;
+ if (WSMOUSECFG_MATCH(key, TP_OPTS)) {
+ flag = 1 << (key & 0xff);
+ if (val)
+ tp->features |= flag;
+ else
+ tp->features &= ~flag;
+ } else { /* WSMOUSECFG_MATCH(k, TP) */
+ p = tp;
+ p += cfg_tp[key & 0xff];
+ if (p != tp) {
+ *((int *) p) = val;
+ } else {
+ printf("wstpad_set_params: "
+ "ignoring key %d\n", key);
+ }
+ }
+ }
+ wstpad_reset(input);
+ return wstpad_configure(input);
+}
+
+int
+wstpad_get_params(struct wsmouseinput *input,
+ struct wsmouse_param *params, u_int nparams)
+{
+ struct wstpad *tp = input->tp;
+ int i, key;
+ u_int flag;
+ void *p;
+
+ if (tp == NULL)
+ return (-1);
+ for (i = 0; i < nparams; i++) {
+ key = params[i].key;
+ if (WSMOUSECFG_MATCH(key, TP_OPTS)) {
+ flag = 1 << (key & 0xff);
+ params[i].value = !!(tp->features & flag);
+ } else { /* WSMOUSECFG_MATCH(k, TP) */
+ p = tp;
+ p += cfg_tp[key & 0xff];
+ if (p != tp)
+ params[i].value = *((int *) p);
+ else
+ printf("wstpad_get_params: "
+ "ignoring key %d\n", key);
+ }
+ }
+
+ return (0);
+}