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:
Ran Benita 2012-03-04 18:43:19 +02:00 committed by David Herrmann
parent d8cf293c94
commit 6b7265447e
2 changed files with 93 additions and 23 deletions

View File

@ -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)

View File

@ -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 */