| summaryrefslogtreecommitdiff |
diff options
Diffstat (limited to 'src/device/virtual/virtual_device.c')
| -rw-r--r-- | src/device/virtual/virtual_device.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/src/device/virtual/virtual_device.c b/src/device/virtual/virtual_device.c new file mode 100644 index 0000000..19d1097 --- /dev/null +++ b/src/device/virtual/virtual_device.c @@ -0,0 +1,272 @@ + + +static void replace_rel_axes +( + struct relabsd_virtual_device * const dev, + const struct relabsd_config * const config +) +{ + int i; + struct input_absinfo absinfo; + unsigned int abs_code, rel_code; + + for (i = RELABSD_VALID_AXES_COUNT; i --> 0;) + { + if (config->axis[i].enabled) + { + rel_code = relabsd_axis_to_rel((enum relabsd_axis) i); + abs_code = relabsd_axis_to_abs((enum relabsd_axis) i); + + relabsd_config_get_absinfo(config, (enum relabsd_axis) i, &absinfo); + libevdev_disable_event_code(dev->dev, EV_REL, rel_code); + libevdev_enable_event_code(dev->dev, EV_ABS, abs_code, &absinfo); + } + } + +} + +static int rename_device +( + struct libevdev * const dev, + const struct relabsd_config * const config +) +{ + size_t new_name_size; + char * new_name; + const char * real_name; + + /* +2: One for the \0, one for the space between prefix and 'real_name'. */ + new_name_size = strlen(RELABSD_DEVICE_PREFIX) + 2; + + if (config->device_name == (char *) NULL) + { + /* XXX + * "The name is never NULL but it may be the empty string." + * I'm assuming that since they use the term 'string', it is \0 + * terminated. + */ + real_name = libevdev_get_name(dev); + } + else + { + real_name = config->device_name; + } + + new_name_size += strlen(real_name); + + new_name = (char *) calloc(new_name_size, sizeof(char)); + + if (new_name == (char *) NULL) + { + RELABSD_ERROR + ( + "Attempt at allocating memory to create the virtual device's name " + "failed: %s.", + strerror(errno) + ); + + /* This frees whatever came from 'libevdev_get_name'. */ + libevdev_set_name(dev, RELABSD_DEVICE_PREFIX); + + return -1; + } + + if + ( + snprintf + ( + new_name, + new_name_size, + "%s %s", + RELABSD_DEVICE_PREFIX, + real_name + ) + != ((int) (new_name_size - 1)) + ) + { + /* This makes for a great message when strerror(errno) returns SUCCESS. */ + RELABSD_ERROR + ( + "Something unexpected happened while renaming the virtual device: %s.", + strerror(errno) + ); + + /* This frees whatever came from 'libevdev_get_name'. */ + libevdev_set_name(dev, RELABSD_DEVICE_PREFIX); + + free((void *) new_name); + + return -1; + } + + /* This frees whatever came from 'libevdev_get_name'. */ + libevdev_set_name(dev, new_name); + free((void *) new_name); + + return 0; +} + +int relabsd_virtual_device_create +( + struct relabsd_virtual_device * const dev, + const struct relabsd_config * const config +) +{ + int fd; + + RELABSD_S_DEBUG(RELABSD_DEBUG_PROGRAM_FLOW, "Creating virtual device..."); + + fd = open(config->input_file, O_RDONLY); + + if (fd < 0) + { + RELABSD_FATAL + ( + "Could not open device '%s' in read only mode: %s.", + config->input_file, + strerror(errno) + ); + + return -1; + } + + if (libevdev_new_from_fd(fd, &(dev->dev)) < 0) + { + RELABSD_FATAL + ( + "libevdev could not open '%s': '%s'.", + config->input_file, + strerror(errno) + ); + + close(fd); + + return -1; + } + + if (rename_device(dev->dev, config) < 0) + { + libevdev_free(dev->dev); + close(fd); + + return -1; + } + + libevdev_enable_event_type(dev->dev, EV_ABS); + + replace_rel_axes(dev, config); + + if + ( + libevdev_uinput_create_from_device + ( + dev->dev, + /* See top of the file. */ + RELABSD_UINPUT_OPEN_MANAGED, + &(dev->uidev) + ) + < 0 + ) + { + RELABSD_FATAL("Could not create relabsd device: %s.", strerror(errno)); + + libevdev_free(dev->dev); + + close(fd); + + return -1; + } + + close(fd); + + return 0; +} + +void relabsd_virtual_device_destroy (const struct relabsd_virtual_device * const dev) +{ + RELABSD_S_DEBUG(RELABSD_DEBUG_PROGRAM_FLOW, "Destroying virtual device..."); + + libevdev_uinput_destroy(dev->uidev); + libevdev_free(dev->dev); +} + +int relabsd_virtual_device_write_evdev_event +( + const struct relabsd_virtual_device * const dev, + unsigned int const type, + unsigned int const code, + int const value +) +{ + RELABSD_DEBUG + ( + RELABSD_DEBUG_VIRTUAL_EVENTS, + "Sending event: {type = %s; code = %s; value = %d}.", + libevdev_event_type_get_name(type), + libevdev_event_code_get_name(type, code), + value + ); + + /* + * We'll also send the 'EV_SYN' events when we receive them from the input + * device. + * OPTIMIZE: prevent 'EV_SYN' from being sent if we haven't sent any new + * values. (It might not be worth it though) + */ + if (libevdev_uinput_write_event(dev->uidev, type, code, value) == 0) + { + /* FIXME: + * Why does activating the timeout trigger the EV_KEYS event to not be + * followed by EV_SYN? + */ + if (type == EV_KEY) + { + libevdev_uinput_write_event + ( + dev->uidev, + EV_SYN, + SYN_REPORT, + 0 + ); + } + + return 0; + } + + return -1; +} + +void relabsd_virtual_device_set_axes_to_zero +( + const struct relabsd_virtual_device * const dev, + const struct relabsd_config * const config +) +{ + int i; + + for (i = 0; i < RELABSD_VALID_AXES_COUNT; ++i) + { + if (config->axis[i].enabled) + { + relabsd_virtual_device_write_evdev_event + ( + dev, + EV_ABS, + relabsd_axis_to_abs((enum relabsd_axis) i), + 0 + ); + } + } + + /* + * Also send a SYN event when the axes have been modified. + */ + libevdev_uinput_write_event + ( + dev->uidev, + EV_SYN, + SYN_REPORT, + 0 + ); +} + |


