From a952acf1c29eaa7ae3bcdec82adcc4d10a7e5366 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 5 Jun 2015 10:10:56 +1000 Subject: [PATCH 01/16] touchpad: fix whitespace issue Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 24f33f46..0154e895 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1150,7 +1150,7 @@ evdev_tag_touchpad(struct evdev_device *device, */ bustype = libevdev_get_id_bustype(device->evdev); if (bustype == BUS_USB) { - if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_APPLE) + if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_APPLE) device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD; } else if (bustype != BUS_BLUETOOTH) device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD; From bd4f129a2a6b03ff24e6a1fe380cdc43e8112047 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 5 Jun 2015 10:39:30 +1000 Subject: [PATCH 02/16] test: replace a strncmp call with strneq Signed-off-by: Peter Hutterer --- test/litest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/litest.c b/test/litest.c index d457a272..c9c17d0b 100644 --- a/test/litest.c +++ b/test/litest.c @@ -393,9 +393,9 @@ litest_reload_udev_rules(void) static int litest_udev_rule_filter(const struct dirent *entry) { - return strncmp(entry->d_name, - UDEV_RULE_PREFIX, - strlen(UDEV_RULE_PREFIX)) == 0; + return strneq(entry->d_name, + UDEV_RULE_PREFIX, + strlen(UDEV_RULE_PREFIX)); } static void From 36753fae8fc2eeb0cefdf5a26e3f837af886f74c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 5 Jun 2015 11:40:26 +1000 Subject: [PATCH 03/16] test: fix litest_log() when libunwind is missing Previous expansion had side-effects when litest_log was called in an if condition without {} Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/litest.c b/test/litest.c index c9c17d0b..0e2cb967 100644 --- a/test/litest.c +++ b/test/litest.c @@ -61,8 +61,8 @@ const char *filter_group = NULL; #define litest_log(...) fprintf(stderr, __VA_ARGS__) #define litest_vlog(format_, args_) vfprintf(stderr, format_, args_) #else -#define litest_log(...) /* __VA_ARGS__ */ -#define litest_vlog(...) /* __VA_ARGS__ */ +#define litest_log(...) { /* __VA_ARGS__ */ } +#define litest_vlog(...) { /* __VA_ARGS__ */ } #endif #ifdef HAVE_LIBUNWIND From d29f17190cb419bf3cf975f5a1d133634fdca97d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Jun 2015 07:45:39 +1000 Subject: [PATCH 04/16] doc: add a note that middle-button dragging is not possible on sticks Just to have something to point bug reporters to. Signed-off-by: Peter Hutterer --- doc/scrolling.dox | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/scrolling.dox b/doc/scrolling.dox index 94aa8158..61d58f38 100644 --- a/doc/scrolling.dox +++ b/doc/scrolling.dox @@ -55,6 +55,10 @@ a designated button is held down. For example, Lenovo devices provide a pointing stick that emulates scroll events when the trackstick's middle mouse button is held down. +@note On-button scrolling is enabled by default for pointing sticks. This +prevents middle-button dragging; all motion events while the middle button is +down are converted to scroll events. + @image html button-scrolling.svg "Button scrolling" The button may be changed with From 91c4770ce6d96045b90189b0088b677c70841353 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Jun 2015 07:52:52 +1000 Subject: [PATCH 05/16] doc: provide an outline of which scroll method is available where Signed-off-by: Peter Hutterer --- doc/scrolling.dox | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/scrolling.dox b/doc/scrolling.dox index 61d58f38..e73ba894 100644 --- a/doc/scrolling.dox +++ b/doc/scrolling.dox @@ -2,8 +2,18 @@ @page scrolling Scrolling libinput supports three different types of scrolling methods: @ref -twofinger_scrolling, @ref edge_scrolling and @ref button_scrolling. Some devices -support multiple methods, though only one can be enabled at a time. See +twofinger_scrolling, @ref edge_scrolling and @ref button_scrolling. Some +devices support multiple methods, though only one can be enabled at a time. +As a general overview: +- touchpad devices with physical buttons below the touchpad support edge and + two-finger scrolling +- touchpad devices without physical buttons (@ref clickpad_softbuttons + "clickpads") support two-finger scrolling only +- pointing sticks provide on-button scrolling by default +- mice and other pointing devices support on-button scrolling but it is not + enabled by default + +A device may differ from the above based on its capabilities. See libinput_device_config_scroll_set_method() for documentation on how to switch methods and libinput_device_config_scroll_get_methods() for documentation on how to query a device for available scroll methods. From d698f1b12fdf6a2fcc0103f338b944e3e6762c0b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Jun 2015 07:56:57 +1000 Subject: [PATCH 06/16] doc: make horizontal scrolling a separate section Signed-off-by: Peter Hutterer --- doc/scrolling.dox | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/scrolling.dox b/doc/scrolling.dox index e73ba894..1ecd0333 100644 --- a/doc/scrolling.dox +++ b/doc/scrolling.dox @@ -18,6 +18,8 @@ libinput_device_config_scroll_set_method() for documentation on how to switch methods and libinput_device_config_scroll_get_methods() for documentation on how to query a device for available scroll methods. +@section horizontal_scrolling Horizontal scrolling + Scroll movements provide vertical and horizontal directions, each scroll event contains both directions where applicable, see libinput_event_pointer_get_axis_value(). libinput does not provide separate From 105e725602791bd02ea800fd4298d3443c14ba58 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Jun 2015 09:54:06 +1000 Subject: [PATCH 07/16] touchpad: restart the motion filter on touch begin Our motion filter takes the last couple of vectors to calculate speed, provided the direction stays the same and it is within a certain timeout. It does not take into account lifting the finger, so the velocity on the first event is off. Real-world impact is mainly on scrolling. Before commit 289e4675 filter: enforce minimum velocity the first motion on a scroll was accelerated by a factor of 0 and swallowed. After 289e4675 the motion was calculated based on the timeout and a fraction of the expected effect. Now the first scroll motion is based on the real finger motion since setting the finger down and thus feels a bit more responsive. It also makes a couple of test cases using litest_assert_scroll() work again since the miniumum motion is now as expected. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 7 +++++++ src/filter-private.h | 3 +++ src/filter.c | 31 +++++++++++++++++++++++++++++++ src/filter.h | 5 +++++ 4 files changed, 46 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 0154e895..8c28ff79 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -666,6 +666,7 @@ 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); @@ -692,8 +693,14 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) tp_motion_history_push(t); tp_unpin_finger(tp, t); + + if (t->state == TOUCH_BEGIN) + restart_filter = true; } + if (restart_filter) + filter_restart(tp->device->pointer.filter, tp, time); + tp_button_handle_state(tp, time); tp_edge_scroll_handle_state(tp, time); diff --git a/src/filter-private.h b/src/filter-private.h index 0e796f1a..8a206d69 100644 --- a/src/filter-private.h +++ b/src/filter-private.h @@ -32,6 +32,9 @@ struct motion_filter_interface { struct motion_filter *filter, const struct normalized_coords *unaccelerated, void *data, uint64_t time); + void (*restart)(struct motion_filter *filter, + void *data, + uint64_t time); void (*destroy)(struct motion_filter *filter); bool (*set_speed)(struct motion_filter *filter, double speed); diff --git a/src/filter.c b/src/filter.c index c54d866c..6d9b9c34 100644 --- a/src/filter.c +++ b/src/filter.c @@ -43,6 +43,13 @@ filter_dispatch(struct motion_filter *filter, return filter->interface->filter(filter, unaccelerated, data, time); } +void +filter_restart(struct motion_filter *filter, + void *data, uint64_t time) +{ + filter->interface->restart(filter, data, time); +} + void filter_destroy(struct motion_filter *filter) { @@ -273,6 +280,29 @@ accelerator_filter(struct motion_filter *filter, return accelerated; } +static void +accelerator_restart(struct motion_filter *filter, + void *data, + uint64_t time) +{ + struct pointer_accelerator *accel = + (struct pointer_accelerator *) filter; + unsigned int offset; + struct pointer_tracker *tracker; + + for (offset = 1; offset < NUM_POINTER_TRACKERS; offset++) { + tracker = tracker_by_offset(accel, offset); + tracker->time = 0; + tracker->dir = 0; + tracker->delta.x = 0; + tracker->delta.y = 0; + } + + tracker = tracker_by_offset(accel, 0); + tracker->time = time; + tracker->dir = UNDEFINED_DIRECTION; +} + static void accelerator_destroy(struct motion_filter *filter) { @@ -309,6 +339,7 @@ accelerator_set_speed(struct motion_filter *filter, struct motion_filter_interface accelerator_interface = { accelerator_filter, + accelerator_restart, accelerator_destroy, accelerator_set_speed, }; diff --git a/src/filter.h b/src/filter.h index 16896a4c..03f510d0 100644 --- a/src/filter.h +++ b/src/filter.h @@ -37,6 +37,11 @@ struct normalized_coords filter_dispatch(struct motion_filter *filter, const struct normalized_coords *unaccelerated, void *data, uint64_t time); + +void +filter_restart(struct motion_filter *filter, + void *data, uint64_t time); + void filter_destroy(struct motion_filter *filter); From 69449ca854849e1782cedd42031df596fe258dc8 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 9 Jun 2015 08:06:20 +1000 Subject: [PATCH 08/16] filter: require minimum acceleration factor of 0.3 For really slow motions, the previous acceleration factor would go down to effectively zero. So the slower the mouse motion was, the more it would be slowed down which made the mouse at low speeds almost unusable. Cap the minimum acceleration at 0.3 which provides a predictable slow motion for the cursor when high precision is required. New/old acceleration functions comparison: ^ | / | / ty| _________/ | / / | / / | / / |/ / <----- new minimum accel factor | / |/___________________> tx i.e. the general shape is maintained, but it doesn't go to zero anymore. The functions aren't parallel, the new shape is slightly flatter than the previous one and they meet at the point where the functions flatten for the threshold (tx/ty). ascii art has its limits... https://bugzilla.redhat.com/show_bug.cgi?id=1227039 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filter.c b/src/filter.c index 6d9b9c34..ee4ce9e6 100644 --- a/src/filter.c +++ b/src/filter.c @@ -385,7 +385,7 @@ pointer_accel_profile_linear(struct motion_filter *filter, const double threshold = accel_filter->threshold; /* units/ms */ const double incline = accel_filter->incline; - s1 = min(1, speed_in * 5); + s1 = min(1, 0.3 + speed_in * 4); s2 = 1 + (speed_in - threshold) * incline; return min(max_accel, s2 > 1 ? s2 : s1); From df78ed8ad613d0616f17660601ab3fe1ad37ba09 Mon Sep 17 00:00:00 2001 From: JoonCheol Park Date: Thu, 11 Jun 2015 16:38:02 +0900 Subject: [PATCH 09/16] test: add missing libunwind cflags to test-litest-selftest Signed-off-by: JoonCheol Park Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- test/Makefile.am | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/Makefile.am b/test/Makefile.am index fc05ff6a..6f2e6e45 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -113,6 +113,10 @@ test_litest_selftest_SOURCES = litest-selftest.c litest.c litest-int.h litest.h test_litest_selftest_CFLAGS = -DLITEST_DISABLE_BACKTRACE_LOGGING -DLITEST_NO_MAIN test_litest_selftest_LDADD = $(TEST_LIBS) test_litest_selftest_LDFLAGS = -no-install +if HAVE_LIBUNWIND +test_litest_selftest_LDADD += $(LIBUNWIND_LIBS) -ldl +test_litest_selftest_CFLAGS += $(LIBUNWIND_CFLAGS) +endif # build-test only test_build_pedantic_c99_SOURCES = build-pedantic.c From 96face60c2e04d6f6739de7456d604d9f4763728 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 5 Jun 2015 10:52:04 +1000 Subject: [PATCH 10/16] test: always install our own udev rule/hwdb files for tests We can't rely on the system having these files installed, at least not in the latest version that we'd like. Copy them over from the source directory into the /run/ and /etc/ directories for each test and update udev and the hwdb. This ensures the tags we set in the hwdb file are always set, regardless of the system configuration. Note that the /run/udev/* files need to have a different filename to the ones we ship to avoid getting overridden by local configuration. systemd does not have support for /run/udev/hwdb.d [1]. So our hwdb.d file is in /etc/udev/hwdb.d instead and marked them with a REMOVEME and a comment that if that file is left after the tests, it should be removed by the user. [1] https://github.com/systemd/systemd/issues/127 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/Makefile.am | 6 ++- test/litest.c | 101 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 90 insertions(+), 17 deletions(-) diff --git a/test/Makefile.am b/test/Makefile.am index 6f2e6e45..2dfd5c1b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -38,7 +38,9 @@ liblitest_la_SOURCES = \ litest-vmware-virtual-usb-mouse.c \ litest.c liblitest_la_LIBADD = $(top_builddir)/src/libinput-util.la -liblitest_la_CFLAGS = $(AM_CFLAGS) +liblitest_la_CFLAGS = $(AM_CFLAGS) \ + -DLIBINPUT_UDEV_RULES_FILE="\"$(abs_top_srcdir)/udev/90-libinput-model-quirks.rules\"" \ + -DLIBINPUT_UDEV_HWDB_FILE="\"$(abs_top_srcdir)/udev/90-libinput-model-quirks.hwdb\"" if HAVE_LIBUNWIND liblitest_la_LIBADD += $(LIBUNWIND_LIBS) -ldl liblitest_la_CFLAGS += $(LIBUNWIND_CFLAGS) @@ -110,7 +112,7 @@ test_device_LDADD = $(TEST_LIBS) test_device_LDFLAGS = -no-install test_litest_selftest_SOURCES = litest-selftest.c litest.c litest-int.h litest.h -test_litest_selftest_CFLAGS = -DLITEST_DISABLE_BACKTRACE_LOGGING -DLITEST_NO_MAIN +test_litest_selftest_CFLAGS = -DLITEST_DISABLE_BACKTRACE_LOGGING -DLITEST_NO_MAIN $(liblitest_la_CFLAGS) test_litest_selftest_LDADD = $(TEST_LIBS) test_litest_selftest_LDFLAGS = -no-install if HAVE_LIBUNWIND diff --git a/test/litest.c b/test/litest.c index 0e2cb967..74d12927 100644 --- a/test/litest.c +++ b/test/litest.c @@ -40,6 +40,7 @@ #include #include "linux/input.h" #include +#include #include #include @@ -49,6 +50,9 @@ #define UDEV_RULES_D "/run/udev/rules.d" #define UDEV_RULE_PREFIX "99-litest-" +#define UDEV_HWDB_D "/etc/udev/hwdb.d" +#define UDEV_COMMON_RULE_FILE UDEV_RULES_D "/91-litest-model-quirks.rules" +#define UDEV_COMMON_HWDB_FILE UDEV_HWDB_D "/91-litest-model-quirks-REMOVEME.hwdb" static int in_debugger = -1; static int verbose = 0; @@ -56,6 +60,8 @@ const char *filter_test = NULL; const char *filter_device = NULL; const char *filter_group = NULL; +static inline void litest_remove_model_quirks(void); + /* defined for the litest selftest */ #ifndef LITEST_DISABLE_BACKTRACE_LOGGING #define litest_log(...) fprintf(stderr, __VA_ARGS__) @@ -374,20 +380,32 @@ struct litest_test_device* devices[] = { static struct list all_tests; +static inline void +litest_system(const char *command) +{ + int ret; + + ret = system(command); + + if (ret == -1) { + litest_abort_msg("Failed to execute: %s", command); + } else if (WIFEXITED(ret)) { + if (WEXITSTATUS(ret)) + litest_abort_msg("'%s' failed with %d", + command, + WEXITSTATUS(ret)); + } else if (WIFSIGNALED(ret)) { + litest_abort_msg("'%s' terminated with signal %d", + command, + WTERMSIG(ret)); + } +} + static void litest_reload_udev_rules(void) { - int ret = system("udevadm control --reload-rules"); - if (ret == -1) { - litest_abort_msg("Failed to execute: udevadm"); - } else if (WIFEXITED(ret)) { - if (WEXITSTATUS(ret)) - litest_abort_msg("udevadm failed with %d", - WEXITSTATUS(ret)); - } else if (WIFSIGNALED(ret)) { - litest_abort_msg("udevadm terminated with signal %d", - WTERMSIG(ret)); - } + litest_system("udevadm control --reload-rules"); + litest_system("udevadm hwdb --update"); } static int @@ -430,6 +448,7 @@ litest_drop_udev_rules(void) } free(entries); + litest_remove_model_quirks(); litest_reload_udev_rules(); } @@ -873,21 +892,70 @@ merge_events(const int *orig, const int *override) return events; } +static inline void +litest_copy_file(const char *dest, const char *src, const char *header) +{ + int in, out; + + out = open(dest, O_CREAT|O_WRONLY, 0644); + litest_assert_int_gt(out, -1); + + if (header) + write(out, header, strlen(header)); + + in = open(src, O_RDONLY); + litest_assert_int_gt(in, -1); + /* lazy, just check for error and empty file copy */ + litest_assert_int_gt(sendfile(out, in, NULL, 4096), 0); + close(out); + close(in); +} + +static inline void +litest_install_model_quirks(void) +{ + litest_copy_file(UDEV_COMMON_RULE_FILE, LIBINPUT_UDEV_RULES_FILE, NULL); + litest_copy_file(UDEV_COMMON_HWDB_FILE, + LIBINPUT_UDEV_HWDB_FILE, + "#################################################################\n" + "# WARNING: REMOVE THIS FILE\n" + "# This is the run-time hwdb file for the libinput test suite and\n" + "# should be removed on exit. If the test-suite is not currently \n" + "# running, remove this file and update your hwdb: \n" + "# sudo udevadm hwdb --update\n" + "#################################################################\n\n"); +} + +static inline void +litest_remove_model_quirks(void) +{ + unlink(UDEV_COMMON_RULE_FILE); + unlink(UDEV_COMMON_HWDB_FILE); +} + static char * litest_init_udev_rules(struct litest_test_device *dev) { int rc; FILE *f; - char *path; - - if (!dev->udev_rule) - return NULL; + char *path = NULL; rc = mkdir(UDEV_RULES_D, 0755); if (rc == -1 && errno != EEXIST) ck_abort_msg("Failed to create udev rules directory (%s)\n", strerror(errno)); + rc = mkdir(UDEV_HWDB_D, 0755); + if (rc == -1 && errno != EEXIST) + ck_abort_msg("Failed to create udev hwdb directory (%s)\n", + strerror(errno)); + + litest_install_model_quirks(); + + /* device-specific udev rules */ + if (!dev->udev_rule) + goto out; + rc = xasprintf(&path, "%s/%s%s.rules", UDEV_RULES_D, @@ -903,6 +971,7 @@ litest_init_udev_rules(struct litest_test_device *dev) litest_assert_int_ge(fputs(dev->udev_rule, f), 0); fclose(f); +out: litest_reload_udev_rules(); return path; @@ -942,6 +1011,7 @@ litest_create(enum litest_device_type which, if ((*dev)->create) { (*dev)->create(d); if (abs_override || events_override) { + litest_remove_model_quirks(); if (udev_file) unlink(udev_file); litest_abort_msg("Custom create cannot be overridden"); @@ -1120,6 +1190,7 @@ litest_delete_device(struct litest_device *d) return; if (d->udev_rule_file) { + litest_remove_model_quirks(); unlink(d->udev_rule_file); free(d->udev_rule_file); d->udev_rule_file = NULL; From 59984fc40d7ac1e346467c46f4b4ecbff49af979 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 5 Jun 2015 10:13:56 +1000 Subject: [PATCH 11/16] Move apple touchpad tagging to the udev rules Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 4 ++-- src/evdev.c | 1 + src/evdev.h | 1 + udev/90-libinput-model-quirks.hwdb | 7 +++++++ udev/90-libinput-model-quirks.rules | 6 ++++-- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 8c28ff79..5143ab16 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1157,7 +1157,7 @@ evdev_tag_touchpad(struct evdev_device *device, */ bustype = libevdev_get_id_bustype(device->evdev); if (bustype == BUS_USB) { - if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_APPLE) + if (device->model == EVDEV_MODEL_APPLE_TOUCHPAD) device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD; } else if (bustype != BUS_BLUETOOTH) device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD; @@ -1412,7 +1412,7 @@ tp_init_palmdetect(struct tp_dispatch *tp, * Apple touchpads are always big enough to warrant palm detection */ if (vendor_id == VENDOR_ID_WACOM) { return 0; - } else if (vendor_id != VENDOR_ID_APPLE) { + } else if (device->model != EVDEV_MODEL_APPLE_TOUCHPAD) { /* We don't know how big the touchpad is */ if (device->abs.absinfo_x->resolution == 1) return 0; diff --git a/src/evdev.c b/src/evdev.c index d6a2fff5..c1fe3e53 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1513,6 +1513,7 @@ evdev_read_model(struct evdev_device *device) { "LIBINPUT_MODEL_SYSTEM76_GALAGO", EVDEV_MODEL_SYSTEM76_GALAGO }, { "LIBINPUT_MODEL_SYSTEM76_KUDU", EVDEV_MODEL_SYSTEM76_KUDU }, { "LIBINPUT_MODEL_CLEVO_W740SU", EVDEV_MODEL_CLEVO_W740SU }, + { "LIBINPUT_MODEL_APPLE_TOUCHPAD", EVDEV_MODEL_APPLE_TOUCHPAD }, { NULL, EVDEV_MODEL_DEFAULT }, }; const struct model_map *m = model_map; diff --git a/src/evdev.h b/src/evdev.h index a8756635..70a66a28 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -103,6 +103,7 @@ enum evdev_device_model { EVDEV_MODEL_SYSTEM76_GALAGO, EVDEV_MODEL_SYSTEM76_KUDU, EVDEV_MODEL_CLEVO_W740SU, + EVDEV_MODEL_APPLE_TOUCHPAD, }; struct mt_slot { diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index 048e5cc6..13ff5180 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -15,6 +15,13 @@ # # Sort by brand, model +########################################## +# Apple +########################################## +libinput:touchpad:input:b0003v05ACp* +libinput:touchpad:input:b0005v05ACp* + LIBINPUT_MODEL_APPLE_TOUCHPAD=1 + ########################################## # Google ########################################## diff --git a/udev/90-libinput-model-quirks.rules b/udev/90-libinput-model-quirks.rules index 4b988748..79b9b36f 100644 --- a/udev/90-libinput-model-quirks.rules +++ b/udev/90-libinput-model-quirks.rules @@ -13,8 +13,9 @@ KERNEL!="event*", GOTO="libinput_model_quirks_end" # hwdb matches: # -# libinput: -IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:", \ +# libinput:touchpad: +ENV{ID_INPUT_TOUCHPAD}=="1", \ + IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:touchpad:", \ GOTO="libinput_model_quirks_end" # libinput:name::dmi: @@ -22,4 +23,5 @@ KERNELS=="input*", \ IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:$attr{[dmi/id]modalias}'", \ GOTO="libinput_model_quirks_end" + LABEL="libinput_model_quirks_end" From 452df0e8d3f23cec6548d232ba6059258cc108fd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 5 Jun 2015 16:17:04 +1000 Subject: [PATCH 12/16] Move Wacom touchpad tagging to the udev rules Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 5 +---- src/evdev.c | 1 + src/evdev.h | 1 + udev/90-libinput-model-quirks.hwdb | 6 ++++++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 5143ab16..34cc85d4 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1395,7 +1395,6 @@ tp_init_palmdetect(struct tp_dispatch *tp, struct evdev_device *device) { int width, height; - unsigned int vendor_id; tp->palm.right_edge = INT_MAX; tp->palm.left_edge = INT_MIN; @@ -1406,11 +1405,9 @@ tp_init_palmdetect(struct tp_dispatch *tp, height = abs(device->abs.absinfo_y->maximum - device->abs.absinfo_y->minimum); - vendor_id = evdev_device_get_id_vendor(device); - /* Wacom doesn't have internal touchpads, * Apple touchpads are always big enough to warrant palm detection */ - if (vendor_id == VENDOR_ID_WACOM) { + if (device->model == EVDEV_MODEL_WACOM_TOUCHPAD) { return 0; } else if (device->model != EVDEV_MODEL_APPLE_TOUCHPAD) { /* We don't know how big the touchpad is */ diff --git a/src/evdev.c b/src/evdev.c index c1fe3e53..b8f798ee 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1514,6 +1514,7 @@ evdev_read_model(struct evdev_device *device) { "LIBINPUT_MODEL_SYSTEM76_KUDU", EVDEV_MODEL_SYSTEM76_KUDU }, { "LIBINPUT_MODEL_CLEVO_W740SU", EVDEV_MODEL_CLEVO_W740SU }, { "LIBINPUT_MODEL_APPLE_TOUCHPAD", EVDEV_MODEL_APPLE_TOUCHPAD }, + { "LIBINPUT_MODEL_WACOM_TOUCHPAD", EVDEV_MODEL_WACOM_TOUCHPAD }, { NULL, EVDEV_MODEL_DEFAULT }, }; const struct model_map *m = model_map; diff --git a/src/evdev.h b/src/evdev.h index 70a66a28..f074ad7b 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -104,6 +104,7 @@ enum evdev_device_model { EVDEV_MODEL_SYSTEM76_KUDU, EVDEV_MODEL_CLEVO_W740SU, EVDEV_MODEL_APPLE_TOUCHPAD, + EVDEV_MODEL_WACOM_TOUCHPAD, }; struct mt_slot { diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index 13ff5180..4fda18da 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -71,3 +71,9 @@ libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrgalu1* # Kudu Professional libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrkudp1* LIBINPUT_MODEL_SYSTEM76_KUDU=1 + +########################################## +# Wacom +########################################## +libinput:touchpad:input:b0003v056Ap* + LIBINPUT_MODEL_WACOM_TOUCHPAD=1 From f13fbc96e84b2bb78bb61886d79aaaeaf8a9b4a1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 11 Jun 2015 16:31:00 +1000 Subject: [PATCH 13/16] touchpad: make the hysteresis dependent on physical distance Some touchpads, e.g. the Cyapa in the Acer c720 have a small axis range ([0, 870], [0, 470]), so the diagonal/magic value yields a hysteresis margin of 1 device unit. On that device, that's one-tenth of a millimeter, causing pointer motion just by holding the finger. For touchpads that provide a physical resolution, set the hysteresis axes to 0.5mm and do away with the magic factor. https://bugzilla.redhat.com/show_bug.cgi?id=1230441 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 34cc85d4..c82d7339 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1492,10 +1492,18 @@ tp_init(struct tp_dispatch *tp, EV_ABS, ABS_MT_DISTANCE); - tp->hysteresis_margin.x = - diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR; - tp->hysteresis_margin.y = - diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR; + if (device->abs.fake_resolution) { + tp->hysteresis_margin.x = + diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR; + tp->hysteresis_margin.y = + diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR; + } else { + int 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 (tp_init_accel(tp, diagonal) != 0) return -1; From 65ce6b10ee7375eb1502bfc3fbcfa9c0af875919 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 15 Jun 2015 14:12:09 +1000 Subject: [PATCH 14/16] udev: drop erroneous trailing colon from dmi matches Signed-off-by: Peter Hutterer --- udev/90-libinput-model-quirks.hwdb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index 4fda18da..7442f828 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -30,18 +30,18 @@ libinput:touchpad:input:b0005v05ACp* # extrapolated from the chromiumos touchad-tests repo # https://chromium.googlesource.com/chromiumos/platform/touchpad-tests libinput:name:Cypress APA Trackpad (cyapa):dmi:*pnFalco:pvr* -libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*Mario*: -libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Butterfly*: -libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Peppy*: -libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*ZGB*: -libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Parrot*: -libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Leon*: -libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Falco*: -libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Wolf*: -libinput:name:Cypress APA Trackpad (cyapa):dmi:*svn*GOOGLE*:pn*Link*: -libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*Alex*: -libinput:name:Cypress APA Trackpad (cyapa):dmi:*svn*SAMSUNG*:pn*Lumpy*: -libinput:name:Atmel maXTouch Touchpad:dmi:*svn*GOOGLE*:pn*Samus*: +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*Mario* +libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Butterfly* +libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Peppy* +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*ZGB* +libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Parrot* +libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Leon* +libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Falco* +libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Wolf* +libinput:name:Cypress APA Trackpad (cyapa):dmi:*svn*GOOGLE*:pn*Link* +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*Alex* +libinput:name:Cypress APA Trackpad (cyapa):dmi:*svn*SAMSUNG*:pn*Lumpy* +libinput:name:Atmel maXTouch Touchpad:dmi:*svn*GOOGLE*:pn*Samus* LIBINPUT_MODEL_CHROMEBOOK=1 ########################################## From b48ecd186d8fb707e89bf04036a48600dc49125f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 12 Jun 2015 17:24:33 +1000 Subject: [PATCH 15/16] touchpad: fix pinned finger drifting This caused the finger to be unpinned on the first motion event after the click, effectively disabling this feature. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index c82d7339..ce79530c 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -440,8 +440,8 @@ tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t) } /* The finger may slowly drift, adjust the center */ - t->pinned.center.x = t->point.x + t->pinned.center.x / 2; - t->pinned.center.y = t->point.y + t->pinned.center.y / 2; + t->pinned.center.x = (t->point.x + t->pinned.center.x)/2; + t->pinned.center.y = (t->point.y + t->pinned.center.y)/2; } static void From 8025b374d564f4a30b089e5cf6fd65e0c6af8da2 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 12 Jun 2015 17:29:41 +1000 Subject: [PATCH 16/16] touchpad: set the finger pin distance to 5mm where possible On touchpads with resolutions, use a 5mm motion threshold before we unpin the finger (allow motion events while a clickpad button is down). This should remove any erroneous finger movements while clicking, at the cost of having to move the finger a bit more for a single-finger click-and-drag (use two fingers already!) And drop the finger drifting, it was per-event based rather than time-based. So unless the motion threshold was hit in a single event it was possible to move the finger around the whole touchpad without ever unpinning it. Drop the finger drifting altogether, if the touchpad drifts by more than 5mm we have other issues. https://bugzilla.redhat.com/show_bug.cgi?id=1230462 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 19 ++++++++----- src/evdev-mt-touchpad.c | 10 +++---- src/evdev-mt-touchpad.h | 5 +++- src/libinput-util.h | 6 +++++ test/touchpad.c | 48 +++++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 5786ea8b..eb0ddcb0 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -31,7 +31,6 @@ #include "evdev-mt-touchpad.h" -#define DEFAULT_BUTTON_MOTION_THRESHOLD 0.02 /* 2% of size */ #define DEFAULT_BUTTON_ENTER_TIMEOUT 100 /* ms */ #define DEFAULT_BUTTON_LEAVE_TIMEOUT 300 /* ms */ @@ -709,11 +708,19 @@ tp_init_buttons(struct tp_dispatch *tp, absinfo_x = device->abs.absinfo_x; absinfo_y = device->abs.absinfo_y; - width = abs(absinfo_x->maximum - absinfo_x->minimum); - height = abs(absinfo_y->maximum - absinfo_y->minimum); - diagonal = sqrt(width*width + height*height); - - tp->buttons.motion_dist = diagonal * DEFAULT_BUTTON_MOTION_THRESHOLD; + /* pinned-finger motion threshold, see tp_unpin_finger. + The MAGIC for resolution-less touchpads ends up as 2% of the diagonal */ + if (device->abs.fake_resolution) { + const int BUTTON_MOTION_MAGIC = 0.007; + width = abs(absinfo_x->maximum - absinfo_x->minimum); + height = abs(absinfo_y->maximum - absinfo_y->minimum); + diagonal = sqrt(width*width + height*height); + tp->buttons.motion_dist.x_scale_coeff = diagonal * BUTTON_MOTION_MAGIC; + tp->buttons.motion_dist.y_scale_coeff = diagonal * BUTTON_MOTION_MAGIC; + } else { + tp->buttons.motion_dist.x_scale_coeff = 1.0/absinfo_x->resolution; + tp->buttons.motion_dist.y_scale_coeff = 1.0/absinfo_y->resolution; + } tp->buttons.config_method.get_methods = tp_button_config_click_get_methods; tp->buttons.config_method.set_method = tp_button_config_click_set_method; diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index ce79530c..6f115994 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -431,17 +431,15 @@ tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t) return; xdist = abs(t->point.x - t->pinned.center.x); + xdist *= tp->buttons.motion_dist.x_scale_coeff; ydist = abs(t->point.y - t->pinned.center.y); + ydist *= tp->buttons.motion_dist.y_scale_coeff; - if (xdist * xdist + ydist * ydist >= - tp->buttons.motion_dist * tp->buttons.motion_dist) { + /* 3mm movement -> unpin */ + if (vector_length(xdist, ydist) >= 3.0) { t->pinned.is_pinned = false; return; } - - /* The finger may slowly drift, adjust the center */ - t->pinned.center.x = (t->point.x + t->pinned.center.x)/2; - t->pinned.center.y = (t->point.y + t->pinned.center.y)/2; } static void diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index fef5cb32..bd2d1633 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -223,7 +223,10 @@ struct tp_dispatch { bool click_pending; uint32_t state; uint32_t old_state; - uint32_t motion_dist; /* for pinned touches */ + struct { + double x_scale_coeff; + double y_scale_coeff; + } motion_dist; /* for pinned touches */ unsigned int active; /* currently active button, for release event */ bool active_is_topbutton; /* is active a top button? */ diff --git a/src/libinput-util.h b/src/libinput-util.h index 910406cf..224e4b65 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -284,4 +284,10 @@ int parse_mouse_dpi_property(const char *prop); int parse_mouse_wheel_click_angle_property(const char *prop); double parse_trackpoint_accel_property(const char *prop); +static inline double +vector_length(double x, double y) +{ + return sqrt(x * x + y * y); +} + #endif /* LIBINPUT_UTIL_H */ diff --git a/test/touchpad.c b/test/touchpad.c index 692698ce..1e5e97bd 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2153,6 +2153,53 @@ START_TEST(clickpad_click_n_drag) } END_TEST +START_TEST(clickpad_finger_pin) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libevdev *evdev = dev->evdev; + const struct input_absinfo *abs; + + abs = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X); + ck_assert_notnull(abs); + if (abs->resolution == 0) + return; + + litest_drain_events(li); + + /* make sure the movement generates pointer events when + not pinned */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 52, 52, 10, 1); + litest_touch_move_to(dev, 0, 52, 52, 48, 48, 10, 1); + litest_touch_move_to(dev, 0, 48, 48, 50, 50, 10, 1); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_button_click(dev, BTN_LEFT, true); + litest_drain_events(li); + + litest_touch_move_to(dev, 0, 50, 50, 52, 52, 10, 1); + litest_touch_move_to(dev, 0, 52, 52, 48, 48, 10, 1); + litest_touch_move_to(dev, 0, 48, 48, 50, 50, 10, 1); + + litest_assert_empty_queue(li); + + litest_button_click(dev, BTN_LEFT, false); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON); + + /* still pinned after release */ + litest_touch_move_to(dev, 0, 50, 50, 52, 52, 10, 1); + litest_touch_move_to(dev, 0, 52, 52, 48, 48, 10, 1); + litest_touch_move_to(dev, 0, 48, 48, 50, 50, 10, 1); + + litest_assert_empty_queue(li); + + /* move to unpin */ + litest_touch_move_to(dev, 0, 50, 50, 70, 70, 10, 1); + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); +} +END_TEST + START_TEST(clickpad_softbutton_left) { struct litest_device *dev = litest_current_device(); @@ -5144,6 +5191,7 @@ litest_setup_tests(void) litest_add("touchpad:click", touchpad_btn_left, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD); litest_add("touchpad:click", clickpad_btn_left, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:click", clickpad_click_n_drag, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH); + litest_add("touchpad:click", clickpad_finger_pin, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:softbutton", clickpad_softbutton_left, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); litest_add("touchpad:softbutton", clickpad_softbutton_right, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD);