Initialize the slots correctly when enabling ABS_MT_SLOT
authorPeter Hutterer <peter.hutterer@who-t.net>
Fri, 14 Jun 2019 01:18:37 +0000 (11:18 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Mon, 17 Jun 2019 23:31:41 +0000 (09:31 +1000)
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 <peter.hutterer@who-t.net>
libevdev/libevdev.c
test/Makefile.am
test/test-context.c [new file with mode: 0644]

index c5798b4..dbf8ea3 100644 (file)
@@ -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;
 }
 
index 6b8af16..25290f2 100644 (file)
@@ -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 (file)
index 0000000..303abb1
--- /dev/null
@@ -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 <config.h>
+#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;
+}