| summaryrefslogtreecommitdiff | 
diff options
Diffstat (limited to 'src/device/virtual')
| -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 +   ); +} + | 


