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 <ran234@gmail.com> Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
parent
d8cf293c94
commit
6b7265447e
109
src/input.c
109
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)
|
||||
|
@ -49,6 +49,7 @@
|
||||
#define KMSCON_INPUT_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user