Commit df1f6ba4 authored by Peter Hutterer's avatar Peter Hutterer

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's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent 60d9defd
Pipeline #5418 passed with stages
in 6 minutes and 12 seconds
......@@ -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;
}
......
......@@ -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 {
......
......@@ -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)
{
......
......@@ -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);
......
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