return 0;
}
+/**
+ * Sanitize/modify events where needed.
+ * @return 0 if untouched, 1 if modified.
+ */
+static inline int
+sanitize_event(const struct libevdev *dev, struct input_event *ev)
+{
+ if (unlikely(dev->num_slots > -1 &&
+ libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT) &&
+ (ev->value < 0 || ev->value >= dev->num_slots))) {
+ log_bug("Device %s received an invalid slot index %d."
+ "Capping to announced max slot number %d.\n",
+ dev->name, ev->value, dev->num_slots - 1);
+ ev->value = dev->num_slots - 1;
+ return 1;
+ }
+
+ return 0;
+}
+
LIBEVDEV_EXPORT int
libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev)
{
if (queue_shift(dev, ev) != 0)
return -EAGAIN;
+ sanitize_event(dev, ev);
update_state(dev, ev);
/* if we disabled a code, get the next event instead */
e.code = code;
e.value = value;
+ if (sanitize_event(dev, &e))
+ return -1;
+
switch(type) {
case EV_ABS: rc = update_abs_state(dev, &e); break;
case EV_KEY: rc = update_key_state(dev, &e); break;
* event code is the value of the currently active slot. You should use
* libevdev_set_slot_value() instead.
*
+ * If the device supports ABS_MT_SLOT and the type is EV_ABS and the code is
+ * ABS_MT_SLOT, the value must be a positive number less then the number of
+ * slots on the device. Otherwise, libevdev_set_event_value() returns -1.
+ *
* @param dev The evdev device, already initialized with libevdev_set_fd()
* @param type The event type for the code to query (EV_SYN, EV_REL, etc.)
* @param code The event code to set the value for, one of ABS_X, LED_NUML, etc.
*
* @return 0 on success, or -1 on failure.
* @retval -1 the device does not have the event type or code enabled, or the code is outside the
- * allowed limits for the given type, or the type cannot be set.
+ * allowed limits for the given type, or the type cannot be set, or the
+ * value is not permitted for the given code.
*
* @see libevdev_set_slot_value
* @see libevdev_get_event_value
}
END_TEST
+START_TEST(test_mt_slot_ranges_invalid)
+{
+ struct uinput_device* uidev;
+ struct libevdev *dev;
+ struct input_event ev[2];
+ int rc;
+ struct input_absinfo abs[5];
+ int num_slots = 2;
+ int pipefd[2];
+
+ memset(abs, 0, sizeof(abs));
+ abs[0].value = ABS_X;
+ abs[0].maximum = 1000;
+ abs[1].value = ABS_MT_POSITION_X;
+ abs[1].maximum = 1000;
+
+ abs[2].value = ABS_Y;
+ abs[2].maximum = 1000;
+ abs[3].value = ABS_MT_POSITION_Y;
+ abs[3].maximum = 1000;
+
+ abs[4].value = ABS_MT_SLOT;
+ abs[4].maximum = num_slots - 1;
+
+ rc = test_create_abs_device(&uidev, &dev,
+ 5, abs,
+ EV_SYN, SYN_REPORT,
+ -1);
+ ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
+
+ rc = pipe2(pipefd, O_NONBLOCK);
+ ck_assert_int_eq(rc, 0);
+ libevdev_change_fd(dev, pipefd[0]);
+
+ ev[0].type = EV_ABS;
+ ev[0].code = ABS_MT_SLOT;
+ ev[0].value = num_slots;
+ ev[1].type = EV_SYN;
+ ev[1].code = SYN_REPORT;
+ ev[1].value = 0;
+ rc = write(pipefd[1], ev, sizeof(ev));
+ ck_assert_int_eq(rc, sizeof(ev));
+
+ libevdev_set_log_function(test_logfunc_ignore_error, NULL);
+
+ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev);
+ ck_assert(libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT));
+ ck_assert_int_eq(ev[0].value, num_slots - 1);
+
+ /* drain the EV_SYN */
+ libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev);
+
+ ev[0].type = EV_ABS;
+ ev[0].code = ABS_MT_SLOT;
+ ev[0].value = -1;
+ ev[1].type = EV_SYN;
+ ev[1].code = SYN_REPORT;
+ ev[1].value = 0;
+ rc = write(pipefd[1], ev, sizeof(ev));
+ ck_assert_int_eq(rc, sizeof(ev));
+
+ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev);
+ ck_assert(libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT));
+ ck_assert_int_eq(ev[0].value, num_slots - 1);
+
+ ck_assert_int_eq(libevdev_get_current_slot(dev), num_slots - 1);
+
+ ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, num_slots), -1);
+ ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, -1), -1);
+
+ libevdev_set_log_function(test_logfunc_abort_on_error, NULL);
+
+ uinput_device_free(uidev);
+ libevdev_free(dev);
+}
+END_TEST
+
START_TEST(test_ev_rep_values)
{
struct uinput_device* uidev;
tcase_add_test(tc, test_event_values_invalid);
tcase_add_test(tc, test_mt_event_values);
tcase_add_test(tc, test_mt_event_values_invalid);
+ tcase_add_test(tc, test_mt_slot_ranges_invalid);
tcase_add_test(tc, test_ev_rep_values);
suite_add_tcase(s, tc);