diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h index 61c2454..e77db32 100644 --- a/libevdev/libevdev-int.h +++ b/libevdev/libevdev-int.h @@ -38,6 +38,7 @@ #define ABS_MT_MAX ABS_MT_TOOL_Y #define ABS_MT_CNT (ABS_MT_MAX - ABS_MT_MIN + 1) #define LIBEVDEV_EXPORT __attribute__((visibility("default"))) +#define LIBEVDEV_PRINTF(_format, _args) __attribute__ ((format (printf, _format, _args))) #undef min #undef max @@ -71,8 +72,6 @@ enum SyncState { struct libevdev { int fd; - libevdev_log_func_t log; - char *name; char *phys; char *uniq; @@ -109,6 +108,30 @@ struct libevdev { struct timeval last_event_time; }; +struct logdata { + enum libevdev_log_priority priority; /** minimum logging priority */ + libevdev_log_func_t handler; /** handler function */ + void *userdata; /** user-defined data pointer */ +}; +extern struct logdata log_data; + +#define log_msg_cond(priority, ...) \ + do { \ + if (libevdev_get_log_priority() >= priority) \ + log_msg(priority, log_data.userdata, __FILE__, __LINE__, __func__, __VA_ARGS__); \ + } while(0) + +#define log_error(...) log_msg_cond(LIBEVDEV_LOG_ERROR, __VA_ARGS__) +#define log_info(...) log_msg_cond(LIBEVDEV_LOG_INFO, __VA_ARGS__) +#define log_dbg(...) log_msg_cond(LIBEVDEV_LOG_DEBUG, __VA_ARGS__) +#define log_bug(...) log_msg_cond(LIBEVDEV_LOG_ERROR, "BUG: "__VA_ARGS__) + +extern void +log_msg(enum libevdev_log_priority priority, + void *data, + const char *file, int line, const char *func, + const char *format, ...) LIBEVDEV_PRINTF(6, 7); + /** * @return a pointer to the next element in the queue, or NULL if the queue * is full. @@ -194,11 +217,11 @@ static inline int queue_alloc(struct libevdev *dev, size_t size) { if (size == 0) - return -ENOSPC; + return -ENOMEM; dev->queue = calloc(size, sizeof(struct input_event)); if (!dev->queue) - return -ENOSPC; + return -ENOMEM; dev->queue_size = size; dev->queue_next = 0; diff --git a/libevdev/libevdev-uinput.c b/libevdev/libevdev-uinput.c index de2833e..af98fa4 100644 --- a/libevdev/libevdev-uinput.c +++ b/libevdev/libevdev-uinput.c @@ -226,10 +226,16 @@ fetch_syspath_and_devnode(struct libevdev_uinput *uinput_dev) buf[len - 1] = '\0'; /* file contains \n */ if (strcmp(buf, uinput_dev->name) == 0) { - strcpy(buf, SYS_INPUT_DIR); - strcat(buf, namelist[i]->d_name); - uinput_dev->syspath = strdup(buf); - uinput_dev->devnode = fetch_device_node(buf); + if (uinput_dev->syspath) { + /* FIXME: could descend into bit comparison here */ + log_info("multiple identical devices found. syspath is unreliable\n"); + break; + } else { + strcpy(buf, SYS_INPUT_DIR); + strcat(buf, namelist[i]->d_name); + uinput_dev->syspath = strdup(buf); + uinput_dev->devnode = fetch_device_node(buf); + } } } @@ -259,8 +265,10 @@ libevdev_uinput_create_from_device(const struct libevdev *dev, int fd, struct li return fd; new_device->fd_is_managed = 1; - } else if (fd < 0) + } else if (fd < 0) { + log_bug("Invalid fd %d\n", fd); return -EBADF; + } memset(&uidev, 0, sizeof(uidev)); @@ -300,6 +308,7 @@ libevdev_uinput_create_from_device(const struct libevdev *dev, int fd, struct li new_device->fd = fd; if (fetch_syspath_and_devnode(new_device) == -1) { + log_error("unable to fetch syspath or device node.\n"); errno = ENODEV; goto error; } diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c index 36160f6..aa5cbd6 100644 --- a/libevdev/libevdev.c +++ b/libevdev/libevdev.c @@ -49,18 +49,57 @@ 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: + 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; - va_start(args, format); - dev->log(format, args); - va_end(args); -} + if (!log_data.handler) + return; -static void -libevdev_noop_log_func(const char *format, va_list args) -{ + va_start(args, format); + log_data.handler(priority, data, file, line, func, format, args); + va_end(args); } LIBEVDEV_EXPORT struct libevdev* @@ -74,7 +113,6 @@ libevdev_new(void) dev->fd = -1; dev->num_slots = -1; dev->current_slot = -1; - dev->log = libevdev_noop_log_func; dev->grabbed = LIBEVDEV_UNGRAB; dev->sync_state = SYNC_NONE; @@ -89,7 +127,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) @@ -112,20 +150,42 @@ libevdev_free(struct libevdev *dev) free(dev); } +/* DEPRECATED */ LIBEVDEV_EXPORT void libevdev_set_log_handler(struct libevdev *dev, libevdev_log_func_t logfunc) { - if (dev == NULL) - return; + /* Can't be backwards compatible to this yet, so don't even try */ + fprintf(stderr, "libevdev: ABI change. Log function will not be honored.\n"); +} - dev->log = logfunc ? logfunc : libevdev_noop_log_func; +LIBEVDEV_EXPORT void +libevdev_set_log_function(libevdev_log_func_t logfunc, void *data) +{ + log_data.handler = logfunc; + log_data.userdata = data; +} + +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; +} + +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->fd == -1) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); return -1; + } dev->fd = fd; return 0; } @@ -137,8 +197,10 @@ libevdev_set_fd(struct libevdev* dev, int fd) int i; char buf[256]; - if (dev->fd != -1) + if (dev->fd != -1) { + log_bug("device already initialized.\n"); return -EBADF; + } rc = ioctl(fd, EVIOCGBIT(0, sizeof(dev->bits)), dev->bits); if (rc < 0) @@ -152,7 +214,7 @@ libevdev_set_fd(struct libevdev* dev, int fd) free(dev->name); dev->name = strdup(buf); if (!dev->name) { - errno = ENOSPC; + errno = ENOMEM; goto out; } @@ -167,7 +229,7 @@ libevdev_set_fd(struct libevdev* dev, int fd) } else { dev->phys = strdup(buf); if (!dev->phys) { - errno = ENOSPC; + errno = ENOMEM; goto out; } } @@ -182,7 +244,7 @@ libevdev_set_fd(struct libevdev* dev, int fd) } else { dev->uniq = strdup(buf); if (!dev->uniq) { - errno = ENOSPC; + errno = ENOMEM; goto out; } } @@ -662,11 +724,15 @@ libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event { int rc = 0; - if (dev->fd < 0) - return -ENODEV; + if (dev->fd < 0) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } - if (!(flags & (LIBEVDEV_READ_NORMAL|LIBEVDEV_READ_SYNC|LIBEVDEV_FORCE_SYNC))) + if (!(flags & (LIBEVDEV_READ_NORMAL|LIBEVDEV_READ_SYNC|LIBEVDEV_FORCE_SYNC))) { + log_bug("invalid flags %#x\n.\n", flags); return -EINVAL; + } if (flags & LIBEVDEV_READ_SYNC) { if (dev->sync_state == SYNC_NEEDED) { @@ -748,8 +814,10 @@ libevdev_has_event_pending(struct libevdev *dev) struct pollfd fds = { dev->fd, POLLIN, 0 }; int rc; - if (dev->fd < 0) + if (dev->fd < 0) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); return -EBADF; + } if (queue_num_elements(dev) != 0) return 1; @@ -1130,6 +1198,11 @@ libevdev_kernel_set_abs_info(struct libevdev *dev, unsigned int code, const stru { int rc; + if (dev->fd < 0) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } + if (code > ABS_MAX) return -EINVAL; @@ -1147,8 +1220,15 @@ libevdev_grab(struct libevdev *dev, enum libevdev_grab_mode grab) { int rc = 0; - if (grab != LIBEVDEV_GRAB && grab != LIBEVDEV_UNGRAB) + if (dev->fd < 0) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + 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; @@ -1250,6 +1330,11 @@ libevdev_kernel_set_led_values(struct libevdev *dev, ...) int rc = 0; size_t nleds = 0; + if (dev->fd < 0) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } + memset(ev, 0, sizeof(ev)); va_start(args, dev); diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h index 3d0b609..82142d6 100644 --- a/libevdev/libevdev.h +++ b/libevdev/libevdev.h @@ -202,7 +202,7 @@ extern "C" { * * dev = libevdev_new(); * if (!dev) - * return ENOSPC; + * return ENOMEM; * * err = libevdev_set_fd(dev, fd); * if (err < 0) { @@ -349,28 +349,58 @@ int libevdev_new_from_fd(int fd, struct libevdev **dev); */ void libevdev_free(struct libevdev *dev); +enum libevdev_log_priority { + LIBEVDEV_LOG_ERROR = 10, /** cricitical errors and application bugs */ + LIBEVDEV_LOG_INFO = 20, /** informational messages */ + LIBEVDEV_LOG_DEBUG = 30, /** debug information */ +}; + /** * Logging function called by library-internal logging. * This function is expected to treat its input like printf would. * + * @param priority Log priority of this message + * @param data User-supplied data pointer (see libevdev_set_log_function()) + * @param file libevdev source code file generating this message + * @param line libevdev source code line generating this message + * @param func libevdev source code function generating this message * @param format printf-style format string * @param args List of arguments * - * @see libevdev_set_log_handler + * @see libevdev_set_log_function */ -typedef void (*libevdev_log_func_t)(const char *format, va_list args); +typedef void (*libevdev_log_func_t)(enum libevdev_log_priority priority, + void *data, + const char *file, int line, + const char *func, + const char *format, va_list args); /** * Set a printf-style logging handler for library-internal logging. The default - * logging function is a noop. + * logging function is to stdout. * - * @param dev The evdev device * @param logfunc The logging function for this device. If NULL, the current - * logging function is unset. + * logging function is unset and no logging is performed. + * @param data User-specific data passed to the log handler. * * @note This function may be called before libevdev_set_fd(). */ -void libevdev_set_log_handler(struct libevdev *dev, libevdev_log_func_t logfunc); +void libevdev_set_log_function(libevdev_log_func_t logfunc, void *data); + +/** + * Define the minimum level to be printed to the log handler. + * Messages higher than this level are printed, others are discarded. This + * is a global setting and applies to any future logging messages. + * + * @param priority Minimum priority to be printed to the log. + * + */ +void libevdev_set_log_priority(enum libevdev_log_priority priority); + +/** + * @return the current log level + */ +enum libevdev_log_priority libevdev_get_log_priority(void); enum libevdev_grab_mode { @@ -1333,6 +1363,8 @@ int libevdev_get_repeat(struct libevdev *dev, int *delay, int *period); /* replacement: libevdev_kernel_set_abs_info */ int libevdev_kernel_set_abs_value(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs) LIBEVDEV_DEPRECATED; +/* replacement: libevdev_set_log_function */ +void libevdev_set_log_handler(struct libevdev *dev, libevdev_log_func_t logfunc) LIBEVDEV_DEPRECATED; /**************************************/ #ifdef __cplusplus diff --git a/test/test-common-uinput.c b/test/test-common-uinput.c index 7f3fe1f..32ecb89 100644 --- a/test/test-common-uinput.c +++ b/test/test-common-uinput.c @@ -75,7 +75,7 @@ uinput_device_new_with_events_v(struct uinput_device **d, const char *name, cons dev = uinput_device_new(name); if (!dev) - return -ENOSPC; + return -ENOMEM; if (id != DEFAULT_IDS) uinput_device_set_ids(dev, id); diff --git a/test/test-int-queue.c b/test/test-int-queue.c index e809048..a57eb93 100644 --- a/test/test-int-queue.c +++ b/test/test-int-queue.c @@ -34,10 +34,10 @@ START_TEST(test_queue_alloc) int rc; rc = queue_alloc(&dev, 0); - ck_assert_int_eq(rc, -ENOSPC); + ck_assert_int_eq(rc, -ENOMEM); rc = queue_alloc(&dev, ULONG_MAX); - ck_assert_int_eq(rc, -ENOSPC); + ck_assert_int_eq(rc, -ENOMEM); rc = queue_alloc(&dev, 100); ck_assert_int_eq(rc, 0); diff --git a/test/test-libevdev-init.c b/test/test-libevdev-init.c index 344bbac..02dfa8b 100644 --- a/test/test-libevdev-init.c +++ b/test/test-libevdev-init.c @@ -93,27 +93,43 @@ START_TEST(test_init_and_change_fd) } END_TEST -static void logfunc(const char *f, va_list args) {} + +static int log_fn_called = 0; +static char *logdata = "test"; +static void logfunc(enum libevdev_log_priority priority, + void *data, + const char *file, int line, const char *func, + const char *f, va_list args) { + ck_assert_int_eq(strcmp(logdata, data), 0); + log_fn_called++; +} START_TEST(test_log_init) { struct libevdev *dev = NULL; - libevdev_set_log_handler(NULL, logfunc); - libevdev_set_log_handler(NULL, NULL); + libevdev_set_log_function(logfunc, NULL); + libevdev_set_log_function(NULL, NULL); dev = libevdev_new(); ck_assert(dev != NULL); - libevdev_set_log_handler(dev, logfunc); - libevdev_free(dev); - dev = libevdev_new(); - ck_assert(dev != NULL); - libevdev_set_log_handler(dev, NULL); - libevdev_set_log_handler(dev, logfunc); - libevdev_free(dev); - /* well, we didn't crash. can't test this otherwise */ + libevdev_set_log_function(logfunc, logdata); + libevdev_next_event(dev, LIBEVDEV_READ_NORMAL, NULL); + libevdev_set_log_function(NULL, NULL); + libevdev_next_event(dev, LIBEVDEV_READ_NORMAL, NULL); + + libevdev_set_log_function(logfunc, logdata); + libevdev_next_event(dev, LIBEVDEV_READ_NORMAL, NULL); + + /* libevdev_next_event(dev, LIBEVDEV_READ_NORMAL, NULL) should + trigger a log message. We called it three times, but only twice + with the logfunc set, thus, ensure we only called the logfunc + twice */ + ck_assert_int_eq(log_fn_called, 2); + + libevdev_free(dev); } END_TEST