Improve the handling of coordinate inputs and filters.
authorbru <bru@openbsd.org>
Thu, 11 Jan 2018 23:50:49 +0000 (23:50 +0000)
committerbru <bru@openbsd.org>
Thu, 11 Jan 2018 23:50:49 +0000 (23:50 +0000)
Single-touch and multi-touch coordinate inputs are treated more uniformly,
and the hysteresis filters have a more consistent implementation.  If
possible, pointer control will be assigned to touches with coordinate
updates that pass the default hysteresis filter (the function has been
moved to wsmouse.c).  The "strong" variant of hysteresis has been
improved, the new version won't double the threshold when a movement
changes the orientation on an axis.

There is an additional change in wstpad_configure, which ensures that a
zero size disables an edge area even if the coordinate limits are
misconfigured.

sys/dev/wscons/wsmouse.c
sys/dev/wscons/wsmouseinput.h
sys/dev/wscons/wstpad.c

index ec006ca..5c78a20 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsmouse.c,v 1.41 2017/11/23 22:59:42 bru Exp $ */
+/* $OpenBSD: wsmouse.c,v 1.42 2018/01/11 23:50:49 bru Exp $ */
 /* $NetBSD: wsmouse.c,v 1.35 2005/02/27 00:27:52 perry Exp $ */
 
 /*
@@ -614,32 +614,49 @@ wsmouse_motion(struct device *sc, int dx, int dy, int dz, int 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.
- */
+static inline void
+set_x(struct position *pos, int x, u_int *sync, u_int mask)
+{
+       if (*sync & mask) {
+               pos->x -= pos->dx;
+               pos->acc_dx -= pos->dx;
+       }
+       if ((pos->dx = x - pos->x)) {
+               pos->x = x;
+               pos->acc_dx += pos->dx;
+               *sync |= mask;
+       }
+}
+
+static inline void
+set_y(struct position *pos, int y, u_int *sync, u_int mask)
+{
+       if (*sync & mask) {
+               pos->y -= pos->dy;
+               pos->acc_dy -= pos->dy;
+       }
+       if ((pos->dy = y - pos->y)) {
+               pos->y = y;
+               pos->acc_dy += pos->dy;
+               *sync |= mask;
+       }
+}
+
+static inline void
+cleardeltas(struct position *pos)
+{
+       pos->dx = pos->acc_dx = 0;
+       pos->dy = pos->acc_dy = 0;
+}
+
 void
 wsmouse_position(struct device *sc, int x, int y)
 {
        struct motion_state *motion =
            &((struct wsmouse_softc *) sc)->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;
-       }
+       set_x(&motion->pos, x, &motion->sync, SYNC_X);
+       set_y(&motion->pos, y, &motion->sync, SYNC_Y);
 }
 
 static inline int
@@ -670,7 +687,6 @@ 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;
@@ -696,14 +712,18 @@ wsmouse_mtstate(struct device *sc, int slot, int x, int y, int pressure)
        initial = ((mt->touches & bit) == (mt->sync[MTS_TOUCH] & bit));
 
        mts = &mt->slots[slot];
-       if (x != mts->x || initial) {
-               mts->x = x;
+
+       if (initial) {
+               mts->pos.x = x;
+               mts->pos.y = y;
+               cleardeltas(&mts->pos);
                mt->sync[MTS_X] |= bit;
-       }
-       if (y != mts->y || initial) {
-               mts->y = y;
                mt->sync[MTS_Y] |= bit;
+       } else {
+               set_x(&mts->pos, x, mt->sync + MTS_X, bit);
+               set_y(&mts->pos, y, mt->sync + MTS_Y, bit);
        }
+
        pressure = normalized_pressure(input, pressure);
        if (pressure != mts->pressure || initial) {
                mts->pressure = pressure;
@@ -738,14 +758,14 @@ wsmouse_set(struct device *sc, enum wsmouseval type, int value, int aux)
 
        switch (type) {
        case WSMOUSE_REL_X:
-               value += input->motion.x; /* fall through */
+               value += input->motion.pos.x; /* fall through */
        case WSMOUSE_ABS_X:
-               wsmouse_position(sc, value, input->motion.y);
+               wsmouse_position(sc, value, input->motion.pos.y);
                return;
        case WSMOUSE_REL_Y:
-               value += input->motion.y;
+               value += input->motion.pos.y;
        case WSMOUSE_ABS_Y:
-               wsmouse_position(sc, input->motion.x, value);
+               wsmouse_position(sc, input->motion.pos.x, value);
                return;
        case WSMOUSE_PRESSURE:
                wsmouse_touch(sc, value, input->touch.contacts);
@@ -764,17 +784,17 @@ wsmouse_set(struct device *sc, enum wsmouseval type, int value, int aux)
                }
                return;
        case WSMOUSE_MT_REL_X:
-               value += mts->x; /* fall through */
+               value += mts->pos.x; /* fall through */
        case WSMOUSE_MT_ABS_X:
-               wsmouse_mtstate(sc, aux, value, mts->y, mts->pressure);
+               wsmouse_mtstate(sc, aux, value, mts->pos.y, mts->pressure);
                return;
        case WSMOUSE_MT_REL_Y:
-               value += mts->y;
+               value += mts->pos.y;
        case WSMOUSE_MT_ABS_Y:
-               wsmouse_mtstate(sc, aux, mts->x, value, mts->pressure);
+               wsmouse_mtstate(sc, aux, mts->pos.x, value, mts->pressure);
                return;
        case WSMOUSE_MT_PRESSURE:
-               wsmouse_mtstate(sc, aux, mts->x, mts->y, value);
+               wsmouse_mtstate(sc, aux, mts->pos.x, mts->pos.y, value);
                return;
        }
 }
@@ -787,17 +807,25 @@ wsmouse_touch_update(struct wsmouseinput *input)
        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;
+               /*
+                * There may be zero coordinates, or coordinates of
+                * touches with pressure values below min_pressure.
+                */
+               if (motion->sync & SYNC_POSITION) {
+                       /* Restore valid coordinates. */
+                       motion->pos.x -= motion->pos.dx;
+                       motion->pos.y -= motion->pos.dy;
+                       motion->sync &= ~SYNC_POSITION;
+               }
+
+               if (touch->prev_contacts == 0)
+                       touch->sync &= ~SYNC_PRESSURE;
+
        }
+
        if (touch->sync & SYNC_CONTACTS)
-               /* Suppress pointer motion. */
-               motion->x_delta = motion->y_delta = 0;
+               /* Suppress pointer movement. */
+               cleardeltas(&motion->pos);
 
        if ((touch->sync & SYNC_PRESSURE) && touch->min_pressure) {
                if (touch->pressure >= input->filter.pressure_hi)
@@ -824,6 +852,26 @@ wsmouse_mt_update(struct wsmouseinput *input)
        }
 }
 
+/* Return TRUE if a coordinate update may be noise. */
+int
+wsmouse_hysteresis(struct wsmouseinput *input, struct position *pos)
+{
+
+       if (!(input->filter.h.hysteresis && input->filter.v.hysteresis))
+               return (0);
+
+       if ((pos->dx > 0 && pos->dx > pos->acc_dx)
+          || (pos->dx < 0 && pos->dx < pos->acc_dx))
+               pos->acc_dx = pos->dx;
+
+       if ((pos->dy > 0 && pos->dy > pos->acc_dy)
+          || (pos->dy < 0 && pos->dy < pos->acc_dy))
+               pos->acc_dy = pos->dy;
+
+       return (abs(pos->acc_dx) < input->filter.h.hysteresis
+           && abs(pos->acc_dy) < input->filter.v.hysteresis);
+}
+
 /*
  * Select the pointer-controlling MT slot.
  *
@@ -838,11 +886,23 @@ wsmouse_mt_update(struct wsmouseinput *input)
  * contain the pointer-controlling slot, a new slot will be selected.
  */
 void
-wsmouse_ptr_ctrl(struct mt_state *mt)
+wsmouse_ptr_ctrl(struct wsmouseinput *input)
 {
+       struct mt_state *mt = &input->mt;
        u_int updates;
        int select, slot;
 
+       updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH];
+       FOREACHBIT(updates, slot) {
+               /*
+                * Touches that just produce noise are no problem if the
+                * frequency of zero deltas is high enough, but there might
+                * be no guarantee for that.
+                */
+               if (wsmouse_hysteresis(input, &mt->slots[slot].pos))
+                       updates ^= (1 << slot);
+       }
+
        mt->prev_ptr = mt->ptr;
 
        if (mt->num_touches <= 1) {
@@ -857,8 +917,7 @@ wsmouse_ptr_ctrl(struct mt_state *mt)
         */
        select = ((mt->ptr & mt->touches & ~mt->ptr_mask) == 0);
 
-       /* Remove slots without X/Y deltas from the cycle. */
-       updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH];
+       /* Remove slots without coordinate deltas from the cycle. */
        mt->ptr_cycle &= ~(mt->frame ^ updates);
 
        if (mt->ptr_cycle & updates) {
@@ -887,15 +946,20 @@ wsmouse_mt_convert(struct device *sc)
        struct mt_slot *mts;
        int slot, pressure;
 
-       wsmouse_ptr_ctrl(mt);
+       wsmouse_ptr_ctrl(input);
 
        if (mt->ptr) {
                slot = ffs(mt->ptr) - 1;
                mts = &mt->slots[slot];
-               wsmouse_position(sc, mts->x, mts->y);
+               if (mts->pos.x != input->motion.pos.x)
+                       input->motion.sync |= SYNC_X;
+               if (mts->pos.y != input->motion.pos.y)
+                       input->motion.sync |= SYNC_Y;
                if (mt->ptr != mt->prev_ptr)
-                       /* Suppress pointer motion. */
-                       input->motion.x_delta = input->motion.y_delta = 0;
+                       /* Suppress pointer movement. */
+                       mts->pos.dx = mts->pos.dy = 0;
+               memcpy(&input->motion.pos, &mts->pos, sizeof(struct position));
+
                pressure = mts->pressure;
        } else {
                pressure = 0;
@@ -980,15 +1044,15 @@ wsmouse_motion_sync(struct wsmouseinput *input, struct evq_access *evq)
        }
        if (motion->sync & SYNC_POSITION) {
                if (motion->sync & SYNC_X) {
-                       x = (h->inv ? h->inv - motion->x : motion->x);
+                       x = (h->inv ? h->inv - motion->pos.x : motion->pos.x);
                        wsmouse_evq_put(evq, ABS_X_EV(input), x);
                }
                if (motion->sync & SYNC_Y) {
-                       y = (v->inv ? v->inv - motion->y : motion->y);
+                       y = (v->inv ? v->inv - motion->pos.y : motion->pos.y);
                        wsmouse_evq_put(evq, ABS_Y_EV(input), y);
                }
-               if (motion->x_delta == 0 && motion->y_delta == 0
-                   && (input->flags & TPAD_NATIVE_MODE))
+               if (motion->pos.dx == 0 && motion->pos.dy == 0
+                   && (input->flags & TPAD_NATIVE_MODE ))
                        /* Suppress pointer motion. */
                        wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_RESET, 0);
        }
@@ -1017,6 +1081,7 @@ clear_sync_flags(struct wsmouseinput *input)
        input->sbtn.sync = 0;
        input->motion.sync = 0;
        input->touch.sync = 0;
+       input->touch.prev_contacts = input->touch.contacts;
        if (input->mt.frame) {
                input->mt.frame = 0;
                for (i = 0; i < MTS_SIZE; i++)
@@ -1039,7 +1104,7 @@ wsmouse_input_sync(struct device *sc)
 
        add_mouse_randomness(input->btn.buttons
            ^ input->motion.dx ^ input->motion.dy
-           ^ input->motion.x ^ input->motion.y
+           ^ input->motion.pos.x ^ input->motion.pos.y
            ^ input->motion.dz ^ input->motion.dw);
 
        if (input->mt.frame) {
@@ -1055,7 +1120,6 @@ wsmouse_input_sync(struct device *sc)
        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)
@@ -1216,8 +1280,8 @@ wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int size)
        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;
+                               dx = pt[i].x - mt->slots[slot].pos.x;
+                               dy = pt[i].y - mt->slots[slot].pos.y;
                                *p++ = dx * dx + dy * dy;
                        }
                m = mt->num_touches;
@@ -1225,8 +1289,8 @@ wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int 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;
+                               dx = pt[i].x - mt->slots[slot].pos.x;
+                               dy = pt[i].y - mt->slots[slot].pos.y;
                                *p++ = dx * dx + dy * dy;
                        }
                m = size;
@@ -1411,11 +1475,9 @@ wsmouse_set_params(struct device *sc,
                        break;
                case WSMOUSECFG_X_HYSTERESIS:
                        input->filter.h.hysteresis = val;
-                       input->filter.h.acc = 0;
                        break;
                case WSMOUSECFG_Y_HYSTERESIS:
                        input->filter.v.hysteresis = val;
-                       input->filter.v.acc = 0;
                        break;
                case WSMOUSECFG_DECELERATION:
                        input->filter.dclr = val;
index 575f546..917a2d4 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsmouseinput.h,v 1.9 2017/11/23 22:59:42 bru Exp $ */
+/* $OpenBSD: wsmouseinput.h,v 1.10 2018/01/11 23:50:49 bru Exp $ */
 
 /*
  * Copyright (c) 2015, 2016 Ulf Brosziewski
 
 #ifdef _KERNEL
 
+struct position {
+       int x;
+       int y;
+       int dx;                 /* unfiltered coordinate deltas */
+       int dy;
+       int acc_dx;             /* delta sums used for filtering */
+       int acc_dy;
+};
+
 struct btn_state {
        u_int buttons;
        u_int sync;
 };
 
 struct motion_state {
-       int dx;
+       int dx;                 /* mouse input, or filtered deltas */
        int dy;
        int dz;
        int dw;
-       int x;
-       int y;
+       struct position pos;
        u_int sync;
-
-       /* deltas of absolute coordinates */
-       int x_delta;
-       int y_delta;
 };
 #define SYNC_DELTAS            (1 << 0)
 #define SYNC_X                 (1 << 1)
@@ -62,8 +66,7 @@ struct touch_state {
 #define SYNC_TOUCH_WIDTH       (1 << 2)
 
 struct mt_slot {
-       int x;
-       int y;
+       struct position pos;
        int pressure;
        int id;         /* tracking ID */
 };
@@ -104,9 +107,8 @@ struct axis_filter {
        int rmdr;
        /* Invert coordinates. */
        int inv;
-       /* Hysteresis limit, accumulated deltas, and weighted delta average. */
+       /* Hysteresis limit, and weighted delta average. */
        int hysteresis;
-       int acc;
        int avg;
        int avg_rmdr;
        /* A [*.12] coefficient for "magnitudes", used for deceleration. */
@@ -181,6 +183,7 @@ struct evq_access {
 
 
 void wsmouse_evq_put(struct evq_access *, int, int);
+int wsmouse_hysteresis(struct wsmouseinput *, struct position *);
 void wsmouse_input_reset(struct wsmouseinput *);
 void wsmouse_input_cleanup(struct wsmouseinput *);
 
index 9700935..f306f0a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: wstpad.c,v 1.15 2017/12/23 10:50:15 bru Exp $ */
+/* $OpenBSD: wstpad.c,v 1.16 2018/01/11 23:50:49 bru Exp $ */
 
 /*
  * Copyright (c) 2015, 2016 Ulf Brosziewski
@@ -240,18 +240,6 @@ normalize_rel(struct axis_filter *filter, int val)
        return (filter->inv ? -val : val);
 }
 
-static inline int
-raw_dx(struct wsmouseinput *input)
-{
-       return ((input->motion.sync & SYNC_X) ? input->motion.x_delta : 0);
-}
-
-static inline int
-raw_dy(struct wsmouseinput *input)
-{
-       return ((input->motion.sync & SYNC_Y) ? input->motion.y_delta : 0);
-}
-
 /*
  * Directions of motion are represented by numbers in the range 0 - 11,
  * corresponding to clockwise counted circle sectors:
@@ -902,8 +890,8 @@ wstpad_mt_inputs(struct wsmouseinput *input)
                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->x = normalize_abs(&input->filter.h, mts->pos.x);
+               t->y = normalize_abs(&input->filter.v, mts->pos.y);
                t->orig.x = t->x;
                t->orig.y = t->y;
                memcpy(&t->orig.time, &tp->time, sizeof(struct timespec));
@@ -931,9 +919,9 @@ wstpad_mt_inputs(struct wsmouseinput *input)
                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;
+                       dx = normalize_abs(&input->filter.h, mts->pos.x) - t->x;
                        t->x += dx;
-                       dy = normalize_abs(&input->filter.v, mts->y) - t->y;
+                       dy = normalize_abs(&input->filter.v, mts->pos.y) - t->y;
                        t->y += dy;
                        t->flags &= (~EDGES | edge_flags(tp, t->x, t->y));
                        wstpad_set_direction(t, dx, dy, tp->ratio);
@@ -999,8 +987,8 @@ wstpad_touch_inputs(struct wsmouseinput *input)
        int slot;
 
        /* Use the unfiltered deltas. */
-       tp->dx = normalize_rel(&input->filter.h, raw_dx(input));
-       tp->dy = normalize_rel(&input->filter.v, raw_dy(input));
+       tp->dx = normalize_rel(&input->filter.h, input->motion.pos.dx);
+       tp->dy = normalize_rel(&input->filter.v, input->motion.pos.dy);
 
        tp->btns = input->btn.buttons;
        tp->btns_sync = input->btn.sync;
@@ -1024,8 +1012,8 @@ wstpad_touch_inputs(struct wsmouseinput *input)
                wstpad_mt_masks(input);
        } else {
                t = tp->t;
-               t->x = normalize_abs(&input->filter.h, input->motion.x);
-               t->y = normalize_abs(&input->filter.v, input->motion.y);
+               t->x = normalize_abs(&input->filter.h, input->motion.pos.x);
+               t->y = normalize_abs(&input->filter.v, input->motion.pos.y);
                if (tp->contacts)
                        t->state = (tp->prev_contacts ?
                            TOUCH_UPDATE : TOUCH_BEGIN);
@@ -1204,108 +1192,90 @@ wstpad_decelerate(struct wsmouseinput *input, int *dx, int *dy)
  * movements.  The "strong" variant applies independently to the axes,
  * and it is applied continuously.  It takes effect whenever the
  * orientation on an axis changes, which makes pointer paths more stable.
- * The "weak" variant is more precise and does not affect paths, it just
- * filters noise at the start- and stop-points of a movement.
+ *
+ * The default variant, wsmouse_hysteresis, is more precise and does not
+ * affect paths, it just filters noise when a touch starts or is resting.
  */
-void
-wstpad_strong_hysteresis(int *dx, int *dy,
-    int *h_acc, int *v_acc, int h_threshold, int v_threshold)
-{
-       *h_acc += *dx;
-       *v_acc += *dy;
-       if (*h_acc > h_threshold)
-               *dx = *h_acc - h_threshold;
-       else if (*h_acc < -h_threshold)
-               *dx = *h_acc + h_threshold;
-       else
-               *dx = 0;
-       *h_acc -= *dx;
-       if (*v_acc > v_threshold)
-               *dy = *v_acc - v_threshold;
-       else if (*v_acc < -v_threshold)
-               *dy = *v_acc + v_threshold;
-       else
-               *dy = 0;
-       *v_acc -= *dy;
-}
-
-void
-wstpad_weak_hysteresis(int *dx, int *dy,
-    int *h_acc, int *v_acc, int h_threshold, int v_threshold)
+static inline void
+strong_hysteresis(int *delta, int *acc, int threshold)
 {
-       *h_acc += *dx;
-       *v_acc += *dy;
-       if ((*dx > 0 && *h_acc < *dx) || (*dx < 0 && *h_acc > *dx))
-               *h_acc = *dx;
-       if ((*dy > 0 && *v_acc < *dy) || (*dy < 0 && *v_acc > *dy))
-               *v_acc = *dy;
-       if (abs(*h_acc) < h_threshold && abs(*v_acc) < v_threshold)
-               *dx = *dy = 0;
+       int d;
+
+       if (*delta > 0) {
+               if (*delta > *acc)
+                       *acc = *delta;
+               if ((d = *acc - threshold) < *delta)
+                       *delta = (d < 0 ? 0 : d);
+       } else if (*delta < 0) {
+               if (*delta < *acc)
+                       *acc = *delta;
+               if ((d = *acc + threshold) > *delta)
+                       *delta = (d > 0 ? 0 : d);
+       }
 }
 
 void
-wstpad_filter(struct wsmouseinput *input, int *dx, int *dy)
+wstpad_filter(struct wsmouseinput *input)
 {
        struct axis_filter *h = &input->filter.h;
        struct axis_filter *v = &input->filter.v;
+       struct position *pos = &input->motion.pos;
        int strength = input->filter.mode & 7;
+       int dx, dy;
 
-       if ((h->dmax && (abs(*dx) > h->dmax))
-           || (v->dmax && (abs(*dy) > v->dmax)))
-               *dx = *dy = 0;
+       if (!(input->motion.sync & SYNC_POSITION)
+           || (h->dmax && (abs(pos->dx) > h->dmax))
+           || (v->dmax && (abs(pos->dy) > v->dmax)))
+               pos->dx = pos->dy = 0;
 
-       if (h->hysteresis || v->hysteresis) {
-               if (input->filter.mode & STRONG_HYSTERESIS)
-                       wstpad_strong_hysteresis(dx, dy, &h->acc,
-                           &v->acc, h->hysteresis, v->hysteresis);
-               else
-                       wstpad_weak_hysteresis(dx, dy, &h->acc,
-                           &v->acc, h->hysteresis, v->hysteresis);
+       dx = pos->dx;
+       dy = pos->dy;
+
+       if (input->filter.mode & STRONG_HYSTERESIS) {
+               strong_hysteresis(&dx, &pos->acc_dx, h->hysteresis);
+               strong_hysteresis(&dy, &pos->acc_dy, v->hysteresis);
+       } else if (wsmouse_hysteresis(input, pos)) {
+               dx = dy = 0;
        }
 
-       if (input->filter.dclr && wstpad_decelerate(input, dx, dy))
+       if (input->filter.dclr && wstpad_decelerate(input, &dx, &dy))
                /* Strong smoothing may hamper the precision at low speeds. */
                strength = imin(strength, 2);
 
        if (strength) {
+               if ((input->touch.sync & SYNC_CONTACTS)
+                   || input->mt.ptr != input->mt.prev_ptr) {
+                       h->avg = v->avg = 0;
+               }
                /* Use a weighted decaying average for smoothing. */
-               *dx = *dx * (8 - strength) + h->avg * strength + h->avg_rmdr;
-               *dy = *dy * (8 - strength) + v->avg * strength + v->avg_rmdr;
-               h->avg_rmdr = (*dx >= 0 ? *dx & 7 : -(-*dx & 7));
-               v->avg_rmdr = (*dy >= 0 ? *dy & 7 : -(-*dy & 7));
-               *dx = h->avg = *dx / 8;
-               *dy = v->avg = *dy / 8;
+               dx = dx * (8 - strength) + h->avg * strength + h->avg_rmdr;
+               dy = dy * (8 - strength) + v->avg * strength + v->avg_rmdr;
+               h->avg_rmdr = (dx >= 0 ? dx & 7 : -(-dx & 7));
+               v->avg_rmdr = (dy >= 0 ? dy & 7 : -(-dy & 7));
+               dx = h->avg = dx / 8;
+               dy = v->avg = dy / 8;
        }
+
+       input->motion.dx = dx;
+       input->motion.dy = dy;
 }
 
 
 /*
- * Compatibility-mode conversions. This function transforms and filters
+ * Compatibility-mode conversions. wstpad_filter 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)
 {
-       int dx, dy;
-
        if (input->flags & TRACK_INTERVAL)
                wstpad_track_interval(input, &evq->ts);
 
-       dx = raw_dx(input);
-       dy = raw_dy(input);
-
-       if ((input->touch.sync & SYNC_CONTACTS)
-           || input->mt.ptr != input->mt.prev_ptr) {
-               input->filter.h.acc = input->filter.v.acc = 0;
-               input->filter.h.avg = input->filter.v.avg = 0;
-       }
+       wstpad_filter(input);
 
-       wstpad_filter(input, &dx, &dy);
-
-       input->motion.dx = dx;
-       input->motion.dy = dy;
-       if ((dx || dy) && !(input->motion.sync & SYNC_DELTAS)) {
+       if ((input->motion.dx || input->motion.dy)
+           && !(input->motion.sync & SYNC_DELTAS)) {
                input->motion.dz = input->motion.dw = 0;
                input->motion.sync |= SYNC_DELTAS;
        }
@@ -1484,13 +1454,13 @@ wstpad_configure(struct wsmouseinput *input)
        tp->freeze = EDGES;
 
        offset = width * tp->params.left_edge / 4096;
-       tp->edge.left = input->hw.x_min + offset;
+       tp->edge.left = (offset ? input->hw.x_min + offset : INT_MIN);
        offset = width * tp->params.right_edge / 4096;
-       tp->edge.right = input->hw.x_max - offset;
+       tp->edge.right = (offset ? input->hw.x_max - offset : INT_MAX);
        offset = height * tp->params.bottom_edge / 4096;
-       tp->edge.bottom = input->hw.y_min + offset;
+       tp->edge.bottom = (offset ? input->hw.y_min + offset : INT_MIN);
        offset = height * tp->params.top_edge / 4096;
-       tp->edge.top = input->hw.y_max - offset;
+       tp->edge.top = (offset ? input->hw.y_max - offset : INT_MAX);
 
        offset = width * abs(tp->params.center_width) / 8192;
        tp->edge.center = (input->hw.x_min + input->hw.x_max) / 2;