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 <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2019-06-14 11:18:37 +10:00
parent 4d745caab4
commit da67db5f59
3 changed files with 281 additions and 34 deletions

View 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;
}

View 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

177
test/test-context.c Normal file
View file

@ -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;
}