| summaryrefslogtreecommitdiff | 
diff options
| -rw-r--r-- | CMakeLists.txt | 44 | ||||
| -rw-r--r-- | conf/space_navigator.conf | 14 | ||||
| -rw-r--r-- | src/config.c | 283 | ||||
| -rw-r--r-- | src/config.h | 15 | ||||
| -rw-r--r-- | src/relabsd_device.c | 91 | ||||
| -rw-r--r-- | udev.rules | 1 | 
6 files changed, 400 insertions, 48 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d274bd..003697f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ -cmake_minimum_required(VERSION 2.8 FATAL_ERROR) +cmake_minimum_required(VERSION 3.0 FATAL_ERROR) +# 3.0 introduces line continuation.  project("relabsd") @@ -66,10 +67,37 @@ else ()  endif (RELABSD_ENABLE_ERROR_LOCATION) -option(RELABSD_REAL_FUZZ "Fuzz is relative to the real device's events." ON) -if (RELABSD_REAL_FUZZ) -   target_compile_definitions(relabsd PUBLIC RELABSD_REAL_FUZZ) -   message(STATUS "[OPTION] Fuzz is relative to the real device's events.") -else () -   message(STATUS "[OPTION] Fuzz is relative to the emulated device's events.") -endif (RELABSD_REAL_FUZZ) +set( +   RELABSD_OPTION_MAX_SIZE +   "64" +   CACHE +   INTEGER +   "Maximum number of characters in an axis option (name + params)." +) +target_compile_definitions( +   relabsd +   PUBLIC +   "-DRELABSD_OPTION_MAX_SIZE=${RELABSD_OPTION_MAX_SIZE}" +) +message( +   STATUS +   "[OPTION] Axis options can contain up to ${RELABSD_OPTION_MAX_SIZE}\ + characters (name + params)." +) + +set( +   RELABSD_DEVICE_PREFIX +   "relabsd:" +   CACHE +   STRING +   "String prefixing the name of the virtual device." +) +target_compile_definitions( +   relabsd +   PUBLIC +   "-DRELABSD_DEVICE_PREFIX=\"${RELABSD_DEVICE_PREFIX}\"" +) +message( +   STATUS +   "[OPTION] Virtual devices' names are prefixed by '${RELABSD_DEVICE_PREFIX}'." +) diff --git a/conf/space_navigator.conf b/conf/space_navigator.conf index 4936713..aa18480 100644 --- a/conf/space_navigator.conf +++ b/conf/space_navigator.conf @@ -1,8 +1,8 @@  # 3DConnexion SpaceNavigator -# AXIS   MIN   MAX   FUZZ  FLAT  RESOLUTION -X        -350  350   0     0     1 -Y        -350  350   0     0     1 -Z        -350  350   0     0     1 -RX       -350  350   0     0     1 -RY       -350  350   0     0     1 -RZ       -350  350   0     0     1 +# AXIS   MIN   MAX   FUZZ  FLAT  RESOLUTION  OPTIONS +X        -350  350   0     0     1           direct,real_fuzz +Y        -350  350   0     0     1           direct,real_fuzz +Z        -350  350   0     0     1           direct,real_fuzz +RX       -350  350   0     0     1           direct,real_fuzz +RY       -350  350   0     0     1           direct,real_fuzz +RZ       -350  350   0     0     1           direct,real_fuzz diff --git a/src/config.c b/src/config.c index 59974b5..02460d0 100644 --- a/src/config.c +++ b/src/config.c @@ -2,12 +2,16 @@  #include <string.h>  #include <stdio.h>  #include <stdlib.h> +#include <limits.h>  #include "error.h"  #include "pervasive.h"  #include "axis.h"  #include "config.h" +#ifndef RELABSD_OPTION_MAX_SIZE +   #define RELABSD_OPTION_MAX_SIZE 64 +#endif  /*   * "errno is never set to zero by any system call or library function."   * This file makes use of this, by setting it to zero and checking if @@ -19,7 +23,7 @@   */  /* - * Returns -1 on error, + * Returns -1 on (fatal) error,   *          0 on EOF,   *          1 on newline.   */ @@ -32,15 +36,21 @@ static int reach_next_line_or_eof (FILE * const f)     errno = 0; -   c = getc(f); +   c = (char) getc(f);     while ((c != '\n') && c != EOF)     { -      c = getc(f); +      c = (char) getc(f);     }     if (errno != 0)     { +      RELABSD_FATAL +      ( +         "[CONFIG] Error while attempting to reach EOF or next line: %s.", +         strerror(errno) +      ); +        errno = prev_errno;        return -1; @@ -57,6 +67,141 @@ static int reach_next_line_or_eof (FILE * const f)  }  /* + * Returns -1 if the option was discarded (an error has been reported), + *         0 if the option was successfully parsed. + * + * ('length' - 1) is the number of relevant characters in 'name'. + * 'name' must support 'length' characters. + * name[length] will be set to \0, so it does not need to be when calling + * this function. + */ +static int parse_option +( +   struct relabsd_config * const conf, +   enum relabsd_axis const axis, +   char * const name, +   int const length +) +{ +   name[length] = '\0'; + +   if (strcmp(name, "direct") == 0) +   { +      conf->axis[axis].option[RELABSD_DIRECT_OPTION] = 1; +   } +   else if (strcmp(name, "real_fuzz") == 0) +   { +      conf->axis[axis].option[RELABSD_REAL_FUZZ_OPTION] = 1; +   } +   else if (strcmp(name, "framed") == 0) +   { +      conf->axis[axis].option[RELABSD_FRAMED_OPTION] = 1; +   } +   else +   { +      RELABSD_ERROR +      ( +         "[CONFIG] Unknown option '%s' for axis '%s'.", +         name, +         relabsd_axis_to_name(axis) +      ); + +      return -1; +   } + +   return 0; +} + +/* + * Returns -1 on error, + *          0 on EOF, + *          1 on newline. + */ +static int read_axis_options +( +   struct relabsd_config * const conf, +   FILE * const f, +   enum relabsd_axis const axis +) +{ +   char option[(RELABSD_OPTION_MAX_SIZE + 1)]; +   int i, prev_errno; +   char c; + +   option[RELABSD_OPTION_MAX_SIZE] = '\0'; + +   prev_errno = errno; + +   errno = 0; + +   memset(conf->axis[axis].option, 0, RELABSD_OPTIONS_COUNT * sizeof(int)); + +   i = 0; + +   while (i <= RELABSD_OPTION_MAX_SIZE) +   { +      c = (char) getc(f); + +      if ((errno != 0) && (c == EOF)) +      { +         RELABSD_FATAL +         ( +            "[CONFIG] Reading error while parsing option name (axis '%s'): %s.", +            relabsd_axis_to_name(axis), +            strerror(errno) +         ); + +         errno = prev_errno; + +         return -1; +      } + +      switch (c) +      { +         case ' ': +         case '\t': +            break; + +         case ',': +            /* We parsed a new option and there is a least another. */ +            parse_option(conf, axis, option, i); + +            i = 0; + +            break; + +         case '\n': +            parse_option(conf, axis, option, i); +            errno = prev_errno; + +            return 1; + +         case EOF: +            parse_option(conf, axis, option, i); +            errno = prev_errno; + +            return 0; + +         default: +            option[i] = c; +            i++; + +            break; +      } +   } + +   RELABSD_FATAL +   ( +      "[CONFIG] Option name '%s[...]' (axis '%s') is too long (%d chars max).", +      option, +      relabsd_axis_to_name(axis), +      RELABSD_OPTION_MAX_SIZE +   ); + +   return -1; +} + +/*   * Returns -1 on (fatal) error,   *          0 on succes.   */ @@ -140,7 +285,7 @@ static int parse_axis_configuration_line     conf->axis[axis].enabled = 1;     conf->axis[axis].previous_value = 0; -   return 0; +   return read_axis_options(conf, f, axis);  }  /* @@ -157,11 +302,7 @@ static int read_config_line  {     if (!RELABSD_IS_PREFIX("#", prefix))     { -      if (parse_axis_configuration_line(conf, f, prefix) < 0) -      { -         /* Fatal error. */ -         return -1; -      } +      return parse_axis_configuration_line(conf, f, prefix);     }     return reach_next_line_or_eof(f); @@ -295,7 +436,7 @@ int relabsd_config_parse     if (argc == 3)     { -      conf->device_name = "relabsd device"; +      conf->device_name = NULL;     }     else     { @@ -314,50 +455,128 @@ int relabsd_config_parse     return 0;  } -int relabsd_config_filter +static int direct_filter  ( -   struct relabsd_config * const conf, -   enum relabsd_axis const axis, +   struct relabsd_config_axis * const axis,     int * const value  )  { -   if ((axis == RELABSD_UNKNOWN) || !conf->axis[axis].enabled) +   if (abs(*value - axis->previous_value) <= axis->fuzz)     { -      return 0; +      if (axis->option[RELABSD_REAL_FUZZ_OPTION]) +      { +         axis->previous_value = *value; +      } + +      return -1;     } -   if (abs(*value - conf->axis[axis].previous_value) <= conf->axis[axis].fuzz) +   if (*value < axis->min)     { -#ifdef RELABSD_REAL_FUZZ -      conf->axis[axis].previous_value = *value; -#endif +      *value = axis->min; +   } +   else if (*value > axis->max) +   { +      *value = axis->max; +   } +   else if (abs(*value) <= axis->flat) +   { +      *value = 0; +   } +   if (*value == axis->previous_value) +   {        return -1;     } -   if (*value < conf->axis[axis].min) +   axis->previous_value = *value; + +   return 1; +} + +static int rel_to_abs_filter +( +   struct relabsd_config_axis * const axis, +   int * const value +) +{ +   long int guard; + +   guard = (((long int) axis->previous_value) + ((long int) *value)); + +   if (guard < ((long int) INT_MIN))     { -      *value = conf->axis[axis].min; +      guard = ((long int) INT_MIN);     } -   else if (*value > conf->axis[axis].max) +   else if (guard > ((long int) INT_MAX))     { -      *value = conf->axis[axis].max; +      guard = ((long int) INT_MAX);     } -   else if (abs(*value) <= conf->axis[axis].flat) + +   *value = (int) guard; + +   if (axis->option[RELABSD_FRAMED_OPTION])     { -      *value = 0; +      if (*value < axis->min) +      { +         *value = axis->min; +      } +      else if (*value > axis->max) +      { +         *value = axis->max; +      } + +      if (*value == axis->previous_value) +      { +         return 0; +      } + +      axis->previous_value = *value; -      /* -       * As long as the 'fuzz' test is done prior the 'flat' one, moving around -       * in the 'flat' zone won't trigger useless '0' value events. -       */ +      return 1;     } +   else +   { +      if (*value == axis->previous_value) +      { +         return 0; +      } -   /* TODO: handle conf->axis[axis].resolution */ +      axis->previous_value = *value; + +      if ((*value < axis->min) || (*value > axis->max)) +      { +         return 0; +      } +      else +      { +         return 1; +      } +   } +} + +int relabsd_config_filter +( +   struct relabsd_config * const conf, +   enum relabsd_axis const axis, +   int * const value +) +{ +   if ((axis == RELABSD_UNKNOWN) || !conf->axis[axis].enabled) +   { +      return 0; +   } -   conf->axis[axis].previous_value = *value; +   /* TODO: handle conf->axis[axis].resolution */ -   return 1; +   if (conf->axis[axis].option[RELABSD_DIRECT_OPTION]) +   { +      return direct_filter((conf->axis + axis), value); +   } +   else +   { +      return rel_to_abs_filter((conf->axis + axis), value); +   }  }  void relabsd_config_get_absinfo diff --git a/src/config.h b/src/config.h index c0c83a7..07fcdfb 100644 --- a/src/config.h +++ b/src/config.h @@ -5,10 +5,25 @@  #include "axis.h" +/* Number of options that can be configured. */ +#define RELABSD_OPTIONS_COUNT 3 + +enum relabsd_option +{ +   RELABSD_DIRECT_OPTION, +   RELABSD_REAL_FUZZ_OPTION, +   RELABSD_FRAMED_OPTION +}; + +  struct relabsd_config_axis  { +/* relabsd axis properties */     int enabled;     int previous_value; +   int option[RELABSD_OPTIONS_COUNT]; + +/* Absolute axis properties */     int min;     int max;     int fuzz; diff --git a/src/relabsd_device.c b/src/relabsd_device.c index 0ca6993..e807ec6 100644 --- a/src/relabsd_device.c +++ b/src/relabsd_device.c @@ -2,6 +2,8 @@  #include <errno.h>  #include <unistd.h>  #include <fcntl.h> +#include <stdlib.h> +#include <stdio.h>  #include <libevdev/libevdev-uinput.h> @@ -49,6 +51,87 @@ static void replace_rel_axes  } +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_device_create  (     struct relabsd_device * const dev, @@ -85,7 +168,13 @@ int relabsd_device_create        return -1;     } -   libevdev_set_name(dev->dev, config->device_name); +   if (rename_device(dev->dev, config) < 0) +   { +      libevdev_free(dev->dev); +      close(fd); + +      return -1; +   }     libevdev_enable_event_type(dev->dev, EV_ABS); diff --git a/udev.rules b/udev.rules new file mode 100644 index 0000000..7a9b4c6 --- /dev/null +++ b/udev.rules @@ -0,0 +1 @@ +SUBSYSTEM=="input", ATTRS{name}=="relabsd:*", ENV{ID_INPUT_JOYSTICK}="1", ENV{ID_CLASS}="joystick"  | 


