diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 749f9413..a7dad940 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -131,6 +131,21 @@ tp_motion_history_push(struct tp_touch *t) t->history.index = motion_index; } +static inline void +tp_maybe_disable_hysteresis(struct tp_dispatch *tp, uint64_t time) +{ + /* If the finger is down for 80ms without seeing motion events, + the firmware filters and we don't need a software hysteresis */ + if (time - tp->hysteresis.last_motion_time > ms2us(80)) { + tp->hysteresis.enabled = false; + evdev_log_debug(tp->device, "hysteresis disabled\n"); + return; + } + + if (tp->queued & TOUCHPAD_EVENT_MOTION) + tp->hysteresis.last_motion_time = time; +} + static inline void tp_motion_hysteresis(struct tp_dispatch *tp, struct tp_touch *t) @@ -138,15 +153,18 @@ tp_motion_hysteresis(struct tp_dispatch *tp, int x = t->point.x, y = t->point.y; + if (!tp->hysteresis.enabled) + return; + if (t->history.count == 0) { t->hysteresis_center = t->point; } else { x = evdev_hysteresis(x, t->hysteresis_center.x, - tp->hysteresis_margin.x); + tp->hysteresis.margin.x); y = evdev_hysteresis(y, t->hysteresis_center.y, - tp->hysteresis_margin.y); + tp->hysteresis.margin.y); t->hysteresis_center.x = x; t->hysteresis_center.y = y; t->point.x = x; @@ -273,6 +291,7 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) t->thumb.first_touch_time = time; t->tap.is_thumb = false; assert(tp->nfingers_down >= 1); + tp->hysteresis.last_motion_time = time; } /** @@ -1526,6 +1545,9 @@ static void tp_handle_state(struct tp_dispatch *tp, uint64_t time) { + if (tp->hysteresis.enabled) + tp_maybe_disable_hysteresis(tp, time); + tp_process_state(tp, time); tp_post_events(tp, time); tp_post_process_state(tp, time); @@ -2912,8 +2934,9 @@ tp_init_hysteresis(struct tp_dispatch *tp) res_x = tp->device->abs.absinfo_x->resolution; res_y = tp->device->abs.absinfo_y->resolution; - tp->hysteresis_margin.x = res_x/2; - tp->hysteresis_margin.y = res_y/2; + tp->hysteresis.margin.x = res_x/2; + tp->hysteresis.margin.y = res_y/2; + tp->hysteresis.enabled = true; } static void diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 9459e76e..3da6c58e 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -269,7 +269,12 @@ struct tp_dispatch { double orientation_to_angle; } touch_size; - struct device_coords hysteresis_margin; + struct { + bool enabled; + struct device_coords margin; + unsigned int other_event_count; + uint64_t last_motion_time; + } hysteresis; struct { double x_scale_coeff; diff --git a/src/evdev.h b/src/evdev.h index 4c42f6eb..2c410b24 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -610,6 +610,19 @@ evdev_to_left_handed(struct evdev_device *device, * calculation to do circular hysteresis are nontrivial, especially since * many touchpads have uneven x/y resolutions. * + * Given coordinates, 0, 1, 2, ... this is what we return for a margin of 3 + * and a center of 0: + * + * Input: 1 2 3 4 5 6 5 4 3 2 1 0 -1 + * Coord: 0 0 0 1 2 3 3 3 3 3 3 3 2 + * Center: 0 0 0 1 2 3 3 3 3 3 3 3 2 + * + * Problem: viewed from a stationary finger that starts moving, the + * hysteresis margin is M in both directions. Once we start moving + * continuously though, the margin is 0 in the movement direction and 2*M to + * change direction. That makes the finger less responsive to directional + * changes than to the original movement. + * * @param in The input coordinate * @param center Current center of the hysteresis * @param margin Hysteresis width (on each side) diff --git a/test/litest.c b/test/litest.c index 0cd540e1..0784228b 100644 --- a/test/litest.c +++ b/test/litest.c @@ -3239,6 +3239,12 @@ litest_timeout_tablet_proxout(void) msleep(70); } +void +litest_timeout_hysteresis(void) +{ + msleep(90); +} + void litest_push_event_frame(struct litest_device *dev) { diff --git a/test/litest.h b/test/litest.h index 53d8153d..d6d5c82c 100644 --- a/test/litest.h +++ b/test/litest.h @@ -766,6 +766,9 @@ litest_timeout_trackpoint(void); void litest_timeout_tablet_proxout(void); +void +litest_timeout_hysteresis(void); + void litest_push_event_frame(struct litest_device *dev);