From f04b276ac19c66af077fbceaebb677e694ec71d5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 14 Jul 2025 14:33:31 +1000 Subject: [PATCH] util: add a helper struct for an evdev mask Allows to keep track of a set of evdev masks, or at least the subset we care about. The EV_KEY range is split into two masks to save some memory. The future use of this is for the plugins to use those masks and some of those will set BTN_TOOL_PEN and friends. This would immediately create an 81 byte mask of zeroes just to keep that one bit. Splitting it into a key/btn mask with the latter starting at BTN_MISC means we duplicate the infmask struct (2x16 bytes) but instead only use 8 bytes for the mask itself to keep the BTN_TOOL_PEN bits. Part-of: --- src/evdev-frame.h | 119 ++++++++++++++++++++++++++++++++++++++++++++++ test/test-utils.c | 47 ++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/src/evdev-frame.h b/src/evdev-frame.h index 0f50e0a3..15a37aaf 100644 --- a/src/evdev-frame.h +++ b/src/evdev-frame.h @@ -29,6 +29,7 @@ #include #include +#include "util-bits.h" #include "util-input-event.h" #include "util-mem.h" #include "util-newtype.h" @@ -652,3 +653,121 @@ evdev_frame_clone(struct evdev_frame *frame) return clone; } + +struct evdev_mask { + bitmask_t ev; + bitmask_t rel; + bitmask_t sw; + infmask_t key; /* < BTN_MISC */ + infmask_t btn; /* >= BTN_MISC */ + infmask_t abs; +}; + +static_assert(sizeof(bitmask_t) * 8 >= EV_MAX, "bitmask size too small"); +static_assert(sizeof(bitmask_t) * 8 >= EV_REL, "bitmask size too small"); +static_assert(sizeof(bitmask_t) * 8 >= EV_SW, "bitmask size too small"); + +static inline void +evdev_mask_reset(struct evdev_mask *mask) +{ + mask->ev = bitmask_new(); + mask->rel = bitmask_new(); + mask->sw = bitmask_new(); + infmask_reset(&mask->key); + infmask_reset(&mask->btn); + infmask_reset(&mask->abs); +} + +static inline struct evdev_mask * +evdev_mask_new(void) +{ + struct evdev_mask *mask = zalloc(sizeof(*mask)); + evdev_mask_reset(mask); + return mask; +} + +static inline void +evdev_mask_destroy(struct evdev_mask *mask) +{ + if (mask) { + evdev_mask_reset(mask); + free(mask); + } +} + +DEFINE_DESTROY_CLEANUP_FUNC(evdev_mask); + +static inline void +evdev_mask_set_usage(struct evdev_mask *mask, evdev_usage_t usage) +{ + unsigned int type = evdev_usage_type(usage); + unsigned int code = evdev_usage_code(usage); + + if (type >= EV_MAX) + return; + + bitmask_set_bit(&mask->ev, type); + + switch (type) { + case EV_ABS: + if (code <= ABS_MAX) + infmask_set_bit(&mask->abs, code); + break; + case EV_KEY: + if (code < BTN_MISC) + infmask_set_bit(&mask->key, code); + else if (code <= KEY_MAX) + infmask_set_bit(&mask->btn, code - BTN_MISC); + break; + case EV_REL: + if (code <= REL_MAX) + bitmask_set_bit(&mask->rel, code); + break; + case EV_SW: + if (code <= SW_MAX) + bitmask_set_bit(&mask->sw, code); + break; + } +} + +static inline void +evdev_mask_set_enum(struct evdev_mask *mask, enum evdev_usage usage) +{ + evdev_mask_set_usage(mask, evdev_usage_from(usage)); +} + +static inline bool +evdev_mask_is_set(const struct evdev_mask *mask, evdev_usage_t usage) +{ + unsigned int type = evdev_usage_type(usage); + unsigned int code = evdev_usage_code(usage); + + if (type >= EV_MAX) + return false; + + if (!bitmask_bit_is_set(mask->ev, type)) + return false; + + bool isset = false; + switch (type) { + case EV_ABS: + isset = infmask_bit_is_set(&mask->abs, code); + break; + case EV_KEY: + if (code < BTN_MISC) + isset = infmask_bit_is_set(&mask->key, code); + else + isset = infmask_bit_is_set(&mask->btn, code - BTN_MISC); + break; + case EV_REL: + isset = bitmask_bit_is_set(mask->rel, code); + break; + case EV_SW: + isset = bitmask_bit_is_set(mask->sw, code); + break; + default: + break; + } + + return isset; +} diff --git a/test/test-utils.c b/test/test-utils.c index 26193d0c..36b4fd16 100644 --- a/test/test-utils.c +++ b/test/test-utils.c @@ -3150,6 +3150,51 @@ START_TEST(infmask_test) } END_TEST +START_TEST(evdev_mask_test) +{ + _destroy_(evdev_mask) *mask = evdev_mask_new(); + + evdev_mask_reset(mask); + + litest_assert(bitmask_is_empty(mask->ev)); + litest_assert(bitmask_is_empty(mask->rel)); + litest_assert(bitmask_is_empty(mask->sw)); + litest_assert(infmask_is_empty(&mask->key)); + litest_assert(infmask_is_empty(&mask->btn)); + litest_assert(infmask_is_empty(&mask->abs)); + + evdev_mask_set_enum(mask, EVDEV_BTN_TOOL_PEN); + evdev_mask_set_enum(mask, EVDEV_BTN_TOOL_AIRBRUSH); + + litest_assert(bitmask_bit_is_set(mask->ev, EV_KEY)); + + /* Verify these are in btn, not key */ + litest_assert(!infmask_is_empty(&mask->btn)); + litest_assert(infmask_is_empty(&mask->key)); + + litest_assert(evdev_mask_is_set(mask, evdev_usage_from(EVDEV_BTN_TOOL_PEN))); + litest_assert( + !evdev_mask_is_set(mask, evdev_usage_from(EVDEV_BTN_TOOL_RUBBER))); + litest_assert( + evdev_mask_is_set(mask, evdev_usage_from(EVDEV_BTN_TOOL_AIRBRUSH))); + + /* Test regular key (should go into key field) */ + evdev_mask_set_enum(mask, EVDEV_KEY_ESC); + litest_assert(!infmask_is_empty(&mask->key)); + litest_assert(evdev_mask_is_set(mask, evdev_usage_from(EVDEV_KEY_ESC))); + + evdev_mask_set_enum(mask, EVDEV_REL_X); + litest_assert(bitmask_bit_is_set(mask->ev, EV_REL)); + litest_assert(bitmask_bit_is_set(mask->rel, REL_X)); + litest_assert(evdev_mask_is_set(mask, evdev_usage_from(EVDEV_REL_X))); + + evdev_mask_set_enum(mask, EVDEV_ABS_X); + litest_assert(bitmask_bit_is_set(mask->ev, EV_ABS)); + litest_assert(!infmask_is_empty(&mask->abs)); + litest_assert(evdev_mask_is_set(mask, evdev_usage_from(EVDEV_ABS_X))); +} +END_TEST + int main(void) { @@ -3238,6 +3283,8 @@ main(void) ADD_TEST(infmask_test); + ADD_TEST(evdev_mask_test); + enum litest_runner_result result = litest_runner_run_tests(runner); litest_runner_destroy(runner);