touchpad: count the tapping fingers separately from the main touchpad code

tp->nfingers_down gives us the current state of the touchpad but in the case
of the tapping state we need the touchpoints separately. If all touchpoints
end in the same SYN_REPORT frame, tp->nfingers_down is 0 when we handle the
touch releases. This changes the tap state to IDLE on the first release and
then logs a bug when the remaining touches are released while the touchpad is
in IDLE.

Avoid this by counting the fingers separately for the tap state, this way we
can count up/down with the down/up events as we process them for the tapping
state machine.

This also adds tests for 4 and 5-finger tapping which is how the bug was
discovered in the first place.

https://bugs.freedesktop.org/show_bug.cgi?id=89800

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
Peter Hutterer 2015-04-08 13:54:57 +10:00 committed by Hans de Goede
parent 7ea7746c77
commit 591a41f9dd
3 changed files with 238 additions and 1 deletions

View file

@ -452,7 +452,7 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp,
switch (event) {
case TAP_EVENT_RELEASE:
if (tp->nfingers_down == 0)
if (tp->tap.tap_finger_count == 0)
tp->tap.state = TAP_STATE_IDLE;
break;
case TAP_EVENT_TOUCH:
@ -567,10 +567,12 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
t->tap.state = TAP_TOUCH_STATE_DEAD;
if (t->state == TOUCH_BEGIN) {
tp->tap.tap_finger_count++;
t->tap.state = TAP_TOUCH_STATE_TOUCH;
t->tap.initial = t->point;
tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time);
} else if (t->state == TOUCH_END) {
tp->tap.tap_finger_count--;
tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
t->tap.state = TAP_TOUCH_STATE_IDLE;
} else if (tp->tap.state != TAP_STATE_IDLE &&
@ -754,6 +756,7 @@ tp_release_all_taps(struct tp_dispatch *tp, uint64_t now)
}
tp->tap.state = tp->nfingers_down ? TAP_STATE_DEAD : TAP_STATE_IDLE;
tp->tap.tap_finger_count = 0;
}
void

View file

@ -255,6 +255,7 @@ struct tp_dispatch {
struct libinput_timer timer;
enum tp_tap_state state;
uint32_t buttons_pressed;
unsigned int tap_finger_count;
} tap;
struct {

View file

@ -369,6 +369,38 @@ START_TEST(touchpad_2fg_tap_inverted)
}
END_TEST
START_TEST(touchpad_2fg_tap_quickrelease)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
litest_drain_events(dev->libinput);
litest_touch_down(dev, 0, 50, 50);
litest_touch_down(dev, 1, 70, 70);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
litest_event(dev, EV_KEY, BTN_TOUCH, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
litest_assert_button_event(li, BTN_RIGHT,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_timeout_tap();
litest_assert_button_event(li, BTN_RIGHT,
LIBINPUT_BUTTON_STATE_RELEASED);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_1fg_tap_click)
{
struct litest_device *dev = litest_current_device();
@ -705,6 +737,46 @@ START_TEST(touchpad_3fg_tap)
}
END_TEST
START_TEST(touchpad_3fg_tap_quickrelease)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
if (libevdev_get_abs_maximum(dev->evdev,
ABS_MT_SLOT) <= 2)
return;
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
litest_drain_events(li);
litest_touch_down(dev, 0, 50, 50);
litest_touch_down(dev, 1, 70, 50);
litest_touch_down(dev, 2, 80, 50);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 2);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0);
litest_event(dev, EV_KEY, BTN_TOUCH, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
litest_assert_button_event(li, BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_PRESSED);
litest_timeout_tap();
litest_assert_button_event(li, BTN_MIDDLE,
LIBINPUT_BUTTON_STATE_RELEASED);
libinput_dispatch(li);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_3fg_tap_btntool)
{
struct litest_device *dev = litest_current_device();
@ -783,6 +855,161 @@ START_TEST(touchpad_3fg_tap_btntool_inverted)
}
END_TEST
START_TEST(touchpad_4fg_tap)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
int i;
if (libevdev_get_abs_maximum(dev->evdev,
ABS_MT_SLOT) <= 3)
return;
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
for (i = 0; i < 4; i++) {
litest_drain_events(li);
litest_touch_down(dev, 0, 50, 50);
litest_touch_down(dev, 1, 70, 50);
litest_touch_down(dev, 2, 80, 50);
litest_touch_down(dev, 3, 90, 50);
litest_touch_up(dev, (i + 3) % 4);
litest_touch_up(dev, (i + 2) % 4);
litest_touch_up(dev, (i + 1) % 4);
litest_touch_up(dev, (i + 0) % 4);
libinput_dispatch(li);
litest_assert_empty_queue(li);
litest_timeout_tap();
litest_assert_empty_queue(li);
event = libinput_get_event(li);
ck_assert(event == NULL);
}
}
END_TEST
START_TEST(touchpad_4fg_tap_quickrelease)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
if (libevdev_get_abs_maximum(dev->evdev,
ABS_MT_SLOT) <= 3)
return;
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
litest_drain_events(li);
litest_touch_down(dev, 0, 50, 50);
litest_touch_down(dev, 1, 70, 50);
litest_touch_down(dev, 2, 80, 50);
litest_touch_down(dev, 3, 90, 50);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 2);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 3);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_KEY, BTN_TOOL_QUADTAP, 0);
litest_event(dev, EV_KEY, BTN_TOUCH, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
litest_assert_empty_queue(li);
litest_timeout_tap();
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_5fg_tap)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
int i;
if (libevdev_get_abs_maximum(dev->evdev,
ABS_MT_SLOT) <= 4)
return;
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
for (i = 0; i < 5; i++) {
litest_drain_events(li);
litest_touch_down(dev, 0, 20, 50);
litest_touch_down(dev, 1, 30, 50);
litest_touch_down(dev, 2, 40, 50);
litest_touch_down(dev, 3, 50, 50);
litest_touch_down(dev, 4, 60, 50);
litest_touch_up(dev, (i + 4) % 5);
litest_touch_up(dev, (i + 3) % 5);
litest_touch_up(dev, (i + 2) % 5);
litest_touch_up(dev, (i + 1) % 5);
litest_touch_up(dev, (i + 0) % 5);
libinput_dispatch(li);
litest_assert_empty_queue(li);
litest_timeout_tap();
litest_assert_empty_queue(li);
event = libinput_get_event(li);
ck_assert(event == NULL);
}
}
END_TEST
START_TEST(touchpad_5fg_tap_quickrelease)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
if (libevdev_get_abs_maximum(dev->evdev,
ABS_MT_SLOT) <= 4)
return;
libinput_device_config_tap_set_enabled(dev->libinput_device,
LIBINPUT_CONFIG_TAP_ENABLED);
litest_drain_events(li);
litest_touch_down(dev, 0, 20, 50);
litest_touch_down(dev, 1, 30, 50);
litest_touch_down(dev, 2, 40, 50);
litest_touch_down(dev, 3, 70, 50);
litest_touch_down(dev, 4, 90, 50);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 1);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 2);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 3);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 4);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
litest_event(dev, EV_KEY, BTN_TOOL_QUINTTAP, 0);
litest_event(dev, EV_KEY, BTN_TOUCH, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
litest_assert_empty_queue(li);
litest_timeout_tap();
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_click_defaults_clickfinger)
{
struct litest_device *dev = litest_current_device();
@ -3370,6 +3597,7 @@ int main(int argc, char **argv) {
litest_add("touchpad:tap", touchpad_2fg_tap_n_drag_3fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_2fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
litest_add("touchpad:tap", touchpad_2fg_tap_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_2fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
litest_add("touchpad:tap", touchpad_1fg_tap_click, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD);
litest_add("touchpad:tap", touchpad_2fg_tap_click, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_SINGLE_TOUCH|LITEST_CLICKPAD);
@ -3381,6 +3609,11 @@ int main(int argc, char **argv) {
litest_add("touchpad:tap", touchpad_3fg_tap_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_3fg_tap_btntool_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_3fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_3fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:tap", touchpad_4fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
litest_add("touchpad:tap", touchpad_4fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
litest_add("touchpad:tap", touchpad_5fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
litest_add("touchpad:tap", touchpad_5fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT);
/* Real buttons don't interfere with tapping, so don't run those for
pads with buttons */