---
src/evdev-mt-touchpad.c | 50 ++++++++++++++++++++++++++++++++------
test/touchpad.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+), 7 deletions(-)
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 8629e40..b4399d2 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 40d9c33..7b8324c 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);