[PATCH v2 libevdev 4/4] Transparently use EVIOCSMASK to disable event codes
Peter Hutterer
peter.hutterer at who-t.net
Tue Apr 26 04:31:00 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>
---
Changes to v1:
- use EVIOCSMASK to disable event types too
- split the mask into mask_updates and mask_update_needed now that we can't
(ab)use the EV_SYN bit anymore
libevdev/libevdev-int.h | 7 +++++
libevdev/libevdev.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++
libevdev/libevdev.h | 5 ++++
3 files changed, 82 insertions(+)
diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h
index 6c5b8b6..f23abb3 100644
--- a/libevdev/libevdev-int.h
+++ b/libevdev/libevdev-int.h
@@ -96,6 +96,13 @@ 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 for that type. EV_SYN is used to disable whole
+ * event types.
+ */
+ unsigned long mask_updates[NLONGS(EV_CNT)];
+ bool mask_update_needed;
+
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..7737d7b 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,54 @@ sanitize_event(const struct libevdev *dev,
return EVENT_FILTER_NONE;
}
+static inline int
+update_event_mask(struct libevdev *dev)
+{
+ int rc = 0;
+ int type;
+ unsigned long *m;
+ int max;
+ size_t sz;
+ struct input_mask mask;
+
+ /* EV_SYN is special, it disables whole event types */
+ if (bit_is_set(dev->mask_updates, EV_SYN)) {
+ m = dev->bits;
+ sz = sizeof(dev->bits);
+ mask.type = EV_SYN;
+ 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;
+ }
+ }
+
+ for (type = 1; type < EV_CNT; type++) {
+ if (!bit_is_set(dev->mask_updates, 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)
+ goto out;
+ }
+
+out:
+ memset(dev->mask_updates, 0, sizeof(dev->mask_update_needed));
+ dev->mask_update_needed = false;
+
+ return rc == -1 ? -errno : 0;
+}
+
LIBEVDEV_EXPORT int
libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev)
{
@@ -1028,6 +1077,9 @@ libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event
return -EINVAL;
}
+ if (dev->mask_update_needed)
+ update_event_mask(dev);
+
if (flags & LIBEVDEV_READ_FLAG_SYNC) {
if (dev->sync_state == SYNC_NEEDED) {
rc = sync_state(dev);
@@ -1430,6 +1482,11 @@ libevdev_enable_event_type(struct libevdev *dev, unsigned int type)
set_bit(dev->bits, type);
+ if (type != EV_SYN) {
+ set_bit(dev->mask_updates, EV_SYN);
+ dev->mask_update_needed = true;
+ }
+
if (type == EV_REP) {
int delay = 0, period = 0;
libevdev_enable_event_code(dev, EV_REP, REP_DELAY, &delay);
@@ -1452,6 +1509,11 @@ libevdev_disable_event_type(struct libevdev *dev, unsigned int type)
clear_bit(dev->bits, type);
+ if (type != EV_SYN) {
+ set_bit(dev->mask_updates, EV_SYN);
+ dev->mask_update_needed = true;
+ }
+
return 0;
}
@@ -1485,6 +1547,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_updates, type);
+ dev->mask_update_needed = true;
+ }
if (type == EV_ABS) {
const struct input_absinfo *abs = data;
@@ -1512,6 +1578,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_updates, type);
+ dev->mask_update_needed = true;
+ }
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.4
More information about the Input-tools
mailing list