Search code examples
clinuxioctl

Filtering keycodes with EVIOCSMASK


I would like to receive only EV_KEY events with codes 224 and 225 when reading from a /dev/input/event* file. How do I do that?


As far as I can tell, I need to run ioctl(fd, EVIOCSMASK, &mask), but I don't understand how to set up a struct input_mask with my specifications.

I tried setting type to EV_KEY, but that seems to filter out EV_KEY, and if I set anything in codes_* (such as an array of codes and a pointer to it), then ioctl returns -1.


Solution

  • Look at include/linux/input.h:

    The event mask is a per-client mask that specifies which events are forwarded to the client. Each event code is represented by a single bit in the event mask. If the bit is set, the event is passed to the client normally.

    In other words:

    #include <stdlib.h>
    #include <limits.h>
    #include <sys/ioctl.h>
    #include <linux/input.h>
    #include <errno.h>
    
    #define  ULONG_BITS  (CHAR_BIT * sizeof (unsigned long))
    
    static void set_bit(unsigned long bits[], size_t bit)
    {
        bits[bit / ULONG_BITS] |= 1UL << (bit % ULONG_BITS);
    }
    
    static void set_keymask(int fd)
    {
        unsigned long      bits[(KEY_CNT + ULONG_BITS - 1) / ULONG_BITS] = { 0 };
        struct input_mask  mask;
    
        mask.type = EV_KEY;
        mask.codes_size = KEY_CNT;
        mask.codes_ptr = &bits;
    
        /* Only report KEY_BRIGHTNESSDOWN and KEY_BRIGHTNESSUP */
        set_bit(bits, KEY_BRIGHTNESSDOWN);
        set_bit(bits, KEY_BRIGHTNESSUP);
    
        if (ioctl(fd, EVIOSMASK, &mask))
            return errno;
    
        return 0;
    }