diff options
| author | Peter Hutterer <peter.hutterer@who-t.net> | 2013-05-27 14:59:41 +1000 |
|---|---|---|
| committer | Peter Hutterer <peter.hutterer@who-t.net> | 2013-05-29 15:33:21 +1000 |
| commit | a3255d3ec78c80918daac7f001dbb246a5970552 (patch) | |
| tree | 5eedd5c3a187e98cb483b42df2b9ff8107a24014 /libevdev | |
| download | libevdev-a3255d3ec78c80918daac7f001dbb246a5970552.tar.gz | |
libevdev is a library to handle evdev devices
Two main goals of this library:
- 'transparently' handle SYN_DROPPED events
- avoid errors in ioctl handling by providing a simpler interface.
Keeps a cached copy of the device for quick querying.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to 'libevdev')
| -rw-r--r-- | libevdev/Makefile.am | 11 | ||||
| -rw-r--r-- | libevdev/libevdev-int.h | 66 | ||||
| -rw-r--r-- | libevdev/libevdev.c | 790 | ||||
| -rw-r--r-- | libevdev/libevdev.h | 427 |
4 files changed, 1294 insertions, 0 deletions
diff --git a/libevdev/Makefile.am b/libevdev/Makefile.am new file mode 100644 index 0000000..e03be9b --- /dev/null +++ b/libevdev/Makefile.am @@ -0,0 +1,11 @@ +lib_LTLIBRARIES=libevdev.la + +libevdev_la_SOURCES = \ + libevdev.h \ + libevdev-int.h \ + libevdev.c + +libevdev_la_LDFLAGS = -version-info $(LIBEVDEV_LT_VERSION) -export-symbols-regex '^libevdev_' + +libevdevincludedir = $(includedir)/libevdev-1.0/libevdev +libevdevinclude_HEADERS = libevdev.h diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h new file mode 100644 index 0000000..c86a656 --- /dev/null +++ b/libevdev/libevdev-int.h @@ -0,0 +1,66 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef libevdev_INT_H +#define libevdev_INT_H + +#include <config.h> +#include "libevdev.h" + +#define LONG_BITS (sizeof(long) * 8) +#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) +#define ARRAY_LENGTH(a) (sizeof(a) / (sizeof((a)[0]))) +#define MAX_NAME 256 +#define MAX_SLOTS 32 +#define ABS_MT_MIN ABS_MT_SLOT +#define ABS_MT_MAX ABS_MT_TOOL_Y +#define ABS_MT_CNT (ABS_MT_MAX - ABS_MT_MIN + 1) + +struct libevdev { + int fd; + libevdev_callback_proc callback; + libevdev_callback_proc sync_callback; + void *userdata; + + char name[MAX_NAME]; + struct input_id ids; + unsigned long bits[NLONGS(EV_CNT)]; + unsigned long props[NLONGS(INPUT_PROP_CNT)]; + unsigned long key_bits[NLONGS(KEY_CNT)]; + unsigned long rel_bits[NLONGS(REL_CNT)]; + unsigned long abs_bits[NLONGS(ABS_CNT)]; + unsigned long led_bits[NLONGS(LED_CNT)]; + unsigned long key_values[NLONGS(KEY_CNT)]; + struct input_absinfo abs_info[ABS_CNT]; + unsigned int mt_slot_vals[MAX_SLOTS][ABS_MT_CNT]; + int num_slots; /**< valid slots in mt_slot_vals */ + + int need_sync; + int grabbed; + + struct input_event *queue; + size_t queue_size; /**< size of queue in elements */ + size_t queue_next; /**< next event index */ +}; + +#endif + diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c new file mode 100644 index 0000000..70ca5de --- /dev/null +++ b/libevdev/libevdev.c @@ -0,0 +1,790 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <config.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "libevdev.h" +#include "libevdev-int.h" + +#define MAXEVENTS 64 + +static inline int +bit_is_set(const unsigned long *array, int bit) +{ + return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS))); +} + +static inline void +set_bit(unsigned long *array, int bit) +{ + array[bit / LONG_BITS] |= (1LL << (bit % LONG_BITS)); +} + +static inline void +clear_bit(unsigned long *array, int bit) +{ + array[bit / LONG_BITS] &= ~(1LL << (bit % LONG_BITS)); +} + +static inline void +set_bit_state(unsigned long *array, int bit, int state) +{ + if (state) + set_bit(array, bit); + else + clear_bit(array, bit); +} + +static unsigned int +type_to_mask_const(const struct libevdev *dev, unsigned int type, const unsigned long **mask) +{ + unsigned int max; + + switch(type) { + case EV_ABS: + *mask = dev->abs_bits; + max = ABS_MAX; + break; + case EV_REL: + *mask = dev->rel_bits; + max = REL_MAX; + break; + case EV_KEY: + *mask = dev->key_bits; + max = KEY_MAX; + break; + case EV_LED: + *mask = dev->led_bits; + max = LED_MAX; + break; + default: + return 0; + } + + return max; +} + +static unsigned int +type_to_mask(struct libevdev *dev, unsigned int type, unsigned long **mask) +{ + unsigned int max; + + switch(type) { + case EV_ABS: + *mask = dev->abs_bits; + max = ABS_MAX; + break; + case EV_REL: + *mask = dev->rel_bits; + max = REL_MAX; + break; + case EV_KEY: + *mask = dev->key_bits; + max = KEY_MAX; + break; + case EV_LED: + *mask = dev->led_bits; + max = LED_MAX; + break; + default: + return 0; + } + + return max; +} + +static int +init_event_queue(struct libevdev *dev) +{ + /* FIXME: count the number of axes, keys, etc. to get a better idea at how many events per + EV_SYN we could possibly get. Then multiply that by the actual buffer size we care about */ + + const int QUEUE_SIZE = 256; + + dev->queue = calloc(QUEUE_SIZE, sizeof(struct input_event)); + if (!dev->queue) + return -ENOSPC; + + dev->queue_size = QUEUE_SIZE; + dev->queue_next = 0; + + return 0; +} + +struct libevdev* +libevdev_new(int fd) +{ + struct libevdev *dev; + + dev = calloc(1, sizeof(*dev)); + dev->num_slots = -1; + + if (fd >= 0) + libevdev_set_fd(dev, fd); + dev->fd = fd; + + return dev; +} + +void +libevdev_free(struct libevdev *dev) +{ + free(dev); +} + +int +libevdev_change_fd(struct libevdev *dev, int fd) +{ + if (dev->fd == -1) + return -1; + dev->fd = fd; + return 0; +} + +int +libevdev_set_fd(struct libevdev* dev, int fd) +{ + int rc; + int i; + + if (dev->fd == -1) { + libevdev_callback_proc cb, scb; + void *userdata; + + /* these may be set before set_fd */ + cb = dev->callback; + scb = dev->sync_callback; + userdata = dev->userdata; + + memset(dev, 0, sizeof(*dev)); + + dev->fd = -1; + dev->callback = cb; + dev->sync_callback = scb; + dev->userdata = userdata; + } + + rc = ioctl(fd, EVIOCGBIT(0, sizeof(dev->bits)), dev->bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGNAME(sizeof(dev->name) - 1), dev->name); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGID, &dev->ids); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGPROP(sizeof(dev->props)), dev->props); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_REL, sizeof(dev->rel_bits)), dev->rel_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(dev->abs_bits)), dev->abs_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_LED, sizeof(dev->led_bits)), dev->led_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(dev->key_bits)), dev->key_bits); + if (rc < 0) + goto out; + + for (i = ABS_X; i <= ABS_MAX; i++) { + if (bit_is_set(dev->abs_bits, i)) { + struct input_absinfo abs_info; + rc = ioctl(fd, EVIOCGABS(i), &dev->abs_info[i]); + if (rc < 0) + goto out; + + if (i == ABS_MT_SLOT) + dev->num_slots = abs_info.maximum + 1; /* FIXME: non-zero min? */ + + } + } + + rc = init_event_queue(dev); + if (rc < 0) + return -rc; + + /* not copying key state because we won't know when we'll start to + * use this fd and key's are likely to change state by then. + * Same with the valuators, really, but they may not change. + */ + + dev->fd = fd; + +out: + return rc ? -errno : 0; +} + +int +libevdev_get_fd(const struct libevdev* dev) +{ + return dev->fd; +} + +int +libevdev_set_callbacks(struct libevdev *dev, + libevdev_callback_proc callback, + libevdev_callback_proc sync_callback, + void *userdata) +{ + dev->callback = callback; + dev->sync_callback = sync_callback; + dev->userdata = userdata; + + return 0; +} + +static inline void +init_event(struct input_event *ev, int type, int code, int value) +{ + ev->time.tv_sec = 0; /* FIXME: blah! */ + ev->time.tv_usec = 0; /* FIXME: blah! */ + ev->type = type; + ev->code = code; + ev->value = value; +} + +static int +sync_key_state(struct libevdev *dev) +{ + int rc; + int i; + unsigned long keystate[NLONGS(KEY_MAX)]; + struct input_event ev; + + rc = ioctl(dev->fd, EVIOCGKEY(sizeof(keystate)), keystate); + if (rc < 0) + goto out; + + for (i = 0; i < KEY_MAX; i++) { + int old, new; + old = bit_is_set(dev->key_values, i); + new = bit_is_set(keystate, i); + if (old ^ new) { + init_event(&ev, EV_KEY, i, new ? 1 : 0); + dev->sync_callback(dev, &ev, dev->userdata); + } + set_bit_state(dev->key_values, i, new); + } + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_abs_state(struct libevdev *dev) +{ + int rc; + int i; + struct input_event ev; + + for (i = ABS_X; i <= ABS_MAX; i++) { + if (i >= ABS_MT_MIN && i <= ABS_MT_MAX) + continue; + + if (bit_is_set(dev->abs_bits, i)) { + struct input_absinfo abs_info; + rc = ioctl(dev->fd, EVIOCGABS(i), &abs_info); + if (rc < 0) + goto out; + + if (dev->abs_info[i].value != abs_info.value) { + init_event(&ev, EV_ABS, i, abs_info.value); + dev->sync_callback(dev, &ev, dev->userdata); + dev->abs_info[i].value = abs_info.value; + } + } + } + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_mt_state(struct libevdev *dev) +{ + int rc; + int i; + struct mt_state { + int code; + int val[MAX_SLOTS]; + } mt_state[ABS_MT_CNT]; + struct input_event ev; + + for (i = ABS_MT_MIN; i < ABS_MT_MAX; i++) { + if (i == ABS_MT_SLOT) + continue; + + mt_state[i].code = i; + rc = ioctl(dev->fd, EVIOCGMTSLOTS(sizeof(struct mt_state)), &mt_state[i]); + if (rc < 0) + goto out; + } + + for (i = 0; i < dev->num_slots; i++) { + int j; + init_event(&ev, EV_ABS, ABS_MT_SLOT, i); + dev->sync_callback(dev, &ev, dev->userdata); + for (j = ABS_MT_MIN; j < ABS_MT_MAX; j++) { + if (dev->mt_slot_vals[i][j] != mt_state[j].val[i]) { + init_event(&ev, EV_ABS, j, mt_state[j].val[i]); + dev->sync_callback(dev, &ev, dev->userdata); + dev->mt_slot_vals[i][j] = mt_state[j].val[i]; + } + } + } + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_state(struct libevdev *dev, unsigned int flags) +{ + int rc = 0; + struct input_event ev; + + if (libevdev_has_event_type(dev, EV_KEY)) + rc = sync_key_state(dev); /* FIXME: handle ER_SINGLE */ + if (rc == 0 && libevdev_has_event_type(dev, EV_ABS)) + rc = sync_abs_state(dev); + if (rc == 0 && libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT)) + rc = sync_mt_state(dev); + + init_event(&ev, EV_SYN, SYN_REPORT, 0); + dev->sync_callback(dev, &ev, dev->userdata); + + dev->need_sync = 0; + + return rc; +} + +static int +update_key_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_KEY)) + return 1; + + if (e->code > KEY_MAX) + return 1; + + if (e->value == 0) + clear_bit(dev->key_values, e->code); + else + set_bit(dev->key_values, e->code); + + return 0; +} + +static int +update_abs_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_ABS)) + return 1; + + if (e->code > ABS_MAX) + return 1; + + dev->abs_info[e->code].value = e->value; + + return 0; +} + +static int +update_state(struct libevdev *dev, const struct input_event *e) +{ + int rc = 0; + + switch(e->type) { + case EV_SYN: + case EV_REL: + break; + case EV_KEY: + rc = update_key_state(dev, e); + break; + case EV_ABS: + rc = update_abs_state(dev, e); + break; + } + + return rc; +} + +static int +read_more_events(struct libevdev *dev) +{ + int free_elem; + int len; + + free_elem = dev->queue_size - dev->queue_next - 1; + if (free_elem <= 0) + return 0; + + len = read(dev->fd, &dev->queue[dev->queue_next], free_elem * sizeof(struct input_event)); + if (len < 0) { + if (errno != EAGAIN || dev->queue_next == 0) + return -errno; + } else if (len > 0 && len % sizeof(struct input_event) != 0) + return -EINVAL; + + dev->queue_next += len/sizeof(struct input_event); + + return 0; +} + +int libevdev_read_events(struct libevdev *dev, + unsigned int flags) +{ + int nevents, processed; + int i; + int rc = 0; + + if (dev->fd < 0) + return -ENODEV; + + if (flags & ER_SYNC) { + if (!dev->need_sync) + return 0; + return sync_state(dev, flags); + } + + /* Always read in some more events. Best case this smoothes over a potential SYN_DROPPED, + worst case we don't read fast enough and end up with SYN_DROPPED anyway */ + rc = read_more_events(dev); + if (rc < 0) + goto out; + + nevents = dev->queue_next; + for (processed = 0; processed < nevents; processed++) { + struct input_event *e = &dev->queue[processed]; + + update_state(dev, e); + + if (e->type == EV_SYN && e->code == SYN_DROPPED) { + dev->need_sync = 0; + rc = 1; + break; + } else if (libevdev_has_event_code(dev, e->type, e->code)) { + if (dev->callback(dev, e, dev->userdata) != 0) { + rc = -ECANCELED; + break; + } + } + } + + for (i = 0; i < nevents - processed; i++) { + dev->queue[i] = dev->queue[i + processed]; + } + + dev->queue_next -= processed; +out: + return rc; +} + +const char * +libevdev_get_name(const struct libevdev *dev) +{ + return dev->name; +} + +int libevdev_get_pid(const struct libevdev *dev) +{ + return dev->ids.product; +} + +int libevdev_get_vid(const struct libevdev *dev) +{ + return dev->ids.vendor; +} + +int libevdev_get_bustype(const struct libevdev *dev) +{ + return dev->ids.bustype; +} + +int +libevdev_has_property(const struct libevdev *dev, unsigned int prop) +{ + return (prop <= INPUT_PROP_MAX) && bit_is_set(dev->props, prop); +} + +int +libevdev_has_event_type(const struct libevdev *dev, unsigned int type) +{ + return (type <= EV_MAX) && bit_is_set(dev->bits, type); +} + +int +libevdev_has_event_code(const struct libevdev *dev, unsigned int type, unsigned int code) +{ + const unsigned long *mask; + unsigned int max; + + if (!libevdev_has_event_type(dev, type)) + return 0; + + if (type == EV_SYN) + return 1; + + max = type_to_mask_const(dev, type, &mask); + + if (code > max) + return 0; + + return bit_is_set(mask, code); +} + +int +libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned int code) +{ + int value; + + if (!libevdev_has_event_type(dev, type) || !libevdev_has_event_code(dev, type, code)) + return 0; + + switch (type) { + case EV_ABS: value = dev->abs_info[code].value; break; + case EV_KEY: value = bit_is_set(dev->key_values, code); break; + default: + value = 0; + break; + } + + return value; +} + +int +libevdev_fetch_event_value(const struct libevdev *dev, unsigned int type, unsigned int code, int *value) +{ + if (libevdev_has_event_type(dev, type) && + libevdev_has_event_code(dev, type, code)) { + *value = libevdev_get_event_value(dev, type, code); + return 1; + } else + return 0; +} + +int +libevdev_get_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code) +{ + if (!libevdev_has_event_type(dev, EV_ABS) || !libevdev_has_event_code(dev, EV_ABS, code)) + return 0; + + if (slot >= dev->num_slots || slot >= MAX_SLOTS) + return 0; + + if (code > ABS_MT_MAX || code < ABS_MT_MIN) + return 0; + + return dev->mt_slot_vals[slot][code - ABS_MT_MIN]; +} + +int +libevdev_fetch_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code, int *value) +{ + if (libevdev_has_event_type(dev, EV_ABS) && + libevdev_has_event_code(dev, EV_ABS, code) && + slot < dev->num_slots && slot < MAX_SLOTS) { + *value = libevdev_get_slot_value(dev, slot, code); + return 1; + } else + return 0; +} + +int +libevdev_get_num_slots(const struct libevdev *dev) +{ + return dev->num_slots; +} + +const struct input_absinfo* +libevdev_get_abs_info(const struct libevdev *dev, unsigned int code) +{ + if (!libevdev_has_event_type(dev, EV_ABS) || + !libevdev_has_event_code(dev, EV_ABS, code)) + return NULL; + + return &dev->abs_info[code]; +} + +int +libevdev_get_abs_min(const struct libevdev *dev, unsigned int code) +{ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); + + return absinfo ? absinfo->minimum : 0; +} + +int +libevdev_get_abs_max(const struct libevdev *dev, unsigned int code) +{ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); + + return absinfo ? absinfo->maximum : 0; +} + +int +libevdev_get_abs_fuzz(const struct libevdev *dev, unsigned int code) +{ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); + + return absinfo ? absinfo->fuzz : 0; +} + +int +libevdev_get_abs_flat(const struct libevdev *dev, unsigned int code) +{ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); + + return absinfo ? absinfo->flat : 0; +} + +int +libevdev_get_abs_resolution(const struct libevdev *dev, unsigned int code) +{ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); + + return absinfo ? absinfo->resolution : 0; +} + +int +libevdev_enable_event_type(struct libevdev *dev, unsigned int type) +{ + if (type > EV_MAX) + return 1; + + set_bit(dev->bits, type); + + /* FIXME: pass through to kernel */ + + return 0; +} + +int +libevdev_disable_event_type(struct libevdev *dev, unsigned int type) +{ + if (type > EV_MAX) + return 1; + + clear_bit(dev->bits, type); + + /* FIXME: pass through to kernel */ + + return 0; +} + +int +libevdev_enable_event_code(struct libevdev *dev, unsigned int type, + unsigned int code, const void *data) +{ + unsigned int max; + unsigned long *mask; + + if (libevdev_enable_event_type(dev, type)) + return 1; + + max = type_to_mask(dev, type, &mask); + + if (code > max) + return 1; + + set_bit(mask, code); + + if (type == EV_ABS) { + const struct input_absinfo *abs = data; + dev->abs_info[code] = *abs; + } + + /* FIXME: pass through to kernel */ + + return 0; +} + +int +libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned int code) +{ + unsigned int max; + unsigned long *mask; + + if (type > EV_MAX) + return 1; + + max = type_to_mask(dev, type, &mask); + + if (code > max) + return 1; + + clear_bit(mask, code); + + /* FIXME: pass through to kernel */ + + return 0; +} + +int +libevdev_kernel_set_abs_value(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs) +{ + int rc; + + if (code > ABS_MAX) + return -EINVAL; + + rc = ioctl(dev->fd, EVIOCSABS(code), *abs); + if (rc < 0) + rc = -errno; + else + rc = libevdev_enable_event_code(dev, EV_ABS, code, abs); + + return rc; +} + +int +libevdev_grab(struct libevdev *dev, int grab) +{ + int rc = 0; + + if (grab && !dev->grabbed) + rc = ioctl(dev->fd, EVIOCGRAB, (void *)1); + else if (!grab && dev->grabbed) + rc = ioctl(dev->fd, EVIOCGRAB, (void *)0); + + if (rc == 0) + dev->grabbed = grab; + + return rc < 0 ? -errno : 0; +} diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h new file mode 100644 index 0000000..a1787ee --- /dev/null +++ b/libevdev/libevdev.h @@ -0,0 +1,427 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef libevdev_H +#define libevdev_H + +#include <config.h> +#include <linux/input.h> + +struct libevdev; + +enum EvdevReadFlags { + ER_SINGLE = 1, /**< Read until including the first EV_SYN or EAGAIN */ + ER_SYNC = 2, /**< Process data in sync mode */ + ER_ALL = 4, /**< Read until EAGAIN */ +}; + + +/** + * Initialize a new libevdev struct. + * + * @param fd If fd >= 0, the device is initialised for the fd. Otherwise, a + * caller must call evdev_set_fd() before attempting to read events. + * + * @see libevdev_set_fd + */ +struct libevdev* libevdev_new(int fd); + +/** + * Clean up and free the libevdev struct. + * + * @note This function may be called before libevdev_set_fd. + */ +void libevdev_free(struct libevdev *dev); + +/** + * Grab or ungrab the device through a kernel EVIOCGRAB. This prevents other + * clients (including kernel-internal ones such as rfkill) from receiving + * events from this device. + * + * This is generally a bad idea. Don't do this. + * + * Grabbing an already grabbed device, or ungrabbing an ungrabbed device is + * a noop and always succeeds. + * + * @param grab If true, grab the device. Otherwise ungrab the device. + * + * @return 0 if the device was successfull grabbed or ungrabbed, or a + * negative errno in case of failure. + */ +int libevdev_grab(struct libevdev *dev, int grab); + +/** + * Set the fd for this struct and initialize internal data. + * The fd must be open for reading and ioctl. + * + * This function may only be called once per device. If you need to re-read + * a device, use libevdev_free and libevdev_new. If you need to change the + * fd, use libevdev_change_fd. + * + * Unless otherwise specified, libevdev function behavior is undefined until + * a successfull call to libevdev_set_fd. + * + * @param fd The file descriptor for the device + * + * @return 0 on success, or a negative error code on failure + * + * @see libevdev_change_fd + * @see libevdev_new + * @see libevdev_free + */ +int libevdev_set_fd(struct libevdev* dev, int fd); + +/** + * Change the fd for this device, without re-reading the actual device. + * + * It is an error to call this function before calling libevdev_set_fd. + * + * @param fd The new fd + * + * @return 0 on success, or -1 on failure. + * + * @see libevdev_set_fd + */ +int libevdev_change_fd(struct libevdev* dev, int fd); + +/** + * + * @return The previously set fd, or -1 if none had been set previously. + * @note This function may be called before libevdev_set_fd. + */ +int libevdev_get_fd(const struct libevdev* dev); + +/** + * Event callback used to report events back to the client. + * + * If this function returns -1, event processing is interrupted and returned + * to the caller of libevdev_read_events. A future call to + * libevdev_read_events will continue with the next event in the queue. + * + * @param dev The device this callback was invoked on + * @param ev The event read from the kernel + * @param userdata Previously assigned caller-specific data + * + * @return 0 on success, or -1 on failure. + * + * @see libevdev_read_events + */ +typedef int (*libevdev_callback_proc)(struct libevdev *dev, struct input_event *ev, void *userdata); + +/** + * Set the callbacks to be used when reading events off the fd. + * Two callbacks are used, one for reporting events during normal operation, one for + * reporting events during an event sync. If either is NULL, no events are + * reported for that mode. + * + * @param callback Callback used for regular events when read off the fd + * @param sync_callback Callback used for events while re-syncing after a + * SYN_DROPPED event. + * @param userdata Caller-specific data, passed back through the callbacks. + * + * @return zero on success, -1 on failure + * + * @see libevdev_read_events + * @note This function may be called before libevdev_set_fd. + */ +int libevdev_set_callbacks(struct libevdev *dev, + libevdev_callback_proc callback, + libevdev_callback_proc sync_callback, + void *userdata); + +/** + * Read events off the fd and call the matching callback. + * + * Depending on the flags, the behaviour changes as follows: + * - ER_SINGLE: read a single event off the fd (i.e. up to the next EV_SYN). + * This should only be used if the caller is time-sensitive and event + * processing of multiple events may prevent other computation. + * - ER_ALL: read all the events off the fd until a read would block or + * an SYN_DROPPED event is read. + * - ER_SYNC: switch to sync mode and report all events that are required to + * bring the device state back in sync with the kernel device state. + * + * @param fd The file descriptor previously set, open in non-blocking mode. If + * the file descriptor differs from the previous one, -EBADF is returned. + * @param flags The set of flags to decide how to handle events. + * + * @return On failure, a negative errno is returned. + * @retval 0 One or more events where read of the fd + * @retval -EAGAIN No events are currently on the fd + * @retval -ECANCELLED The callback returned non-zero + * @retval 1 A SYN_DROPPED event was received + * + * @see libevdev_set_callbacks + * + * @note This function is signal-safe. + */ +int libevdev_read_events(struct libevdev *dev, unsigned int flags); + +/** + * @return The device name as read off the kernel device + * + * @note This function is signal-safe. + */ +const char* libevdev_get_name(const struct libevdev *dev); + +/** + * @return The device's product ID + * + * @note This function is signal-safe. + */ +int libevdev_get_pid(const struct libevdev *dev); +/** + * @return The device's vendor ID + * + * @note This function is signal-safe. + */ +int libevdev_get_vid(const struct libevdev *dev); + +/** + * @return The device's bus type + * + * @note This function is signal-safe. + */ +int libevdev_get_bustype(const struct libevdev *dev); + +/** + * @return 1 if the device supports this event type, or 0 otherwise. + * + * @note This function is signal-safe + */ +int libevdev_has_property(const struct libevdev *dev, unsigned int prop); + +/** + * @return 1 if the device supports this event type, or 0 otherwise. + * + * @note This function is signal-safe. + */ +int libevdev_has_event_type(const struct libevdev *dev, unsigned int type); + +/** + * @return 1 if the device supports this event type, or 0 otherwise. + * + * @note This function is signal-safe. + */ +int libevdev_has_event_code(const struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * @return axis minimum for the given axis or 0 if the axis is invalid + */ +int libevdev_get_abs_min(const struct libevdev *dev, unsigned int code); +/** + * @return axis maximum for the given axis or 0 if the axis is invalid + */ +int libevdev_get_abs_max(const struct libevdev *dev, unsigned int code); +/** + * @return axis fuzz for the given axis or 0 if the axis is invalid + */ +int libevdev_get_abs_fuzz(const struct libevdev *dev, unsigned int code); +/** + * @return axis flat for the given axis or 0 if the axis is invalid + */ +int libevdev_get_abs_flat(const struct libevdev *dev, unsigned int code); +/** + * @return axis resolution for the given axis or 0 if the axis is invalid + */ +int libevdev_get_abs_resolution(const struct libevdev *dev, unsigned int code); + +/** + * @return The input_absinfo for the given code, or NULL if the device does + * not support this event code. + */ +const struct input_absinfo* libevdev_get_abs_info(const struct libevdev *dev, unsigned int code); + +/** + * Behaviour of this function is undefined if the device does not provide + * the event. + * + * @return The current value of the event. + * + * @note This function is signal-safe. + * @note The value for ABS_MT_ events is undefined, use + * libevdev_get_slot_value instead + * + * @see libevdev_get_slot_value + */ +int libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * Fetch the current value of the event type. This is a shortcut for + * + * <pre> + * if (libevdev_has_event_type(dev, t) && libevdev_has_event_code(dev, t, c)) + * val = libevdev_get_event_value(dev, t, c); + * </pre> + * + * @return non-zero if the device supports this event code, or zero + * otherwise. On return of zero, value is unmodified. + * + * @note This function is signal-safe. + * @note The value for ABS_MT_ events is undefined, use + * libevdev_fetch_slot_value instead + * + * @see libevdev_fetch_slot_value + */ +int libevdev_fetch_event_value(const struct libevdev *dev, unsigned int type, unsigned int code, int *value); + +/** + * Return the current value of the code for the given slot. + * + * The return value is undefined for a slot exceeding the available slots on + * the device, or for a device that does not have slots. + * + * @note This function is signal-safe. + * @note The value for events other than ABS_MT_ is undefined, use + * libevdev_fetch_value instead + * + * @see libevdev_get_value + */ +int libevdev_get_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code); + +/** + * Fetch the current value of the code for the given slot. This is a shortcut for + * + * <pre> + * if (libevdev_has_event_type(dev, EV_ABS) && + * libevdev_has_event_code(dev, EV_ABS, c) && + * slot < device->number_of_slots) + * val = libevdev_get_slot_value(dev, slot, c); + * </pre> + * + * @return non-zero if the device supports this event code, or zero + * otherwise. On return of zero, value is unmodified. + * + * @note This function is signal-safe. + * @note The value for ABS_MT_ events is undefined, use + * libevdev_fetch_slot_value instead + * + * @see libevdev_fetch_slot_value + */ +int libevdev_fetch_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code, int *value); + +/** + * Get the number of slots supported by this device. + * + * Note that the slot offset may be non-zero, use libevdev_get_abs_min() or + * libevdev_get_abs_info() to get the minimum slot number. + * + * @return The number of slots supported, or -1 + */ +int libevdev_get_num_slots(const struct libevdev *dev); + +/** + * Forcibly enable an event type on this device, even if the underlying + * device does not support it. While this cannot make the device actually + * report such events, it will now return true for libevdev_has_event_type. + * + * This is a local modification only affecting only this process and only + * this device. + * + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_type + */ +int libevdev_enable_event_type(struct libevdev *dev, unsigned int type); + +/** + * Forcibly disable an event type on this device, even if the underlying + * device provides it, effectively muting all keys or axes. libevdev will + * filter any events matching this type and none will reach the caller. + * libevdev_has_event_type will return false for this type. + * + * In most cases, a caller likely only wants to disable a single code, not + * the whole type. Use libevdev_disable_event_code for that. + * + * This is a local modification only affecting only this process and only + * this device. + * + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_type + * @see libevdev_disable_event_type + */ +int libevdev_disable_event_type(struct libevdev *dev, unsigned int type); + +/** + * Forcibly enable an event type on this device, even if the underlying + * device does not support it. While this cannot make the device actually + * report such events, it will now return true for libevdev_has_event_code. + * + * The last argument depends on the type and code: + * - If type is EV_ABS, the vararg must be a pointer to a struct input_absinfo + * containing the data for this axis. + * - For all other types, the argument is ignored. + * + * This function calls libevdev_enable_event_type if necessary. + * + * This is a local modification only affecting only this process and only + * this device. + * + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * @param code The event code to enable (ABS_X, REL_X, etc.) + * @param data Axis/key data, depending on type and code + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_enable_event_type + */ +int libevdev_enable_event_code(struct libevdev *dev, unsigned int type, unsigned int code, const void *data); + +/** + * Forcibly disable an event code on this device, even if the underlying + * device provides it, effectively muting this key or axis. libevdev will + * filter any events matching this type and code and none will reach the + * caller. + * libevdev_has_event_code will return false for this code combination. + * + * Disabling all event codes for a given type will not disable the event + * type. Use libevdev_disable_event_type for that. + * + * This is a local modification only affecting only this process and only + * this device. + * + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * @param code The event code to enable (ABS_X, REL_X, etc.) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_code + * @see libevdev_disable_event_type + */ +int libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * Set the device's EV_ABS/<code> axis to the value defined in the abs + * parameter. This will be written to the kernel. + * + * @return zero on success, or a negative errno on failure + * + * @see libevdev_enable_event_code + */ +int libevdev_kernel_set_abs_value(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs); + +#endif /* libevdev_H */ |
