[PATCH synaptics v2 16/21] Add four new motion filters

Daniel Stone daniel at fooishbar.org
Tue Jun 14 10:06:04 PDT 2011


From: Derek Foreman <derek.foreman at collabora.co.uk>

Attempt to decrease the possibility of errant motion as much as possible
by adding three new configurable filters, disabled by default:
    - Synaptics Max Jerk: maximum change in acceleration before the
      packet is declared errant (xorg.conf MaxJerk)
    - Synaptics Max Accel: maximum acceleration the pointer can have
      before being declared errant (xorg.conf MaxAccel)
    - Synaptics Max Error: maximum error from a best-fit prediction of
      the next position before being declared errant (xorg.conf MaxError)
    - Synaptics Max Distance: maximum distance a finger can move in a
      single report before being declared errant (xorg.conf MaxDistance)

Signed-off-by: Derek Foreman <derek.foreman at collabora.co.uk>
Reviewed-by: Daniel Stone <daniel at fooishbar.org>
---
 include/synaptics-properties.h |   12 +++++++
 man/synaptics.man              |   23 +++++++++++++
 src/properties.c               |   46 +++++++++++++++++++++++++-
 src/synaptics.c                |   70 ++++++++++++++++++++++++++++++++++++++++
 src/synapticsstr.h             |    6 +++
 5 files changed, 156 insertions(+), 1 deletions(-)

v2: Added much more extensive commentary, fixed style for max_distance test,
    replaced all mentions of pointer acceleration with finger acceleration.

diff --git a/include/synaptics-properties.h b/include/synaptics-properties.h
index 77a1806..3e54fbb 100644
--- a/include/synaptics-properties.h
+++ b/include/synaptics-properties.h
@@ -158,4 +158,16 @@
 /* 32 Bit Integer, 2 values, horizontal hysteresis, vertical hysteresis */
 #define SYNAPTICS_PROP_NOISE_CANCELLATION "Synaptics Noise Cancellation"
 
+/* FLOAT */
+#define SYNAPTICS_PROP_MAX_DISTANCE "Synaptics Max Distance"
+
+/* FLOAT */
+#define SYNAPTICS_PROP_MAX_JERK "Synaptics Max Jerk"
+
+/* FLOAT */
+#define SYNAPTICS_PROP_MAX_ACCEL "Synaptics Max Accel"
+
+/* FLOAT */
+#define SYNAPTICS_PROP_MAX_ERROR "Synaptics Max Error"
+
 #endif /* _SYNAPTICS_PROPERTIES_H_ */
diff --git a/man/synaptics.man b/man/synaptics.man
index cb5f4c6..5b365e6 100644
--- a/man/synaptics.man
+++ b/man/synaptics.man
@@ -510,6 +510,29 @@ AreaBottomEdge option to any integer value other than zero. If supported by the
 server (version 1.9 and later), the edge may be specified in percent of
 the total height of the touchpad. Property: "Synaptics Area"
 .
+.TP
+.BI "Option \*qMaxAccel\*q \*q" integer \*q
+If acceleration increases beyond the specified value, ignore the packet that
+provoked it, assuming that it is erroneous. Property: "Synaptics Max Accel"
+.
+.TP
+.BI "Option \*qMaxDistance\*q \*q" integer \*q
+Ignore any single packet containing movement greater than the specified value
+in pixels, assuming that it is erroneous. Property: "Synaptics Max Distance"
+.
+.TP
+.BI "Option \*qMaxError\*q \*q" integer \*q
+Ignore any single packet containing movement where the error from a best-fit
+line determining the expected pointer position from historical values is larger
+than the specified value, assuming that it is erroneous.
+Property: "Synaptics Max Error"
+.
+.TP
+.BI "Option \*qMaxJerk\*q \*q" integer \*q
+If the derivative of acceleration increases beyond the specified value, ignore
+the packet that provoked it, assuming that it is erroneous.
+Property: "Synaptics Max Jerk"
+.
 
 .SH CONFIGURATION DETAILS
 .SS Area handling
diff --git a/src/properties.c b/src/properties.c
index 5f11cf2..d371c20 100644
--- a/src/properties.c
+++ b/src/properties.c
@@ -94,6 +94,10 @@ Atom prop_area                  = 0;
 Atom prop_noise_cancellation    = 0;
 Atom prop_product_id            = 0;
 Atom prop_device_node           = 0;
+Atom prop_max_distance          = 0;
+Atom prop_max_jerk              = 0;
+Atom prop_max_accel             = 0;
+Atom prop_max_error             = 0;
 
 static Atom
 InitTypedAtom(DeviceIntPtr dev, char *name, Atom type, int format, int nvalues,
@@ -319,6 +323,17 @@ InitDeviceProperties(InputInfoPtr pInfo)
         XISetDevicePropertyDeletable(pInfo->dev, prop_device_node, FALSE);
     }
 
+    fvalues[0] = para->max_distance;
+    prop_max_distance = InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_MAX_DISTANCE, 1, fvalues);
+
+    fvalues[0] = para->max_jerk;
+    prop_max_jerk = InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_MAX_JERK, 1, fvalues);
+
+    fvalues[0] = para->max_accel;
+    prop_max_accel = InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_MAX_ACCEL, 1, fvalues);
+
+    fvalues[0] = para->max_error;
+    prop_max_error = InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_MAX_ERROR, 1, fvalues);
 }
 
 int
@@ -700,8 +715,37 @@ SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
             return BadValue;
         para->hyst_x = hyst[0];
         para->hyst_y = hyst[1];
-    } else if (property == prop_product_id || property == prop_device_node)
+    } else if (property == prop_product_id || property == prop_device_node) {
         return BadValue; /* read-only */
+    } else if (property == prop_max_distance) {
+        float max_distance;
+        if (prop->size != 1 || prop->format != 32 || prop->type != float_type)
+            return BadMatch;
+
+        max_distance = *(float*)prop->data;
+        para->max_distance = max_distance;
+    } else if (property == prop_max_jerk) {
+        float max_jerk;
+        if (prop->size != 1 || prop->format != 32 || prop->type != float_type)
+            return BadMatch;
+
+        max_jerk = *(float*)prop->data;
+        para->max_jerk = max_jerk;
+    } else if (property == prop_max_accel) {
+        float max_accel;
+        if (prop->size != 1 || prop->format != 32 || prop->type != float_type)
+            return BadMatch;
+
+        max_accel = *(float*)prop->data;
+        para->max_accel = max_accel;
+    } else if (property == prop_max_error) {
+        float max_error;
+        if (prop->size != 1 || prop->format != 32 || prop->type != float_type)
+            return BadMatch;
+
+        max_error = *(float*)prop->data;
+        para->max_error = max_error;
+    }
 
     return Success;
 }
diff --git a/src/synaptics.c b/src/synaptics.c
index a68a6fd..b6cf6e5 100644
--- a/src/synaptics.c
+++ b/src/synaptics.c
@@ -586,6 +586,11 @@ static void set_default_parameters(InputInfoPtr pInfo)
     pars->resolution_horiz = xf86SetIntOption(opts, "HorizResolution", horizResolution);
     pars->resolution_vert = xf86SetIntOption(opts, "VertResolution", vertResolution);
 
+    pars->max_jerk = xf86SetRealOption(opts, "MaxJerk", 0.0);
+    pars->max_distance = xf86SetRealOption(opts, "MaxDistance", 0.0);
+    pars->max_accel = xf86SetRealOption(opts, "MaxAccel", 0.0);
+    pars->max_error = xf86SetRealOption(opts, "MaxError", 0.0);
+
     /* Warn about (and fix) incorrectly configured TopEdge/BottomEdge parameters */
     if (pars->top_edge > pars->bottom_edge) {
 	int tmp = pars->top_edge;
@@ -1806,6 +1811,20 @@ get_edge_speed(SynapticsPrivate *priv, const struct SynapticsHwState *hw,
 /*
  * Fit a line through the three most recent points in the motion
  * history and return relative co-ordinates.
+ *
+ * Four forms of filtering are present:
+ *      Acceleration - finger accelerating too fast
+ *      Jerk - too great a change in finger acceleration
+ *      Error - finger position deviates too much from predicted position
+ *      Distance - finger has moved too far in a single report
+ *
+ * If any of these filters are active and are tripped, the motion will
+ * be declared erroneous and discarded, with the motion history being
+ * zapped on the assumption that it is now useless for predicting further
+ * motion.
+ *
+ * Note - The current state is used for filtering, but only its time is used
+ *        in the delta calculation.
  */
 static void regress(SynapticsPrivate *priv, const struct SynapticsHwState *hw,
                     double *dx, double *dy, unsigned long start_time)
@@ -1816,6 +1835,43 @@ static void regress(SynapticsPrivate *priv, const struct SynapticsHwState *hw,
     double yb1n = 0, xb1n = 0, b1d = 0, xb1, yb1;
     double dista, distb, distc, vela, velb, velc, acca, accb, jerk;
 
+    /* Check if the position has jumped too far for a single report. */
+    if (pars->max_distance > 0 &&
+        hypot(hw->x - HIST(0).x, hw->y - HIST(0).y) > pars->max_distance)
+        goto filtered;
+
+    if (pars->max_accel > 0 || pars->max_jerk > 0) {
+        /* We need at least three packets to filter on, else we can't
+         * determine the acceleration, let alone the change in acceleration.
+         * Discard the motion packets, but we don't want to zap the motion
+         * history, else we'd never leave this state. */
+        if (priv->count_packet_finger < 3) {
+            *dx = 0;
+            *dy = 0;
+            return;
+        }
+
+        /* Determine distance and acceleration, and see if acceleration is
+         * higher than MaxAccel. */
+        dista = hypot(HIST(0).x - hw->x, HIST(0).y - hw->y);
+        distb = hypot(HIST_DELTA(1, 0, x), HIST_DELTA(1, 0, y));
+        distc = hypot(HIST_DELTA(2, 1, x), HIST_DELTA(2, 1, y));
+        vela = dista / (HIST(0).millis - hw->millis);
+        velb = distb / HIST_DELTA(1, 0, millis);
+        velc = distc / HIST_DELTA(2, 1, millis);
+        acca = (vela - velb) / (HIST(1).millis - hw->millis);
+        if (pars->max_accel > 0 && fabs(acca) > pars->max_accel)
+            goto filtered;
+
+        /* Determine change in acceleration, and see if it's higher than
+         * MaxJerk. */
+        accb = (velc - velb) / HIST_DELTA(2, 0, millis);
+        jerk = (acca - accb) / (HIST(2).millis - hw->millis);
+        if (pars->max_jerk > 0 && fabs(jerk) > pars->max_jerk)
+            goto filtered;
+    }
+
+    /* If there's only one packet, we can't really draw a best-fit line. */
     if (priv->count_packet_finger == 1) {
 	*dx = hw->x - HIST(0).x;
 	*dy = hw->y - HIST(0).y;
@@ -1841,6 +1897,20 @@ static void regress(SynapticsPrivate *priv, const struct SynapticsHwState *hw,
     xb1 = xb1n/b1d;
     yb1 = yb1n/b1d;
 
+    /* Check if the new position falls too far outside our best-fit line, and
+     * discard it if so. */
+    if (pars->max_error > 0) {
+        double X, Y, e, xb0, yb0;
+
+        xb0 = xm - xb1*tm;
+        yb0 = ym - yb1*tm;
+        X = xb1 * (hw->millis - HIST(0).millis) + xb0;
+        Y = yb1 * (hw->millis - HIST(0).millis) + yb0;
+        e = hypot(hw->x - X, hw->y - Y);
+        if (e > pars->max_error)
+            goto filtered;
+    }
+
     *dx = -xb1 * (start_time - hw->millis);
     *dy = -yb1 * (start_time - hw->millis);
     return;
diff --git a/src/synapticsstr.h b/src/synapticsstr.h
index cdb6d99..23b854f 100644
--- a/src/synapticsstr.h
+++ b/src/synapticsstr.h
@@ -163,6 +163,12 @@ typedef struct _SynapticsParameters
     Bool tap_and_drag_gesture;		    /* Switches the tap-and-drag gesture on/off */
     unsigned int resolution_horiz;          /* horizontal resolution of touchpad in units/mm */
     unsigned int resolution_vert;           /* vertical resolution of touchpad in units/mm */
+
+    double max_distance;                    /* maximum distance in a single report for valid input */
+    double max_jerk;                        /* maximum jerk for valid input */
+    double max_accel;                       /* maximum acceleration for valid input */
+    double max_error;                       /* maximum deviation from estimated position for valid input */
+
     int area_left_edge, area_right_edge, area_top_edge, area_bottom_edge; /* area coordinates absolute */
     int hyst_x, hyst_y;                     /* x and y width of hysteresis box */
 } SynapticsParameters;
-- 
1.7.5.3



More information about the xorg-devel mailing list