| summaryrefslogtreecommitdiff | 
diff options
Diffstat (limited to 'src/device')
| -rw-r--r-- | src/device/axis/axis_name.c | 302 | ||||
| -rw-r--r-- | src/device/physical_device.c | 276 | ||||
| -rw-r--r-- | src/device/virtual_device.c | 272 | 
3 files changed, 850 insertions, 0 deletions
| diff --git a/src/device/axis/axis_name.c b/src/device/axis/axis_name.c new file mode 100644 index 0000000..c18a28f --- /dev/null +++ b/src/device/axis/axis_name.c @@ -0,0 +1,302 @@ +/**** LIBEVDEV ****************************************************************/ +#include <libevdev/libevdev.h> + +/**** RELABSD *****************************************************************/ +#include <relabsd/debug.h> + +#include <relabsd/util/string.h> + +#include <relabsd/device/axis.h> + +/******************************************************************************/ +/**** LOCAL FUNCTIONS *********************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +/**** EXPORTED FUNCTIONS ******************************************************/ +/******************************************************************************/ +enum relabsd_axis_name relabsd_axis_parse_name +( +   const char name [const restrict static 1] +) +{ +   if (RELABSD_STRING_EQUALS("X", name)) +   { +      return RELABSD_X; +   } +   else if (RELABSD_STRING_EQUALS("Y", name)) +   { +      return RELABSD_Y; +   } +   else if (RELABSD_STRING_EQUALS("Z", name)) +   { +      return RELABSD_Z; +   } +   else if (RELABSD_STRING_EQUALS("RX", name)) +   { +      return RELABSD_RX; +   } +   else if (RELABSD_STRING_EQUALS("RY", name)) +   { +      return RELABSD_RY; +   } +   else if (RELABSD_STRING_EQUALS("RZ", name)) +   { +      return RELABSD_RZ; +   } +   else if (RELABSD_STRING_EQUALS("WL", name)) +   { +      return RELABSD_WHEEL; +   } +   else if (RELABSD_STRING_EQUALS("MC", name)) +   { +      return RELABSD_MISC; +   } + +   return RELABSD_UNKNOWN; +} + +const char * relabsd_axis_name_to_string (const enum relabsd_axis_name e) +{ +   switch (e) +   { +      case RELABSD_X: +         return "X"; + +      case RELABSD_Y: +         return "Y"; + +      case RELABSD_Z: +         return "Z"; + +      case RELABSD_RX: +         return "RX"; + +      case RELABSD_RY: +         return "RY"; + +      case RELABSD_RZ: +         return "RZ"; + +      case RELABSD_WHEEL: +         return "WL"; + +      case RELABSD_MISC: +         return "MC"; + +      case RELABSD_UNKNOWN: +         return "??"; + +      default: +         break; +   } + +   RELABSD_S_PROG_ERROR("relabsd_axis_to_name is missing at least 1 case."); + +   return ".."; +} + +enum relabsd_axis_name relabsd_axis_name_and_evdev_abs_from_evdev_rel +( +   const unsigned int rel_code, +   unsigned int abs_code [const restrict static 1] +) +{ +   switch (rel_code) +   { +      case REL_X: +         *abs_code = ABS_X; +         return RELABSD_X; + +      case REL_Y: +         *abs_code = ABS_Y; +         return RELABSD_Y; + +      case REL_Z: +         *abs_code = ABS_Z; +         return RELABSD_Z; + +      case REL_RX: +         *abs_code = ABS_RX; +         return RELABSD_RX; + +      case REL_RY: +         *abs_code = ABS_RY; +         return RELABSD_RY; + +      case REL_RZ: +         *abs_code = ABS_RZ; +         return RELABSD_RZ; + +      case REL_WHEEL: +         *abs_code = ABS_WHEEL; +         return RELABSD_WHEEL; + +      case REL_MISC: +         *abs_code = ABS_MISC; +         return RELABSD_MISC; + +      default: +         return RELABSD_UNKNOWN; +   } +} + +unsigned int relabsd_axis_name_to_evdev_rel (const enum relabsd_axis_name e) +{ +   switch (e) +   { +      case RELABSD_X: +         return REL_X; + +      case RELABSD_Y: +         return REL_Y; + +      case RELABSD_Z: +         return REL_Z; + +      case RELABSD_RX: +         return REL_RX; + +      case RELABSD_RY: +         return REL_RY; + +      case RELABSD_RZ: +         return REL_RZ; + +      case RELABSD_WHEEL: +         return REL_WHEEL; + +      case RELABSD_MISC: +         return REL_MISC; + +      case RELABSD_UNKNOWN: +         RELABSD_S_PROG_ERROR +         ( +            "relabsd_axis_name_to_evdev_rel(RELABSD_UNKNOWN) is forbidden." +         ); +         return REL_MAX; + +      default: +         break; +   } + +   RELABSD_S_PROG_ERROR +   ( +      "relabsd_axis_name_to_evdev_rel is missing at least 1 case." +   ); + +   return REL_MAX; +} + +unsigned int relabsd_axis_name_to_evdev_abs (const enum relabsd_axis_name e) +{ +   switch (e) +   { +      case RELABSD_X: +         return ABS_X; + +      case RELABSD_Y: +         return ABS_Y; + +      case RELABSD_Z: +         return ABS_Z; + +      case RELABSD_RX: +         return ABS_RX; + +      case RELABSD_RY: +         return ABS_RY; + +      case RELABSD_RZ: +         return ABS_RZ; + +      case RELABSD_WHEEL: +         return ABS_WHEEL; + +      case RELABSD_MISC: +         return ABS_MISC; + +      case RELABSD_UNKNOWN: +         RELABSD_S_PROG_ERROR +         ( +            "relabsd_axis_to_abs(RELABSD_UNKNOWN) is forbidden." +         ); +         return ABS_MAX; + +      default: +         break; +   } + +   RELABSD_S_PROG_ERROR("relabsd_axis_to_abs is missing at least 1 case."); + +   return REL_MAX; +} + +/* + * Returns the relabsd_axis equivalent of a EV_REL/EV_ABS code. + */ +enum relabsd_axis_name relabsd_axis_name_from_evdev_rel (const unsigned int rel) +{ +   switch (rel) +   { +      case REL_X: +         return RELABSD_X; + +      case REL_Y: +         return RELABSD_Y; + +      case REL_Z: +         return RELABSD_Z; + +      case REL_RX: +         return RELABSD_RX; + +      case REL_RY: +         return RELABSD_RY; + +      case REL_RZ: +         return RELABSD_RZ; + +      case REL_WHEEL: +         return RELABSD_WHEEL; + +      case REL_MISC: +         return RELABSD_MISC; + +      default: +         return RELABSD_UNKNOWN; +   } +} + +enum relabsd_axis_name relabsd_axis_name_from_evdev_abs (const unsigned int abs) +{ +   switch (abs) +   { +      case ABS_X: +         return RELABSD_X; + +      case ABS_Y: +         return RELABSD_Y; + +      case ABS_Z: +         return RELABSD_Z; + +      case ABS_RX: +         return RELABSD_RX; + +      case ABS_RY: +         return RELABSD_RY; + +      case ABS_RZ: +         return RELABSD_RZ; + +      case ABS_WHEEL: +         return RELABSD_WHEEL; + +      case ABS_MISC: +         return RELABSD_MISC; + +      default: +         return RELABSD_UNKNOWN; +   } +} diff --git a/src/device/physical_device.c b/src/device/physical_device.c new file mode 100644 index 0000000..a518750 --- /dev/null +++ b/src/device/physical_device.c @@ -0,0 +1,276 @@ +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include <libevdev/libevdev.h> + +#include "error.h" +#include "axis.h" +#include "config.h" + +#include "input.h" + +/* + * Ensures that the input device has enabled the EV_REL axes mentioned + * in the configuration file. + * + * Returns -1 on (fatal) error, + *         0 all configured axes are accounted for. + */ +static int check_for_axes +( +   const struct libevdev * const dev, +   const struct relabsd_config * const conf +) +{ +   int i, device_is_valid; +   unsigned int rel_code; + +   device_is_valid = 1; + +   for (i = RELABSD_VALID_AXES_COUNT; i --> 0;) +   { +      if (conf->axis[i].enabled) +      { +         rel_code = relabsd_axis_to_rel((enum relabsd_axis) i); + +         if (!libevdev_has_event_code(dev, EV_REL, rel_code)) +         { +            RELABSD_FATAL +            ( +               "Input device has no relative %s axis, yet the configuration " +               "file asks to convert it.", +               relabsd_axis_to_name((enum relabsd_axis) i) +            ); + +            device_is_valid = 0; +         } +      } +   } + +   return (device_is_valid - 1); +} + +/* + * Ensures that the input device is compatible with the config file. + * + * Returns -1 on (fatal) error, + *         0 is the device is compatible. + */ +static int device_is_compatible +( +   const struct libevdev * const dev, +   const struct relabsd_config * const conf +) +{ +   if (!libevdev_has_event_type(dev, EV_REL)) +   { +      RELABSD_S_FATAL("Input device has no relative axis."); + +      return -1; +   } + +   if (check_for_axes(dev, conf) < 0) +   { +      return -1; +   } + +   return 0; +} + +int relabsd_physical_device_open +( +   struct relabsd_physical_device * const input, +   const struct relabsd_config * const conf +) +{ +   RELABSD_S_DEBUG(RELABSD_DEBUG_PROGRAM_FLOW, "Opening input device..."); + +   input->fd = open(conf->input_file, O_RDONLY); + +   if (input->fd < 0) +   { +      RELABSD_FATAL +      ( +         "Could not open device '%s' in read only mode: %s.", +         conf->input_file, +         strerror(errno) +      ); + +      return -1; +   } + +   if +   ( +      libevdev_new_from_fd(input->fd, &(input->dev)) < 0 +   ) +   { +      RELABSD_FATAL +      ( +         "libevdev could not open '%s': %s.", +         conf->input_file, +         strerror(errno) +      ); + +      close(input->fd); + +      return -1; +   } + +   if (device_is_compatible(input->dev, conf) < 0) +   { +      return -1; +   } + +   return 0; +} + +void relabsd_physical_device_close (const struct relabsd_physical_device * const input) +{ +   RELABSD_S_DEBUG(RELABSD_DEBUG_PROGRAM_FLOW, "Closing input device..."); + +   libevdev_free(input->dev); +   close(input->fd); +} + +int relabsd_physical_device_read +( +   const struct relabsd_physical_device * const input, +   unsigned int * const input_type, +   unsigned int * const input_code, +   int * const input_value +) +{ +   int rc; +   struct input_event event; + +   /* +   if (libevdev_has_event_pending(input->dev) == 0) +   { +      return -1; +   } +   */ +   rc = +      libevdev_next_event +      ( +         input->dev, +         (LIBEVDEV_READ_FLAG_NORMAL | LIBEVDEV_READ_FLAG_BLOCKING), +         &event +      ); + +   /* TODO: Look into LIBEVDEV_READ_STATUS_SYNC, handle it. */ +   /* +   if (rc == LIBEVDEV_READ_STATUS_SYNC) +   { +      handle_syn_dropped(input->dev); +   } +   else +   */ +   if (rc != LIBEVDEV_READ_STATUS_SUCCESS) +   { +      RELABSD_WARNING("[INPUT] Could not get next event: %s.", strerror(-rc)); + +      return -1; +   } + +   RELABSD_DEBUG +   ( +      RELABSD_DEBUG_REAL_EVENTS, +      "Valid event received: {type = %s; code = %s; value = %d}.", +       libevdev_event_type_get_name(event.type), +       libevdev_event_code_get_name(event.type, event.code), +       event.value +   ); + +   *input_type = event.type; +   *input_code = event.code; +   *input_value = event.value; + +   return 0; +} + +int relabsd_physical_device_wait_for_next_event +( +   const struct relabsd_physical_device * const input, +   const struct relabsd_config * const config +) +{ +   int ready_fds; +   const int old_errno = errno; +   fd_set ready_to_read; +   struct timeval curr_timeout; + +   FD_ZERO(&ready_to_read); +   FD_SET(input->fd, &ready_to_read); + +   /* call to select may alter timeout */ +   memcpy +   ( +      (void *) &(curr_timeout), +      (const void *) &(config->timeout), +      sizeof(struct timeval) +   ); + +   errno = 0; + +   RELABSD_S_ERROR +   ( +      "Waiting for input to be ready..." +   ); + +   ready_fds = select +   ( +      (input->fd + 1), +      &ready_to_read, +      (fd_set *) NULL, +      (fd_set *) NULL, +      (input->timed_out) ? NULL : &(curr_timeout) +   ); + +   if (errno != 0) +   { +      RELABSD_ERROR +      ( +         "Unable to wait for timeout: %s (errno: %d).", +         strerror(errno), +         errno +      ); + +      if (errno == EINTR) +      { +         /* Signal interruption? */ +      } +      else +      { +         /* TODO: error message */ +      } + +      errno = old_errno; + +      return -1; +   } + +   if (ready_fds == -1) +   { +      /* TODO: error message */ + +      RELABSD_S_ERROR +      ( +         "Unable to wait for timeout, yet errno was not set to anything." +      ); + +      errno = old_errno; + +      return -1; +   } + +   RELABSD_ERROR +   ( +      "Input is ready, ready_fds = %d", ready_fds +   ); + +   errno = old_errno; + +   return ready_fds; +} diff --git a/src/device/virtual_device.c b/src/device/virtual_device.c new file mode 100644 index 0000000..19d1097 --- /dev/null +++ b/src/device/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 +   ); +} + | 


