Commit 4ed0f7ee authored by Peter Hutterer's avatar Peter Hutterer

Merge branch 'wip/ignore-moved-touches'

parents 3d30015d de5246da
......@@ -384,7 +384,8 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
if (!t->dirty)
continue;
if (t->palm.state != PALM_NONE)
if (t->palm.state != PALM_NONE ||
t->thumb.state == THUMB_STATE_YES)
continue;
/* only scroll with the finger in the previous edge */
......
......@@ -35,8 +35,9 @@
#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 ms2us(500)
#define THUMB_MOVE_TIMEOUT ms2us(300)
#define FAKE_FINGER_OVERFLOW (1 << 7)
#define THUMB_IGNORE_SPEED_THRESHOLD 20 /* mm/s */
static inline struct device_coords *
static inline struct tp_history_point*
tp_motion_history_offset(struct tp_touch *t, int offset)
{
int offset_index =
......@@ -82,6 +83,41 @@ tp_filter_motion_unaccelerated(struct tp_dispatch *tp,
&raw, tp, time);
}
static inline void
tp_calculate_motion_speed(struct tp_dispatch *tp, struct tp_touch *t)
{
const struct tp_history_point *last;
struct device_coords delta;
struct phys_coords mm;
double distance;
double speed;
/* This doesn't kick in until we have at least 4 events in the
* motion history. As a side-effect, this automatically handles the
* 2fg scroll where a finger is down and moving fast before the
* other finger comes down for the scroll.
*
* We do *not* reset the speed to 0 here though. The motion history
* is reset whenever a new finger is down, so we'd be resetting the
* speed and failing.
*/
if (t->history.count < 4)
return;
/* TODO: we probably need a speed history here so we can average
* across a few events */
last = tp_motion_history_offset(t, 1);
delta.x = abs(t->point.x - last->point.x);
delta.y = abs(t->point.y - last->point.y);
mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
distance = length_in_mm(mm);
speed = distance/(t->time - last->time); /* mm/us */
speed *= 1000000; /* mm/s */
t->speed.last_speed = speed;
}
static inline void
tp_motion_history_push(struct tp_touch *t)
{
......@@ -90,7 +126,8 @@ tp_motion_history_push(struct tp_touch *t)
if (t->history.count < TOUCHPAD_HISTORY_LENGTH)
t->history.count++;
t->history.samples[motion_index] = t->point;
t->history.samples[motion_index].point = t->point;
t->history.samples[motion_index].time = t->time;
t->history.index = motion_index;
}
......@@ -218,6 +255,8 @@ tp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
t->state = TOUCH_HOVERING;
t->pinned.is_pinned = false;
t->time = time;
t->speed.last_speed = 0;
t->speed.exceeded_count = 0;
tp->queued |= TOUCHPAD_EVENT_MOTION;
}
......@@ -293,10 +332,10 @@ tp_get_delta(struct tp_touch *t)
if (t->history.count <= 1)
return zero;
delta.x = tp_motion_history_offset(t, 0)->x -
tp_motion_history_offset(t, 1)->x;
delta.y = tp_motion_history_offset(t, 0)->y -
tp_motion_history_offset(t, 1)->y;
delta.x = tp_motion_history_offset(t, 0)->point.x -
tp_motion_history_offset(t, 1)->point.x;
delta.y = tp_motion_history_offset(t, 0)->point.y -
tp_motion_history_offset(t, 1)->point.y;
return tp_normalize_delta(t->tp, delta);
}
......@@ -1252,9 +1291,10 @@ tp_need_motion_history_reset(struct tp_dispatch *tp)
static bool
tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t)
{
struct device_coords *last, delta;
struct device_coords delta;
struct phys_coords mm;
const int JUMP_THRESHOLD_MM = 20;
struct tp_history_point *last;
/* We haven't seen pointer jumps on Wacom tablets yet, so exclude
* those.
......@@ -1268,19 +1308,62 @@ tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t)
/* called before tp_motion_history_push, so offset 0 is the most
* recent coordinate */
last = tp_motion_history_offset(t, 0);
delta.x = abs(t->point.x - last->x);
delta.y = abs(t->point.y - last->y);
delta.x = abs(t->point.x - last->point.x);
delta.y = abs(t->point.y - last->point.y);
mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
return hypot(mm.x, mm.y) > JUMP_THRESHOLD_MM;
}
static void
tp_detect_thumb_while_moving(struct tp_dispatch *tp)
{
struct tp_touch *t;
struct tp_touch *first = NULL,
*second = NULL;
struct device_coords distance;
struct phys_coords mm;
tp_for_each_touch(tp, t) {
if (t->state != TOUCH_BEGIN)
first = t;
else
second = t;
if (first && second)
break;
}
assert(first);
assert(second);
if (tp->scroll.method == LIBINPUT_CONFIG_SCROLL_2FG) {
/* If the second finger comes down next to the other one, we
* assume this is a scroll motion.
*/
distance.x = abs(first->point.x - second->point.x);
distance.y = abs(first->point.y - second->point.y);
mm = evdev_device_unit_delta_to_mm(tp->device, &distance);
if (mm.x <= 25 && mm.y <= 15)
return;
}
/* Finger are too far apart or 2fg scrolling is disabled, mark
* second finger as thumb */
evdev_log_debug(tp->device,
"touch is speed-based thumb\n");
second->thumb.state = THUMB_STATE_YES;
}
static void
tp_process_state(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
bool restart_filter = false;
bool want_motion_reset;
bool have_new_touch = false;
unsigned int speed_exceeded_count = 0;
tp_process_fake_touches(tp, time);
tp_unhover_touches(tp, time);
......@@ -1297,8 +1380,15 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
t->quirks.reset_motion_history = false;
}
if (!t->dirty)
if (!t->dirty) {
/* A non-dirty touch must be below the speed limit */
if (t->speed.exceeded_count > 0)
t->speed.exceeded_count--;
speed_exceeded_count = max(speed_exceeded_count,
t->speed.exceeded_count);
continue;
}
if (tp_detect_jumps(tp, t)) {
if (!tp->semi_mt)
......@@ -1315,12 +1405,45 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
tp_motion_hysteresis(tp, t);
tp_motion_history_push(t);
/* Touch speed handling: if we'are above the threshold,
* count each event that we're over the threshold up to 10
* events. Count down when we are below the speed.
*
* Take the touch with the highest speed excess, if it is
* above a certain threshold (5, see below), assume a
* dropped finger is a thumb.
*
* Yes, this relies on the touchpad to keep sending us
* events even if the finger doesn't move, otherwise we
* never count down. Let's see how far we get with that.
*/
if (t->speed.last_speed > THUMB_IGNORE_SPEED_THRESHOLD) {
if (t->speed.exceeded_count < 10)
t->speed.exceeded_count++;
} else if (t->speed.exceeded_count > 0) {
t->speed.exceeded_count--;
}
speed_exceeded_count = max(speed_exceeded_count,
t->speed.exceeded_count);
tp_calculate_motion_speed(tp, t);
tp_unpin_finger(tp, t);
if (t->state == TOUCH_BEGIN)
if (t->state == TOUCH_BEGIN) {
have_new_touch = true;
restart_filter = true;
}
}
/* If we have one touch that exceeds the speed and we get a new
* touch down while doing that, the second touch is a thumb */
if (have_new_touch &&
tp->nfingers_down == 2 &&
speed_exceeded_count > 5)
tp_detect_thumb_while_moving(tp);
if (restart_filter)
filter_restart(tp->device->pointer.filter, tp, time);
......
......@@ -164,7 +164,10 @@ struct tp_touch {
} quirks;
struct {
struct device_coords samples[TOUCHPAD_HISTORY_LENGTH];
struct tp_history_point {
uint64_t time;
struct device_coords point;
} samples[TOUCHPAD_HISTORY_LENGTH];
unsigned int index;
unsigned int count;
} history;
......@@ -217,6 +220,11 @@ struct tp_touch {
uint64_t first_touch_time;
struct device_coords initial;
} thumb;
struct {
double last_speed; /* speed in mm/s at last sample */
unsigned int exceeded_count;
} speed;
};
struct tp_dispatch {
......
......@@ -859,6 +859,15 @@ litest_enable_edge_scroll(struct litest_device *dev)
litest_assert_int_eq(status, expected);
}
static inline bool
litest_has_clickfinger(struct litest_device *dev)
{
struct libinput_device *device = dev->libinput_device;
uint32_t methods = libinput_device_config_click_get_methods(device);
return methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
}
static inline void
litest_enable_clickfinger(struct litest_device *dev)
{
......
......@@ -1428,8 +1428,8 @@ START_TEST(touchpad_no_palm_detect_2fg_scroll)
/* first finger is palm, second finger isn't so we trigger 2fg
* scrolling */
litest_touch_down(dev, 0, 99, 50);
litest_touch_move_to(dev, 0, 99, 50, 99, 40, 10, 0);
litest_touch_move_to(dev, 0, 99, 40, 99, 50, 10, 0);
litest_touch_move_to(dev, 0, 99, 50, 99, 40, 35, 12);
litest_touch_move_to(dev, 0, 99, 40, 99, 50, 35, 12);
litest_assert_empty_queue(li);
litest_touch_down(dev, 1, 50, 50);
litest_assert_empty_queue(li);
......@@ -5411,6 +5411,84 @@ START_TEST(touchpad_palm_detect_touch_size)
}
END_TEST
START_TEST(touchpad_speed_ignore_finger)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
if (litest_has_clickfinger(dev))
litest_enable_clickfinger(dev);
litest_drain_events(li);
litest_touch_down(dev, 0, 20, 20);
litest_touch_move_to(dev, 0, 20, 20, 85, 80, 20, 0);
litest_touch_down(dev, 1, 20, 80);
litest_touch_move_two_touches(dev, 85, 80, 20, 80, -20, -20, 10, 0);
libinput_dispatch(li);
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
}
END_TEST
START_TEST(touchpad_speed_allow_nearby_finger)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
if (!litest_has_2fg_scroll(dev))
return;
if (litest_has_clickfinger(dev))
litest_enable_clickfinger(dev);
litest_enable_2fg_scroll(dev);
litest_drain_events(li);
litest_touch_down(dev, 0, 20, 20);
litest_touch_move_to(dev, 0, 20, 20, 80, 80, 20, 0);
litest_drain_events(li);
litest_touch_down(dev, 1, 79, 80);
litest_touch_move_two_touches(dev, 80, 80, 79, 80, -20, -20, 10, 0);
libinput_dispatch(li);
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
}
END_TEST
START_TEST(touchpad_speed_ignore_finger_edgescroll)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
litest_enable_edge_scroll(dev);
if (litest_has_clickfinger(dev))
litest_enable_clickfinger(dev);
litest_drain_events(li);
litest_touch_down(dev, 0, 20, 20);
litest_touch_move_to(dev, 0, 20, 20, 60, 80, 20, 0);
litest_drain_events(li);
litest_touch_down(dev, 1, 59, 80);
litest_touch_move_two_touches(dev, 60, 80, 59, 80, -20, -20, 10, 0);
libinput_dispatch(li);
litest_touch_up(dev, 0);
libinput_dispatch(li);
litest_touch_up(dev, 1);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
}
END_TEST
void
litest_setup_tests_touchpad(void)
{
......@@ -5576,4 +5654,8 @@ litest_setup_tests_touchpad(void)
litest_add("touchpad:touch-size", touchpad_touch_size, LITEST_APPLE_CLICKPAD, LITEST_ANY);
litest_add("touchpad:touch-size", touchpad_touch_size_2fg, LITEST_APPLE_CLICKPAD, LITEST_ANY);
litest_add("touchpad:speed", touchpad_speed_ignore_finger, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
litest_add("touchpad:speed", touchpad_speed_allow_nearby_finger, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
litest_add("touchpad:speed", touchpad_speed_ignore_finger_edgescroll, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment