From 50418a0153f271a03dc9039e8e7776360f0fcba1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 9 Mar 2018 09:41:57 +1000 Subject: [PATCH 1/4] fallback: fix touchscreen defuzzing The hysteresis-returned point always differs from the current point, even if the hysteresis kicks in. We need to compare to the hysteresis center. And the returned point is only the new center if we exceed the margin, otherwise the center stays as-is. The touch_fuzz() test only succeeded for this because for the values we were introducing jitter by, the kernel filtered out all the actual movement so these paths weren't hit. Signed-off-by: Peter Hutterer --- src/evdev-fallback.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c index d1ca81f0..269b3f5a 100644 --- a/src/evdev-fallback.c +++ b/src/evdev-fallback.c @@ -130,12 +130,13 @@ fallback_filter_defuzz_touch(struct fallback_dispatch *dispatch, point = evdev_hysteresis(&slot->point, &slot->hysteresis_center, &dispatch->mt.hysteresis_margin); + slot->point = point; - slot->hysteresis_center = slot->point; - if (point.x == slot->point.x && point.y == slot->point.y) + if (point.x == slot->hysteresis_center.x && + point.y == slot->hysteresis_center.y) return true; - slot->point = point; + slot->hysteresis_center = point; return false; } From ea7498ef971350454db9c78b9ba160e7d6bb455b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 21 Feb 2018 13:41:31 +1000 Subject: [PATCH 2/4] touchpad: use the fuzz value (if any) for the hysteresis margin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently used 0.5mm on touchpads as hysteresis value. This causes pointer movement delays, it is likely too high. Reduce it to a kernel-set fuzz value (if any) and see how we go with that. On many touchpads, the fuzz is 8 which would be closer to 0.2mm on e.g. a T440. Note that the does some defuzzing anyway, but the response of that function is nonlinear, e.g. for a fuzz of 8, the physical deltas map to: phys 0..3 → delta 0 phys 4..7 → delta 1 phys 8..15 → delta 4, 5, 6, 7 phys 16..N → delta 16..N In other words, we never see some logical deltas 2 and 3. While this shouldn't matter given the average touchpad resolution, reducing the hysteresis margin is likely to provide some better response. We never see values 8-15 either which could be the cause of some pointer jumps we've been seeing. see https://bugs.freedesktop.org/show_bug.cgi?id=105303 Devices with a fuzz of 0 have the hysteresis margin reduced to 0.25mm (from 0.5mm). https://bugs.freedesktop.org/show_bug.cgi?id=105108 Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 833e5c99..0cd3e79d 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -3093,12 +3093,22 @@ tp_init_default_resolution(struct tp_dispatch *tp, static inline void tp_init_hysteresis(struct tp_dispatch *tp) { - int res_x, res_y; + int xmargin, ymargin; + const struct input_absinfo *ax = tp->device->abs.absinfo_x, + *ay = tp->device->abs.absinfo_y; - res_x = tp->device->abs.absinfo_x->resolution; - res_y = tp->device->abs.absinfo_y->resolution; - tp->hysteresis.margin.x = res_x/2; - tp->hysteresis.margin.y = res_y/2; + if (ax->fuzz) + xmargin = ax->fuzz; + else + xmargin = ax->resolution/4; + + if (ay->fuzz) + ymargin = ay->fuzz; + else + ymargin = ay->resolution/4; + + tp->hysteresis.margin.x = xmargin; + tp->hysteresis.margin.y = ymargin; tp->hysteresis.enabled = false; } From 1b64888a2248af2a287365598cf520e4e2be33fd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 23 Feb 2018 11:44:23 +1000 Subject: [PATCH 3/4] touchpad: enable hysteresis based on a 0 fuzz value If the fuzz is 0, assume we don't need hysteresis and use the wobble detection code instead. If the fuzz is non-zero, enable it by default. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 0cd3e79d..aaad9f5d 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -3109,7 +3109,9 @@ tp_init_hysteresis(struct tp_dispatch *tp) tp->hysteresis.margin.x = xmargin; tp->hysteresis.margin.y = ymargin; - tp->hysteresis.enabled = false; + tp->hysteresis.enabled = (ax->fuzz || ay->fuzz); + if (tp->hysteresis.enabled) + evdev_log_debug(tp->device, "hysteresis enabled\n"); } static void From 1523d8bb2e066bec297f7a03ce4a0d8cada8f383 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 5 Mar 2018 13:17:43 +1000 Subject: [PATCH 4/4] Extract and reset the abs fuzz value for the x/y axes The kernel fuzz handling is buggy, especially when we want to rely on the fuzz value for our hysteresis. But since this is a hw property and (at least sometimes) set by the driver, we can't make this a pure libinput hwdb set either. So our workaround is: * extract the (non-zero) fuzz into a udev property so we don't lose it * set the fuzz to 0 to disable the in-kernel hysteresis * overwrite our internal absinfo with the property fuzz This way we get to use the hw-specified fuzz without having the kernel muck around with it. We also get to use the EVDEV_ABS_ values in 60-evdev.hwdb to override a driver-set fuzz. Two drawbacks: - we're resetting the kernel fuzz to 0, this affects any other users of the device node. That's probably a minor impact only. - we can only save this in a udev property there's a risk of this information getting lost when playing around with udev rules. That too should be a minor issue. https://bugs.freedesktop.org/show_bug.cgi?id=105303 Signed-off-by: Peter Hutterer --- meson.build | 2 +- src/evdev.c | 39 +++++++++++++++++ src/evdev.h | 3 ++ udev/90-libinput-model-quirks.rules.in | 1 - udev/libinput-model-quirks.c | 58 ++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 60caf997..1c12fe1c 100644 --- a/meson.build +++ b/meson.build @@ -87,7 +87,7 @@ executable('libinput-device-group', install_dir : udev_dir) executable('libinput-model-quirks', 'udev/libinput-model-quirks.c', - dependencies : dep_udev, + dependencies : [dep_udev, dep_libevdev], include_directories : [includes_src, includes_include], install : true, install_dir : udev_dir) diff --git a/src/evdev.c b/src/evdev.c index 257824aa..7d4f0183 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1505,6 +1505,7 @@ static void evdev_extract_abs_axes(struct evdev_device *device) { struct libevdev *evdev = device->evdev; + int fuzz; if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X) || !libevdev_has_event_code(evdev, EV_ABS, ABS_Y)) @@ -1512,6 +1513,12 @@ evdev_extract_abs_axes(struct evdev_device *device) if (evdev_fix_abs_resolution(device, ABS_X, ABS_Y)) device->abs.is_fake_resolution = true; + + if ((fuzz = evdev_read_fuzz_prop(device, ABS_X))) + libevdev_set_abs_fuzz(evdev, ABS_X, fuzz); + if ((fuzz = evdev_read_fuzz_prop(device, ABS_Y))) + libevdev_set_abs_fuzz(evdev, ABS_Y, fuzz); + device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_X); device->abs.absinfo_y = libevdev_get_abs_info(evdev, ABS_Y); device->abs.dimensions.x = abs(device->abs.absinfo_x->maximum - @@ -1529,6 +1536,11 @@ evdev_extract_abs_axes(struct evdev_device *device) ABS_MT_POSITION_Y)) device->abs.is_fake_resolution = true; + if ((fuzz = evdev_read_fuzz_prop(device, ABS_MT_POSITION_X))) + libevdev_set_abs_fuzz(evdev, ABS_MT_POSITION_X, fuzz); + if ((fuzz = evdev_read_fuzz_prop(device, ABS_MT_POSITION_Y))) + libevdev_set_abs_fuzz(evdev, ABS_MT_POSITION_Y, fuzz); + device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X); device->abs.absinfo_y = libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y); device->abs.dimensions.x = abs(device->abs.absinfo_x->maximum - @@ -2162,6 +2174,33 @@ evdev_read_calibration_prop(struct evdev_device *device) calibration[5]); } +int +evdev_read_fuzz_prop(struct evdev_device *device, unsigned int code) +{ + const char *prop; + char name[32]; + int rc; + int fuzz = 0; + + rc = snprintf(name, sizeof(name), "LIBINPUT_FUZZ_%02x", code); + if (rc == -1) + return 0; + + prop = udev_device_get_property_value(device->udev_device, name); + if (prop == NULL) + return 0; + + rc = safe_atoi(prop, &fuzz); + if (rc == -1 || fuzz < 0) { + evdev_log_bug_libinput(device, + "invalid LIBINPUT_FUZZ property value: %s\n", + prop); + return 0; + } + + return fuzz; +} + bool evdev_device_has_capability(struct evdev_device *device, enum libinput_device_capability capability) diff --git a/src/evdev.h b/src/evdev.h index 7fc21690..e5361ad2 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -378,6 +378,9 @@ evdev_init_calibration(struct evdev_device *device, void evdev_read_calibration_prop(struct evdev_device *device); +int +evdev_read_fuzz_prop(struct evdev_device *device, unsigned int code); + enum switch_reliability evdev_read_switch_reliability_prop(struct evdev_device *device); diff --git a/udev/90-libinput-model-quirks.rules.in b/udev/90-libinput-model-quirks.rules.in index e7d56bbe..5ddc0ba4 100644 --- a/udev/90-libinput-model-quirks.rules.in +++ b/udev/90-libinput-model-quirks.rules.in @@ -15,7 +15,6 @@ KERNEL!="event*", GOTO="libinput_model_quirks_end" # First, run the program and import the LIBINPUT_MODEL_FIRMWARE_VERSION # environment (if any) KERNELS=="*input*", \ - ENV{ID_INPUT_TOUCHPAD}=="1", \ IMPORT{program}="@UDEV_TEST_PATH@libinput-model-quirks %S%p" # Second, match on anything with that env set and import from the hwdb diff --git a/udev/libinput-model-quirks.c b/udev/libinput-model-quirks.c index 2dc917d5..d1d5a68a 100644 --- a/udev/libinput-model-quirks.c +++ b/udev/libinput-model-quirks.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "libinput-util.h" @@ -107,6 +108,61 @@ handle_touchpad(struct udev_device *device) handle_touchpad_synaptics(device); } +/** + * For a non-zero fuzz on the x/y axes, print that fuzz as property and + * reset the kernel's fuzz to 0. + * https://bugs.freedesktop.org/show_bug.cgi?id=105202 + */ +static void +handle_absfuzz(struct udev_device *device) +{ + const char *devnode; + struct libevdev *evdev = NULL; + int fd = -1; + int rc; + unsigned int *code; + unsigned int axes[] = {ABS_X, + ABS_Y, + ABS_MT_POSITION_X, + ABS_MT_POSITION_Y}; + + devnode = udev_device_get_devnode(device); + if (!devnode) + goto out; + + fd = open(devnode, O_RDWR); + if (fd == -1 && errno == EACCES) + fd = open(devnode, O_RDONLY); + if (fd < 0) + goto out; + + rc = libevdev_new_from_fd(fd, &evdev); + if (rc != 0) + goto out; + + if (!libevdev_has_event_type(evdev, EV_ABS)) + goto out; + + ARRAY_FOR_EACH(axes, code) { + struct input_absinfo abs; + int fuzz; + + fuzz = libevdev_get_abs_fuzz(evdev, *code); + if (!fuzz) + continue; + + abs = *libevdev_get_abs_info(evdev, *code); + abs.fuzz = 0; + libevdev_kernel_set_abs_info(evdev, *code, &abs); + + printf("LIBINPUT_FUZZ_%02x=%d\n", *code, fuzz); + } + +out: + close(fd); + libevdev_free(evdev); +} + int main(int argc, char **argv) { int rc = 1; @@ -127,6 +183,8 @@ int main(int argc, char **argv) if (!device) goto out; + handle_absfuzz(device); + if (prop_value(device, "ID_INPUT_TOUCHPAD")) handle_touchpad(device);