input: add features mechanism
[platform/upstream/kmscon.git] / src / input.c
index 5f6f18f..beba2b2 100644 (file)
 #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)