My last patch to wstpad has solved some problems by introducing two new
ones:  The identification of thumb contacts in the bottom area of a
touchpad may be too slow, and too strict under certain circumstances.

This patch extends the test for stable movement.  Touches moving at a
medium or high speed pass it immediately, there is no delay in this
case.  Both thumb detection and scrolling profit from this change.

Speed is estimated in the same way as for deceleration; it is not
possible, of course, to calculate averages here.

It is not unlikely that the center of pressure of a thumb contact
changes when a second touch starts a movement quickly; the requirement
that it is resting is too strict.  The patch relaxes that condition
(again).  However, it is important that the begin of a scroll gesture is
not misinterpreted.  The new version will mark a contact in the bottom
area as thumb if it is moving slowly while the other touch is moving
fast.

The "thumb" flag is cleared when a touch moves into the upper area of
the touchpad surface.  That's a mitigation for false positives; in my
tests they occured, very rarely, on an Elantech-v4 touchpad when I
started two-finger scrolling with some odd maneuvres. I couldn't
reproduce them on a MacBook.

OK?

Index: dev/wscons/wstpad.c
===================================================================
RCS file: /cvs/src/sys/dev/wscons/wstpad.c,v
retrieving revision 1.20
diff -u -p -r1.20 wstpad.c
--- dev/wscons/wstpad.c 5 Dec 2018 19:49:47 -0000       1.20
+++ dev/wscons/wstpad.c 15 Dec 2018 16:38:04 -0000
@@ -63,6 +63,9 @@
 #define MATCHINTERVAL_MS       45
 #define STOPINTERVAL_MS                55

+#define MAG_LOW                        (10 << 12)
+#define MAG_MEDIUM             (18 << 12)
+
 enum tpad_handlers {
        SOFTBUTTON_HDLR,
        TOPBUTTON_HDLR,
@@ -124,6 +127,7 @@ struct tpad_touch {
        int dir;
        struct timespec start;
        struct timespec match;
+       struct position *pos;
        struct {
                int x;
                int y;
@@ -184,7 +188,7 @@ struct wstpad {
                int center;
                int center_left;
                int center_right;
-               int middle;
+               int low;
        } edge;

        struct {
@@ -339,15 +343,47 @@ wstpad_set_direction(struct wstpad *tp,
        }
 }

+/*
+ * Make a very rough, but quick estimation of the speed of a touch.
+ * Its distance to its previous position is scaled by factors derived
+ * from the average update rate and the deceleration parameter
+ * (filter.dclr).  The unit of the result is:
+ *         (filter.dclr / 100) device units per millisecond
+ *
+ * Magnitudes are returned in [*.12] fixed-point format.  For purposes
+ * of filtering, they are divided into medium and high speeds
+ * (> MAG_MEDIUM), low speeds, and very low speeds (< MAG_LOW).
+ *
+ * The scale factors are not affected if deceleration is turned off.
+ */
+static inline int
+magnitude(struct wsmouseinput *input, int dx, int dy)
+{
+       int h, v;
+
+       h = abs(dx) * input->filter.h.mag_scale;
+       v = abs(dy) * input->filter.v.mag_scale;
+       /* Return an "alpha-max-plus-beta-min" approximation: */
+       return (h >= v ? h + 3 * v / 8 : v + 3 * h / 8);
+}
+
+/*
+ * Treat a touch as stable if it is moving at a medium or high speed,
+ * if it is moving continuously, or if it has stopped for a certain
+ * time span.
+ */
 int
-wstpad_is_stable(struct wstpad *tp, struct tpad_touch *t)
+wstpad_is_stable(struct wsmouseinput *input, struct tpad_touch *t)
 {
        struct timespec ts;

-       if (t->dir >= 0)
+       if (t->dir >= 0) {
+               if (magnitude(input, t->pos->dx, t->pos->dy) > MAG_MEDIUM)
+                       return (1);
                timespecsub(&t->match, &t->start, &ts);
-       else
-               timespecsub(&tp->time, &t->start, &ts);
+       } else {
+               timespecsub(&input->tp->time, &t->start, &ts);
+       }

        return (timespeccmp(&ts, &match_interval, >=));
 }
@@ -399,8 +435,10 @@ set_freeze_ts(struct wstpad *tp, int sec

 /* Return TRUE if two-finger- or edge-scrolling would be valid. */
 static inline int
-chk_scroll_state(struct wstpad *tp)
+chk_scroll_state(struct wsmouseinput *input)
 {
+       struct wstpad *tp = input->tp;
+
        if (tp->contacts != tp->prev_contacts || tp->btns || tp->btns_sync) {
                tp->scroll.dz = 0;
                tp->scroll.dw = 0;
@@ -412,10 +450,10 @@ chk_scroll_state(struct wstpad *tp)
         * a short delay, is only applied initially, a touch that stops and
         * resumes scrolling is not affected.
         */
-       if (!wstpad_is_stable(tp, tp->t) && !(tp->scroll.dz || tp->scroll.dw))
-               return (0);
+       if (tp->scroll.dz || tp->scroll.dw || wstpad_is_stable(input, tp->t))
+               return (tp->dx || tp->dy);

-       return (tp->dx || tp->dy);
+       return (0);
 }

 void
@@ -463,7 +501,7 @@ wstpad_f2scroll(struct wsmouseinput *inp
                return;
        }

-       if (!chk_scroll_state(tp))
+       if (!chk_scroll_state(input))
                return;

        dir = tp->t->dir;
@@ -481,7 +519,7 @@ wstpad_f2scroll(struct wsmouseinput *inp
                                return;
                        if ((dx > 0 && !EAST(dir)) || (dx < 0 && !WEST(dir)))
                                return;
-                       if (!wstpad_is_stable(tp, t2) &&
+                       if (!wstpad_is_stable(input, t2) &&
                            !(tp->scroll.dz || tp->scroll.dw))
                                return;
                        centered |= CENTERED(t2);
@@ -501,7 +539,7 @@ wstpad_edgescroll(struct wsmouseinput *i
        u_int v_edge, b_edge;
        int dx, dy;

-       if (tp->contacts != 1 || !chk_scroll_state(tp))
+       if (tp->contacts != 1 || !chk_scroll_state(input))
                return;

        v_edge = (tp->features & WSTPAD_SWAPSIDES) ? L_EDGE : R_EDGE;
@@ -944,6 +982,7 @@ wstpad_mt_inputs(struct wsmouseinput *in
                mts = &input->mt.slots[slot];
                t->x = normalize_abs(&input->filter.h, mts->pos.x);
                t->y = normalize_abs(&input->filter.v, mts->pos.y);
+               t->pos = &mts->pos;
                t->orig.x = t->x;
                t->orig.y = t->y;
                memcpy(&t->orig.time, &tp->time, sizeof(struct timespec));
@@ -992,13 +1031,26 @@ wstpad_mt_masks(struct wsmouseinput *inp
 {
        struct wstpad *tp = input->tp;
        struct tpad_touch *t;
+       struct position *pos;
        u_int mask;
        int slot;

        tp->ignore &= input->mt.touches;

-       if (tp->contacts < 2 || tp->ignore)
+       if (tp->contacts < 2)
+               return;
+
+       if (tp->ignore) {
+               slot = ffs(tp->ignore) - 1;
+               t = &tp->tpad_touches[slot];
+               /*
+                * By default, edge.low defines a somewhat larger area than
+                * the bottom area.  If t has left it, clear the 'ignore' bit.
+                */
+               if (t->y > tp->edge.low)
+                       tp->ignore = 0;
                return;
+       }

        /*
         * If there is exactly one touch in the bottom area, try to
@@ -1018,19 +1070,42 @@ wstpad_mt_masks(struct wsmouseinput *inp

        /*
         * If the pointer-controlling touch is moving stably while a masked
-        * touch is not, treat the latter as "thumb".  It will not block
-        * pointer movement, and wstpad_f2scroll will ignore it.
+        * touch is resting, or only moving minimally, treat the latter as
+        * "thumb".  It will not block pointer movement, and wstpad_f2scroll
+        * will ignore it.
         */
        if (tp->t->dir >= 0
-           && wstpad_is_stable(tp, tp->t)
-           && (input->mt.ptr_mask & ~input->mt.ptr)) {
+           && wstpad_is_stable(input, tp->t)
+           && (input->mt.ptr_mask & ~input->mt.ptr)
+           && !(tp->scroll.dz || tp->scroll.dw)) {

                slot = ffs(input->mt.ptr_mask) - 1;
                t = &tp->tpad_touches[slot];

-               if ((t->flags & B_EDGE)
-                   && t->dir < 0 && wstpad_is_stable(tp, t))
-                       tp->ignore = input->mt.ptr_mask;
+               if ((t->flags & B_EDGE) == 0)
+                       return;
+
+               /*
+                * Thumb detection should not hamper scrolling.
+                * No decision is possible if t is not stable.
+                */
+               if (!wstpad_is_stable(input, t))
+                       return;
+
+               /* Default hysteresis limits are low.  Make a strict check. */
+               pos = tp->t->pos;
+               if (abs(pos->acc_dx) < 3 * input->filter.h.hysteresis
+                   && abs(pos->acc_dy) < 3 * input->filter.v.hysteresis)
+                       return;
+
+               if (t->dir >= 0) {
+                       /* Mark t as thumb if it is slow while tp->t is fast. */
+                       if (magnitude(input, t->pos->dx, t->pos->dy) > MAG_LOW
+                           || magnitude(input, pos->dx, pos->dy) < MAG_MEDIUM)
+                               return;
+               }
+
+               tp->ignore = input->mt.ptr_mask;
        }
 }

@@ -1077,6 +1152,7 @@ wstpad_touch_inputs(struct wsmouseinput
                t = tp->t;
                t->x = normalize_abs(&input->filter.h, input->motion.pos.x);
                t->y = normalize_abs(&input->filter.v, input->motion.pos.y);
+               t->pos = &input->motion.pos;
                if (tp->contacts)
                        t->state = (tp->prev_contacts ?
                            TOUCH_UPDATE : TOUCH_BEGIN);
@@ -1215,9 +1291,8 @@ wstpad_track_interval(struct wsmouseinpu
  * 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,
+ * filter applies a simple deceleration scheme to small deltas, based
+ * on the "magnitude" of the delta pair. A magnitude 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
@@ -1226,10 +1301,9 @@ wstpad_track_interval(struct wsmouseinpu
 int
 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;
+       int mag, n, h, v;
+
+       mag = magnitude(input, *dx, *dy);

        /* Don't change deceleration levels abruptly. */
        mag = (mag + 7 * input->filter.mag) / 8;
@@ -1261,11 +1335,12 @@ wstpad_filter(struct wsmouseinput *input

        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;
-
-       dx = pos->dx;
-       dy = pos->dy;
+           || (v->dmax && (abs(pos->dy) > v->dmax))) {
+               dx = dy = 0;
+       } else {
+               dx = pos->dx;
+               dy = pos->dy;
+       }

        if (wsmouse_hysteresis(input, pos))
                dx = dy = 0;
@@ -1494,11 +1569,12 @@ wstpad_configure(struct wsmouseinput *in
        offset = height * tp->params.top_edge / 4096;
        tp->edge.top = (offset ? input->hw.y_max - offset : INT_MAX);

+       tp->edge.low = imax(tp->edge.bottom, input->hw.y_min + height / 4);
+
        offset = width * abs(tp->params.center_width) / 8192;
-       tp->edge.center = (input->hw.x_min + input->hw.x_max) / 2;
+       tp->edge.center = input->hw.x_min + width / 2;
        tp->edge.center_left = tp->edge.center - offset;
        tp->edge.center_right = tp->edge.center + offset;
-       tp->edge.middle = (input->hw.y_max - input->hw.y_min) / 2;

        tp->handlers = 0;

Reply via email to