Support EV_REP values through libevdev_get_event_value
[platform/upstream/libevdev.git] / libevdev / libevdev.c
index 76973bc..60bbbfc 100644 (file)
 
 #include <config.h>
 #include <errno.h>
+#include <poll.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdarg.h>
+#include <stdbool.h>
 
 #include "libevdev.h"
 #include "libevdev-int.h"
@@ -34,6 +36,8 @@
 
 #define MAXEVENTS 64
 
+static int sync_mt_state(struct libevdev *dev, int create_events);
+
 static int
 init_event_queue(struct libevdev *dev)
 {
@@ -46,37 +50,88 @@ init_event_queue(struct libevdev *dev)
 }
 
 static void
-_libevdev_log(struct libevdev *dev, const char *format, ...)
+libevdev_dflt_log_func(enum libevdev_log_priority priority,
+                      void *data,
+                      const char *file, int line, const char *func,
+                      const char *format, va_list args)
+{
+       const char *prefix;
+       switch(priority) {
+               case LIBEVDEV_LOG_ERROR: prefix = "libevdev error"; break;
+               case LIBEVDEV_LOG_INFO: prefix = "libevdev info"; break;
+               case LIBEVDEV_LOG_DEBUG:
+                                       prefix = "libevdev debug";
+                                       break;
+               default:
+                                       prefix = "libevdev INVALID LOG PRIORITY";
+                                       break;
+       }
+       /* default logging format:
+          libevev error in libevdev_some_func: blah blah
+          libevev info in libevdev_some_func: blah blah
+          libevev debug in file.c:123:libevdev_some_func: blah blah
+        */
+
+       fprintf(stderr, "%s in ", prefix);
+       if (priority == LIBEVDEV_LOG_DEBUG)
+               fprintf(stderr, "%s:%d:", file, line);
+       fprintf(stderr, "%s: ", func);
+       vfprintf(stderr, format, args);
+}
+
+/*
+ * Global logging settings.
+ */
+struct logdata log_data = {
+       LIBEVDEV_LOG_INFO,
+       libevdev_dflt_log_func,
+       NULL,
+};
+
+void
+log_msg(enum libevdev_log_priority priority,
+       void *data,
+       const char *file, int line, const char *func,
+       const char *format, ...)
 {
        va_list args;
 
+       if (!log_data.handler)
+               return;
+
        va_start(args, format);
-       dev->log(format, args);
+       log_data.handler(priority, data, file, line, func, format, args);
        va_end(args);
 }
 
 static void
-libevdev_noop_log_func(const char *format, va_list args)
+libevdev_reset(struct libevdev *dev)
 {
+       memset(dev, 0, sizeof(*dev));
+       dev->fd = -1;
+       dev->initialized = false;
+       dev->num_slots = -1;
+       dev->current_slot = -1;
+       dev->grabbed = LIBEVDEV_UNGRAB;
+       dev->sync_state = SYNC_NONE;
+       libevdev_enable_event_type(dev, EV_SYN);
 }
 
-struct libevdev*
+LIBEVDEV_EXPORT struct libevdev*
 libevdev_new(void)
 {
        struct libevdev *dev;
 
-       dev = calloc(1, sizeof(*dev));
+       dev = malloc(sizeof(*dev));
        if (!dev)
                return NULL;
-       dev->fd = -1;
-       dev->num_slots = -1;
-       dev->current_slot = -1;
-       dev->log = libevdev_noop_log_func;
+
+       libevdev_reset(dev);
 
        return dev;
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_new_from_fd(int fd, struct libevdev **dev)
 {
        struct libevdev *d;
@@ -84,7 +139,7 @@ libevdev_new_from_fd(int fd, struct libevdev **dev)
 
        d = libevdev_new();
        if (!d)
-               return -ENOSPC;
+               return -ENOMEM;
 
        rc = libevdev_set_fd(d, fd);
        if (rc < 0)
@@ -94,7 +149,7 @@ libevdev_new_from_fd(int fd, struct libevdev **dev)
        return rc;
 }
 
-void
+LIBEVDEV_EXPORT void
 libevdev_free(struct libevdev *dev)
 {
        if (!dev)
@@ -107,34 +162,53 @@ libevdev_free(struct libevdev *dev)
        free(dev);
 }
 
-void
-libevdev_set_log_handler(struct libevdev *dev, libevdev_log_func_t logfunc)
+LIBEVDEV_EXPORT void
+libevdev_set_log_function(libevdev_log_func_t logfunc, void *data)
 {
-       if (dev == NULL)
-               return;
+       log_data.handler = logfunc;
+       log_data.userdata = data;
+}
 
-       dev->log = logfunc ? logfunc : libevdev_noop_log_func;
+LIBEVDEV_EXPORT void
+libevdev_set_log_priority(enum libevdev_log_priority priority)
+{
+       if (priority > LIBEVDEV_LOG_DEBUG)
+               priority = LIBEVDEV_LOG_DEBUG;
+       log_data.priority = priority;
 }
 
-int
+LIBEVDEV_EXPORT enum libevdev_log_priority
+libevdev_get_log_priority(void)
+{
+       return log_data.priority;
+}
+
+LIBEVDEV_EXPORT int
 libevdev_change_fd(struct libevdev *dev, int fd)
 {
-       if (dev->fd == -1)
+       if (!dev->initialized) {
+               log_bug("device not initialized. call libevdev_set_fd() first\n");
                return -1;
+       }
        dev->fd = fd;
        return 0;
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_set_fd(struct libevdev* dev, int fd)
 {
        int rc;
        int i;
        char buf[256];
 
-       if (dev->fd != -1)
+       if (dev->initialized) {
+               log_bug("device already initialized.\n");
+               return -EBADF;
+       } else if (fd < 0)
                return -EBADF;
 
+       libevdev_reset(dev);
+
        rc = ioctl(fd, EVIOCGBIT(0, sizeof(dev->bits)), dev->bits);
        if (rc < 0)
                goto out;
@@ -144,13 +218,15 @@ libevdev_set_fd(struct libevdev* dev, int fd)
        if (rc < 0)
                goto out;
 
-       dev->name = calloc(strlen(buf) + 1, sizeof(char));
+       free(dev->name);
+       dev->name = strdup(buf);
        if (!dev->name) {
-               errno = ENOSPC;
+               errno = ENOMEM;
                goto out;
        }
-       strcpy(dev->name, buf);
 
+       free(dev->phys);
+       dev->phys = NULL;
        memset(buf, 0, sizeof(buf));
        rc = ioctl(fd, EVIOCGPHYS(sizeof(buf) - 1), buf);
        if (rc < 0) {
@@ -158,26 +234,26 @@ libevdev_set_fd(struct libevdev* dev, int fd)
                if (errno != ENOENT)
                        goto out;
        } else {
-               dev->phys = calloc(strlen(buf) + 1, sizeof(char));
+               dev->phys = strdup(buf);
                if (!dev->phys) {
-                       errno = ENOSPC;
+                       errno = ENOMEM;
                        goto out;
                }
-               strcpy(dev->phys, buf);
        }
 
+       free(dev->uniq);
+       dev->uniq = NULL;
        memset(buf, 0, sizeof(buf));
        rc = ioctl(fd, EVIOCGUNIQ(sizeof(buf) - 1), buf);
        if (rc < 0) {
                if (errno != ENOENT)
                        goto out;
        } else  {
-               dev->uniq = calloc(strlen(buf) + 1, sizeof(char));
+               dev->uniq = strdup(buf);
                if (!dev->uniq) {
-                       errno = ENOSPC;
+                       errno = ENOMEM;
                        goto out;
                }
-               strcpy(dev->uniq, buf);
        }
 
        rc = ioctl(fd, EVIOCGID, &dev->ids);
@@ -188,8 +264,12 @@ libevdev_set_fd(struct libevdev* dev, int fd)
        if (rc < 0)
                goto out;
 
+       /* Built on a kernel with props, running against a kernel without property
+          support. This should not be a fatal case, we'll be missing properties but other
+          than that everything is as expected.
+        */
        rc = ioctl(fd, EVIOCGPROP(sizeof(dev->props)), dev->props);
-       if (rc < 0)
+       if (rc < 0 && errno != EINVAL)
                goto out;
 
        rc = ioctl(fd, EVIOCGBIT(EV_REL, sizeof(dev->rel_bits)), dev->rel_bits);
@@ -208,6 +288,43 @@ libevdev_set_fd(struct libevdev* dev, int fd)
        if (rc < 0)
                goto out;
 
+       rc = ioctl(fd, EVIOCGBIT(EV_SW, sizeof(dev->sw_bits)), dev->sw_bits);
+       if (rc < 0)
+               goto out;
+
+       rc = ioctl(fd, EVIOCGBIT(EV_MSC, sizeof(dev->msc_bits)), dev->msc_bits);
+       if (rc < 0)
+               goto out;
+
+       rc = ioctl(fd, EVIOCGBIT(EV_FF, sizeof(dev->ff_bits)), dev->ff_bits);
+       if (rc < 0)
+               goto out;
+
+       rc = ioctl(fd, EVIOCGBIT(EV_SND, sizeof(dev->snd_bits)), dev->snd_bits);
+       if (rc < 0)
+               goto out;
+
+       rc = ioctl(fd, EVIOCGKEY(sizeof(dev->key_values)), dev->key_values);
+       if (rc < 0)
+               goto out;
+
+       rc = ioctl(fd, EVIOCGLED(sizeof(dev->led_values)), dev->led_values);
+       if (rc < 0)
+               goto out;
+
+       rc = ioctl(fd, EVIOCGSW(sizeof(dev->sw_values)), dev->sw_values);
+       if (rc < 0)
+               goto out;
+
+       /* rep is a special case, always set it to 1 for both values if EV_REP is set */
+       if (bit_is_set(dev->bits, EV_REP)) {
+               for (i = 0; i < REP_CNT; i++)
+                       set_bit(dev->rep_bits, i);
+               rc = ioctl(fd, EVIOCGREP, dev->rep_values);
+               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;
@@ -217,29 +334,35 @@ libevdev_set_fd(struct libevdev* dev, int fd)
 
                        dev->abs_info[i] = abs_info;
                        if (i == ABS_MT_SLOT) {
-                               dev->num_slots = abs_info.maximum + 1; /* FIXME: non-zero min? */
+                               dev->num_slots = abs_info.maximum + 1;
                                dev->current_slot = abs_info.value;
                        }
 
                }
        }
 
+       dev->fd = fd;
+       sync_mt_state(dev, 0);
+
        rc = init_event_queue(dev);
-       if (rc < 0)
+       if (rc < 0) {
+               dev->fd = -1;
                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;
-
+       dev->initialized = true;
 out:
+       if (rc)
+               libevdev_reset(dev);
        return rc ? -errno : 0;
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_get_fd(const struct libevdev* dev)
 {
        return dev->fd;
@@ -259,13 +382,13 @@ sync_key_state(struct libevdev *dev)
 {
        int rc;
        int i;
-       unsigned long keystate[NLONGS(KEY_MAX)];
+       unsigned long keystate[NLONGS(KEY_CNT)] = {0};
 
        rc = ioctl(dev->fd, EVIOCGKEY(sizeof(keystate)), keystate);
        if (rc < 0)
                goto out;
 
-       for (i = 0; i < KEY_MAX; i++) {
+       for (i = 0; i < KEY_CNT; i++) {
                int old, new;
                old = bit_is_set(dev->key_values, i);
                new = bit_is_set(keystate, i);
@@ -273,21 +396,77 @@ sync_key_state(struct libevdev *dev)
                        struct input_event *ev = queue_push(dev);
                        init_event(dev, ev, EV_KEY, i, new ? 1 : 0);
                }
-               set_bit_state(dev->key_values, i, new);
        }
 
+       memcpy(dev->key_values, keystate, rc);
+
+       rc = 0;
+out:
+       return rc ? -errno : 0;
+}
+
+static int
+sync_sw_state(struct libevdev *dev)
+{
+       int rc;
+       int i;
+       unsigned long swstate[NLONGS(SW_CNT)] = {0};
+
+       rc = ioctl(dev->fd, EVIOCGSW(sizeof(swstate)), swstate);
+       if (rc < 0)
+               goto out;
+
+       for (i = 0; i < SW_CNT; i++) {
+               int old, new;
+               old = bit_is_set(dev->sw_values, i);
+               new = bit_is_set(swstate, i);
+               if (old ^ new) {
+                       struct input_event *ev = queue_push(dev);
+                       init_event(dev, ev, EV_SW, i, new ? 1 : 0);
+               }
+       }
+
+       memcpy(dev->sw_values, swstate, rc);
+
        rc = 0;
 out:
        return rc ? -errno : 0;
 }
 
 static int
+sync_led_state(struct libevdev *dev)
+{
+       int rc;
+       int i;
+       unsigned long ledstate[NLONGS(LED_CNT)] = {0};
+
+       rc = ioctl(dev->fd, EVIOCGLED(sizeof(ledstate)), ledstate);
+       if (rc < 0)
+               goto out;
+
+       for (i = 0; i < LED_CNT; i++) {
+               int old, new;
+               old = bit_is_set(dev->led_values, i);
+               new = bit_is_set(ledstate, i);
+               if (old ^ new) {
+                       struct input_event *ev = queue_push(dev);
+                       init_event(dev, ev, EV_LED, i, new ? 1 : 0);
+               }
+       }
+
+       memcpy(dev->led_values, ledstate, rc);
+
+       rc = 0;
+out:
+       return rc ? -errno : 0;
+}
+static int
 sync_abs_state(struct libevdev *dev)
 {
        int rc;
        int i;
 
-       for (i = ABS_X; i <= ABS_MAX; i++) {
+       for (i = ABS_X; i < ABS_CNT; i++) {
                struct input_absinfo abs_info;
 
                if (i >= ABS_MT_MIN && i <= ABS_MT_MAX)
@@ -314,44 +493,65 @@ out:
 }
 
 static int
-sync_mt_state(struct libevdev *dev)
+sync_mt_state(struct libevdev *dev, int create_events)
 {
        int rc;
        int i;
+       int ioctl_success = 0;
        struct mt_state {
                int code;
                int val[MAX_SLOTS];
        } mt_state[ABS_MT_CNT];
 
-       for (i = ABS_MT_MIN; i < ABS_MT_MAX; i++) {
+       memset(&mt_state, 0, sizeof(mt_state));
+
+       for (i = ABS_MT_MIN; i <= ABS_MT_MAX; i++) {
                int idx;
                if (i == ABS_MT_SLOT)
                        continue;
 
+               if (!libevdev_has_event_code(dev, EV_ABS, i))
+                       continue;
+
                idx = i - ABS_MT_MIN;
                mt_state[idx].code = i;
                rc = ioctl(dev->fd, EVIOCGMTSLOTS(sizeof(struct mt_state)), &mt_state[idx]);
-               if (rc < 0)
-                       goto out;
+               if (rc < 0) {
+                       /* if the first ioctl fails with -EINVAL, chances are the kernel
+                          doesn't support the ioctl. Simply continue */
+                       if (errno == -EINVAL && !ioctl_success) {
+                               rc = 0;
+                       } else /* if the second, ... ioctl fails, really fail */
+                               goto out;
+               } else if (ioctl_success == 0)
+                       ioctl_success = 1;
        }
 
        for (i = 0; i < dev->num_slots; i++) {
                int j;
                struct input_event *ev;
 
-               ev = queue_push(dev);
-               init_event(dev, ev, EV_ABS, ABS_MT_SLOT, i);
-               for (j = ABS_MT_MIN; j < ABS_MT_MAX; j++) {
+               if (create_events) {
+                       ev = queue_push(dev);
+                       init_event(dev, ev, EV_ABS, ABS_MT_SLOT, i);
+               }
+
+               for (j = ABS_MT_MIN; j <= ABS_MT_MAX; j++) {
                        int jdx = j - ABS_MT_MIN;
 
                        if (j == ABS_MT_SLOT)
                                continue;
 
+                       if (!libevdev_has_event_code(dev, EV_ABS, j))
+                               continue;
+
                        if (dev->mt_slot_vals[i][jdx] == mt_state[jdx].val[i])
                                continue;
 
-                       ev = queue_push(dev);
-                       init_event(dev, ev, EV_ABS, j, mt_state[jdx].val[i]);
+                       if (create_events) {
+                               ev = queue_push(dev);
+                               init_event(dev, ev, EV_ABS, j, mt_state[jdx].val[i]);
+                       }
                        dev->mt_slot_vals[i][jdx] = mt_state[jdx].val[i];
                }
        }
@@ -371,7 +571,7 @@ sync_state(struct libevdev *dev)
        /* FIXME: if we have events in the queue after the SYN_DROPPED (which was
           queue[0]) we need to shift this backwards. Except that chances are that the
           queue may be either full or too full to prepend all the events needed for
-          syncing.
+          SYNC_IN_PROGRESS.
 
           so we search for the last sync event in the queue and drop everything before
           including that event and rely on the kernel to tell us the right value for that
@@ -379,7 +579,7 @@ sync_state(struct libevdev *dev)
         */
 
        for (i = queue_num_elements(dev) - 1; i >= 0; i--) {
-               struct input_event e;
+               struct input_event e = {{0,0}, 0, 0, 0};
                queue_peek(dev, i, &e);
                if (e.type == EV_SYN)
                        break;
@@ -390,16 +590,22 @@ sync_state(struct libevdev *dev)
 
        if (libevdev_has_event_type(dev, EV_KEY))
                rc = sync_key_state(dev);
+       if (libevdev_has_event_type(dev, EV_LED))
+               rc = sync_led_state(dev);
+       if (libevdev_has_event_type(dev, EV_SW))
+               rc = sync_sw_state(dev);
        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);
-
-       ev = queue_push(dev);
-       init_event(dev, ev, EV_SYN, SYN_REPORT, 0);
+               rc = sync_mt_state(dev, 1);
 
        dev->queue_nsync = queue_num_elements(dev);
-       dev->need_sync = 0;
+
+       if (dev->queue_nsync > 0) {
+               ev = queue_push(dev);
+               init_event(dev, ev, EV_SYN, SYN_REPORT, 0);
+               dev->queue_nsync++;
+       }
 
        return rc;
 }
@@ -413,10 +619,7 @@ update_key_state(struct libevdev *dev, const struct input_event *e)
        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);
+       set_bit_state(dev->key_values, e->code, e->value != 0);
 
        return 0;
 }
@@ -425,7 +628,14 @@ static int
 update_mt_state(struct libevdev *dev, const struct input_event *e)
 {
        if (e->code == ABS_MT_SLOT) {
+               int i;
                dev->current_slot = e->value;
+               /* sync abs_info with the current slot values */
+               for (i = ABS_MT_SLOT + 1; i <= ABS_MT_MAX; i++) {
+                       if (libevdev_has_event_code(dev, EV_ABS, i))
+                               dev->abs_info[i].value = dev->mt_slot_vals[dev->current_slot][i - ABS_MT_MIN];
+               }
+
                return 0;
        } else if (dev->current_slot == -1)
                return 1;
@@ -445,7 +655,7 @@ update_abs_state(struct libevdev *dev, const struct input_event *e)
                return 1;
 
        if (e->code >= ABS_MT_MIN && e->code <= ABS_MT_MAX)
-               return update_mt_state(dev, e);
+               update_mt_state(dev, e);
 
        dev->abs_info[e->code].value = e->value;
 
@@ -453,6 +663,34 @@ update_abs_state(struct libevdev *dev, const struct input_event *e)
 }
 
 static int
+update_led_state(struct libevdev *dev, const struct input_event *e)
+{
+       if (!libevdev_has_event_type(dev, EV_LED))
+               return 1;
+
+       if (e->code > LED_MAX)
+               return 1;
+
+       set_bit_state(dev->led_values, e->code, e->value != 0);
+
+       return 0;
+}
+
+static int
+update_sw_state(struct libevdev *dev, const struct input_event *e)
+{
+       if (!libevdev_has_event_type(dev, EV_SW))
+               return 1;
+
+       if (e->code > SW_MAX)
+               return 1;
+
+       set_bit_state(dev->sw_values, e->code, e->value != 0);
+
+       return 0;
+}
+
+static int
 update_state(struct libevdev *dev, const struct input_event *e)
 {
        int rc = 0;
@@ -467,6 +705,12 @@ update_state(struct libevdev *dev, const struct input_event *e)
                case EV_ABS:
                        rc = update_abs_state(dev, e);
                        break;
+               case EV_LED:
+                       rc = update_led_state(dev, e);
+                       break;
+               case EV_SW:
+                       rc = update_sw_state(dev, e);
+                       break;
        }
 
        dev->last_event_time = e->time;
@@ -499,118 +743,202 @@ read_more_events(struct libevdev *dev)
        return 0;
 }
 
-int libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev)
+LIBEVDEV_EXPORT int
+libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev)
 {
-       int rc = 0;
+       int rc = LIBEVDEV_READ_STATUS_SUCCESS;
 
-       if (dev->fd < 0)
-               return -ENODEV;
+       if (!dev->initialized) {
+               log_bug("device not initialized. call libevdev_set_fd() first\n");
+               return -EBADF;
+       } else if (dev->fd < 0)
+               return -EBADF;
 
-       if (flags & LIBEVDEV_READ_SYNC) {
-               if (!dev->need_sync && dev->queue_nsync == 0)
-                       return -EAGAIN;
-               else if (dev->need_sync) {
+       if (!(flags & (LIBEVDEV_READ_FLAG_NORMAL|LIBEVDEV_READ_FLAG_SYNC|LIBEVDEV_READ_FLAG_FORCE_SYNC))) {
+               log_bug("invalid flags %#x\n.\n", flags);
+               return -EINVAL;
+       }
+
+       if (flags & LIBEVDEV_READ_FLAG_SYNC) {
+               if (dev->sync_state == SYNC_NEEDED) {
                        rc = sync_state(dev);
                        if (rc != 0)
                                return rc;
+                       dev->sync_state = SYNC_IN_PROGRESS;
+               }
+
+               if (dev->queue_nsync == 0) {
+                       dev->sync_state = SYNC_NONE;
+                       return -EAGAIN;
                }
-       } else if (dev->need_sync) {
-               /* FIXME: still need to call update_state for all events
-                * here, otherwise the library has the wrong view of the
-                * device too */
-               queue_shift_multiple(dev, dev->queue_nsync, NULL);
+
+       } else if (dev->sync_state != SYNC_NONE) {
+               struct input_event e;
+
+               /* call update_state for all events here, otherwise the library has the wrong view
+                  of the device too */
+               while (queue_shift(dev, &e) == 0) {
+                       dev->queue_nsync--;
+                       update_state(dev, &e);
+               }
+
+               dev->sync_state = SYNC_NONE;
        }
 
-       /* FIXME: check for O_NONBLOCK and if not set, skip if we have an
-        * event in the queue from the previous read.
-        */
+       /* FIXME: if the first event after SYNC_IN_PROGRESS is a SYN_DROPPED, log this */
 
        /* 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 && rc != -EAGAIN)
-               goto out;
+          worst case we don't read fast enough and end up with SYN_DROPPED anyway.
+
+          Except if the fd is in blocking mode and we still have events from the last read, don't
+          read in any more.
+        */
+       do {
+               if (!(flags & LIBEVDEV_READ_FLAG_BLOCKING) ||
+                   queue_num_elements(dev) == 0) {
+                       rc = read_more_events(dev);
+                       if (rc < 0 && rc != -EAGAIN)
+                               goto out;
+               }
+
+               if (flags & LIBEVDEV_READ_FLAG_FORCE_SYNC) {
+                       dev->sync_state = SYNC_NEEDED;
+                       rc = LIBEVDEV_READ_STATUS_SYNC;
+                       goto out;
+               }
+
 
-       if (queue_shift(dev, ev) != 0)
-               return -EAGAIN;
+               if (queue_shift(dev, ev) != 0)
+                       return -EAGAIN;
 
-       update_state(dev, ev);
+               update_state(dev, ev);
+
+       /* if we disabled a code, get the next event instead */
+       } while(!libevdev_has_event_code(dev, ev->type, ev->code));
 
        rc = 0;
        if (ev->type == EV_SYN && ev->code == SYN_DROPPED) {
-               dev->need_sync = 1;
-               rc = 1;
+               dev->sync_state = SYNC_NEEDED;
+               rc = LIBEVDEV_READ_STATUS_SYNC;
        }
 
-       if (flags & LIBEVDEV_READ_SYNC && dev->queue_nsync > 0) {
+       if (flags & LIBEVDEV_READ_FLAG_SYNC && dev->queue_nsync > 0) {
                dev->queue_nsync--;
-               rc = 1;
+               rc = LIBEVDEV_READ_STATUS_SYNC;
+               if (dev->queue_nsync == 0)
+                       dev->sync_state = SYNC_NONE;
        }
 
 out:
        return rc;
 }
 
-const char *
+LIBEVDEV_EXPORT int
+libevdev_has_event_pending(struct libevdev *dev)
+{
+       struct pollfd fds = { dev->fd, POLLIN, 0 };
+       int rc;
+
+       if (!dev->initialized) {
+               log_bug("device not initialized. call libevdev_set_fd() first\n");
+               return -EBADF;
+       } else if (dev->fd < 0)
+               return -EBADF;
+
+       if (queue_num_elements(dev) != 0)
+               return 1;
+
+       rc = poll(&fds, 1, 0);
+       return (rc >= 0) ? rc : -errno;
+}
+
+LIBEVDEV_EXPORT const char *
 libevdev_get_name(const struct libevdev *dev)
 {
-       return dev->name;
+       return dev->name ? dev->name : "";
 }
 
-const char *
+LIBEVDEV_EXPORT const char *
 libevdev_get_phys(const struct libevdev *dev)
 {
        return dev->phys;
 }
 
-const char *
+LIBEVDEV_EXPORT const char *
 libevdev_get_uniq(const struct libevdev *dev)
 {
        return dev->uniq;
 }
 
-int libevdev_get_product_id(const struct libevdev *dev)
-{
-       return dev->ids.product;
+#define STRING_SETTER(field) \
+LIBEVDEV_EXPORT void libevdev_set_##field(struct libevdev *dev, const char *field) \
+{ \
+       if (field == NULL) \
+               return; \
+       free(dev->field); \
+       dev->field = strdup(field); \
 }
 
-int libevdev_get_vendor_id(const struct libevdev *dev)
-{
-       return dev->ids.vendor;
-}
+STRING_SETTER(name)
+STRING_SETTER(phys)
+STRING_SETTER(uniq)
 
-int libevdev_get_bustype(const struct libevdev *dev)
-{
-       return dev->ids.bustype;
+
+#define PRODUCT_GETTER(name) \
+LIBEVDEV_EXPORT int libevdev_get_id_##name(const struct libevdev *dev) \
+{ \
+       return dev->ids.name; \
 }
 
-int libevdev_get_version(const struct libevdev *dev)
-{
-       return dev->ids.version;
+PRODUCT_GETTER(product)
+PRODUCT_GETTER(vendor)
+PRODUCT_GETTER(bustype)
+PRODUCT_GETTER(version)
+
+#define PRODUCT_SETTER(field) \
+LIBEVDEV_EXPORT void libevdev_set_id_##field(struct libevdev *dev, int field) \
+{ \
+       dev->ids.field = field;\
 }
 
-int libevdev_get_driver_version(const struct libevdev *dev)
+PRODUCT_SETTER(product)
+PRODUCT_SETTER(vendor)
+PRODUCT_SETTER(bustype)
+PRODUCT_SETTER(version)
+
+LIBEVDEV_EXPORT int
+libevdev_get_driver_version(const struct libevdev *dev)
 {
        return dev->driver_version;
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_has_property(const struct libevdev *dev, unsigned int prop)
 {
        return (prop <= INPUT_PROP_MAX) && bit_is_set(dev->props, prop);
 }
 
-int
+LIBEVDEV_EXPORT int
+libevdev_enable_property(struct libevdev *dev, unsigned int prop)
+{
+       if (prop > INPUT_PROP_MAX)
+               return -1;
+
+       set_bit(dev->props, prop);
+       return 0;
+}
+
+LIBEVDEV_EXPORT int
 libevdev_has_event_type(const struct libevdev *dev, unsigned int type)
 {
-       return (type <= EV_MAX) && bit_is_set(dev->bits, type);
+       return type == EV_SYN ||(type <= EV_MAX && bit_is_set(dev->bits, type));
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_has_event_code(const struct libevdev *dev, unsigned int type, unsigned int code)
 {
        const unsigned long *mask;
-       unsigned int max;
+       int max;
 
        if (!libevdev_has_event_type(dev, type))
                return 0;
@@ -620,13 +948,13 @@ libevdev_has_event_code(const struct libevdev *dev, unsigned int type, unsigned
 
        max = type_to_mask_const(dev, type, &mask);
 
-       if (code > max)
+       if (max == -1 || code > (unsigned int)max)
                return 0;
 
        return bit_is_set(mask, code);
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned int code)
 {
        int value;
@@ -637,6 +965,21 @@ libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned
        switch (type) {
                case EV_ABS: value = dev->abs_info[code].value; break;
                case EV_KEY: value = bit_is_set(dev->key_values, code); break;
+               case EV_LED: value = bit_is_set(dev->led_values, code); break;
+               case EV_SW: value = bit_is_set(dev->sw_values, code); break;
+               case EV_REP:
+                           switch(code) {
+                                   case REP_DELAY:
+                                           libevdev_get_repeat(dev, &value, NULL);
+                                           break;
+                                   case REP_PERIOD:
+                                           libevdev_get_repeat(dev, NULL, &value);
+                                           break;
+                                   default:
+                                           value = 0;
+                                           break;
+                           }
+                           break;
                default:
                        value = 0;
                        break;
@@ -645,7 +988,33 @@ libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned
        return value;
 }
 
-int
+LIBEVDEV_EXPORT int
+libevdev_set_event_value(struct libevdev *dev, unsigned int type, unsigned int code, int value)
+{
+       int rc = 0;
+       struct input_event e;
+
+       if (!libevdev_has_event_type(dev, type) || !libevdev_has_event_code(dev, type, code))
+               return -1;
+
+       e.type = type;
+       e.code = code;
+       e.value = value;
+
+       switch(type) {
+               case EV_ABS: rc = update_abs_state(dev, &e); break;
+               case EV_KEY: rc = update_key_state(dev, &e); break;
+               case EV_LED: rc = update_led_state(dev, &e); break;
+               case EV_SW: rc = update_sw_state(dev, &e); break;
+               default:
+                            rc = -1;
+                            break;
+       }
+
+       return rc;
+}
+
+LIBEVDEV_EXPORT int
 libevdev_fetch_event_value(const struct libevdev *dev, unsigned int type, unsigned int code, int *value)
 {
        if (libevdev_has_event_type(dev, type) &&
@@ -656,13 +1025,13 @@ libevdev_fetch_event_value(const struct libevdev *dev, unsigned int type, unsign
                return 0;
 }
 
-int
+LIBEVDEV_EXPORT 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)
+       if (dev->num_slots < 0 || slot >= (unsigned int)dev->num_slots || slot >= MAX_SLOTS)
                return 0;
 
        if (code > ABS_MT_MAX || code < ABS_MT_MIN)
@@ -671,31 +1040,56 @@ libevdev_get_slot_value(const struct libevdev *dev, unsigned int slot, unsigned
        return dev->mt_slot_vals[slot][code - ABS_MT_MIN];
 }
 
-int
+LIBEVDEV_EXPORT int
+libevdev_set_slot_value(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))
+               return -1;
+
+       if (dev->num_slots == -1 || slot >= (unsigned int)dev->num_slots || slot >= MAX_SLOTS)
+               return -1;
+
+       if (code > ABS_MT_MAX || code < ABS_MT_MIN)
+               return -1;
+
+       if (code == ABS_MT_SLOT) {
+               if (value < 0 || value >= libevdev_get_num_slots(dev))
+                       return -1;
+               dev->current_slot = value;
+       }
+
+       dev->mt_slot_vals[slot][code - ABS_MT_MIN] = value;
+
+
+       return 0;
+}
+
+LIBEVDEV_EXPORT 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) {
+           dev->num_slots >= 0 &&
+           slot < (unsigned int)dev->num_slots && slot < MAX_SLOTS) {
                *value = libevdev_get_slot_value(dev, slot, code);
                return 1;
        } else
                return 0;
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_get_num_slots(const struct libevdev *dev)
 {
        return dev->num_slots;
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_get_current_slot(const struct libevdev *dev)
 {
        return dev->current_slot;
 }
 
-const struct input_absinfo*
+LIBEVDEV_EXPORT const struct input_absinfo*
 libevdev_get_abs_info(const struct libevdev *dev, unsigned int code)
 {
        if (!libevdev_has_event_type(dev, EV_ABS) ||
@@ -705,129 +1099,160 @@ libevdev_get_abs_info(const struct libevdev *dev, unsigned int code)
        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;
+#define ABS_GETTER(name) \
+LIBEVDEV_EXPORT int libevdev_get_abs_##name(const struct libevdev *dev, unsigned int code) \
+{ \
+       const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); \
+       return absinfo ? absinfo->name : 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;
+ABS_GETTER(maximum)
+ABS_GETTER(minimum)
+ABS_GETTER(fuzz)
+ABS_GETTER(flat)
+ABS_GETTER(resolution)
+
+#define ABS_SETTER(field) \
+LIBEVDEV_EXPORT void libevdev_set_abs_##field(struct libevdev *dev, unsigned int code, int val) \
+{ \
+       if (!libevdev_has_event_code(dev, EV_ABS, code)) \
+               return; \
+       dev->abs_info[code].field = val; \
 }
 
-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;
-}
+ABS_SETTER(maximum)
+ABS_SETTER(minimum)
+ABS_SETTER(fuzz)
+ABS_SETTER(flat)
+ABS_SETTER(resolution)
 
-int
-libevdev_get_abs_flat(const struct libevdev *dev, unsigned int code)
+LIBEVDEV_EXPORT void
+libevdev_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs)
 {
-       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);
+       if (!libevdev_has_event_code(dev, EV_ABS, code))
+               return;
 
-       return absinfo ? absinfo->resolution : 0;
+       dev->abs_info[code] = *abs;
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_enable_event_type(struct libevdev *dev, unsigned int type)
 {
+       int max;
+
        if (type > EV_MAX)
-               return 1;
+               return -1;
 
-       set_bit(dev->bits, type);
+       if (libevdev_has_event_type(dev, type))
+               return 0;
 
-       /* FIXME: pass through to kernel */
+       max = libevdev_event_type_get_max(type);
+       if (max == -1)
+               return -1;
+
+       set_bit(dev->bits, type);
 
+       if (type == EV_REP) {
+               int delay = 0, period = 0;
+               libevdev_enable_event_code(dev, EV_REP, REP_DELAY, &delay);
+               libevdev_enable_event_code(dev, EV_REP, REP_PERIOD, &period);
+       }
        return 0;
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_disable_event_type(struct libevdev *dev, unsigned int type)
 {
-       if (type > EV_MAX)
-               return 1;
+       int max;
 
-       clear_bit(dev->bits, type);
+       if (type > EV_MAX || type == EV_SYN)
+               return -1;
 
-       /* FIXME: pass through to kernel */
+       max = libevdev_event_type_get_max(type);
+       if (max == -1)
+               return -1;
+
+       clear_bit(dev->bits, type);
 
        return 0;
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_enable_event_code(struct libevdev *dev, unsigned int type,
                           unsigned int code, const void *data)
 {
        unsigned int max;
-       unsigned long *mask;
+       unsigned long *mask = NULL;
 
        if (libevdev_enable_event_type(dev, type))
-               return 1;
+               return -1;
+
+       switch(type) {
+               case EV_SYN:
+                       return 0;
+               case EV_ABS:
+               case EV_REP:
+                       if (data == NULL)
+                               return -1;
+                       break;
+               default:
+                       if (data != NULL)
+                               return -1;
+                       break;
+       }
 
        max = type_to_mask(dev, type, &mask);
 
-       if (code > max)
-               return 1;
+       if (code > max || (int)max == -1)
+               return -1;
 
        set_bit(mask, code);
 
        if (type == EV_ABS) {
                const struct input_absinfo *abs = data;
                dev->abs_info[code] = *abs;
+       } else if (type == EV_REP) {
+               const int *value = data;
+               dev->rep_values[code] = *value;
        }
 
-       /* FIXME: pass through to kernel */
-
        return 0;
 }
 
-int
+LIBEVDEV_EXPORT int
 libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned int code)
 {
        unsigned int max;
-       unsigned long *mask;
+       unsigned long *mask = NULL;
 
-       if (type > EV_MAX)
-               return 1;
+       if (type > EV_MAX || type == EV_SYN)
+               return -1;
 
        max = type_to_mask(dev, type, &mask);
 
-       if (code > max)
-               return 1;
+       if (code > max || (int)max == -1)
+               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)
+LIBEVDEV_EXPORT int
+libevdev_kernel_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs)
 {
        int rc;
 
+       if (!dev->initialized) {
+               log_bug("device not initialized. call libevdev_set_fd() first\n");
+               return -EBADF;
+       } else if (dev->fd < 0)
+               return -EBADF;
+
        if (code > ABS_MAX)
                return -EINVAL;
 
-       rc = ioctl(dev->fd, EVIOCSABS(code), *abs);
+       rc = ioctl(dev->fd, EVIOCSABS(code), abs);
        if (rc < 0)
                rc = -errno;
        else
@@ -836,13 +1261,21 @@ libevdev_kernel_set_abs_value(struct libevdev *dev, unsigned int code, const str
        return rc;
 }
 
-int
-libevdev_grab(struct libevdev *dev, int grab)
+LIBEVDEV_EXPORT int
+libevdev_grab(struct libevdev *dev, enum libevdev_grab_mode grab)
 {
        int rc = 0;
 
-       if (grab != LIBEVDEV_GRAB && grab != LIBEVDEV_UNGRAB)
+       if (!dev->initialized) {
+               log_bug("device not initialized. call libevdev_set_fd() first\n");
+               return -EBADF;
+       } else if (dev->fd < 0)
+               return -EBADF;
+
+       if (grab != LIBEVDEV_GRAB && grab != LIBEVDEV_UNGRAB) {
+               log_bug("invalid grab parameter %#x\n", grab);
                return -EINVAL;
+       }
 
        if (grab == dev->grabbed)
                return 0;
@@ -858,8 +1291,26 @@ libevdev_grab(struct libevdev *dev, int grab)
        return rc < 0 ? -errno : 0;
 }
 
-const char*
-libevdev_get_event_type_name(unsigned int type)
+LIBEVDEV_EXPORT int
+libevdev_event_is_type(const struct input_event *ev, unsigned int type)
+{
+       return type < EV_CNT && ev->type == type;
+}
+
+LIBEVDEV_EXPORT int
+libevdev_event_is_code(const struct input_event *ev, unsigned int type, unsigned int code)
+{
+       int max;
+
+       if (!libevdev_event_is_type(ev, type))
+               return 0;
+
+       max = libevdev_event_type_get_max(type);
+       return (max > -1 && code <= (unsigned int)max && ev->code == code);
+}
+
+LIBEVDEV_EXPORT const char*
+libevdev_event_type_get_name(unsigned int type)
 {
        if (type > EV_MAX)
                return NULL;
@@ -867,23 +1318,126 @@ libevdev_get_event_type_name(unsigned int type)
        return ev_map[type];
 }
 
-const char*
-libevdev_get_event_code_name(unsigned int type, unsigned int code)
+LIBEVDEV_EXPORT const char*
+libevdev_event_code_get_name(unsigned int type, unsigned int code)
 {
-       if (type > EV_MAX)
-               return NULL;
+       int max = libevdev_event_type_get_max(type);
 
-       if (code > ev_max[type])
+       if (max == -1 || code > (unsigned int)max)
                return NULL;
 
        return event_type_map[type][code];
 }
 
-const char*
-libevdev_get_input_prop_name(unsigned int prop)
+LIBEVDEV_EXPORT const char*
+libevdev_property_get_name(unsigned int prop)
 {
        if (prop > INPUT_PROP_MAX)
                return NULL;
 
        return input_prop_map[prop];
 }
+
+LIBEVDEV_EXPORT int
+libevdev_event_type_get_max(unsigned int type)
+{
+       if (type > EV_MAX)
+               return -1;
+
+       return ev_max[type];
+}
+
+LIBEVDEV_EXPORT int
+libevdev_get_repeat(const struct libevdev *dev, int *delay, int *period)
+{
+       if (!libevdev_has_event_type(dev, EV_REP))
+               return -1;
+
+       if (delay != NULL)
+               *delay = dev->rep_values[REP_DELAY];
+       if (period != NULL)
+               *period = dev->rep_values[REP_PERIOD];
+
+       return 0;
+}
+
+LIBEVDEV_EXPORT int
+libevdev_kernel_set_led_value(struct libevdev *dev, unsigned int code, enum libevdev_led_value value)
+{
+       return libevdev_kernel_set_led_values(dev, code, value, -1);
+}
+
+LIBEVDEV_EXPORT int
+libevdev_kernel_set_led_values(struct libevdev *dev, ...)
+{
+       struct input_event ev[LED_MAX + 1];
+       enum libevdev_led_value val;
+       va_list args;
+       int code;
+       int rc = 0;
+       size_t nleds = 0;
+
+       if (!dev->initialized) {
+               log_bug("device not initialized. call libevdev_set_fd() first\n");
+               return -EBADF;
+       } else if (dev->fd < 0)
+               return -EBADF;
+
+       memset(ev, 0, sizeof(ev));
+
+       va_start(args, dev);
+       code = va_arg(args, unsigned int);
+       while (code != -1) {
+               if (code > LED_MAX) {
+                       rc = -EINVAL;
+                       break;
+               }
+               val = va_arg(args, enum libevdev_led_value);
+               if (val != LIBEVDEV_LED_ON && val != LIBEVDEV_LED_OFF) {
+                       rc = -EINVAL;
+                       break;
+               }
+
+               if (libevdev_has_event_code(dev, EV_LED, code)) {
+                       struct input_event *e = ev;
+
+                       while (e->type > 0 && e->code != code)
+                               e++;
+
+                       if (e->type == 0)
+                               nleds++;
+                       e->type = EV_LED;
+                       e->code = code;
+                       e->value = (val == LIBEVDEV_LED_ON);
+               }
+               code = va_arg(args, unsigned int);
+       }
+       va_end(args);
+
+       if (rc == 0 && nleds > 0) {
+               ev[nleds].type = EV_SYN;
+               ev[nleds++].code = SYN_REPORT;
+
+               rc = write(libevdev_get_fd(dev), ev, nleds * sizeof(ev[0]));
+               if (rc > 0) {
+                       nleds--; /* last is EV_SYN */
+                       while (nleds--)
+                               update_led_state(dev, &ev[nleds]);
+               }
+               rc = (rc != -1) ? 0 : -errno;
+       }
+
+       return rc;
+}
+
+LIBEVDEV_EXPORT int
+libevdev_set_clock_id(struct libevdev *dev, int clockid)
+{
+       if (!dev->initialized) {
+               log_bug("device not initialized. call libevdev_set_fd() first\n");
+               return -EBADF;
+       } else if (dev->fd < 0)
+               return -EBADF;
+
+       return ioctl(dev->fd, EVIOCSCLOCKID, &clockid) ? -errno : 0;
+}