[PATCH libevdev 4/4] Transparently use EVIOCSMASK to disable event codes

Peter Hutterer peter.hutterer at who-t.net
Thu Apr 21 21:59:43 UTC 2016


Disable the event code at the kernel level. When set, the kernel doesn't pass
that code on to userspace at all so we reduce the wakeups needed.

Rather than setting the mask immediately on disabling/enabling, we wait until
the next libevdev_next_event() call and update the mask then. This reduces
ioctls if multiple event codes need to be disabled or enabled.

This introduces a behavior change: previously, libevdev would update the
internal device state even for disabled event codes. Even though the event
wouldn't reach the client, a call to libevdev_get_event_value() would yield
the current kernel state. With this state libevdev itself does not see the
events and cannot update its state. libevdev_get_event_value() will thus
return the last known state only. The use-case of disabling a code but relying
on libevdev's state is discouraged though.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 libevdev/libevdev-int.h |  6 ++++++
 libevdev/libevdev.c     | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 libevdev/libevdev.h     |  5 +++++
 3 files changed, 60 insertions(+)

diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h
index 6c5b8b6..bced2e2 100644
--- a/libevdev/libevdev-int.h
+++ b/libevdev/libevdev-int.h
@@ -96,6 +96,12 @@ struct libevdev {
 	unsigned long key_values[NLONGS(KEY_CNT)];
 	unsigned long led_values[NLONGS(LED_CNT)];
 	unsigned long sw_values[NLONGS(SW_CNT)];
+	/* A bit for each event type, if set we need to update the
+	 * EVIOCSMASK. EV_SYN is used as general flag that another one has
+	 * changed
+	 */
+	unsigned long mask_update_needed[NLONGS(EV_CNT)];
+
 	struct input_absinfo abs_info[ABS_CNT];
 	int *mt_slot_vals; /* [num_slots * ABS_MT_CNT] */
 	int num_slots; /**< valid slots in mt_slot_vals */
diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
index 149a0d7..194b17c 100644
--- a/libevdev/libevdev.c
+++ b/libevdev/libevdev.c
@@ -24,6 +24,7 @@
 #include <errno.h>
 #include <poll.h>
 #include <stdlib.h>
+#include <stdint.h>
 #include <string.h>
 #include <limits.h>
 #include <unistd.h>
@@ -1007,6 +1008,43 @@ sanitize_event(const struct libevdev *dev,
 	return EVENT_FILTER_NONE;
 }
 
+static inline int
+update_event_mask(struct libevdev *dev)
+{
+	int rc = 0;
+	int type;
+
+	/* skip EV_SYN, it's the general "update needed" flag */
+	for (type = 1; type < EV_CNT; type++) {
+		unsigned long *m;
+		int max;
+		size_t sz;
+		struct input_mask mask;
+
+		if (!bit_is_set(dev->mask_update_needed, type))
+			continue;
+
+		max = type_to_mask(dev, type, &m, &sz);
+		if (max == -1)
+			continue;
+
+		mask.type = type;
+		mask.codes_size = sz;
+		mask.codes_ptr = (uint64_t)m;
+		rc = ioctl(dev->fd, EVIOCSMASK, &mask);
+		if (rc == -1) {
+			if (errno == EINVAL) /* unsupported by kernel */
+				rc = 0;
+			goto out;
+		}
+	}
+
+out:
+	memset(dev->mask_update_needed, 0, sizeof(dev->mask_update_needed));
+
+	return rc == -1 ? -errno : 0;
+}
+
 LIBEVDEV_EXPORT int
 libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev)
 {
@@ -1028,6 +1066,9 @@ libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event
 		return -EINVAL;
 	}
 
+	if (bit_is_set(dev->mask_update_needed, EV_SYN))
+		update_event_mask(dev);
+
 	if (flags & LIBEVDEV_READ_FLAG_SYNC) {
 		if (dev->sync_state == SYNC_NEEDED) {
 			rc = sync_state(dev);
@@ -1485,6 +1526,10 @@ libevdev_enable_event_code(struct libevdev *dev, unsigned int type,
 		return -1;
 
 	set_bit(mask, code);
+	if (type != EV_SYN) {
+		set_bit(dev->mask_update_needed, type);
+		set_bit(dev->mask_update_needed, EV_SYN);
+	}
 
 	if (type == EV_ABS) {
 		const struct input_absinfo *abs = data;
@@ -1512,6 +1557,10 @@ libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned in
 		return -1;
 
 	clear_bit(mask, code);
+	if (type != EV_SYN) {
+		set_bit(dev->mask_update_needed, type);
+		set_bit(dev->mask_update_needed, EV_SYN);
+	}
 
 	return 0;
 }
diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
index 856aeb9..a12a8d7 100644
--- a/libevdev/libevdev.h
+++ b/libevdev/libevdev.h
@@ -528,6 +528,8 @@ extern "C" {
  * <dt>EVIOCREVOKE:</dt>
  * <dd>currently not supported, see
  * http://lists.freedesktop.org/archives/input-tools/2014-January/000688.html</dd>
+ * <dt>EVIOCGMASK/EVIOCGSMASK:</dt>
+ * <dd>supported, see libevdev_disable_event_code()
  * </dl>
  *
  */
@@ -1858,6 +1860,9 @@ int libevdev_enable_event_code(struct libevdev *dev, unsigned int type, unsigned
  * Disabling codes of type EV_SYN will not work. Don't shoot yourself in the
  * foot. It hurts.
  *
+ * libevdev may transparently use the EVIOCSMASK ioctl if available. This
+ * ioctl disables delivery of that event type in the kernel.
+ *
  * @note This function should only be invoked when the respective event type
  * is in a neutral state. Otherwise, a caller may get unexpected event
  * sequences. For example, disabling an event code while a touch is down may
-- 
2.7.3



More information about the Input-tools mailing list