From 6b7265447e7e06be7e48d91a8c1bae9b6525c886 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 4 Mar 2012 18:43:19 +0200 Subject: [PATCH] input: add features mechanism This commit discards the simplistic udev check for the ID_KEYBOARD property in favor of a more direct "feature" probing. This is done for the following reasons: - We will need to use input devices which are not necessarily keyboards, for example the PC speaker to sound the bell. - To differentiate between keyboard with certain capabilities, such as LEDs (indicators). We can then perform actions on devices according to their feature bits. - We check directly for what we need, i.e. the ability to send/receive certain evdev events, without relying on logic in udev. This also reduces our dependency on udev if we ever want to make it optional. Signed-off-by: Ran Benita Signed-off-by: David Herrmann --- src/input.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++------------- src/input.h | 7 ++++ 2 files changed, 93 insertions(+), 23 deletions(-) diff --git a/src/input.c b/src/input.c index 5f6f18f..beba2b2 100644 --- a/src/input.c +++ b/src/input.c @@ -53,16 +53,26 @@ #include "kbd.h" #include "log.h" +/* How many longs are needed to hold \n bits. */ +#define NLONGS(n) (((n) + LONG_BIT - 1) / LONG_BIT) + enum input_state { INPUT_ASLEEP, INPUT_AWAKE, }; +/* See probe_device_features(). */ +enum device_feature { + FEATURE_HAS_KEYS = 0x01, +}; + struct kmscon_input_device { size_t ref; struct kmscon_input_device *next; struct kmscon_input *input; + unsigned int features; + int rfd; char *devnode; struct kmscon_fd *fd; @@ -157,15 +167,18 @@ int kmscon_input_device_wake_up(struct kmscon_input_device *device) return -errno; } - /* this rediscovers the keyboard state if sth changed during sleep */ - kmscon_kbd_reset(device->kbd, device->rfd); + if (device->features & FEATURE_HAS_KEYS) { + /* rediscover the keyboard state if sth changed during sleep */ + kmscon_kbd_reset(device->kbd, device->rfd); - ret = kmscon_eloop_new_fd(device->input->eloop, &device->fd, - device->rfd, KMSCON_READABLE, device_data_arrived, device); - if (ret) { - close(device->rfd); - device->rfd = -1; - return ret; + ret = kmscon_eloop_new_fd(device->input->eloop, &device->fd, + device->rfd, KMSCON_READABLE, + device_data_arrived, device); + if (ret) { + close(device->rfd); + device->rfd = -1; + return ret; + } } return 0; @@ -179,14 +192,17 @@ void kmscon_input_device_sleep(struct kmscon_input_device *device) if (device->rfd < 0) return; - kmscon_eloop_rm_fd(device->fd); + if (device->features & FEATURE_HAS_KEYS) + kmscon_eloop_rm_fd(device->fd); + device->fd = NULL; close(device->rfd); device->rfd = -1; } static int kmscon_input_device_new(struct kmscon_input_device **out, - struct kmscon_input *input, const char *devnode) + struct kmscon_input *input, const char *devnode, + unsigned int features) { int ret; struct kmscon_input_device *device; @@ -217,6 +233,7 @@ static int kmscon_input_device_new(struct kmscon_input_device **out, } device->input = input; + device->features = features; device->rfd = -1; *out = device; @@ -343,34 +360,80 @@ void kmscon_input_unref(struct kmscon_input *input) log_debug("input: destroying input object\n"); } +/* + * See if the device has anything useful to offer. + * We go over the desired features and return a mask of enum device_feature's. + * */ +static unsigned int probe_device_features(const char *node) +{ + int i, fd; + unsigned int features = 0; + unsigned long evbits[NLONGS(EV_CNT)] = { 0 }; + unsigned long keybits[NLONGS(KEY_CNT)] = { 0 }; + + fd = open(node, O_NONBLOCK | O_CLOEXEC); + if (fd < 0) + return 0; + + /* Which types of input events the device supports. */ + errno = 0; + ioctl(fd, EVIOCGBIT(0, sizeof(evbits)), evbits); + if (errno) + goto err_ioctl; + + /* Device supports keys/buttons. */ + if (kmscon_evdev_bit_is_set(evbits, EV_KEY)) { + errno = 0; + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits); + if (errno) + goto err_ioctl; + + /* + * If the device support any of the normal keyboard keys, we + * take it. Even if the keys are not ordinary they can be + * mapped to anything by the keyboard backend. + */ + for (i = KEY_RESERVED; i <= KEY_MIN_INTERESTING; i++) { + if (kmscon_evdev_bit_is_set(keybits, i)) { + features |= FEATURE_HAS_KEYS; + break; + } + } + } + + close(fd); + return features; + +err_ioctl: + if (errno != ENOTTY) + log_warn("input: cannot probe features of device (%s): %m\n", + node); + close(fd); + return 0; +} + static void add_device(struct kmscon_input *input, struct udev_device *udev_device) { int ret; struct kmscon_input_device *device; - const char *value, *node; + const char *node; + unsigned int features; if (!input || !udev_device) return; - /* - * TODO: Here should go a proper filtering of input devices we're - * interested in. Currently, we add all kinds of devices. A simple - * blacklist should be the easiest way. - */ - node = udev_device_get_devnode(udev_device); if (!node) return; - value = udev_device_get_property_value(udev_device, - "ID_INPUT_KEYBOARD"); - if (!value || strcmp(value, "1")) { - log_debug("input: ignoring non-keyboard device %s\n", node); + features = probe_device_features(node); + if (!(features & FEATURE_HAS_KEYS)) { + log_debug("input: ignoring non-useful device %s\n", node); return; } - ret = kmscon_input_device_new(&device, input, node); + ret = kmscon_input_device_new(&device, input, node, features); if (ret) { log_warn("input: cannot create input device for %s\n", node); @@ -389,7 +452,7 @@ static void add_device(struct kmscon_input *input, device->next = input->devices; input->devices = device; - log_debug("input: added device %s\n", node); + log_debug("input: added device %s (features: %#x)\n", node, features); } static void remove_device(struct kmscon_input *input, const char *node) diff --git a/src/input.h b/src/input.h index 9b70e31..d8029ad 100644 --- a/src/input.h +++ b/src/input.h @@ -49,6 +49,7 @@ #define KMSCON_INPUT_H #include +#include #include #include "eloop.h" @@ -89,4 +90,10 @@ void kmscon_input_sleep(struct kmscon_input *input); void kmscon_input_wake_up(struct kmscon_input *input); bool kmscon_input_is_asleep(struct kmscon_input *input); +/* Querying the results of evdev ioctl's. Also used by kbd backends. */ +static inline bool kmscon_evdev_bit_is_set(const unsigned long *array, int bit) +{ + return !!(array[bit / LONG_BIT] & (1LL << (bit % LONG_BIT))); +} + #endif /* KMSCON_INPUT_H */ -- 2.7.4