From 7d7ea4ec2ea87f73cefaab86e5e6ac17ff60e4c0 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 28 Jan 2015 12:52:22 +1000 Subject: [PATCH 01/20] Add missing doxygen ingroup tag for libinput_device_get_udev_device Signed-off-by: Peter Hutterer --- src/libinput.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libinput.h b/src/libinput.h index 7b7a2dbf..4a3c44d7 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1515,6 +1515,8 @@ libinput_device_set_seat_logical_name(struct libinput_device *device, const char *name); /** + * @ingroup device + * * Return a udev handle to the device that is this libinput device, if any. * The returned handle has a refcount of at least 1, the caller must call * udev_device_unref() once to release the associated resources. From 4ec2947cc9fb804c9bc0262563e5b373affb4c42 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 2 Feb 2015 10:47:52 +1000 Subject: [PATCH 02/20] test: add per-device udev rule support Don't rely on a magic version tag, instead let a device define a udev rule and drop that into the udev runtime directory before the device is created. There are a couple of caveats with this approach: first, since this changes system-wide state it may cause issues on the device the test suite is run on. This can be avoided if the udev rules have filter patterns that ensure only test devices are affected. Second, the check test suite aborts but it doesn't run the teardown() function if a test fails. So far this wasn't a problem since uinput devices disappear whenever we exit. The rules files will hang around though, so an unchecked fixture was added to delete all litest-foo.rules files before and after a test case starts. Unchecked fixtures are run regardless of the exit status of the test but run in the same address space - i.e. no ck_assert() usage. Also unchecked fixtures are only run once per test-case, not once per test function. For us, that means they're only run once per device (we use the devices as test case), i.e. if a test fails and the udev rule isn't tidied up, the next test may be unpredictable. This shouldn't matter too much though. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- configure.ac | 1 + src/evdev-mt-touchpad.c | 6 -- test/litest-int.h | 2 + test/litest-synaptics-x1-carbon-3rd.c | 14 +++- test/litest.c | 109 +++++++++++++++++++++++++- test/litest.h | 2 + 6 files changed, 125 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 39e42d0f..803d5733 100644 --- a/configure.ac +++ b/configure.ac @@ -34,6 +34,7 @@ LIBINPUT_LT_VERSION=8:0:1 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) +AC_USE_SYSTEM_EXTENSIONS # Check for programs AC_PROG_CC_C99 diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 94cbc136..ae37ab17 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1107,12 +1107,6 @@ tp_tag_device(struct evdev_device *device, if (udev_device_get_property_value(udev_device, "TOUCHPAD_HAS_TRACKPOINT_BUTTONS")) device->tags |= EVDEV_TAG_TOUCHPAD_TRACKPOINT; - - /* Magic version tag: used by the litest device. Should never be set - * in real life but allows us to test for these features without - * requiring custom udev rules during make check */ - if (libevdev_get_id_version(device->evdev) == 0xfffa) - device->tags |= EVDEV_TAG_TOUCHPAD_TRACKPOINT; } static struct evdev_dispatch_interface tp_interface = { diff --git a/test/litest-int.h b/test/litest-int.h index 95bc2483..12746f1b 100644 --- a/test/litest-int.h +++ b/test/litest-int.h @@ -69,6 +69,8 @@ struct litest_test_device { */ struct input_absinfo *absinfo; struct litest_device_interface *interface; + + const char *udev_rule; }; struct litest_device_interface { diff --git a/test/litest-synaptics-x1-carbon-3rd.c b/test/litest-synaptics-x1-carbon-3rd.c index 8be29e03..67d6f467 100644 --- a/test/litest-synaptics-x1-carbon-3rd.c +++ b/test/litest-synaptics-x1-carbon-3rd.c @@ -65,7 +65,6 @@ static struct input_id input_id = { .bustype = 0x11, .vendor = 0x2, .product = 0x7, - .version = 0xfffa, /* Magic value, used to detect this test device */ }; static int events[] = { @@ -97,6 +96,16 @@ static struct input_absinfo absinfo[] = { { .value = -1 } }; +static const char udev_rule[] = +"ACTION==\"remove\", GOTO=\"touchpad_end\"\n" +"KERNEL!=\"event*\", GOTO=\"touchpad_end\"\n" +"ENV{ID_INPUT_TOUCHPAD}==\"\", GOTO=\"touchpad_end\"\n" +"\n" +"ATTRS{name}==\"litest*X1C3rd*\",\\\n" +" ENV{TOUCHPAD_HAS_TRACKPOINT_BUTTONS}=\"1\"\n" +"\n" +"LABEL=\"touchpad_end\""; + struct litest_test_device litest_synaptics_carbon3rd_device = { .type = LITEST_SYNAPTICS_TRACKPOINT_BUTTONS, .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON, @@ -104,8 +113,9 @@ struct litest_test_device litest_synaptics_carbon3rd_device = { .setup = litest_synaptics_carbon3rd_setup, .interface = &interface, - .name = "SynPS/2 Synaptics TouchPad", + .name = "SynPS/2 Synaptics TouchPad X1C3rd", .id = &input_id, .events = events, .absinfo = absinfo, + .udev_rule = udev_rule, }; diff --git a/test/litest.c b/test/litest.c index 2a336e9a..b220b2fe 100644 --- a/test/litest.c +++ b/test/litest.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,9 @@ #include "litest-int.h" #include "libinput-util.h" +#define UDEV_RULES_D "/run/udev/rules.d" +#define UDEV_RULE_PREFIX "99-litest-" + static int in_debugger = -1; static int verbose = 0; @@ -114,6 +118,55 @@ struct litest_test_device* devices[] = { static struct list all_tests; +static void +litest_reload_udev_rules(void) +{ + system("udevadm control --reload-rules"); +} + +static int +litest_udev_rule_filter(const struct dirent *entry) +{ + return strncmp(entry->d_name, + UDEV_RULE_PREFIX, + strlen(UDEV_RULE_PREFIX)) == 0; +} + +static void +litest_drop_udev_rules(void) +{ + int n; + int rc; + struct dirent **entries; + char path[PATH_MAX]; + + n = scandir(UDEV_RULES_D, + &entries, + litest_udev_rule_filter, + alphasort); + if (n < 0) + return; + + while (n--) { + rc = snprintf(path, sizeof(path), + "%s/%s", + UDEV_RULES_D, + entries[n]->d_name); + if (rc > 0 && + (size_t)rc == strlen(UDEV_RULES_D) + + strlen(entries[n]->d_name) + 1) + unlink(path); + else + fprintf(stderr, + "Failed to delete %s. Remaining tests are unreliable\n", + entries[n]->d_name); + free(entries[n]); + } + free(entries); + + litest_reload_udev_rules(); +} + static void litest_add_tcase_for_device(struct suite *suite, void *func, @@ -134,6 +187,13 @@ litest_add_tcase_for_device(struct suite *suite, t->name = strdup(test_name); t->tc = tcase_create(test_name); list_insert(&suite->tests, &t->node); + /* we can't guarantee that we clean up properly if a test fails, the + udev rules used for a previous test may still be in place. Add an + unchecked fixture to always clean up all rules before/after a + test case completes */ + tcase_add_unchecked_fixture(t->tc, + litest_drop_udev_rules, + litest_drop_udev_rules); tcase_add_checked_fixture(t->tc, dev->setup, dev->teardown ? dev->teardown : litest_generic_device_teardown); tcase_add_test(t->tc, func); @@ -464,6 +524,40 @@ merge_events(const int *orig, const int *override) return events; } +static char * +litest_init_udev_rules(struct litest_test_device *dev) +{ + int rc; + FILE *f; + char *path; + + if (!dev->udev_rule) + return 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 = asprintf(&path, + "%s/%s%s.rules", + UDEV_RULES_D, + UDEV_RULE_PREFIX, + dev->shortname); + ck_assert_int_eq(rc, + strlen(UDEV_RULES_D) + + strlen(UDEV_RULE_PREFIX) + + strlen(dev->shortname) + 7); + f = fopen(path, "w"); + ck_assert_notnull(f); + ck_assert_int_ge(fputs(dev->udev_rule, f), 0); + fclose(f); + + litest_reload_udev_rules(); + + return path; +} + static struct litest_device * litest_create(enum litest_device_type which, const char *name_override, @@ -477,6 +571,7 @@ litest_create(enum litest_device_type which, const struct input_id *id; struct input_absinfo *abs; int *events; + char *udev_file; dev = devices; while (*dev) { @@ -491,12 +586,17 @@ litest_create(enum litest_device_type which, d = zalloc(sizeof(*d)); ck_assert(d != NULL); + udev_file = litest_init_udev_rules(*dev); + /* device has custom create method */ if ((*dev)->create) { (*dev)->create(d); - if (abs_override || events_override) + if (abs_override || events_override) { + if (udev_file) + unlink(udev_file); ck_abort_msg("Custom create cannot" "be overridden"); + } return d; } @@ -511,6 +611,7 @@ litest_create(enum litest_device_type which, abs, events); d->interface = (*dev)->interface; + d->udev_rule_file = udev_file; free(abs); free(events); @@ -629,6 +730,12 @@ litest_delete_device(struct litest_device *d) if (!d) return; + if (d->udev_rule_file) { + unlink(d->udev_rule_file); + free(d->udev_rule_file); + d->udev_rule_file = NULL; + } + libinput_device_unref(d->libinput_device); libinput_path_remove_device(d->libinput_device); if (d->owns_context) diff --git a/test/litest.h b/test/litest.h index 0625a71e..60ec4587 100644 --- a/test/litest.h +++ b/test/litest.h @@ -84,6 +84,8 @@ struct litest_device { bool skip_ev_syn; void *private; /* device-specific data */ + + char *udev_rule_file; }; struct libinput *litest_create_context(void); From ecec6721d14f593ff3a767adac1eec8def9703c5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 Feb 2015 09:14:20 +1000 Subject: [PATCH 03/20] cosmetic: remove double empty-line Signed-off-by: Peter Hutterer --- src/evdev.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/evdev.c b/src/evdev.c index 24d30e0a..1b3bb90c 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1232,7 +1232,6 @@ evdev_device_init_pointer_acceleration(struct evdev_device *device) return 0; } - static inline int evdev_need_mtdev(struct evdev_device *device) { From b21fbfd947d4bc18b766c46ab1d85a3f8d0977f4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 Feb 2015 09:20:59 +1000 Subject: [PATCH 04/20] filter: zalloc the struct to make sure the speed is initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filter.c b/src/filter.c index 89390d12..72ef7609 100644 --- a/src/filter.c +++ b/src/filter.c @@ -286,7 +286,7 @@ create_pointer_accelerator_filter(accel_profile_func_t profile) { struct pointer_accelerator *filter; - filter = malloc(sizeof *filter); + filter = zalloc(sizeof *filter); if (filter == NULL) return NULL; From 199cd87b071f6fd0d4a1065ae3d678552259f883 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 Feb 2015 09:24:08 +1000 Subject: [PATCH 05/20] evdev: set the default speed after initializing ptraccel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/evdev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index 1b3bb90c..b2b8cbf8 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1229,6 +1229,9 @@ evdev_device_init_pointer_acceleration(struct evdev_device *device) device->pointer.config.get_default_speed = evdev_accel_config_get_default_speed; device->base.config.accel = &device->pointer.config; + evdev_accel_config_set_speed(&device->base, + evdev_accel_config_get_default_speed(&device->base)); + return 0; } From 2a95a8586d792bfb8850d366f283f615f2a58489 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 Feb 2015 09:35:19 +1000 Subject: [PATCH 06/20] test: add pointer acceleration defaults test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- test/pointer.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/pointer.c b/test/pointer.c index 45e0d57e..9a912318 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -732,6 +732,44 @@ START_TEST(pointer_scroll_button) } END_TEST +START_TEST(pointer_accel_defaults) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_status status; + double speed; + + ck_assert(libinput_device_config_accel_is_available(device)); + ck_assert(libinput_device_config_accel_get_default_speed(device) == 0.0); + ck_assert(libinput_device_config_accel_get_speed(device) == 0.0); + + for (speed = -2.0; speed < -1.0; speed += 0.2) { + status = libinput_device_config_accel_set_speed(device, + speed); + ck_assert_int_eq(status, + LIBINPUT_CONFIG_STATUS_INVALID); + ck_assert(libinput_device_config_accel_get_speed(device) == 0.0); + } + + for (speed = -1.0; speed <= 1.0; speed += 0.2) { + status = libinput_device_config_accel_set_speed(device, + speed); + ck_assert_int_eq(status, + LIBINPUT_CONFIG_STATUS_SUCCESS); + ck_assert(libinput_device_config_accel_get_speed(device) == speed); + } + + for (speed = 1.2; speed <= -2.0; speed += 0.2) { + status = libinput_device_config_accel_set_speed(device, + speed); + ck_assert_int_eq(status, + LIBINPUT_CONFIG_STATUS_INVALID); + ck_assert(libinput_device_config_accel_get_speed(device) == 1.0); + } + +} +END_TEST + int main (int argc, char **argv) { litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY); @@ -754,5 +792,7 @@ int main (int argc, char **argv) { litest_add("pointer:left-handed", pointer_left_handed_during_click, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); litest_add("pointer:left-handed", pointer_left_handed_during_click_multiple_buttons, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); + litest_add("pointer:accel", pointer_accel_defaults, LITEST_RELATIVE, LITEST_ANY); + return litest_run(argc, argv); } From 154b3cf74941c26cdf77a24136c273ee7d9f6e65 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 Feb 2015 09:37:53 +1000 Subject: [PATCH 07/20] Don't init pointer acceleration on absolute devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note: touchpads have a different backend, we never get here in that case. This only applies to true absolute pointer devices. Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/evdev.c | 3 ++- test/pointer.c | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/evdev.c b/src/evdev.c index b2b8cbf8..6e318dc4 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1473,7 +1473,8 @@ evdev_configure_device(struct evdev_device *device) has_keyboard = 1; if ((has_abs || has_rel) && has_button) { - if (evdev_device_init_pointer_acceleration(device) == -1) + if (has_rel && + evdev_device_init_pointer_acceleration(device) == -1) return -1; device->seat_caps |= EVDEV_DEVICE_POINTER; diff --git a/test/pointer.c b/test/pointer.c index 9a912318..9a1e9d6c 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -770,6 +770,31 @@ START_TEST(pointer_accel_defaults) } END_TEST +START_TEST(pointer_accel_defaults_absolute) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_status status; + double speed; + + ck_assert(!libinput_device_config_accel_is_available(device)); + ck_assert(libinput_device_config_accel_get_default_speed(device) == 0.0); + ck_assert(libinput_device_config_accel_get_speed(device) == 0.0); + + for (speed = -2.0; speed <= 2.0; speed += 0.2) { + status = libinput_device_config_accel_set_speed(device, + speed); + if (speed >= -1.0 && speed <= 1.0) + ck_assert_int_eq(status, + LIBINPUT_CONFIG_STATUS_UNSUPPORTED); + else + ck_assert_int_eq(status, + LIBINPUT_CONFIG_STATUS_INVALID); + ck_assert(libinput_device_config_accel_get_speed(device) == 0.0); + } +} +END_TEST + int main (int argc, char **argv) { litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY); @@ -793,6 +818,7 @@ int main (int argc, char **argv) { litest_add("pointer:left-handed", pointer_left_handed_during_click_multiple_buttons, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); litest_add("pointer:accel", pointer_accel_defaults, LITEST_RELATIVE, LITEST_ANY); + litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_ANY); return litest_run(argc, argv); } From 536dd6a40fd1f48cdf1cdce888a836708e2e05cc Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 Feb 2015 09:36:17 +1000 Subject: [PATCH 08/20] zalloc the libinput_source, don't malloc it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/libinput.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libinput.c b/src/libinput.c index 951698a0..7456b908 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -510,7 +510,7 @@ libinput_add_fd(struct libinput *libinput, struct libinput_source *source; struct epoll_event ep; - source = malloc(sizeof *source); + source = zalloc(sizeof *source); if (!source) return NULL; From 26140aba235fe91257b8ee7fb3178c92f20a5229 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 Feb 2015 13:42:44 +1000 Subject: [PATCH 09/20] test: switch event conversion tests to use litest devices Makes the code use more commonly used paths, no real functional changes at this point. This was using hand-crafted devices as it predates the litest_add_for_device() helper. For an upcoming patch to use the udev ID_INPUT_. tags the event_conversion_key test requires this change: without it the device will be tagged with ID_INPUT_KEY but not ID_INPUT_KEYBOARD. This could be fixed by adding all normal keyboard keys to the uinput device but it's easier to just re-use litest. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/misc.c | 132 ++++++++++++++++------------------------------------ 1 file changed, 41 insertions(+), 91 deletions(-) diff --git a/test/misc.c b/test/misc.c index 779b600f..5b8191f2 100644 --- a/test/misc.c +++ b/test/misc.c @@ -144,30 +144,20 @@ END_TEST START_TEST(event_conversion_pointer) { - struct libevdev_uinput *uinput; - struct libinput *li; + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; struct libinput_event *event; int motion = 0, button = 0; - uinput = create_simple_test_device("litest test device", - EV_REL, REL_X, - EV_REL, REL_Y, - EV_KEY, BTN_LEFT, - EV_KEY, BTN_MIDDLE, - EV_KEY, BTN_LEFT, - -1, -1); - li = libinput_path_create_context(&simple_interface, NULL); - libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput)); - /* Queue at least two relative motion events as the first one may * be absorbed by the pointer acceleration filter. */ - libevdev_uinput_write_event(uinput, EV_REL, REL_X, -1); - libevdev_uinput_write_event(uinput, EV_REL, REL_Y, -1); - libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0); - libevdev_uinput_write_event(uinput, EV_REL, REL_X, -1); - libevdev_uinput_write_event(uinput, EV_REL, REL_Y, -1); - libevdev_uinput_write_event(uinput, EV_KEY, BTN_LEFT, 1); - libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_REL, REL_X, -1); + litest_event(dev, EV_REL, REL_Y, -1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_REL, REL_X, -1); + litest_event(dev, EV_REL, REL_Y, -1); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); libinput_dispatch(li); @@ -195,9 +185,6 @@ START_TEST(event_conversion_pointer) libinput_event_destroy(event); } - libinput_unref(li); - libevdev_uinput_destroy(uinput); - ck_assert_int_gt(motion, 0); ck_assert_int_gt(button, 0); } @@ -205,29 +192,18 @@ END_TEST START_TEST(event_conversion_pointer_abs) { - struct libevdev_uinput *uinput; - struct libinput *li; + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; struct libinput_event *event; int motion = 0, button = 0; - uinput = create_simple_test_device("litest test device", - EV_ABS, ABS_X, - EV_ABS, ABS_Y, - EV_KEY, BTN_LEFT, - EV_KEY, BTN_MIDDLE, - EV_KEY, BTN_LEFT, - -1, -1); - li = libinput_path_create_context(&simple_interface, NULL); - libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput)); - libinput_dispatch(li); - - libevdev_uinput_write_event(uinput, EV_ABS, ABS_X, 10); - libevdev_uinput_write_event(uinput, EV_ABS, ABS_Y, 50); - libevdev_uinput_write_event(uinput, EV_KEY, BTN_LEFT, 1); - libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0); - libevdev_uinput_write_event(uinput, EV_ABS, ABS_X, 30); - libevdev_uinput_write_event(uinput, EV_ABS, ABS_Y, 30); - libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_ABS, ABS_X, 10); + litest_event(dev, EV_ABS, ABS_Y, 50); + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_ABS, ABS_X, 30); + litest_event(dev, EV_ABS, ABS_Y, 30); + litest_event(dev, EV_SYN, SYN_REPORT, 0); libinput_dispatch(li); @@ -255,9 +231,6 @@ START_TEST(event_conversion_pointer_abs) libinput_event_destroy(event); } - libinput_unref(li); - libevdev_uinput_destroy(uinput); - ck_assert_int_gt(motion, 0); ck_assert_int_gt(button, 0); } @@ -265,23 +238,15 @@ END_TEST START_TEST(event_conversion_key) { - struct libevdev_uinput *uinput; - struct libinput *li; + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; struct libinput_event *event; int key = 0; - uinput = create_simple_test_device("litest test device", - EV_KEY, KEY_A, - EV_KEY, KEY_B, - -1, -1); - li = libinput_path_create_context(&simple_interface, NULL); - libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput)); - libinput_dispatch(li); - - libevdev_uinput_write_event(uinput, EV_KEY, KEY_A, 1); - libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0); - libevdev_uinput_write_event(uinput, EV_KEY, KEY_A, 0); - libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, KEY_A, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, KEY_A, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); libinput_dispatch(li); @@ -305,42 +270,28 @@ START_TEST(event_conversion_key) libinput_event_destroy(event); } - libinput_unref(li); - libevdev_uinput_destroy(uinput); - ck_assert_int_gt(key, 0); } END_TEST START_TEST(event_conversion_touch) { - struct libevdev_uinput *uinput; - struct libinput *li; + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; struct libinput_event *event; int touch = 0; - uinput = create_simple_test_device("litest test device", - EV_KEY, BTN_TOUCH, - EV_ABS, ABS_X, - EV_ABS, ABS_Y, - EV_ABS, ABS_MT_SLOT, - EV_ABS, ABS_MT_TRACKING_ID, - EV_ABS, ABS_MT_POSITION_X, - EV_ABS, ABS_MT_POSITION_Y, - -1, -1); - li = libinput_path_create_context(&simple_interface, NULL); - libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput)); libinput_dispatch(li); - libevdev_uinput_write_event(uinput, EV_KEY, BTN_TOOL_FINGER, 1); - libevdev_uinput_write_event(uinput, EV_KEY, BTN_TOUCH, 1); - libevdev_uinput_write_event(uinput, EV_ABS, ABS_X, 10); - libevdev_uinput_write_event(uinput, EV_ABS, ABS_Y, 10); - libevdev_uinput_write_event(uinput, EV_ABS, ABS_MT_SLOT, 0); - libevdev_uinput_write_event(uinput, EV_ABS, ABS_MT_TRACKING_ID, 1); - libevdev_uinput_write_event(uinput, EV_ABS, ABS_MT_POSITION_X, 10); - libevdev_uinput_write_event(uinput, EV_ABS, ABS_MT_POSITION_Y, 10); - libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1); + litest_event(dev, EV_KEY, BTN_TOUCH, 1); + litest_event(dev, EV_ABS, ABS_X, 10); + litest_event(dev, EV_ABS, ABS_Y, 10); + 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, 10); + litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 10); + litest_event(dev, EV_SYN, SYN_REPORT, 0); libinput_dispatch(li); @@ -365,9 +316,6 @@ START_TEST(event_conversion_touch) libinput_event_destroy(event); } - libinput_unref(li); - libevdev_uinput_destroy(uinput); - ck_assert_int_gt(touch, 0); } END_TEST @@ -601,10 +549,12 @@ END_TEST int main (int argc, char **argv) { litest_add_no_device("events:conversion", event_conversion_device_notify); - litest_add_no_device("events:conversion", event_conversion_pointer); - litest_add_no_device("events:conversion", event_conversion_pointer_abs); - litest_add_no_device("events:conversion", event_conversion_key); - litest_add_no_device("events:conversion", event_conversion_touch); + litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE); + litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE); + litest_add_for_device("events:conversion", event_conversion_pointer_abs, LITEST_XEN_VIRTUAL_POINTER); + litest_add_for_device("events:conversion", event_conversion_key, LITEST_KEYBOARD); + litest_add_for_device("events:conversion", event_conversion_touch, LITEST_WACOM_TOUCH); + litest_add_no_device("context:refcount", context_ref_counting); litest_add_no_device("config:status string", config_status_string); From bcb6f22d89783418b70363d6a2fddefd450c441d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 Feb 2015 14:18:15 +1000 Subject: [PATCH 10/20] path: make sure udev devices are initialized before usage When creating uinput devices, we get the devnode from the kernel directly rather than through udev. When we add this to the path backend too quickly the udev_device we get may not be fully initialized and properties may be missing. This causes false test results. Avoid this by making sure the handle we have is initialized. This should never trigger on a real device anyway, even creating a device through litest is slow enough to avoid this issue. Only affected are the tests in misc.c where we create the uinput device directly. Nonetheless, handle this for the generic case so we don't run into heisenbugs later. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/path.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/path.c b/src/path.c index dd70efc6..832a1fd8 100644 --- a/src/path.c +++ b/src/path.c @@ -291,14 +291,34 @@ libinput_path_create_context(const struct libinput_interface *interface, } static inline struct udev_device * -udev_device_from_devnode(struct udev *udev, const char *devnode) +udev_device_from_devnode(struct libinput *libinput, + struct udev *udev, + const char *devnode) { + struct udev_device *dev; struct stat st; + size_t count = 0; if (stat(devnode, &st) < 0) return NULL; - return udev_device_new_from_devnum(udev, 'c', st.st_rdev); + dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev); + + while (dev && !udev_device_get_is_initialized(dev)) { + udev_device_unref(dev); + msleep(10); + dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev); + + count++; + if (count > 10) { + log_bug_libinput(libinput, + "udev device never initialized (%s)\n", + devnode); + break; + } + } + + return dev; } LIBINPUT_EXPORT struct libinput_device * @@ -315,7 +335,7 @@ libinput_path_add_device(struct libinput *libinput, return NULL; } - udev_device = udev_device_from_devnode(udev, path); + udev_device = udev_device_from_devnode(libinput, udev, path); if (!udev_device) { log_bug_client(libinput, "Invalid path %s\n", path); return NULL; From 0968c067f6b13324e92145977ab24cdb0995436a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 4 Feb 2015 09:11:46 +1000 Subject: [PATCH 11/20] touchpad: mark two switch fallthroughs with comments Coverity pointed these out, they're false positives but mark them with comments to make it obvious to the reader. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-buttons.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index ca7495f8..9dbb5137 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -823,6 +823,7 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time) break; case BUTTON_EVENT_IN_TOP_L: is_top = 1; + /* fallthrough */ case BUTTON_EVENT_IN_BOTTOM_L: button |= LEFT; break; @@ -832,6 +833,7 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time) break; case BUTTON_EVENT_IN_TOP_R: is_top = 1; + /* fallthrough */ case BUTTON_EVENT_IN_BOTTOM_R: button |= RIGHT; break; From 65b5647076f72731e3e49dd4ddfbbb09fdb48351 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 5 Feb 2015 11:46:55 +1000 Subject: [PATCH 12/20] cosmetic: fix grammar in doxygen Signed-off-by: Peter Hutterer --- src/libinput.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libinput.h b/src/libinput.h index 4a3c44d7..c986bfe7 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1364,7 +1364,7 @@ libinput_device_ref(struct libinput_device *device); * the device correctly to avoid dangling pointers. * * @param device A previously obtained device - * @return NULL if device was destroyed, otherwise the passed device + * @return NULL if the device was destroyed, otherwise the passed device */ struct libinput_device * libinput_device_unref(struct libinput_device *device); From f420c54a991f714d9821e054ae70f06cfc8894db Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Thu, 5 Feb 2015 14:33:31 +0100 Subject: [PATCH 13/20] Fix an abort if the device speed is NaN When using libinput with xf86-input-libinput, the device speed is represented as a float passed via X properties. If a buggy client gives a broken value, the conversions that occur can cause the value of speed to be NaN (not a number), aka infinity. In C, any comparison with NaN always gives false, whatever the value. So that test in libinput_device_config_accel_set_speed(): (speed < 1.0 || speed > 1.0) will necessarily return FALSE, defeating the test of range. However, since since any comparison with NaN is false, the opposite assert() in accelerator_set_speed(): (speed >= 1.0 && speed <= 1.0) will be false as well, thus triggering the abort() and the crash of the entire X server along with it. The solution is to use the same construct in both routines, so that it fails gracefully in libinput_device_config_accel_set_speed(). Signed-off-by: Olivier Fourdan Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/libinput.c | 3 ++- test/pointer.c | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/libinput.c b/src/libinput.c index 7456b908..ba60c13c 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1534,7 +1534,8 @@ LIBINPUT_EXPORT enum libinput_config_status libinput_device_config_accel_set_speed(struct libinput_device *device, double speed) { - if (speed < -1.0 || speed > 1.0) + /* Need the negation in case speed is NaN */ + if (!(speed >= -1.0 && speed <= 1.0)) return LIBINPUT_CONFIG_STATUS_INVALID; if (!libinput_device_config_accel_is_available(device)) diff --git a/test/pointer.c b/test/pointer.c index 9a1e9d6c..2e52a206 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -770,6 +770,23 @@ START_TEST(pointer_accel_defaults) } END_TEST +START_TEST(pointer_accel_invalid) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_status status; + + ck_assert(libinput_device_config_accel_is_available(device)); + + status = libinput_device_config_accel_set_speed(device, + NAN); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID); + status = libinput_device_config_accel_set_speed(device, + INFINITY); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID); +} +END_TEST + START_TEST(pointer_accel_defaults_absolute) { struct litest_device *dev = litest_current_device(); @@ -818,6 +835,7 @@ int main (int argc, char **argv) { litest_add("pointer:left-handed", pointer_left_handed_during_click_multiple_buttons, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); litest_add("pointer:accel", pointer_accel_defaults, LITEST_RELATIVE, LITEST_ANY); + litest_add("pointer:accel", pointer_accel_invalid, LITEST_RELATIVE, LITEST_ANY); litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_ANY); return litest_run(argc, argv); From ca4e03fb11429b6a3704206124fbff2f57d121f9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 6 Feb 2015 10:33:02 +1000 Subject: [PATCH 14/20] configure.ac: libinput 0.10.0 Signed-off-by: Peter Hutterer --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 803d5733..f47c5a49 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.64]) m4_define([libinput_major_version], [0]) -m4_define([libinput_minor_version], [9]) +m4_define([libinput_minor_version], [10]) m4_define([libinput_micro_version], [0]) m4_define([libinput_version], [libinput_major_version.libinput_minor_version.libinput_micro_version]) @@ -30,7 +30,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) # - If binary compatibility has been broken (eg removed or changed interfaces) # change to C+1:0:0 # - If the interface is the same as the previous version, change to C:R+1:A -LIBINPUT_LT_VERSION=8:0:1 +LIBINPUT_LT_VERSION=8:1:1 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) From e3a43902f9aaa34bc5931c33c83482dea9dc55ec Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 5 Feb 2015 13:39:04 +1000 Subject: [PATCH 15/20] Introduce device groups to group logical devices together MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Devices like Wacom tablets have multiple event nodes (touch, pad and stylus). This requires some logical grouping, e.g. setting an Intuos 5 tablet left-handed effectively turns it upside down. That then applies to both the stylus and the touch device. Merging the devices into one struct libinput_device is not feasable, it complicates the API for little benefit. A caller would still need access to all subdevices to get udev handles, etc. Some configuration options apply to the whole device (left-handed) but some (may) only apply to a single subdevice (calibration, natural scrolling). Addressing this would make the libinput API unwieldly and hard to use. Instead, add a device group concept. Each device is a member of a device group - a singleton for most devices. Wacom tablets will have a single group across multiple devices, allowing the caller to associate the devices together if needed. The API is intentionally very simple and requires the caller to keep track of groups and which/how many devices are in it. The caller has more powerful libraries available to do that than we have. This patch does not address the actual merging of devices into the same device group, it simply creates a new group for each new device. [rebased on top of 0.10] Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Reviewed-by: Jonas Ådahl --- src/evdev.c | 10 +++++ src/libinput-private.h | 13 ++++++ src/libinput.c | 64 ++++++++++++++++++++++++++ src/libinput.h | 100 +++++++++++++++++++++++++++++++++++++++++ src/libinput.sym | 8 ++++ test/device.c | 51 +++++++++++++++++++++ 6 files changed, 246 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index 6e318dc4..6024d383 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1572,6 +1572,7 @@ evdev_device_create(struct libinput_seat *seat, int fd; int unhandled_device = 0; const char *devnode = udev_device_get_devnode(udev_device); + struct libinput_device_group *group; /* Use non-blocking mode so that we can loop on read on * evdev_device_data() until all events on the fd are @@ -1642,6 +1643,12 @@ evdev_device_create(struct libinput_seat *seat, if (!device->source) goto err; + group = libinput_device_group_create(); + if (!group) + goto err; + libinput_device_set_device_group(&device->base, group); + libinput_device_group_unref(group); + list_insert(seat->devices_list.prev, &device->base.link); evdev_tag_device(device); @@ -2123,6 +2130,9 @@ evdev_device_destroy(struct evdev_device *device) if (dispatch) dispatch->interface->destroy(dispatch); + if (device->base.group) + libinput_device_group_unref(device->base.group); + filter_destroy(device->pointer.filter); libinput_seat_unref(device->base.seat); libevdev_free(device->evdev); diff --git a/src/libinput-private.h b/src/libinput-private.h index b938ed09..23f66e40 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -165,8 +165,14 @@ struct libinput_device_config { struct libinput_device_config_click_method *click_method; }; +struct libinput_device_group { + int refcount; + void *user_data; +}; + struct libinput_device { struct libinput_seat *seat; + struct libinput_device_group *group; struct list link; struct list event_listeners; void *user_data; @@ -240,6 +246,13 @@ void libinput_device_init(struct libinput_device *device, struct libinput_seat *seat); +struct libinput_device_group * +libinput_device_group_create(void); + +void +libinput_device_set_device_group(struct libinput_device *device, + struct libinput_device_group *group); + void libinput_device_add_event_listener(struct libinput_device *device, struct libinput_event_listener *listener, diff --git a/src/libinput.c b/src/libinput.c index ba60c13c..81862d56 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1277,6 +1277,12 @@ libinput_device_get_context(struct libinput_device *device) return libinput_seat_get_context(device->seat); } +LIBINPUT_EXPORT struct libinput_device_group * +libinput_device_get_device_group(struct libinput_device *device) +{ + return device->group; +} + LIBINPUT_EXPORT const char * libinput_device_get_sysname(struct libinput_device *device) { @@ -1387,6 +1393,64 @@ libinput_event_touch_get_base_event(struct libinput_event_touch *event) return &event->base; } +LIBINPUT_EXPORT struct libinput_device_group * +libinput_device_group_ref(struct libinput_device_group *group) +{ + group->refcount++; + return group; +} + +struct libinput_device_group * +libinput_device_group_create(void) +{ + struct libinput_device_group *group; + + group = zalloc(sizeof *group); + if (group) + group->refcount = 1; + return group; +} + +void +libinput_device_set_device_group(struct libinput_device *device, + struct libinput_device_group *group) +{ + device->group = group; + libinput_device_group_ref(group); +} + +static void +libinput_device_group_destroy(struct libinput_device_group *group) +{ + free(group); +} + +LIBINPUT_EXPORT struct libinput_device_group * +libinput_device_group_unref(struct libinput_device_group *group) +{ + assert(group->refcount > 0); + group->refcount--; + if (group->refcount == 0) { + libinput_device_group_destroy(group); + return NULL; + } else { + return group; + } +} + +LIBINPUT_EXPORT void +libinput_device_group_set_user_data(struct libinput_device_group *group, + void *user_data) +{ + group->user_data = user_data; +} + +LIBINPUT_EXPORT void * +libinput_device_group_get_user_data(struct libinput_device_group *group) +{ + return group->user_data; +} + LIBINPUT_EXPORT const char * libinput_config_status_to_str(enum libinput_config_status status) { diff --git a/src/libinput.h b/src/libinput.h index c986bfe7..3b179a8c 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -194,6 +194,16 @@ struct libinput; */ struct libinput_device; +/** + * @ingroup device + * @struct libinput_device_group + * + * A base handle for accessing libinput device groups. This struct is + * refcounted, use libinput_device_group_ref() and + * libinput_device_group_unref(). + */ +struct libinput_device_group; + /** * @ingroup seat * @struct libinput_seat @@ -1406,6 +1416,39 @@ libinput_device_get_user_data(struct libinput_device *device); struct libinput * libinput_device_get_context(struct libinput_device *device); +/** + * @ingroup device + * + * Get the device group this device is assigned to. Some physical + * devices like graphics tablets are represented by multiple kernel + * devices and thus by multiple struct libinput_device. + * + * libinput assigns these devices to the same libinput_device_group + * allowing the caller to identify such devices and adjust configuration + * settings accordingly. For example, setting a tablet to left-handed often + * means turning it upside down. A touch device on the same tablet would + * need to be turned upside down too to work correctly. + * + * All devices are part of a device group though for most devices the group + * will be a singleton. A device is assigned to a device group on @ref + * LIBINPUT_EVENT_DEVICE_ADDED and removed from that group on @ref + * LIBINPUT_EVENT_DEVICE_REMOVED. It is up to the caller to track how many + * devices are in each device group. + * + * Device groups do not get re-used once the last device in the group was + * removed, i.e. unplugging and re-plugging a physical device with grouped + * devices will return a different device group after every unplug. + * + * The returned device group is not refcounted and may become invalid after + * the next call to libinput. Use libinput_device_group_ref() and + * libinput_device_group_unref() to continue using the handle outside of the + * immediate scope. + * + * @return The device group this device belongs to + */ +struct libinput_device_group * +libinput_device_get_device_group(struct libinput_device *device); + /** * @ingroup device * @@ -1593,6 +1636,63 @@ libinput_device_get_size(struct libinput_device *device, int libinput_device_has_button(struct libinput_device *device, uint32_t code); +/** + * @ingroup device + * + * Increase the refcount of the device group. A device group will be freed + * whenever the refcount reaches 0. This may happen during dispatch if all + * devices of this group were removed from the system. A caller must ensure + * to reference the device group correctly to avoid dangling pointers. + * + * @param group A previously obtained device group + * @return The passed device group + */ +struct libinput_device_group * +libinput_device_group_ref(struct libinput_device_group *group); + +/** + * @ingroup device + * + * Decrease the refcount of the device group. A device group will be freed + * whenever the refcount reaches 0. This may happen during dispatch if all + * devices of this group were removed from the system. A caller must ensure + * to reference the device group correctly to avoid dangling pointers. + * + * @param group A previously obtained device group + * @return NULL if the device group was destroyed, otherwise the passed + * device group + */ +struct libinput_device_group * +libinput_device_group_unref(struct libinput_device_group *group); + +/** + * @ingroup device + * + * Set caller-specific data associated with this device group. libinput does + * not manage, look at, or modify this data. The caller must ensure the + * data is valid. + * + * @param group A previously obtained device group + * @param user_data Caller-specific data pointer + * @see libinput_device_group_get_user_data + */ +void +libinput_device_group_set_user_data(struct libinput_device_group *group, + void *user_data); + +/** + * @ingroup device + * + * Get the caller-specific data associated with this input device group, if + * any. + * + * @param group A previously obtained group + * @return Caller-specific data pointer or NULL if none was set + * @see libinput_device_group_set_user_data + */ +void * +libinput_device_group_get_user_data(struct libinput_device_group *group); + /** * @defgroup config Device configuration * diff --git a/src/libinput.sym b/src/libinput.sym index 77fccb84..cf13789f 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -126,3 +126,11 @@ global: libinput_device_config_click_get_methods; libinput_device_config_click_set_method; } LIBINPUT_0.8.0; + +LIBINPUT_0.11.0 { + libinput_device_get_device_group; + libinput_device_group_get_user_data; + libinput_device_group_ref; + libinput_device_group_set_user_data; + libinput_device_group_unref; +} LIBINPUT_0.9.0; diff --git a/test/device.c b/test/device.c index 76486f03..e72ef198 100644 --- a/test/device.c +++ b/test/device.c @@ -661,6 +661,54 @@ START_TEST(device_context) } END_TEST +START_TEST(device_group_get) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device_group *group; + + int userdata = 10; + + group = libinput_device_get_device_group(dev->libinput_device); + ck_assert_notnull(group); + + libinput_device_group_ref(group); + + libinput_device_group_set_user_data(group, &userdata); + ck_assert_ptr_eq(&userdata, + libinput_device_group_get_user_data(group)); + + libinput_device_group_unref(group); +} +END_TEST + +START_TEST(device_group_ref) +{ + struct libinput *li = litest_create_context(); + struct litest_device *dev = litest_add_device(li, + LITEST_MOUSE); + struct libinput_device *device = dev->libinput_device; + struct libinput_device_group *group; + + group = libinput_device_get_device_group(device); + ck_assert_notnull(group); + libinput_device_group_ref(group); + + libinput_device_ref(device); + litest_drain_events(li); + litest_delete_device(dev); + litest_drain_events(li); + + /* make sure the device is dead but the group is still around */ + ck_assert(libinput_device_unref(device) == NULL); + + libinput_device_group_ref(group); + ck_assert_notnull(libinput_device_group_unref(group)); + ck_assert(libinput_device_group_unref(group) == NULL); + + libinput_unref(li); +} +END_TEST + int main (int argc, char **argv) { litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD); @@ -686,5 +734,8 @@ int main (int argc, char **argv) litest_add("device:udev", device_get_udev_handle, LITEST_ANY, LITEST_ANY); + litest_add("device:group", device_group_get, LITEST_ANY, LITEST_ANY); + litest_add_no_device("device:group", device_group_ref); + return litest_run(argc, argv); } From eeac661d93f15c683c295c0304e3813a6502516c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 6 Feb 2015 14:32:05 +1000 Subject: [PATCH 16/20] doc: document that we're ignoring position-less clicks This can happen a lot easier on the new Lenovo series, so document that this is intentional behavior. Signed-off-by: Peter Hutterer --- doc/clickpad-softbuttons.dox | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/clickpad-softbuttons.dox b/doc/clickpad-softbuttons.dox index d9ebfbbc..8d919369 100644 --- a/doc/clickpad-softbuttons.dox +++ b/doc/clickpad-softbuttons.dox @@ -54,6 +54,13 @@ The movement of a finger can alter the button area behavior: - once a finger has moved out of the button area, it cannot move back in and trigger a right or middle button event +On some touchpads, notably the 2015 Lenovo X1 Carbon 3rd series, the very +bottom end of the touchpad is outside of the sensor range but it is possible +to trigger a physical click there. To libinput, the click merely shows up as +a left button click without any positional finger data and it is +impossible to determine whether it is a left or a right click. libinput +ignores such button clicks, this behavior is intentional. + @section clickfinger Clickfinger behavior This is the default behavior on Apple touchpads. From cbee7ce7831640edec42311a7318f0326cd17534 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 6 Feb 2015 08:58:55 +1000 Subject: [PATCH 17/20] README: Add the libinput component to the bugzilla URL Signed-off-by: Peter Hutterer --- README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.txt b/README.txt index fabe304b..c5dc61e5 100644 --- a/README.txt +++ b/README.txt @@ -22,7 +22,7 @@ For more information, visit: http://www.freedesktop.org/wiki/Software/libinput/ Bugs can be filed in the libinput component of Wayland: -https://bugs.freedesktop.org/enter_bug.cgi?product=Wayland +https://bugs.freedesktop.org/enter_bug.cgi?product=Wayland&component=libinput Online API documentation: http://wayland.freedesktop.org/libinput/doc/latest/modules.html From 1edbac96fa322578fddcbe3fd0563bc881a10bb9 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 2 Feb 2015 13:37:14 -0500 Subject: [PATCH 18/20] evdev: retrieve udev tags udev already tags the devices by opening each of them and analyzing their features. We are basically re-doing this in libinput. The advantage of udev tags over the plain heuristic from libinput is that users (or driver writers) can force some tags that are not detected by common rules. For instance, the pad part of the Wacom tablets is difficult to discriminate from a joystick or a pointer. For now we tread INPUT_ID_KEY and INPUT_ID_KEYBOARD as equivalent. It may become necessary to separate them later. Signed-off-by: Benjamin Tissoires Signed-off-by: Peter Hutterer Reviewed-by: Benjamin Tissoires --- src/evdev.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index 6024d383..dad6616a 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -49,6 +49,37 @@ enum evdev_key_type { EVDEV_KEY_TYPE_BUTTON, }; +enum evdev_device_udev_tags { + EVDEV_UDEV_TAG_INPUT = (1 << 0), + EVDEV_UDEV_TAG_KEYBOARD = (1 << 1), + EVDEV_UDEV_TAG_MOUSE = (1 << 2), + EVDEV_UDEV_TAG_TOUCHPAD = (1 << 3), + EVDEV_UDEV_TAG_TOUCHSCREEN = (1 << 4), + EVDEV_UDEV_TAG_TABLET = (1 << 5), + EVDEV_UDEV_TAG_JOYSTICK = (1 << 6), + EVDEV_UDEV_TAG_ACCELEROMETER = (1 << 7), +}; + +struct evdev_udev_tag_match { + const char *name; + enum evdev_device_udev_tags tag; +}; + +static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = { + {"ID_INPUT", EVDEV_UDEV_TAG_INPUT}, + {"ID_INPUT_KEYBOARD", EVDEV_UDEV_TAG_KEYBOARD}, + {"ID_INPUT_KEY", EVDEV_UDEV_TAG_KEYBOARD}, + {"ID_INPUT_MOUSE", EVDEV_UDEV_TAG_MOUSE}, + {"ID_INPUT_TOUCHPAD", EVDEV_UDEV_TAG_TOUCHPAD}, + {"ID_INPUT_TOUCHSCREEN", EVDEV_UDEV_TAG_TOUCHSCREEN}, + {"ID_INPUT_TABLET", EVDEV_UDEV_TAG_TABLET}, + {"ID_INPUT_JOYSTICK", EVDEV_UDEV_TAG_JOYSTICK}, + {"ID_INPUT_ACCELEROMETER", EVDEV_UDEV_TAG_ACCELEROMETER}, + + /* sentinel value */ + { 0 }, +}; + static void hw_set_key_down(struct evdev_device *device, int code, int pressed) { @@ -1319,6 +1350,25 @@ evdev_fix_abs_resolution(struct libevdev *evdev, } } +static enum evdev_device_udev_tags +evdev_device_get_udev_tags(struct evdev_device *device, + struct udev_device *udev_device) +{ + const char *prop; + enum evdev_device_udev_tags tags = 0; + const struct evdev_udev_tag_match *match = evdev_udev_tag_matches; + + while (match->name) { + prop = udev_device_get_property_value(device->udev_device, + match->name); + if (prop) + tags |= match->tag; + + match++; + } + return tags; +} + static int evdev_configure_device(struct evdev_device *device) { @@ -1333,6 +1383,9 @@ evdev_configure_device(struct evdev_device *device) int slot; unsigned int i; const char *devnode = udev_device_get_devnode(device->udev_device); + enum evdev_device_udev_tags udev_tags; + + udev_tags = evdev_device_get_udev_tags(device, device->udev_device); has_rel = 0; has_abs = 0; @@ -1342,6 +1395,18 @@ evdev_configure_device(struct evdev_device *device) has_keyboard = 0; has_touch = 0; + if (udev_tags) + log_info(libinput, + "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s\n", + device->devname, devnode, + udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "", + udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "", + udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "", + udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "", + udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "", + udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "", + udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : ""); + for (i = BTN_JOYSTICK; i <= BTN_PINKIE; i++) if (libevdev_has_event_code(evdev, EV_KEY, i)) has_joystick_button = 1; From b384f2a8abfb80c43e0266c9c44c8e782400b0e5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 3 Feb 2015 12:21:19 +1000 Subject: [PATCH 19/20] evdev: switch to using udev's device tagging system Use ID_INPUT_FOO to assume a device is a FOO, don't decide ourselves based on whatever bits are available. This moves the categorization out to udev's input_id builtin by default and other bits that tag the device. libwacom tags all known devices as ID_INPUT_TABLET and (for touch-enabled ones) ID_INPUT_TOUCH - we can re-use that knowledge then. Ignore anything that doesn't have ID_INPUT set, this provides for an easy way of making devices "invisible" to libinput. Signed-off-by: Peter Hutterer Reviewed-by: Benjamin Tissoires --- src/evdev.c | 116 +++++++++++++++------------------------------------- 1 file changed, 34 insertions(+), 82 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index dad6616a..c2d172fc 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1375,47 +1375,37 @@ evdev_configure_device(struct evdev_device *device) struct libinput *libinput = device->base.seat->libinput; struct libevdev *evdev = device->evdev; const struct input_absinfo *absinfo; - int has_abs, has_rel, has_mt; - int has_button, has_keyboard, has_touch, has_joystick_button; struct mt_slot *slots; int num_slots; int active_slot; int slot; - unsigned int i; const char *devnode = udev_device_get_devnode(device->udev_device); enum evdev_device_udev_tags udev_tags; udev_tags = evdev_device_get_udev_tags(device, device->udev_device); - has_rel = 0; - has_abs = 0; - has_mt = 0; - has_button = 0; - has_joystick_button = 0; - has_keyboard = 0; - has_touch = 0; - - if (udev_tags) + if ((udev_tags & EVDEV_UDEV_TAG_INPUT) == 0 || + (udev_tags & ~EVDEV_UDEV_TAG_INPUT) == 0) { log_info(libinput, - "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s\n", - device->devname, devnode, - udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "", - udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "", - udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "", - udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "", - udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "", - udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "", - udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : ""); + "input device '%s', %s not tagged as input device\n", + device->devname, devnode); + return -1; + } - for (i = BTN_JOYSTICK; i <= BTN_PINKIE; i++) - if (libevdev_has_event_code(evdev, EV_KEY, i)) - has_joystick_button = 1; + log_info(libinput, + "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s\n", + device->devname, devnode, + udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "", + udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "", + udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "", + udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "", + udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "", + udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "", + udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : ""); - for (i = BTN_GAMEPAD; i <= BTN_TR2; i++) - if (libevdev_has_event_code(evdev, EV_KEY, i)) - has_joystick_button = 1; - - if (has_joystick_button) { + /* libwacom *adds* TABLET, TOUCHPAD but leaves JOYSTICK in place, so + make sure we only ignore real joystick devices */ + if ((udev_tags & EVDEV_UDEV_TAG_JOYSTICK) == udev_tags) { log_info(libinput, "input device '%s', %s is a joystick, ignoring\n", device->devname, devnode); @@ -1430,7 +1420,6 @@ evdev_configure_device(struct evdev_device *device) absinfo)) device->abs.fake_resolution = 1; device->abs.absinfo_x = absinfo; - has_abs = 1; } if ((absinfo = libevdev_get_abs_info(evdev, ABS_Y))) { if (evdev_fix_abs_resolution(evdev, @@ -1438,7 +1427,6 @@ evdev_configure_device(struct evdev_device *device) absinfo)) device->abs.fake_resolution = 1; device->abs.absinfo_y = absinfo; - has_abs = 1; } /* Fake MT devices have the ABS_MT_SLOT bit set because of @@ -1446,8 +1434,7 @@ evdev_configure_device(struct evdev_device *device) just have too many ABS_ axes */ if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT) && libevdev_get_num_slots(evdev) == -1) { - has_mt = 0; - has_touch = 0; + udev_tags &= ~EVDEV_UDEV_TAG_TOUCHSCREEN; } else if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) && libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y)) { absinfo = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X); @@ -1464,8 +1451,6 @@ evdev_configure_device(struct evdev_device *device) device->abs.fake_resolution = 1; device->abs.absinfo_y = absinfo; device->is_mt = 1; - has_touch = 1; - has_mt = 1; /* We only handle the slotted Protocol B in libinput. Devices with ABS_MT_POSITION_* but not ABS_MT_SLOT @@ -1500,75 +1485,42 @@ evdev_configure_device(struct evdev_device *device) } } - if (libevdev_has_event_code(evdev, EV_REL, REL_X) || - libevdev_has_event_code(evdev, EV_REL, REL_Y)) - has_rel = 1; - - if (libevdev_has_event_type(evdev, EV_KEY)) { - if (!libevdev_has_property(evdev, INPUT_PROP_DIRECT) && - libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_FINGER) && - !libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN) && - (has_abs || has_mt)) { - device->dispatch = evdev_mt_touchpad_create(device); - log_info(libinput, - "input device '%s', %s is a touchpad\n", - device->devname, devnode); - return device->dispatch == NULL ? -1 : 0; - } - - for (i = 0; i < KEY_MAX; i++) { - if (libevdev_has_event_code(evdev, EV_KEY, i)) { - switch (get_key_type(i)) { - case EVDEV_KEY_TYPE_NONE: - break; - case EVDEV_KEY_TYPE_KEY: - has_keyboard = 1; - break; - case EVDEV_KEY_TYPE_BUTTON: - has_button = 1; - break; - } - } - } - - if (libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH)) - has_touch = 1; + if (udev_tags & EVDEV_UDEV_TAG_TOUCHPAD) { + device->dispatch = evdev_mt_touchpad_create(device); + log_info(libinput, + "input device '%s', %s is a touchpad\n", + device->devname, devnode); + return device->dispatch == NULL ? -1 : 0; } - if (libevdev_has_event_type(evdev, EV_LED)) - has_keyboard = 1; - if ((has_abs || has_rel) && has_button) { - if (has_rel && + if (udev_tags & EVDEV_UDEV_TAG_MOUSE) { + if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X) && + !libevdev_has_event_code(evdev, EV_ABS, ABS_Y) && evdev_device_init_pointer_acceleration(device) == -1) return -1; device->seat_caps |= EVDEV_DEVICE_POINTER; log_info(libinput, - "input device '%s', %s is a pointer caps =%s%s%s\n", - device->devname, devnode, - has_abs ? " absolute-motion" : "", - has_rel ? " relative-motion": "", - has_button ? " button" : ""); + "input device '%s', %s is a pointer caps\n", + device->devname, devnode); /* want left-handed config option */ device->left_handed.want_enabled = true; /* want natural-scroll config option */ device->scroll.natural_scrolling_enabled = true; - } - - if (has_rel && has_button) { /* want button scrolling config option */ device->scroll.want_button = 1; } - if (has_keyboard) { + if (udev_tags & EVDEV_UDEV_TAG_KEYBOARD) { device->seat_caps |= EVDEV_DEVICE_KEYBOARD; log_info(libinput, "input device '%s', %s is a keyboard\n", device->devname, devnode); } - if (has_touch && !has_button) { + + if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) { device->seat_caps |= EVDEV_DEVICE_TOUCH; log_info(libinput, "input device '%s', %s is a touch device\n", From 911059b1e7a0ffb25de1f0395d0e6b88b998e77a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 9 Feb 2015 09:45:33 +1000 Subject: [PATCH 20/20] doc: add a dot graph for device groups and two missing @refs Signed-off-by: Peter Hutterer --- src/libinput.h | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/libinput.h b/src/libinput.h index 3b179a8c..577c0067 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1421,9 +1421,9 @@ libinput_device_get_context(struct libinput_device *device); * * Get the device group this device is assigned to. Some physical * devices like graphics tablets are represented by multiple kernel - * devices and thus by multiple struct libinput_device. + * devices and thus by multiple struct @ref libinput_device. * - * libinput assigns these devices to the same libinput_device_group + * libinput assigns these devices to the same @ref libinput_device_group * allowing the caller to identify such devices and adjust configuration * settings accordingly. For example, setting a tablet to left-handed often * means turning it upside down. A touch device on the same tablet would @@ -1435,6 +1435,33 @@ libinput_device_get_context(struct libinput_device *device); * LIBINPUT_EVENT_DEVICE_REMOVED. It is up to the caller to track how many * devices are in each device group. * + * @dot + * digraph groups_libinput { + * rankdir="TB"; + * node [ + * shape="box"; + * ] + * + * mouse [ label="mouse"; URL="\ref libinput_device"]; + * kbd [ label="keyboard"; URL="\ref libinput_device"]; + * + * pen [ label="tablet pen"; URL="\ref libinput_device"]; + * touch [ label="tablet touch"; URL="\ref libinput_device"]; + * pad [ label="tablet pad"; URL="\ref libinput_device"]; + * + * group1 [ label="group 1"; URL="\ref libinput_device_group"]; + * group2 [ label="group 2"; URL="\ref libinput_device_group"]; + * group3 [ label="group 3"; URL="\ref libinput_device_group"]; + * + * mouse -> group1 + * kbd -> group2 + * + * pen -> group3; + * touch -> group3; + * pad -> group3; + * } + * @enddot + * * Device groups do not get re-used once the last device in the group was * removed, i.e. unplugging and re-plugging a physical device with grouped * devices will return a different device group after every unplug.