From 596a90aaa46c2d452429d07a101248e3efa83efb Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 1 Feb 2017 12:24:37 +1000 Subject: [PATCH libinput] evdev: change hysteresis to work across both axes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous approach causes axis snapping, preventing things like drawing a circle, see e.g. https://bugs.freedesktop.org/show_bug.cgi?id=99410 Switch to a different approach that at least looks at both axes at the same time. If one axis is outside of the margin take the other axis as-is and don't apply hysteresis to it. This prevents axis snapping but exposes another bug: there is a 'margin'-sized zone where we can get cursor jumps. Assume a vector of x/y with x larger than the margin, the value y can take: * y for [0, margin) * y-margin for [margin, ∞) Thus y increases towards margin, then jumps back to 0 when it hits margin. Then in increases linearly from there on. The only way around this is calculating the exact vector, but that requires a lot of expensive calculations. It's unclear whether this change is perceptible as the actual movement is miniscule and the upside is that we don't have axis snapping anymore, so there's a small win. You're allowed a very small bottle of champagne. Don't pop it too loud. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 19 +++++++------------ src/evdev.c | 11 ++++------- src/evdev.h | 50 ++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index e43a9ba..cc5049d 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -98,22 +98,17 @@ static inline void tp_motion_hysteresis(struct tp_dispatch *tp, struct tp_touch *t) { - int x = t->point.x, - y = t->point.y; + struct device_coords p; if (t->history.count == 0) { t->hysteresis_center = t->point; } else { - x = evdev_hysteresis(x, - t->hysteresis_center.x, - tp->hysteresis_margin.x); - y = evdev_hysteresis(y, - t->hysteresis_center.y, - tp->hysteresis_margin.y); - t->hysteresis_center.x = x; - t->hysteresis_center.y = y; - t->point.x = x; - t->point.y = y; + + p = evdev_hysteresis(t->point, + t->hysteresis_center, + tp->hysteresis_margin); + t->hysteresis_center = p; + t->point = p; } } diff --git a/src/evdev.c b/src/evdev.c index d086c18..d0d9b4e 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -381,14 +381,11 @@ fallback_filter_defuzz_touch(struct fallback_dispatch *dispatch, if (!dispatch->mt.want_hysteresis) return false; - point.x = evdev_hysteresis(slot->point.x, - slot->hysteresis_center.x, - dispatch->mt.hysteresis_margin.x); - point.y = evdev_hysteresis(slot->point.y, - slot->hysteresis_center.y, - dispatch->mt.hysteresis_margin.y); + point = evdev_hysteresis(slot->point, + slot->hysteresis_center, + dispatch->mt.hysteresis_margin); - slot->hysteresis_center = slot->point; + slot->hysteresis_center = point; if (point.x == slot->point.x && point.y == slot->point.y) return true; diff --git a/src/evdev.h b/src/evdev.h index 3fe5d3a..8a79941 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -567,10 +567,18 @@ evdev_to_left_handed(struct evdev_device *device, * Otherwise, the center has a dead zone of size margin around it and the * first reachable point is the margin edge. * - * Hysteresis is handled separately per axis (and the window is thus - * rectangular, not circular). It is unkown if that's an issue, but the - * calculation to do circular hysteresis are nontrivial, especially since - * many touchpads have uneven x/y resolutions. + * If either x or y is outside the margin, then no hysteresis is applied for + * the other axis and that value is taken as is. Thus, the four return + * values for a vector (x, y) are: + * 1) (center.x, center.y) if x/y are within margin + * 2) (x - margin, y - margin) if x/y are outside margin + * 1) (x - margin, y) if x is outside margin, y inside + * 1) (x, y - margin) if y is outside margin, x inside + * + * This falsifies pointer motion slightly but the hysteresis is small enough + * to make this a nonissue. The calculations to do correct circular + * hysteresis are nontrivial, especially since many touchpads have uneven + * x/y resolutions. * * @param in The input coordinate * @param center Current center of the hysteresis @@ -578,17 +586,37 @@ evdev_to_left_handed(struct evdev_device *device, * * @return The new center of the hysteresis */ -static inline int -evdev_hysteresis(int in, int center, int margin) + +static inline struct device_coords +evdev_hysteresis(const struct device_coords point, + const struct device_coords center, + const struct device_coords margin) { - int diff = in - center; - if (abs(diff) <= margin) + struct device_coords diff; + struct device_coords p; + + diff.x = point.x - center.x; + diff.y = point.y - center.y; + + if (abs(diff.x) <= margin.x && + abs(diff.y) <= margin.y) return center; - if (diff > 0) - return in - margin; + if (diff.x > margin.x) + p.x = point.x - margin.x; + else if (diff.x < -margin.x) + p.x = point.x + margin.x; else - return in + margin; + p.x = point.x; + + if (diff.y > margin.y) + p.y = point.y - margin.y; + else if (diff.y < -margin.y) + p.y = point.y + margin.y; + else + p.y = point.y; + + return p; } static inline struct libinput * -- 2.9.3