diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 9cfa491c..a2fb4b41 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -639,22 +639,19 @@ static enum libinput_config_click_method tp_click_get_default_method(struct tp_dispatch *tp) { struct evdev_device *device = tp->device; + uint32_t clickfinger_models = EVDEV_MODEL_CHROMEBOOK | + EVDEV_MODEL_SYSTEM76_BONOBO | + EVDEV_MODEL_SYSTEM76_GALAGO | + EVDEV_MODEL_SYSTEM76_KUDU | + EVDEV_MODEL_CLEVO_W740SU; if (!tp->buttons.is_clickpad) return LIBINPUT_CONFIG_CLICK_METHOD_NONE; else if (libevdev_get_id_vendor(tp->device->evdev) == VENDOR_ID_APPLE) return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; - switch (device->model) { - case EVDEV_MODEL_CHROMEBOOK: - case EVDEV_MODEL_SYSTEM76_BONOBO: - case EVDEV_MODEL_SYSTEM76_GALAGO: - case EVDEV_MODEL_SYSTEM76_KUDU: - case EVDEV_MODEL_CLEVO_W740SU: + if (device->model_flags & clickfinger_models) return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; - default: - break; - } return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; } @@ -686,7 +683,7 @@ tp_init_middlebutton_emulation(struct tp_dispatch *tp, if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) { enable_by_default = true; want_config_option = false; - } else if (device->model == EVDEV_MODEL_ALPS_TOUCHPAD) { + } else if (device->model_flags & EVDEV_MODEL_ALPS_TOUCHPAD) { enable_by_default = true; want_config_option = true; } else diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index f45d6bbc..ad198e9c 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -341,6 +341,40 @@ tp_process_absolute_st(struct tp_dispatch *tp, } } +static inline void +tp_restore_synaptics_touches(struct tp_dispatch *tp, + uint64_t time) +{ + unsigned int i; + unsigned int nfake_touches; + + nfake_touches = tp_fake_finger_count(tp); + if (nfake_touches < 3) + return; + + if (tp->nfingers_down >= nfake_touches || + tp->nfingers_down == tp->num_slots) + return; + + /* Synaptics devices may end touch 2 on BTN_TOOL_TRIPLETAP + * and start it again on the next frame with different coordinates + * (#91352). We search the touches we have, if there is one that has + * just ended despite us being on tripletap, we move it back to + * update. + */ + for (i = 0; i < tp->num_slots; i++) { + struct tp_touch *t = tp_get_touch(tp, i); + + if (t->state != TOUCH_END) + continue; + + /* new touch, move it through begin to update immediately */ + tp_new_touch(tp, t, time); + tp_begin_touch(tp, t, time); + t->state = TOUCH_UPDATE; + } +} + static void tp_process_fake_touches(struct tp_dispatch *tp, uint64_t time) @@ -353,6 +387,10 @@ tp_process_fake_touches(struct tp_dispatch *tp, if (nfake_touches == FAKE_FINGER_OVERFLOW) return; + if (tp->device->model_flags & + EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD) + tp_restore_synaptics_touches(tp, time); + start = tp->has_mt ? tp->num_slots : 0; for (i = start; i < tp->ntouches; i++) { t = tp_get_touch(tp, i); @@ -722,6 +760,24 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time) } +static inline bool +tp_need_motion_history_reset(struct tp_dispatch *tp) +{ + /* semi-mt finger postions may "jump" when nfingers changes */ + if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down) + return true; + + /* if we're transitioning between slots and fake touches in either + * direction, we may get a coordinate jump + */ + if (tp->nfingers_down != tp->old_nfingers_down && + (tp->nfingers_down > tp->num_slots || + tp->old_nfingers_down > tp->num_slots)) + return true; + + return false; +} + static void tp_process_state(struct tp_dispatch *tp, uint64_t time) { @@ -729,16 +785,23 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) struct tp_touch *first = tp_get_touch(tp, 0); unsigned int i; bool restart_filter = false; + bool want_motion_reset; tp_process_fake_touches(tp, time); tp_unhover_touches(tp, time); + want_motion_reset = tp_need_motion_history_reset(tp); + for (i = 0; i < tp->ntouches; i++) { t = tp_get_touch(tp, i); - /* semi-mt finger postions may "jump" when nfingers changes */ - if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down) + if (want_motion_reset) { tp_motion_history_reset(t); + t->quirks.reset_motion_history = true; + } else if (t->quirks.reset_motion_history) { + tp_motion_history_reset(t); + t->quirks.reset_motion_history = false; + } if (i >= tp->num_slots && t->state != TOUCH_NONE) { t->point = first->point; @@ -1224,7 +1287,7 @@ evdev_tag_touchpad(struct evdev_device *device, */ bustype = libevdev_get_id_bustype(device->evdev); if (bustype == BUS_USB) { - if (device->model == EVDEV_MODEL_APPLE_TOUCHPAD) + if (device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD) device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD; } else if (bustype != BUS_BLUETOOTH) device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD; @@ -1350,14 +1413,10 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal) tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x; tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y; - switch (tp->device->model) { - case EVDEV_MODEL_LENOVO_X230: + if (tp->device->model_flags & EVDEV_MODEL_LENOVO_X230) profile = touchpad_lenovo_x230_accel_profile; - break; - default: + else profile = touchpad_accel_profile_linear; - break; - } if (evdev_device_init_pointer_acceleration(tp->device, profile) == -1) return -1; @@ -1460,7 +1519,7 @@ tp_init_palmdetect(struct tp_dispatch *tp, /* Wacom doesn't have internal touchpads, * Apple touchpads are always big enough to warrant palm detection */ - if (device->model == EVDEV_MODEL_WACOM_TOUCHPAD) + if (device->model_flags & EVDEV_MODEL_WACOM_TOUCHPAD) return 0; /* Enable palm detection on touchpads >= 70 mm. Anything smaller diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 61c41660..fc6d38e2 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -147,6 +147,15 @@ struct tp_touch { int distance; /* distance == 0 means touch */ int pressure; + struct { + /* A quirk mostly used on Synaptics touchpads. In a + transition to/from fake touches > num_slots, the current + event data is likely garbage and the subsequent event + is likely too. This marker tells us to reset the motion + history again -> this effectively swallows any motion */ + bool reset_motion_history; + } quirks; + struct { struct device_coords samples[TOUCHPAD_HISTORY_LENGTH]; unsigned int index; diff --git a/src/evdev.c b/src/evdev.c index 07934de9..78d1f5df 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1525,8 +1525,8 @@ evdev_read_dpi_prop(struct evdev_device *device) return dpi; } -static inline enum evdev_device_model -evdev_read_model(struct evdev_device *device) +static inline uint32_t +evdev_read_model_flags(struct evdev_device *device) { const struct model_map { const char *property; @@ -1541,18 +1541,20 @@ evdev_read_model(struct evdev_device *device) { "LIBINPUT_MODEL_APPLE_TOUCHPAD", EVDEV_MODEL_APPLE_TOUCHPAD }, { "LIBINPUT_MODEL_WACOM_TOUCHPAD", EVDEV_MODEL_WACOM_TOUCHPAD }, { "LIBINPUT_MODEL_ALPS_TOUCHPAD", EVDEV_MODEL_ALPS_TOUCHPAD }, + { "LIBINPUT_MODEL_SYNAPTICS_SERIAL_TOUCHPAD", EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD }, { NULL, EVDEV_MODEL_DEFAULT }, }; const struct model_map *m = model_map; + uint32_t model_flags = 0; while (m->property) { if (!!udev_device_get_property_value(device->udev_device, m->property)) - break; + model_flags |= m->model; m++; } - return m->model; + return model_flags; } static inline int @@ -2146,7 +2148,7 @@ evdev_device_create(struct libinput_seat *seat, device->scroll.direction = 0; device->scroll.wheel_click_angle = evdev_read_wheel_click_prop(device); - device->model = evdev_read_model(device); + device->model_flags = evdev_read_model_flags(device); device->dpi = DEFAULT_MOUSE_DPI; /* at most 5 SYN_DROPPED log-messages per 30s */ diff --git a/src/evdev.h b/src/evdev.h index 77db1b47..6a712324 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -95,16 +95,17 @@ enum evdev_middlebutton_event { }; enum evdev_device_model { - EVDEV_MODEL_DEFAULT, - EVDEV_MODEL_LENOVO_X230, - EVDEV_MODEL_CHROMEBOOK, - EVDEV_MODEL_SYSTEM76_BONOBO, - EVDEV_MODEL_SYSTEM76_GALAGO, - EVDEV_MODEL_SYSTEM76_KUDU, - EVDEV_MODEL_CLEVO_W740SU, - EVDEV_MODEL_APPLE_TOUCHPAD, - EVDEV_MODEL_WACOM_TOUCHPAD, - EVDEV_MODEL_ALPS_TOUCHPAD, + EVDEV_MODEL_DEFAULT = 0, + EVDEV_MODEL_LENOVO_X230 = (1 << 0), + EVDEV_MODEL_CHROMEBOOK = (1 << 1), + EVDEV_MODEL_SYSTEM76_BONOBO = (1 << 2), + EVDEV_MODEL_SYSTEM76_GALAGO = (1 << 3), + EVDEV_MODEL_SYSTEM76_KUDU = (1 << 4), + EVDEV_MODEL_CLEVO_W740SU = (1 << 5), + EVDEV_MODEL_APPLE_TOUCHPAD = (1 << 6), + EVDEV_MODEL_WACOM_TOUCHPAD = (1 << 7), + EVDEV_MODEL_ALPS_TOUCHPAD = (1 << 8), + EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD = (1 << 9), }; struct mt_slot { @@ -221,7 +222,7 @@ struct evdev_device { int dpi; /* HW resolution */ struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */ - enum evdev_device_model model; + uint32_t model_flags; }; #define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1) diff --git a/src/libinput-util.h b/src/libinput-util.h index 12f6d981..3832b728 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -37,6 +37,8 @@ #define VENDOR_ID_APPLE 0x5ac #define VENDOR_ID_WACOM 0x56a +#define VENDOR_ID_SYNAPTICS_SERIAL 0x002 +#define PRODUCT_ID_SYNAPTICS_SERIAL 0x007 /* The HW DPI rate we normalize to before calculating pointer acceleration */ #define DEFAULT_MOUSE_DPI 1000 diff --git a/test/device.c b/test/device.c index f43228c6..59939d67 100644 --- a/test/device.c +++ b/test/device.c @@ -1008,6 +1008,28 @@ START_TEST(device_udev_tag_apple) udev_device_unref(d); } END_TEST + +START_TEST(device_udev_tag_synaptics_serial) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + struct udev_device *d; + const char *prop; + + d = libinput_device_get_udev_device(device); + prop = udev_device_get_property_value(d, + "LIBINPUT_MODEL_SYNAPTICS_SERIAL_TOUCHPAD"); + + if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_SYNAPTICS_SERIAL && + libevdev_get_id_product(dev->evdev) == PRODUCT_ID_SYNAPTICS_SERIAL) + ck_assert_notnull(prop); + else + ck_assert(prop == NULL); + + udev_device_unref(d); +} +END_TEST + void litest_setup_tests(void) { @@ -1054,4 +1076,5 @@ litest_setup_tests(void) litest_add("device:udev tags", device_udev_tag_alps, LITEST_TOUCHPAD, LITEST_ANY); litest_add("device:udev tags", device_udev_tag_wacom, LITEST_TOUCHPAD, LITEST_ANY); litest_add("device:udev tags", device_udev_tag_apple, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("device:udev tags", device_udev_tag_synaptics_serial, LITEST_TOUCHPAD, LITEST_ANY); } diff --git a/test/touchpad.c b/test/touchpad.c index 927d7258..cae85e9e 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4323,6 +4323,120 @@ START_TEST(touchpad_thumb_tap_hold_2ndfg_tap) } END_TEST +START_TEST(touchpad_tool_tripletap_touch_count) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + + /* Synaptics touchpads sometimes end one touch point while + * simultaneously setting BTN_TOOL_TRIPLETAP. + * https://bugs.freedesktop.org/show_bug.cgi?id=91352 + */ + litest_drain_events(li); + enable_clickfinger(dev); + + /* touch 1 down */ + 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_POSITION_X, 1200); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 3200); + litest_event(dev, EV_ABS, ABS_MT_PRESSURE, 78); + litest_event(dev, EV_ABS, ABS_X, 1200); + litest_event(dev, EV_ABS, ABS_Y, 3200); + litest_event(dev, EV_ABS, ABS_PRESSURE, 78); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + msleep(2); + + /* touch 2 down */ + 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_POSITION_X, 2200); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 3200); + litest_event(dev, EV_ABS, ABS_MT_PRESSURE, 73); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + msleep(2); + + /* touch 3 down, coordinate jump + ends slot 1 */ + litest_event(dev, EV_ABS, ABS_MT_SLOT, 0); + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, 4000); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 4000); + litest_event(dev, EV_ABS, ABS_MT_PRESSURE, 78); + 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_X, 4000); + litest_event(dev, EV_ABS, ABS_Y, 4000); + litest_event(dev, EV_ABS, ABS_PRESSURE, 78); + 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); + msleep(2); + + /* slot 2 reactivated: + * Note, slot is activated close enough that we don't accidentally + * trigger the clickfinger distance check, remains to be seen if + * that is true for real-world interaction. + */ + litest_event(dev, EV_ABS, ABS_MT_SLOT, 0); + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, 4000); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 4000); + litest_event(dev, EV_ABS, ABS_MT_PRESSURE, 78); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 1); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, 3); + litest_event(dev, EV_ABS, ABS_MT_POSITION_X, 3500); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 3500); + litest_event(dev, EV_ABS, ABS_MT_PRESSURE, 73); + litest_event(dev, EV_ABS, ABS_X, 4000); + litest_event(dev, EV_ABS, ABS_Y, 4000); + litest_event(dev, EV_ABS, ABS_PRESSURE, 78); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + msleep(2); + + /* now a click should trigger middle click */ + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + litest_wait_for_event(li); + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + libinput_event_destroy(event); + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + /* silence gcc set-but-not-used warning, litest_is_button_event + * checks what we care about */ + event = libinput_event_pointer_get_base_event(ptrev); + libinput_event_destroy(event); + + /* release everything */ + 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, 0); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + 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); +} +END_TEST + void litest_setup_tests(void) { @@ -4457,4 +4571,6 @@ litest_setup_tests(void) litest_add("touchpad:thumb", touchpad_thumb_tap_hold, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:thumb", touchpad_thumb_tap_hold_2ndfg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:thumb", touchpad_thumb_tap_hold_2ndfg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + + litest_add_for_device("touchpad:bugs", touchpad_tool_tripletap_touch_count, LITEST_SYNAPTICS_TOPBUTTONPAD); } diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index eec00b62..a34b8f17 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -70,6 +70,12 @@ libinput:name:Atmel maXTouch Touchpad:dmi:*svn*GOOGLE*:pn*Samus* libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*:pvrThinkPadX230* LIBINPUT_MODEL_LENOVO_X230=1 +########################################## +# Synaptics +########################################## +libinput:touchpad:input:b0011v0002p0007* + LIBINPUT_MODEL_SYNAPTICS_SERIAL_TOUCHPAD=1 + ########################################## # System76 ########################################## diff --git a/udev/90-libinput-model-quirks.rules.in b/udev/90-libinput-model-quirks.rules.in index d8341558..5b077266 100644 --- a/udev/90-libinput-model-quirks.rules.in +++ b/udev/90-libinput-model-quirks.rules.in @@ -25,18 +25,12 @@ KERNELS=="*input*", \ IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:fwversion:$env{LIBINPUT_MODEL_FIRMWARE_VERSION}'" # End of touchpad firmware detection -# Matches below are exclusive, if one matches we skip the rest -# hwdb matches: -# # libinput:touchpad: ENV{ID_INPUT_TOUCHPAD}=="1", \ - IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:touchpad:", \ - GOTO="libinput_model_quirks_end" + IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:touchpad:" # libinput:name::dmi: KERNELS=="input*", \ - IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:$attr{[dmi/id]modalias}'", \ - GOTO="libinput_model_quirks_end" - + IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:$attr{[dmi/id]modalias}'" LABEL="libinput_model_quirks_end"