diff options
author | Simon Thum <simon.thum@gmx.de> | 2011-02-06 17:57:17 +0100 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2011-02-21 11:46:37 +1000 |
commit | cc26edfba13216ceda02d9d352643535ba359e5e (patch) | |
tree | 81d6db9b78e2c1e05a04d722b23450aae5712e75 /src | |
parent | 5aaeea79eea98705fbbbea363a7ee4be1eeed827 (diff) |
Add hysteresis-based noise reduction
This introduces hysteresis into the driver's processing. It significantly
reduces noise motion, i.e. now the pad does no longer generate a stream of
sub-pixel events when just holding the position with the finger down.
Also, taking off the finger no longer generates additional motion,
scrolling becomes flicker-free etc.
The code makes use of "fuzz" from the kernel, if available. This has not
been tested extensively, as an overwhelming majority of evdev touchpad
drivers view 0 (zero) as a good value for fuzz, forcing userland into
assuming "zero fuzz" means "make zero assumptions about fuzz", not
"there is no fuzz". Until things improve, this is what we do.
Anyway, the fuzz a.k.a. hysteresis can be set/overridden with options
and properties, as documented.
Signed-off-by: Simon Thum <simon.thum@gmx.de>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to 'src')
-rw-r--r-- | src/eventcomm.c | 8 | ||||
-rw-r--r-- | src/properties.c | 17 | ||||
-rw-r--r-- | src/synaptics.c | 55 | ||||
-rw-r--r-- | src/synapticsstr.h | 3 |
4 files changed, 75 insertions, 8 deletions
diff --git a/src/eventcomm.c b/src/eventcomm.c index 4593bba..1a31c54 100644 --- a/src/eventcomm.c +++ b/src/eventcomm.c @@ -184,6 +184,11 @@ event_query_axis_ranges(InputInfoPtr pInfo) abs.minimum, abs.maximum); priv->minx = abs.minimum; priv->maxx = abs.maximum; + /* The kernel's fuzziness concept seems a bit weird, but it can more or + * less be applied as hysteresis directly, i.e. no factor here. Though, + * we don't trust a zero fuzz as it probably is just a lazy value. */ + if (abs.fuzz > 0) + priv->synpara.hyst_x = abs.fuzz; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30) priv->resx = abs.resolution; #endif @@ -198,6 +203,9 @@ event_query_axis_ranges(InputInfoPtr pInfo) abs.minimum, abs.maximum); priv->miny = abs.minimum; priv->maxy = abs.maximum; + /* don't trust a zero fuzz */ + if (abs.fuzz > 0) + priv->synpara.hyst_y = abs.fuzz; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30) priv->resy = abs.resolution; #endif diff --git a/src/properties.c b/src/properties.c index 5400928..23b5a6a 100644 --- a/src/properties.c +++ b/src/properties.c @@ -82,6 +82,7 @@ Atom prop_gestures = 0; Atom prop_capabilities = 0; Atom prop_resolution = 0; Atom prop_area = 0; +Atom prop_noise_cancellation = 0; static Atom InitAtom(DeviceIntPtr dev, char *name, int format, int nvalues, int *values) @@ -278,6 +279,12 @@ InitDeviceProperties(InputInfoPtr pInfo) values[2] = para->area_top_edge; values[3] = para->area_bottom_edge; prop_area = InitAtom(pInfo->dev, SYNAPTICS_PROP_AREA, 32, 4, values); + + values[0] = para->hyst_x; + values[1] = para->hyst_y; + prop_noise_cancellation = InitAtom(pInfo->dev, + SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 2, values); + } int @@ -649,6 +656,16 @@ SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, para->area_right_edge = area[1]; para->area_top_edge = area[2]; para->area_bottom_edge = area[3]; + } else if (property == prop_noise_cancellation) { + INT32 *hyst; + if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER) + return BadMatch; + + hyst = (INT32*)prop->data; + if (hyst[0] < 0 || hyst[1] < 0) + return BadValue; + para->hyst_x = hyst[0]; + para->hyst_y = hyst[1]; } return Success; diff --git a/src/synaptics.c b/src/synaptics.c index 03092f2..276c024 100644 --- a/src/synaptics.c +++ b/src/synaptics.c @@ -387,18 +387,19 @@ calculate_edge_widths(SynapticsPrivate *priv, int *l, int *r, int *t, int *b) * the log message. */ static int set_percent_option(pointer options, const char* optname, - const int range, const int offset) + const int range, const int offset, + const int default_value) { int result; #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11 - int percent = xf86CheckPercentOption(options, optname, -1); + double percent = xf86CheckPercentOption(options, optname, -1); - if (percent != -1) { + if (percent >= 0.0) { percent = xf86SetPercentOption(options, optname, -1); result = percent/100.0 * range + offset; } else #endif - result = xf86SetIntOption(options, optname, 0); + result = xf86SetIntOption(options, optname, default_value); return result; } @@ -427,6 +428,7 @@ static void set_default_parameters(InputInfoPtr pInfo) int horizResolution = 1; int vertResolution = 1; int width, height, diag, range; + int horizHyst, vertHyst; /* read the parameters */ if (priv->synshm) @@ -458,6 +460,10 @@ static void set_default_parameters(InputInfoPtr pInfo) edgeMotionMaxSpeed = diag * .080; accelFactor = 200.0 / diag; /* trial-and-error */ + /* hysteresis, assume >= 0 is a detected value (e.g. evdev fuzz) */ + horizHyst = pars->hyst_x >= 0 ? pars->hyst_x : diag * 0.005; + vertHyst = pars->hyst_y >= 0 ? pars->hyst_y : diag * 0.005; + range = priv->maxp - priv->minp; /* scaling based on defaults and a pressure of 256 */ @@ -513,10 +519,13 @@ static void set_default_parameters(InputInfoPtr pInfo) pars->top_edge = xf86SetIntOption(opts, "TopEdge", t); pars->bottom_edge = xf86SetIntOption(opts, "BottomEdge", b); - pars->area_top_edge = set_percent_option(opts, "AreaTopEdge", height, priv->miny); - pars->area_bottom_edge = set_percent_option(opts, "AreaBottomEdge", height, priv->miny); - pars->area_left_edge = set_percent_option(opts, "AreaLeftEdge", width, priv->minx); - pars->area_right_edge = set_percent_option(opts, "AreaRightEdge", width, priv->minx); + pars->area_top_edge = set_percent_option(opts, "AreaTopEdge", height, priv->miny, 0); + pars->area_bottom_edge = set_percent_option(opts, "AreaBottomEdge", height, priv->miny, 0); + pars->area_left_edge = set_percent_option(opts, "AreaLeftEdge", width, priv->minx, 0); + pars->area_right_edge = set_percent_option(opts, "AreaRightEdge", width, priv->minx, 0); + + pars->hyst_x = set_percent_option(opts, "HorizHysteresis", width, 0, horizHyst); + pars->hyst_y = set_percent_option(opts, "VertHysteresis", height, 0, vertHyst); pars->finger_low = xf86SetIntOption(opts, "FingerLow", fingerLow); pars->finger_high = xf86SetIntOption(opts, "FingerHigh", fingerHigh); @@ -722,6 +731,8 @@ SynapticsPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) priv->tap_button = 0; priv->tap_button_state = TBS_BUTTON_UP; priv->touch_on.millis = 0; + priv->synpara.hyst_x = -1; + priv->synpara.hyst_y = -1; /* read hardware dimensions */ ReadDevDimensions(pInfo); @@ -1713,6 +1724,26 @@ estimate_delta(double x0, double x1, double x2, double x3) return x0 * 0.3 + x1 * 0.1 - x2 * 0.1 - x3 * 0.3; } +/** + * Applies hysteresis. center is shifted such that it is in range with + * in by the margin again. The new center is returned. + * @param in the current value + * @param center the current center + * @param margin the margin to center in which no change is applied + * @return the new center (which might coincide with the previous) + */ +static int hysteresis(int in, int center, int margin) { + int diff = in - center; + if (abs(diff) <= margin) { + diff = 0; + } else if (diff > margin) { + diff -= margin; + } else if (diff < -margin) { + diff += margin; + } + return center + diff; +} + static int ComputeDeltas(SynapticsPrivate *priv, const struct SynapticsHwState *hw, edge_type edge, int *dxP, int *dyP, Bool inside_area) @@ -2364,6 +2395,14 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw) if (para->touchpad_off == 1) return delay; + /* apply hysteresis before doing anything serious. This cancels + * out a lot of noise which might surface in strange phenomena + * like flicker in scrolling or noise motion. */ + priv->hyst_center_x = hysteresis(hw->x, priv->hyst_center_x, para->hyst_x); + priv->hyst_center_y = hysteresis(hw->y, priv->hyst_center_y, para->hyst_y); + hw->x = priv->hyst_center_x; + hw->y = priv->hyst_center_y; + inside_active_area = is_inside_active_area(priv, hw->x, hw->y); /* now we know that these _coordinates_ aren't in the area. diff --git a/src/synapticsstr.h b/src/synapticsstr.h index 9ad8638..066b3f3 100644 --- a/src/synapticsstr.h +++ b/src/synapticsstr.h @@ -160,6 +160,7 @@ typedef struct _SynapticsParameters unsigned int resolution_horiz; /* horizontal resolution of touchpad in units/mm */ unsigned int resolution_vert; /* vertical resolution of touchpad in units/mm */ 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; @@ -183,6 +184,8 @@ typedef struct _SynapticsPrivateRec Bool absolute_events; /* post absolute motion events instead of relative */ SynapticsMoveHistRec move_hist[SYNAPTICS_MOVE_HISTORY]; /* movement history */ int hist_index; /* Last added entry in move_hist[] */ + int hyst_center_x; /* center x of hysteresis*/ + int hyst_center_y; /* center y of hysteresis*/ int scroll_y; /* last y-scroll position */ int scroll_x; /* last x-scroll position */ double scroll_a; /* last angle-scroll position */ |