diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox index 4e839e62..a5b578ba 100644 --- a/doc/palm-detection.dox +++ b/doc/palm-detection.dox @@ -23,6 +23,10 @@ screen, it is common for a finger to start inside an exclusion zone and move rapidly across the touchpad. libinput detects such movements and avoids palm detection on such touch sequences. +Each exclusion zone is divided into a top part and a bottom part. A touch +starting in the top part of the exclusion zone does not trigger a +tap (see @ref tapping). + In the diagram below, the exclusion zones are painted red. Touch 'A' starts inside the exclusion zone and moves almost vertically. It is considered a palm and ignored for cursor movement, @@ -31,6 +35,11 @@ despite moving out of the exclusion zone. Touch 'B' starts inside the exclusion zone but moves horizontally out of the zone. It is considered a valid touch and controls the cursor. +Touch 'C' occurs in the top part of the exclusion zone. Despite being a +tapping motion, it does not generate an emulated button event. Touch 'D' +likewise occurs within the exclusion zone but in the bottom half. libinput +will generate a button event for this touch. + @image html palm-detection.svg @section trackpoint-disabling Palm detection during trackpoint use diff --git a/doc/svg/palm-detection.svg b/doc/svg/palm-detection.svg index 9fb6077d..c3e45f44 100644 --- a/doc/svg/palm-detection.svg +++ b/doc/svg/palm-detection.svg @@ -2,14 +2,66 @@ + id="svg2" + inkscape:version="0.91 r13725" + sodipodi:docname="palm-detection.svg"> + + + + image/svg+xml + + + + + + + + - - - - - - A - B - - + + + + + + A + + B + + C + D + + diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 69c9669f..49fabb50 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -571,6 +571,14 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) t->tap.state = TAP_TOUCH_STATE_TOUCH; t->tap.initial = t->point; tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time); + + /* If we think this is a palm, pretend there's a + * motion event which will prevent tap clicks + * without requiring extra states in the FSM. + */ + if (tp_palm_tap_is_palm(tp, t)) + tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time); + } else if (t->state == TOUCH_END) { tp->tap.tap_finger_count--; tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time); diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 16846e9d..4e5a5580 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -458,6 +458,24 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t) tp_edge_scroll_touch_active(tp, t); } +bool +tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t) +{ + if (t->state != TOUCH_BEGIN) + return false; + + if (t->point.x > tp->palm.left_edge && + t->point.x < tp->palm.right_edge) + return false; + + /* We're inside the left/right palm edge and in the northern half of + * the touchpad - this tap is a palm */ + if (t->point.y < tp->palm.vert_center) + return true; + + return false; +} + static void tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { @@ -1098,13 +1116,16 @@ static int tp_init_palmdetect(struct tp_dispatch *tp, struct evdev_device *device) { - int width; + int width, height; tp->palm.right_edge = INT_MAX; tp->palm.left_edge = INT_MIN; + tp->palm.vert_center = INT_MIN; width = abs(device->abs.absinfo_x->maximum - device->abs.absinfo_x->minimum); + height = abs(device->abs.absinfo_y->maximum - + device->abs.absinfo_y->minimum); /* Apple touchpads are always big enough to warrant palm detection */ if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) { @@ -1121,6 +1142,7 @@ tp_init_palmdetect(struct tp_dispatch *tp, /* palm edges are 5% of the width on each side */ tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05; tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05; + tp->palm.vert_center = device->abs.absinfo_y->minimum + height/2; return 0; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 6ab09817..7b7600c3 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -261,6 +261,7 @@ struct tp_dispatch { struct { int32_t right_edge; /* in device coordinates */ int32_t left_edge; /* in device coordinates */ + int32_t vert_center; /* in device coordinates */ } palm; struct { @@ -387,4 +388,7 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time); void tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time); +bool +tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t); + #endif diff --git a/test/touchpad.c b/test/touchpad.c index b06e00dc..cd86f049 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2626,6 +2626,49 @@ START_TEST(touchpad_palm_detect_no_palm_moving_into_edges) } END_TEST +START_TEST(touchpad_palm_detect_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (!touchpad_has_palm_detect_size(dev)) + return; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 95, 5); + litest_touch_up(dev, 0); + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 5, 5); + litest_touch_up(dev, 0); + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 5, 90); + litest_touch_up(dev, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 95, 90); + litest_touch_up(dev, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(touchpad_left_handed) { struct litest_device *dev = litest_current_device(); @@ -3681,6 +3724,7 @@ int main(int argc, char **argv) { litest_add("touchpad:palm", touchpad_palm_detect_palm_becomes_pointer, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:palm", touchpad_palm_detect_palm_stays_palm, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:palm", touchpad_palm_detect_no_palm_moving_into_edges, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:palm", touchpad_palm_detect_tap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:left-handed", touchpad_left_handed, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD); litest_add("touchpad:left-handed", touchpad_left_handed_clickpad, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD);