diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 8629e40c..b4399d2e 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -722,16 +722,58 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time) } +static inline void +tp_position_fake_touches(struct tp_dispatch *tp) +{ + struct tp_touch *t; + struct tp_touch *topmost = NULL; + unsigned int start, i; + + if (tp_fake_finger_count(tp) <= tp->num_slots) + return; + + /* We have at least one fake touch down. Find the top-most real + * touch and copy its coordinates over to to all fake touches. + * This is more reliable than just taking the first touch. + */ + for (i = 0; i < tp->num_slots; i++) { + t = tp_get_touch(tp, i); + if (t->state == TOUCH_END || + t->state == TOUCH_NONE) + continue; + + if (topmost == NULL || t->point.y < topmost->point.y) + topmost = t; + } + + if (!topmost) { + log_bug_libinput(tp_libinput_context(tp), + "Unable to find topmost touch\n"); + return; + } + + start = tp->has_mt ? tp->num_slots : 1; + for (i = start; i < tp->ntouches; i++) { + t = tp_get_touch(tp, i); + if (t->state == TOUCH_NONE) + continue; + + t->point = topmost->point; + if (!t->dirty) + t->dirty = topmost->dirty; + } +} + static void tp_process_state(struct tp_dispatch *tp, uint64_t time) { struct tp_touch *t; - struct tp_touch *first = tp_get_touch(tp, 0); unsigned int i; bool restart_filter = false; tp_process_fake_touches(tp, time); tp_unhover_touches(tp, time); + tp_position_fake_touches(tp); for (i = 0; i < tp->ntouches; i++) { t = tp_get_touch(tp, i); @@ -740,12 +782,6 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down) tp_motion_history_reset(t); - if (i >= tp->num_slots && t->state != TOUCH_NONE) { - t->point = first->point; - if (!t->dirty) - t->dirty = first->dirty; - } - if (!t->dirty) continue; diff --git a/test/touchpad.c b/test/touchpad.c index 40d9c33a..7b8324cc 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -795,6 +795,66 @@ START_TEST(touchpad_area_to_clickfinger_method_while_down) } END_TEST +START_TEST(touchpad_clickfinger_3fg_tool_position) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + enable_clickfinger(dev); + litest_drain_events(li); + + /* one in thumb area, one in normal area. spread is wide so the two + * real fingers don't count together. we expect a 2-finger click */ + litest_touch_down(dev, 0, 5, 99); + litest_touch_down(dev, 1, 90, 15); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + +START_TEST(touchpad_clickfinger_4fg_tool_position) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + enable_clickfinger(dev); + litest_drain_events(li); + + litest_touch_down(dev, 0, 5, 99); + litest_touch_down(dev, 1, 90, 15); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOOL_QUADTAP, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + START_TEST(touchpad_btn_left) { struct litest_device *dev = litest_current_device(); @@ -4348,6 +4408,10 @@ litest_setup_tests(void) litest_add("touchpad:clickfinger", touchpad_area_to_clickfinger_method, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_area_to_clickfinger_method_while_down, LITEST_CLICKPAD, LITEST_ANY); + /* run those two for the T440 one only so we don't have to worry + * about small touchpads messing with thumb detection expectations */ + litest_add_for_device("touchpad:clickfinger", touchpad_clickfinger_3fg_tool_position, LITEST_SYNAPTICS_TOPBUTTONPAD); + litest_add_for_device("touchpad:clickfinger", touchpad_clickfinger_4fg_tool_position, LITEST_SYNAPTICS_TOPBUTTONPAD); litest_add("touchpad:click", touchpad_click_defaults_clickfinger, LITEST_APPLE_CLICKPAD, LITEST_ANY); litest_add("touchpad:click", touchpad_click_defaults_btnarea, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD);