mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-02-04 09:20:29 +01:00
Merge branch 'wip/ignore-moved-touches'
This commit is contained in:
commit
4ed0f7ee0e
5 changed files with 238 additions and 15 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue