touchpad: avoid motion events when moving one finger into AREA

If a 2fg scroll motion starts with both fingers in the bottom button area and
one finger moves into the main area before the other, we used to send motion
events for that finger. Once the second finger moved into the main area the
scroll was detected correctly but by then the cursor may have moved out of the
intended focus area.

We have two transitions where we may start sending motion events: when we move
out of the bottom area and when the finger moves by more than 5mm within the
button area. In both cases, check for any touches that are in the
bottom area and started at the 'same' time as our moving touch. Mark those as
'moved' to release them for gestures so we get the right finger count and
axis/gesture events instead of just motion events.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2018-10-04 08:34:33 +10:00
parent 60d9defdb7
commit df1f6ba40f
4 changed files with 92 additions and 1 deletions

View file

@ -251,6 +251,36 @@ tp_button_area_handle_event(struct tp_dispatch *tp,
}
}
/**
* Release any button in the bottom area, provided it started within a
* threshold around start_time (i.e. simultaneously with the other touch
* that triggered this call).
*/
static inline void
tp_button_release_other_bottom_touches(struct tp_dispatch *tp,
uint64_t other_start_time)
{
struct tp_touch *t;
tp_for_each_touch(tp, t) {
uint64_t tdelta;
if (t->button.state != BUTTON_STATE_BOTTOM ||
t->button.has_moved)
continue;
if (other_start_time > t->button.initial_time)
tdelta = other_start_time - t->button.initial_time;
else
tdelta = t->button.initial_time - other_start_time;
if (tdelta > ms2us(80))
continue;
t->button.has_moved = true;
}
}
static void
tp_button_bottom_handle_event(struct tp_dispatch *tp,
struct tp_touch *t,
@ -271,6 +301,14 @@ tp_button_bottom_handle_event(struct tp_dispatch *tp,
case BUTTON_EVENT_IN_TOP_L:
case BUTTON_EVENT_IN_AREA:
tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
/* We just transitioned one finger from BOTTOM to AREA,
* if there are other fingers in BOTTOM that started
* simultaneously with this finger, release those fingers
* because they're part of a gesture.
*/
tp_button_release_other_bottom_touches(tp,
t->button.initial_time);
break;
case BUTTON_EVENT_UP:
tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
@ -485,8 +523,12 @@ tp_button_check_for_movement(struct tp_dispatch *tp, struct tp_touch *t)
mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
vector_length = hypot(mm.x, mm.y);
if (vector_length > 5.0 /* mm */)
if (vector_length > 5.0 /* mm */) {
t->button.has_moved = true;
tp_button_release_other_bottom_touches(tp,
t->button.initial_time);
}
}
void
@ -500,6 +542,7 @@ tp_button_handle_state(struct tp_dispatch *tp, uint64_t time)
if (t->state == TOUCH_BEGIN) {
t->button.initial = t->point;
t->button.initial_time = time;
t->button.has_moved = false;
}

View file

@ -209,6 +209,7 @@ struct tp_touch {
struct libinput_timer timer;
struct device_coords initial;
bool has_moved; /* has moved more than threshold */
uint64_t initial_time;
} button;
struct {

View file

@ -961,6 +961,15 @@ litest_has_clickfinger(struct litest_device *dev)
return methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
}
static inline bool
litest_has_btnareas(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_BUTTON_AREAS;
}
static inline void
litest_enable_clickfinger(struct litest_device *dev)
{

View file

@ -511,6 +511,43 @@ START_TEST(touchpad_2fg_scroll_return_to_motion)
}
END_TEST
START_TEST(touchpad_2fg_scroll_from_btnareas)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
if (!litest_has_2fg_scroll(dev) ||
!litest_has_btnareas(dev))
return;
litest_enable_2fg_scroll(dev);
litest_enable_buttonareas(dev);
litest_drain_events(li);
litest_touch_down(dev, 0, 30, 95);
litest_touch_down(dev, 1, 50, 95);
libinput_dispatch(li);
/* First finger moves out of the area first but it's a scroll
* motion, should not trigger POINTER_MOTION */
for (int i = 0; i < 5; i++) {
litest_touch_move(dev, 0, 30, 95 - i);
}
libinput_dispatch(li);
for (int i = 0; i < 20; i++) {
litest_touch_move(dev, 0, 30, 90 - i);
litest_touch_move(dev, 1, 30, 95 - i);
}
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_scroll_natural_defaults)
{
struct litest_device *dev = litest_current_device();
@ -6713,6 +6750,7 @@ TEST_COLLECTION(touchpad)
litest_add("touchpad:scroll", touchpad_2fg_scroll_return_to_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_2fg_scroll_source, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_2fg_scroll_semi_mt, LITEST_SEMI_MT, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_2fg_scroll_from_btnareas, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:scroll", touchpad_scroll_natural_defaults, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_scroll_natural_enable_config, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:scroll", touchpad_scroll_natural_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);