From da67db5f59be147911cec441e9fd81d2b3b17cf3 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 14 Jun 2019 11:18:37 +1000 Subject: [PATCH] Initialize the slots correctly when enabling ABS_MT_SLOT Previously, enabling or disabling ABS_MT_SLOT would not change the actual slots, it was treated as a normal bitflag. This means we couldn't initialize a libevdev context from scratch and have it behave like a correct MT context. Fixes #4 Signed-off-by: Peter Hutterer --- libevdev/libevdev.c | 137 +++++++++++++++++++++++++--------- test/Makefile.am | 1 + test/test-context.c | 177 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+), 34 deletions(-) create mode 100644 test/test-context.c diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c index c5798b4..dbf8ea3 100644 --- a/libevdev/libevdev.c +++ b/libevdev/libevdev.c @@ -315,6 +315,90 @@ libevdev_change_fd(struct libevdev *dev, int fd) return 0; } +static void +reset_tracking_ids(struct libevdev *dev) +{ + if (dev->num_slots == -1 || + !libevdev_has_event_code(dev, EV_ABS, ABS_MT_TRACKING_ID)) + return; + + for (int slot = 0; slot < dev->num_slots; slot++) + libevdev_set_slot_value(dev, slot, ABS_MT_TRACKING_ID, -1); +} + +static inline void +free_slots(struct libevdev *dev) +{ + dev->num_slots = -1; + free(dev->mt_slot_vals); + free(dev->mt_sync.tracking_id_changes); + free(dev->mt_sync.slot_update); + free(dev->mt_sync.mt_state); + dev->mt_slot_vals = NULL; + dev->mt_sync.tracking_id_changes = NULL; + dev->mt_sync.slot_update = NULL; + dev->mt_sync.mt_state = NULL; +} + +static int +init_slots(struct libevdev *dev) +{ + const struct input_absinfo *abs_info; + int rc = 0; + + free(dev->mt_slot_vals); + free(dev->mt_sync.tracking_id_changes); + free(dev->mt_sync.slot_update); + free(dev->mt_sync.mt_state); + dev->mt_slot_vals = NULL; + dev->mt_sync.tracking_id_changes = NULL; + dev->mt_sync.slot_update = NULL; + dev->mt_sync.mt_state = NULL; + + /* devices with ABS_RESERVED aren't MT devices, + see the documentation for multitouch-related + functions for more details */ + if (libevdev_has_event_code(dev, EV_ABS, ABS_RESERVED) || + !libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT)) { + if (dev->num_slots != -1) { + free_slots(dev); + } + return rc; + } + + abs_info = libevdev_get_abs_info(dev, ABS_MT_SLOT); + + free_slots(dev); + dev->num_slots = abs_info->maximum + 1; + dev->mt_slot_vals = calloc(dev->num_slots * ABS_MT_CNT, sizeof(int)); + if (!dev->mt_slot_vals) { + rc = -ENOMEM; + goto out; + } + dev->current_slot = abs_info->value; + + dev->mt_sync.mt_state_sz = sizeof(*dev->mt_sync.mt_state) + + (dev->num_slots) * sizeof(int); + dev->mt_sync.mt_state = calloc(1, dev->mt_sync.mt_state_sz); + + dev->mt_sync.tracking_id_changes_sz = NLONGS(dev->num_slots) * sizeof(long); + dev->mt_sync.tracking_id_changes = malloc(dev->mt_sync.tracking_id_changes_sz); + + dev->mt_sync.slot_update_sz = NLONGS(dev->num_slots * ABS_MT_CNT) * sizeof(long); + dev->mt_sync.slot_update = malloc(dev->mt_sync.slot_update_sz); + + if (!dev->mt_sync.tracking_id_changes || + !dev->mt_sync.slot_update || + !dev->mt_sync.mt_state) { + rc = -ENOMEM; + goto out; + } + + reset_tracking_ids(dev); +out: + return rc; +} + LIBEVDEV_EXPORT int libevdev_set_fd(struct libevdev* dev, int fd) { @@ -461,42 +545,12 @@ libevdev_set_fd(struct libevdev* dev, int fd) dev->fd = fd; - /* devices with ABS_RESERVED aren't MT devices, - see the documentation for multitouch-related - functions for more details */ - if (!libevdev_has_event_code(dev, EV_ABS, ABS_RESERVED) && - libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT)) { - const struct input_absinfo *abs_info; - - abs_info = libevdev_get_abs_info(dev, ABS_MT_SLOT); - - dev->num_slots = abs_info->maximum + 1; - dev->mt_slot_vals = calloc(dev->num_slots * ABS_MT_CNT, sizeof(int)); - if (!dev->mt_slot_vals) { - rc = -ENOMEM; - goto out; - } - dev->current_slot = abs_info->value; - - dev->mt_sync.mt_state_sz = sizeof(*dev->mt_sync.mt_state) + - (dev->num_slots) * sizeof(int); - dev->mt_sync.mt_state = calloc(1, dev->mt_sync.mt_state_sz); - - dev->mt_sync.tracking_id_changes_sz = NLONGS(dev->num_slots) * sizeof(long); - dev->mt_sync.tracking_id_changes = malloc(dev->mt_sync.tracking_id_changes_sz); - - dev->mt_sync.slot_update_sz = NLONGS(dev->num_slots * ABS_MT_CNT) * sizeof(long); - dev->mt_sync.slot_update = malloc(dev->mt_sync.slot_update_sz); - - if (!dev->mt_sync.tracking_id_changes || - !dev->mt_sync.slot_update || - !dev->mt_sync.mt_state) { - rc = -ENOMEM; - goto out; - } + rc = init_slots(dev); + if (rc != 0) + goto out; + if (dev->num_slots != -1) sync_mt_state(dev, 0); - } rc = init_event_queue(dev); if (rc < 0) { @@ -1487,6 +1541,12 @@ libevdev_enable_event_code(struct libevdev *dev, unsigned int type, if (type == EV_ABS) { const struct input_absinfo *abs = data; dev->abs_info[code] = *abs; + if (code == ABS_MT_SLOT) { + if (init_slots(dev) != 0) + return -1; + } else if (code == ABS_MT_TRACKING_ID) { + reset_tracking_ids(dev); + } } else if (type == EV_REP) { const int *value = data; dev->rep_values[code] = *value; @@ -1511,6 +1571,15 @@ libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned in clear_bit(mask, code); + if (type == EV_ABS) { + if (code == ABS_MT_SLOT) { + if (init_slots(dev) != 0) + return -1; + } else if (code == ABS_MT_TRACKING_ID) { + reset_tracking_ids(dev); + } + } + return 0; } diff --git a/test/Makefile.am b/test/Makefile.am index 6b8af16..25290f2 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -52,6 +52,7 @@ test_event_codes_SOURCES = \ test-main.c \ test-event-codes.c \ test-event-names.c \ + test-context.c \ $(common_sources) test_event_codes_LDADD = $(CHECK_LIBS) $(top_builddir)/libevdev/libevdev.la test_event_codes_LDFLAGS = -no-install diff --git a/test/test-context.c b/test/test-context.c new file mode 100644 index 0000000..303abb1 --- /dev/null +++ b/test/test-context.c @@ -0,0 +1,177 @@ +/* + * Copyright © 2019 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include "test-common.h" + +START_TEST(test_info) +{ + struct libevdev *d = libevdev_new(); + + libevdev_set_name(d, "some name"); + ck_assert_str_eq(libevdev_get_name(d), "some name"); + libevdev_set_phys(d, "physical"); + ck_assert_str_eq(libevdev_get_phys(d), "physical"); + libevdev_set_uniq(d, "very unique"); + ck_assert_str_eq(libevdev_get_uniq(d), "very unique"); + + libevdev_set_id_bustype(d, 1); + libevdev_set_id_vendor(d, 2); + libevdev_set_id_product(d, 3); + libevdev_set_id_version(d, 4); + ck_assert_int_eq(libevdev_get_id_bustype(d), 1); + ck_assert_int_eq(libevdev_get_id_vendor(d), 2); + ck_assert_int_eq(libevdev_get_id_product(d), 3); + ck_assert_int_eq(libevdev_get_id_version(d), 4); + + libevdev_free(d); +} +END_TEST + +START_TEST(test_properties) +{ + for (unsigned prop = 0; prop < INPUT_PROP_CNT; prop++) { + struct libevdev *d = libevdev_new(); + + ck_assert(!libevdev_has_property(d, prop)); + libevdev_enable_property(d, prop); + ck_assert(libevdev_has_property(d, prop)); + libevdev_free(d); + } +} +END_TEST + +START_TEST(test_bits) +{ + for (unsigned type = 1; type < EV_CNT; type++) { + unsigned max = libevdev_event_type_get_max(type); + + if((int)max == -1) + continue; + + for (unsigned code = 0; code <= max; code++) { + struct libevdev *d = libevdev_new(); + const struct input_absinfo abs = { + 10, 20, 30, 40, 50 + }; + const void *data = NULL; + + if (type == EV_ABS || type == EV_REP) + data = &abs; + + ck_assert(!libevdev_has_event_code(d, type, code)); + libevdev_enable_event_code(d, type, code, data); + ck_assert(libevdev_has_event_code(d, type, code)); + libevdev_free(d); + } + } +} +END_TEST + +START_TEST(test_mt_slots_enable_disable) +{ + struct libevdev *d = libevdev_new(); + struct input_absinfo abs = {0}; + + abs.maximum = 5; + libevdev_enable_event_code(d, EV_ABS, ABS_MT_SLOT, &abs); + ck_assert(libevdev_has_event_code(d, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(libevdev_get_num_slots(d), 6); + + libevdev_disable_event_code(d, EV_ABS, ABS_MT_SLOT); + ck_assert(!libevdev_has_event_code(d, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(libevdev_get_num_slots(d), -1); + + abs.maximum = 2; + libevdev_enable_event_code(d, EV_ABS, ABS_MT_SLOT, &abs); + ck_assert(libevdev_has_event_code(d, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(libevdev_get_num_slots(d), 3); + + libevdev_free(d); +} +END_TEST + +START_TEST(test_mt_slots_increase_decrease) +{ + struct libevdev *d = libevdev_new(); + struct input_absinfo abs = {0}; + + abs.maximum = 5; + libevdev_enable_event_code(d, EV_ABS, ABS_MT_SLOT, &abs); + ck_assert(libevdev_has_event_code(d, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(libevdev_get_num_slots(d), 6); + + abs.maximum = 2; + libevdev_enable_event_code(d, EV_ABS, ABS_MT_SLOT, &abs); + ck_assert(libevdev_has_event_code(d, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(libevdev_get_num_slots(d), 3); + + abs.maximum = 6; + libevdev_enable_event_code(d, EV_ABS, ABS_MT_SLOT, &abs); + ck_assert(libevdev_has_event_code(d, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(libevdev_get_num_slots(d), 7); + + abs.maximum = 10; + libevdev_enable_event_code(d, EV_ABS, ABS_MT_SLOT, &abs); + ck_assert(libevdev_has_event_code(d, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(libevdev_get_num_slots(d), 11); + + libevdev_free(d); +} +END_TEST + +START_TEST(test_mt_tracking_id) +{ + struct libevdev *d = libevdev_new(); + struct input_absinfo abs = { .maximum = 5 }; + + libevdev_enable_event_code(d, EV_ABS, ABS_MT_SLOT, &abs); + + /* Not yet enabled, so 0. This is technically undefined */ + for (int slot = 0; slot < 5; slot++) + ck_assert_int_eq(libevdev_get_slot_value(d, 0, ABS_MT_TRACKING_ID), 0); + + libevdev_enable_event_code(d, EV_ABS, ABS_MT_TRACKING_ID, &abs); + + for (int slot = 0; slot < 5; slot++) + ck_assert_int_eq(libevdev_get_slot_value(d, 0, ABS_MT_TRACKING_ID), -1); + + libevdev_free(d); +} +END_TEST + +TEST_SUITE(event_name_suite) +{ + Suite *s = suite_create("Context manipulation"); + TCase *tc; + + tc = tcase_create("Device info"); + tcase_add_test(tc, test_info); + tcase_add_test(tc, test_properties); + tcase_add_test(tc, test_bits); + tcase_add_test(tc, test_mt_slots_enable_disable); + tcase_add_test(tc, test_mt_slots_increase_decrease); + tcase_add_test(tc, test_mt_tracking_id); + suite_add_tcase(s, tc); + + return s; +} -- 2.34.1