diff --git a/src/evdev/input_hook.c b/src/evdev/input_hook.c index a2664c23..00afac15 100644 --- a/src/evdev/input_hook.c +++ b/src/evdev/input_hook.c @@ -134,351 +134,256 @@ void hook_event_proc(XPointer closeure, XRecordInterceptData *recorded_data) { // TODO There is no way to consume the XRecord event. } -static int xrecord_block() { - int status = UIOHOOK_FAILURE; - - /* - - // Save the data display associated with this hook so it is passed to each event. - //XPointer closeure = (XPointer) (ctrl_display); - XPointer closeure = NULL; - - #ifdef USE_XRECORD_ASYNC - // Async requires that we loop so that our thread does not return. - if (XRecordEnableContextAsync(hook->data.display, context, hook_event_proc, closeure) != 0) { - // Time in MS to sleep the runloop. - int timesleep = 100; - // Allow the thread loop to block. - pthread_mutex_lock(&hook_xrecord_mutex); - running = true; - do { - // Unlock the mutex from the previous iteration. - pthread_mutex_unlock(&hook_xrecord_mutex); - - XRecordProcessReplies(hook->data.display); - - // Prevent 100% CPU utilization. - struct timeval tv; - gettimeofday(&tv, NULL); - - struct timespec ts; - ts.tv_sec = time(NULL) + timesleep / 1000; - ts.tv_nsec = tv.tv_usec * 1000 + 1000 * 1000 * (timesleep % 1000); - ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); - ts.tv_nsec %= (1000 * 1000 * 1000); +struct hook_info { + int fd; + struct libevdev *evdev; + struct libevdev_uinput *uinput; +}; - pthread_mutex_lock(&hook_xrecord_mutex); - pthread_cond_timedwait(&hook_xrecord_cond, &hook_xrecord_mutex, &ts); - } while (running); - // Unlock after loop exit. - pthread_mutex_unlock(&hook_xrecord_mutex); +static int destroy_hook_info(struct hook_info **info) { + fprintf(stderr, "\tTest0\n"); + struct hook_info *hook = *info; - // Set the exit status. - status = NULL; - } - #else - // Sync blocks until XRecordDisableContext() is called. - if (XRecordEnableContext(hook->data.display, hook->ctrl.context, hook_event_proc, closeure) != 0) { - status = UIOHOOK_SUCCESS; - } - #endif - else { - logger(LOG_LEVEL_ERROR, "%s [%u]: XRecordEnableContext failure!\n", - __FUNCTION__, __LINE__); + fprintf(stderr, "\tTest1\n"); + if (hook != NULL) { +fprintf(stderr, "\tTest2\n"); + if (hook->evdev != NULL) { + libevdev_free(hook->evdev); + hook->evdev = NULL; + } - #ifdef USE_XRECORD_ASYNC - // Reset the running state. - pthread_mutex_lock(&hook_xrecord_mutex); - running = false; - pthread_mutex_unlock(&hook_xrecord_mutex); - #endif + fprintf(stderr, "\tTest3\n"); + if (hook->uinput) { + libevdev_uinput_destroy(hook->uinput); + hook->uinput = NULL; + } - // Set the exit status. - status = UIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT; + fprintf(stderr, "\tTest4\n"); + free(hook); + hook = NULL; } - */ - - return status; } -static int xrecord_alloc() { - int status = UIOHOOK_FAILURE; - -/* - // Make sure the data display is synchronized to prevent late event delivery! - // See Bug 42356 for more information. - // https://bugs.freedesktop.org/show_bug.cgi?id=42356#c4 - XSynchronize(hook->data.display, True); +static int create_hook_info(int fd, struct hook_info **info) { + struct hook_info *hook = *info; - // Setup XRecord range. - XRecordClientSpec clients = XRecordAllClients; - - hook->data.range = XRecordAllocRange(); - if (hook->data.range != NULL) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: XRecordAllocRange successful.\n", + hook = malloc(sizeof(struct hook_info)); + if (hook == NULL) { + logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for evdev buffer!\n", __FUNCTION__, __LINE__); - hook->data.range->device_events.first = KeyPress; - hook->data.range->device_events.last = MappingNotify; - - // Note that the documentation for this function is incorrect, - // hook->data.display should be used! - // See: http://www.x.org/releases/X11R7.6/doc/libXtst/recordlib.txt - hook->ctrl.context = XRecordCreateContext(hook->data.display, XRecordFromServerTime, &clients, 1, &hook->data.range, 1); - if (hook->ctrl.context != 0) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: XRecordCreateContext successful.\n", - __FUNCTION__, __LINE__); - - // Block until hook_stop() is called. - status = xrecord_block(); - - // Free up the context if it was set. - XRecordFreeContext(hook->data.display, hook->ctrl.context); - } else { - logger(LOG_LEVEL_ERROR, "%s [%u]: XRecordCreateContext failure!\n", - __FUNCTION__, __LINE__); - - // Set the exit status. - status = UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT; - } + return UIOHOOK_ERROR_OUT_OF_MEMORY; + } - // Free the XRecord range. - XFree(hook->data.range); - } else { - logger(LOG_LEVEL_ERROR, "%s [%u]: XRecordAllocRange failure!\n", - __FUNCTION__, __LINE__); + int err = libevdev_new_from_fd(fd, &hook->evdev); + if (err < 0) { + logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create evdev from file descriptor! (%d)\n", + __FUNCTION__, __LINE__, + err); - // Set the exit status. - status = UIOHOOK_ERROR_X_RECORD_ALLOC_RANGE; + destroy_hook_info(&hook); + return UIOHOOK_FAILURE; } -*/ - return status; -} + err = libevdev_uinput_create_from_device(hook->evdev, LIBEVDEV_UINPUT_OPEN_MANAGED, &hook->uinput); + if (err < 0) { + logger(LOG_LEVEL_WARN, "%s [%u]: Failed to create uinput from device! (%d)\n", + __FUNCTION__, __LINE__, + err); -static int xrecord_query() { - int status = UIOHOOK_FAILURE; + hook->uinput = NULL; + } - /* - // Check to make sure XRecord is installed and enabled. - int major, minor; - if (XRecordQueryVersion(hook->ctrl.display, &major, &minor) != 0) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: XRecord version: %i.%i.\n", - __FUNCTION__, __LINE__, major, minor); - status = xrecord_alloc(); + char *device_label; + if (libevdev_has_event_type(hook->evdev, EV_REP) && libevdev_has_event_code(hook->evdev, EV_KEY, KEY_ESC)) { + device_label = "Keyboard"; + } else if (libevdev_has_event_type(hook->evdev, EV_REL) && libevdev_has_event_code(hook->evdev, EV_KEY, BTN_LEFT)) { + device_label = "Pointing"; } else { - logger(LOG_LEVEL_ERROR, "%s [%u]: XRecord is not currently available!\n", - __FUNCTION__, __LINE__); + // TODO Log INFO unsupported device. - status = UIOHOOK_ERROR_X_RECORD_NOT_FOUND; + destroy_hook_info(&hook); + return UIOHOOK_FAILURE; } - */ - return status; + // FIXME Log DEBUG + //printf("Found %s Device: %s\n", device_label, glob_buffer.gl_pathv[i]); + + return UIOHOOK_SUCCESS; } -static int xrecord_start() { - int status = UIOHOOK_FAILURE; - /* - // Use the helper display for XRecord. - hook->ctrl.display = XOpenDisplay(NULL); +static void destroy_epoll_event(struct epoll_event **event) { + struct epoll_event *epoll = *event; - // Open a data display for XRecord. - // NOTE This display must be opened on the same thread as XRecord. - hook->data.display = XOpenDisplay(NULL); - if (hook->ctrl.display != NULL && hook->data.display != NULL) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: XOpenDisplay successful.\n", - __FUNCTION__, __LINE__); +fprintf(stderr, "Test1\n"); + if (epoll != NULL) { + fprintf(stderr, "Test2\n"); + if (epoll->data.fd >= 0) { + close(epoll->data.fd); + epoll->data.fd = -1; + } - bool is_auto_repeat = enable_key_repeat(); - if (is_auto_repeat) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: Successfully enabled detectable auto-repeat.\n", - __FUNCTION__, __LINE__); - } else { - logger(LOG_LEVEL_WARN, "%s [%u]: Could not enable detectable auto-repeat!\n", - __FUNCTION__, __LINE__); +fprintf(stderr, "Test3\n"); + if (epoll->data.ptr != NULL) { + //destroy_hook_info(epoll->data.ptr); } - // Initialize starting modifiers. - // FIXME initialize_modifiers(); should happen somewhere else +fprintf(stderr, "Test4\n"); + free(epoll); + epoll = NULL; + } +} + +static int create_epoll_event(char *path, struct epoll_event *device) { + struct epoll_event *epoll = *device; - status = xrecord_query(); - } else { - logger(LOG_LEVEL_ERROR, "%s [%u]: XOpenDisplay failure!\n", - __FUNCTION__, __LINE__); - status = UIOHOOK_ERROR_X_OPEN_DISPLAY; - } + epoll->events = EPOLLIN; - // Close down the XRecord data display. - if (hook->data.display != NULL) { - XCloseDisplay(hook->data.display); - hook->data.display = NULL; + int fd = open(path, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to open file: %s! (%d)\n", + __FUNCTION__, __LINE__, + path, errno); + + destroy_epoll_event(device); + return UIOHOOK_FAILURE; } - // Close down the XRecord control display. - if (hook->ctrl.display) { - XCloseDisplay(hook->ctrl.display); - hook->ctrl.display = NULL; + int status = create_hook_info(fd, epoll->data.ptr); // FIXME struct hook_info ** + if (status != UIOHOOK_SUCCESS) { + destroy_epoll_event(device); + return status; } - */ - return status; + return UIOHOOK_SUCCESS; } -struct hook_info { - struct libevdev *evdev; - struct libevdev_uinput *uinput; -}; - -UIOHOOK_API int hook_run() { - glob_t glob_buffer; - int glob_status = glob("/dev/input/event*", GLOB_ERR | GLOB_NOSORT | GLOB_NOESCAPE, NULL, &glob_buffer); // /dev/input/by-path/*event? TODO use a compile time variable - switch (glob_status) { +#define EVENT_GLOB_PATTERN "/dev/input/event*" +static int create_glob(glob_t *glob_buffer) { + int status = glob(EVENT_GLOB_PATTERN, GLOB_ERR | GLOB_NOSORT | GLOB_NOESCAPE, NULL, glob_buffer); + switch (status) { case GLOB_NOSPACE: - // FIXME logging + logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for glob!\n", + __FUNCTION__, __LINE__); return UIOHOOK_ERROR_OUT_OF_MEMORY; - case GLOB_NOMATCH: - logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to locate any event devices at %s!\n", + default: + logger(LOG_LEVEL_ERROR, "%s [%u]: Failed response for glob! (%d)\n", __FUNCTION__, __LINE__, - "/dev/input"); - // FIXME No event devices found! + status); return UIOHOOK_FAILURE; - case GLOB_ABORTED: - // FIXME logging - return UIOHOOK_FAILURE; + case 0: + // Success } + return UIOHOOK_SUCCESS; +} - struct hook_info *hook_buffer = malloc(sizeof(struct hook_info) * glob_buffer.gl_pathc); - if (hook_buffer == NULL) { - logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for evdev buffer!\n", - __FUNCTION__, __LINE__); +static void destroy_epoll_event(glob_t *glob_buffer) { + globfree(glob_buffer); +} - return UIOHOOK_ERROR_OUT_OF_MEMORY; + +static int create_input_hooks(int epoll_fd, struct epoll_event **devices) { + glob_t glob_buffer; + int status = create_glob(&glob_buffer); + if (status != UIOHOOK_SUCCESS) { + return status; } - struct epoll_event *event_buffer = malloc(sizeof(struct epoll_event) * glob_buffer.gl_pathc); - if (event_buffer == NULL) { - logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for epoll event buffer!\n", + *devices = malloc(sizeof(struct epoll_event) * glob_buffer.gl_pathc); + if (*devices == NULL) { + logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for epoll event devices!\n", __FUNCTION__, __LINE__); + destroy_epoll_event(&glob_buffer); return UIOHOOK_ERROR_OUT_OF_MEMORY; } - - int epoll_fd = epoll_create1(0); - if (epoll_fd == -1) { - // FIXME Error - fprintf(stderr, "Failed to create epoll file descriptor\n"); - return UIOHOOK_ERROR_EPOLL_CREATE; - } - - - int found = 0, err = 0; + int found = 0; for (int i = 0; i < glob_buffer.gl_pathc; i++) { - int fd = open(glob_buffer.gl_pathv[i], O_RDONLY | O_NONBLOCK); - if (fd < 0) { - // FIXME Error log errno - logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to open file: %s! (%#X) (errno)\n", - __FUNCTION__, __LINE__, - glob_buffer.gl_pathv[i], O_RDONLY | O_NONBLOCK); + fprintf(stderr, "Testing: %s... ", glob_buffer.gl_pathv[i]); + if (create_epoll_event(glob_buffer.gl_pathv[i], &devices[found]) != UIOHOOK_SUCCESS) { continue; } + fprintf(stderr, "ok\n"); - err = libevdev_new_from_fd(fd, &hook_buffer[found].evdev); - if (err < 0) { - // FIXME Error log err - logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create evdev from file descriptor! (%d)\n", - __FUNCTION__, __LINE__, - err); - close(fd); + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, devices[found]->data.fd, devices[found]) < 0) { + // FIXME LOG Error + fprintf(stderr, "Failed to add file descriptor to epoll\n"); + + destroy_epoll_event(&devices[found]); continue; } + found++; + } - err = libevdev_uinput_create_from_device(hook_buffer[found].evdev, LIBEVDEV_UINPUT_OPEN_MANAGED, &hook_buffer[found].uinput); - if (err < 0) { - // FIXME Error log err - logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create uinput from device! (%d)\n", - __FUNCTION__, __LINE__, - err); - - //libevdev_free(hook_buffer[found].evdev); - //close(fd); - continue; - } + destroy_epoll_event(&glob_buffer); - if (libevdev_has_event_type(hook_buffer[found].evdev, EV_REP) && libevdev_has_event_code(hook_buffer[found].evdev, EV_KEY, KEY_ESC)) { - printf("Found Keyboard Device: %s\n", glob_buffer.gl_pathv[i]); - } else if (libevdev_has_event_type(hook_buffer[found].evdev, EV_REL) && libevdev_has_event_code(hook_buffer[found].evdev, EV_KEY, BTN_LEFT)) { - printf("Found Pointing Device: %s\n", glob_buffer.gl_pathv[i]); - } else { - printf("Unsupported Device: %s\n", glob_buffer.gl_pathv[i]); - // Unsupported device, ignore. - libevdev_uinput_destroy(hook_buffer[found].uinput); - libevdev_free(hook_buffer[found].evdev); - close(fd); - continue; - } + struct epoll_event **new_devices = realloc(devices, sizeof(struct epoll_event *) * found); + if (new_devices != NULL) { + devices = new_devices; + new_devices = NULL; + } - event_buffer[found].events = EPOLLIN; - event_buffer[found].data.fd = fd; - event_buffer[found].data.ptr = &hook_buffer[found]; + return UIOHOOK_SUCCESS; +} - printf("Testing adding %p %d\n", &hook_buffer[found], event_buffer[found].data.fd); - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event_buffer[found])) - { - // FIXME Error - fprintf(stderr, "Failed to add file descriptor to epoll\n"); - // Unsupported device, ignore. - libevdev_uinput_destroy(hook_buffer[found].uinput); - libevdev_free(hook_buffer[found].evdev); - close(fd); - continue; - } +UIOHOOK_API int hook_run() { + int epoll_fd = epoll_create1(0); + if (epoll_fd == -1) { + // FIXME Error + fprintf(stderr, "Failed to create epoll file descriptor\n"); - found++; + return UIOHOOK_ERROR_EPOLL_CREATE; } - globfree(&glob_buffer); + struct epoll_event *devices = NULL; + int status = create_input_hooks(epoll_fd, &devices); + if (status != UIOHOOK_SUCCESS) { + // TODO Log Failure? + close(epoll_fd); - struct hook_info *hooks = realloc(hook_buffer, sizeof(struct hook_info) * found); - if (hooks == NULL) { - // FIXME Warning, - hooks = hook_buffer; + return status; } - struct epoll_event *devices = realloc(event_buffer, sizeof(struct epoll_event) * found); - if (devices == NULL) { - // FIXME Warning, - devices = event_buffer; - } + #define EVENT_BUFFER_SIZE 8 + #define EVENT_BUFFER_WAIT 30 - // Now we implement epoll on the remaining FDs? - struct epoll_event events[16]; int event_count; + struct epoll_event *event_buffer = malloc(sizeof(struct epoll_event) * EVENT_BUFFER_SIZE); + if (event_buffer == NULL) { + logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for device buffer!\n", + __FUNCTION__, __LINE__); + close(epoll_fd); + return UIOHOOK_ERROR_OUT_OF_MEMORY; + } + + + int err; struct input_event ev; + while (1) { printf("\nPolling for input...\n"); - event_count = epoll_wait(epoll_fd, events, 16, 30000); + event_count = epoll_wait(epoll_fd, event_buffer, EVENT_BUFFER_SIZE, EVENT_BUFFER_WAIT * 1000); printf("%d ready events\n", event_count); for (int i = 0; i < event_count; i++) { - printf("Reading file descriptor %p '%d' -- ", events[i].data.ptr, events[i].data.fd); + printf("Reading file descriptor %p '%d' -- ", event_buffer[i].data.ptr, event_buffer[i].data.fd); - struct hook_info *device = (struct hook_info *) events[i].data.ptr; + struct hook_info *device = (struct hook_info *) event_buffer[i].data.ptr; err = libevdev_next_event(device->evdev, LIBEVDEV_READ_FLAG_NORMAL, &ev); if (err == LIBEVDEV_READ_STATUS_SUCCESS) { @@ -512,13 +417,6 @@ UIOHOOK_API int hook_run() { return 1; } - if (hook_buffer != NULL) { - free(hook_buffer); - } - - if (event_buffer != NULL) { - free(event_buffer); - } logger(LOG_LEVEL_DEBUG, "%s [%u]: Something, something, something, complete.\n", __FUNCTION__, __LINE__); @@ -530,44 +428,7 @@ UIOHOOK_API int hook_run() { UIOHOOK_API int hook_stop() { int status = UIOHOOK_FAILURE; -/* - if (hook != NULL && hook->ctrl.display != NULL && hook->ctrl.context != 0) { - // We need to make sure the context is still valid. - XRecordState *state = malloc(sizeof(XRecordState)); - if (state != NULL) { - if (XRecordGetContext(hook->ctrl.display, hook->ctrl.context, &state) != 0) { - // Try to exit the thread naturally. - if (state->enabled && XRecordDisableContext(hook->ctrl.display, hook->ctrl.context) != 0) { - #ifdef USE_XRECORD_ASYNC - pthread_mutex_lock(&hook_xrecord_mutex); - running = false; - pthread_cond_signal(&hook_xrecord_cond); - pthread_mutex_unlock(&hook_xrecord_mutex); - #endif - - // See Bug 42356 for more information. - // https://bugs.freedesktop.org/show_bug.cgi?id=42356#c4 - //XFlush(hook->ctrl.display); - XSync(hook->ctrl.display, False); - - status = UIOHOOK_SUCCESS; - } - } else { - logger(LOG_LEVEL_ERROR, "%s [%u]: XRecordGetContext failure!\n", - __FUNCTION__, __LINE__); - - status = UIOHOOK_ERROR_X_RECORD_GET_CONTEXT; - } - - free(state); - } else { - logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for XRecordState!\n", - __FUNCTION__, __LINE__); - - status = UIOHOOK_ERROR_OUT_OF_MEMORY; - } - } - */ + // FIXME Implement logger(LOG_LEVEL_DEBUG, "%s [%u]: Status: %#X.\n", __FUNCTION__, __LINE__, status);