diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 1325355c..830e9fee 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -148,7 +148,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t) t->dirty = true; t->is_pointer = false; - t->is_palm = false; + t->palm.is_palm = false; t->state = TOUCH_END; t->pinned.is_pinned = false; assert(tp->nfingers_down >= 1); @@ -340,7 +340,7 @@ static int tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t) { return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) && - !t->is_palm && + !t->palm.is_palm && !t->pinned.is_pinned && tp_button_touch_active(tp, t); } @@ -360,11 +360,21 @@ tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t) } static void -tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t) +tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { - /* once a palm, always a palm */ - if (t->is_palm) + const int PALM_TIMEOUT = 200; /* ms */ + + /* If labelled a touch as palm, we unlabel as palm when + we move out of the palm edge zone within the timeout. + */ + if (t->palm.is_palm) { + if (time < t->palm.time + PALM_TIMEOUT && + (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge)) { + t->palm.is_palm = false; + tp_set_pointer(tp, t); + } return; + } /* palm must start in exclusion zone, it's ok to move into the zone without being a palm */ @@ -379,7 +389,8 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t) tp_button_is_inside_softbutton_area(tp, t)) return; - t->is_palm = true; + t->palm.is_palm = true; + t->palm.time = time; } static void @@ -398,7 +409,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) continue; } - tp_palm_detect(tp, t); + tp_palm_detect(tp, t, time); tp_motion_hysteresis(tp, t); tp_motion_history_push(t); diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 689687ee..8255a1cd 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -103,7 +103,6 @@ struct tp_touch { bool dirty; bool fake; /* a fake touch */ bool is_pointer; /* the pointer-controlling touch */ - bool is_palm; int32_t x; int32_t y; uint64_t millis; @@ -140,6 +139,11 @@ struct tp_touch { struct { enum tp_tap_touch_state state; } tap; + + struct { + bool is_palm; + uint32_t time; /* first timestamp if is_palm == true */ + } palm; }; struct tp_dispatch { diff --git a/test/touchpad.c b/test/touchpad.c index 672cbf9b..18427c1a 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -1302,17 +1302,31 @@ START_TEST(touchpad_palm_detect_at_top_corners) } END_TEST -START_TEST(touchpad_palm_detect_palm_stays_palm) +START_TEST(touchpad_palm_detect_palm_becomes_pointer) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + struct libinput_event *ev; + enum libinput_event_type type; litest_drain_events(li); litest_touch_down(dev, 0, 99, 50); - litest_touch_move_to(dev, 0, 99, 50, 0, 70, 5); + litest_touch_move_to(dev, 0, 99, 70, 0, 70, 5); litest_touch_up(dev, 0); + libinput_dispatch(li); + + ev = libinput_get_event(li); + ck_assert_notnull(ev); + do { + type = libinput_event_get_type(ev); + ck_assert_int_eq(type, LIBINPUT_EVENT_POINTER_MOTION); + + libinput_event_destroy(ev); + ev = libinput_get_event(li); + } while (ev); + litest_assert_empty_queue(li); } END_TEST @@ -1404,7 +1418,7 @@ int main(int argc, char **argv) { litest_add("touchpad:palm", touchpad_palm_detect_at_edge, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:palm", touchpad_palm_detect_at_bottom_corners, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("touchpad:palm", touchpad_palm_detect_at_top_corners, LITEST_TOUCHPAD, LITEST_TOPBUTTONPAD); - litest_add("touchpad:palm", touchpad_palm_detect_palm_stays_palm, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:palm", touchpad_palm_detect_palm_becomes_pointer, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:palm", touchpad_palm_detect_no_palm_moving_into_edges, LITEST_TOUCHPAD, LITEST_ANY); return litest_run(argc, argv);