Logo Search packages:      
Sourcecode: libjsw version File versions  Download package

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#if defined(__FreeBSD__)
# include <sys/joystick.h>
#endif

#include "forcefeedback.h"

#include "../include/string.h"
#include "../include/strexp.h"
#include "../include/disk.h"
#include "../include/fio.h"

#include "../include/jsw.h"


int JSInit(
      js_data_struct *jsd,
      const char *device,
      const char *calibration,
      unsigned int flags
);
static void SetAxisValue(js_axis_struct *axis, int value, time_t t);
static void SetButtonValue(js_button_struct *button, int value, time_t t);
int JSUpdate(js_data_struct *jsd);
void JSClose(js_data_struct *jsd);


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : 1)


/*
 *      Initializes the joystick and stores the new initialized values
 *      into the jsd structure.
 *
 *      If the device is not specified (set to NULL), then it will
 *      be defauled to JSDefaultDevice.
 *
 *      If the calibration file is not specified (set to NULL), then
 *      it will be defaulted to JSDefaultCalibration. The HOME
 *      enviroment value will be used as the prefix to the path of
 *      JSDefaultCalibration. The calibration file does not have to
 *      exist.
 *
 *    Available flags are:
 *
 *    JSFlagNonBlocking       Open in non-blocking mode.
 *    JSFlagForceFeedback           Open in read/write mode.
 */
int JSInit(
      js_data_struct *jsd,
      const char *device,
      const char *calibration,
      unsigned int flags
)
{
      int i;
      js_axis_struct *axis;
      js_button_struct *button;

#if defined(__linux__) || defined(__FreeBSD__)
      unsigned char axes = 2;
      unsigned char buttons = 2;
      int version = 0x000800;

# ifndef LINUX_JS_NAME_MAX
#  define LINUX_JS_NAME_MAX   128
# endif

      char name[LINUX_JS_NAME_MAX] = "Unknown";

#endif      /* __linux__ || __FreeBSD__ */


      if(jsd == NULL)
          return(JSBadValue);


      /* Explicitly reset values */
      jsd->axis = NULL;
      jsd->total_axises = 0;

      jsd->button = NULL;
      jsd->total_buttons = 0;

      jsd->device_name = NULL;
      jsd->calibration_file = NULL;

      jsd->events_received = 0;
      jsd->events_sent = 0;

      jsd->fd = -1;
      jsd->flags = 0;
      jsd->driver_version = 0;
      jsd->last_calibrated = 0;
      jsd->force_feedback = NULL;


      /* Set default device name as needed */
      if(device == NULL)
          device = JSDefaultDevice;

      /* Set default calibration file name as needed */
      if(calibration == NULL)
      {
          const char *home = getenv("HOME");
          calibration = PrefixPaths(
            (home != NULL) ? home : "/",
            JSDefaultCalibration
          );
          if(calibration == NULL)
            calibration = JSDefaultCalibration;
      }

      /* From this point on device and calibration are not NULL, so
       * record device and calibration file names on the jsd
       */
      jsd->device_name = STRDUP(device);
      jsd->calibration_file = STRDUP(calibration);


#if defined(__linux__) || defined(__FreeBSD__)
      /* Open joystick */
      jsd->fd = open(jsd->device_name, O_RDONLY);
      if(jsd->fd < 0)
      {
          JSClose(jsd);
          return(JSNoAccess);
      }
#endif

#if defined(__linux__)
      /* Fetch device values */
      /* Raw version string */
      ioctl(jsd->fd, JSIOCGVERSION, &version);
      jsd->driver_version = (unsigned int)version;

      /* Total number of axises */
      ioctl(jsd->fd, JSIOCGAXES, &axes);  /* Total axises */
      jsd->total_axises = axes;

      /* Total number of buttons */
      ioctl(jsd->fd, JSIOCGBUTTONS, &buttons);  /* Total buttons */
      jsd->total_buttons = buttons;

      /* Device descriptive name */
      ioctl(jsd->fd, JSIOCGNAME(LINUX_JS_NAME_MAX), name);
      jsd->name = STRDUP(name);
#elif defined(__FreeBSD__)
      jsd->driver_version = version = 1;
      jsd->total_axises = axes = 2;
      jsd->total_buttons = buttons = 2;
      strlcpy(name, "FreeBSD-Gameport", LINUX_JS_NAME_MAX);
      jsd->name = STRDUP(name);
#endif
      /* Allocate axises */
      if(jsd->total_axises > 0)
      {
          jsd->axis = (js_axis_struct **)calloc(
              jsd->total_axises,
              sizeof(js_axis_struct *)
          );
          if(jsd->axis == NULL)
          {
              jsd->total_axises = 0;
              JSClose(jsd);
              return(JSNoBuffers);
          }
      }
      for(i = 0; i < jsd->total_axises; i++)
      {
          jsd->axis[i] = axis = (js_axis_struct *)calloc(
            1, sizeof(js_axis_struct)
          );
          if(axis == NULL)
          {
            JSClose(jsd);
            return(JSNoBuffers);
          }

          /* Reset axis values */
          axis->cur = JSDefaultCenter;
          axis->min = JSDefaultMin;
          axis->max = JSDefaultMax;
          axis->cen = JSDefaultCenter;
          axis->nz = JSDefaultNullZone;
          axis->tolorance = JSDefaultTolorance;
          axis->flags = 0;
      }

      /* Allocate buttons */  
      if(jsd->total_buttons > 0)
      {
          jsd->button = (js_button_struct **)calloc(
            jsd->total_buttons,
            sizeof(js_button_struct *)
          );
          if(jsd->button == NULL)   
          {
            jsd->total_buttons = 0;
            JSClose(jsd);   
            return(JSNoBuffers);
          }
      }
      for(i = 0; i < jsd->total_buttons; i++)
      {
          jsd->button[i] = button = (js_button_struct *)calloc(
            1, sizeof(js_button_struct)
          );
          if(button == NULL)
          {
            JSClose(jsd);
            return(JSNoBuffers);
          }

          /* Reset button values */
          button->state = JSButtonStateOff;
      }

      /* Set to non-blocking? */
      if(flags & JSFlagNonBlocking)
      {
          fcntl(jsd->fd, F_SETFL, O_NONBLOCK);
          jsd->flags |= JSFlagNonBlocking;
      }

      /* Mark successful initialization */
      jsd->flags |= JSFlagIsInit;

      /* Load calibration from calibration file */
      JSLoadCalibrationUNIX(jsd);

      /* Set axis tolorance for error correction */
      JSResetAllAxisTolorance(jsd);


      return(JSSuccess);
}


/*
 *    Called by JSUpdate() to set axis structure value.
 */
static void SetAxisValue(js_axis_struct *axis, int value, time_t t)
{
       /* Record previous axis position */
       axis->prev = axis->cur;

       /* Set new current axis value */
       axis->cur = value;

       /* Record time stamp (in ms) */
       axis->last_time = axis->time;

       /* Set new time stamp (in ms) */
       axis->time = t;
}

/*
 *    Called by JSUpdate() to set button structure value.
 */
static void SetButtonValue(js_button_struct *button, int value, time_t t)
{
       /* Record previous state */
       button->prev_state = button->state;

       /* Set new button state */
       button->state = value ? JSButtonStateOn : JSButtonStateOff;

       /* Update state change */
       if((button->prev_state == JSButtonStateOn) &&
             (button->state == JSButtonStateOff)
       )
             button->changed_state = JSButtonChangedStateOnToOff;
       else if((button->prev_state == JSButtonStateOff) &&
             (button->state == JSButtonStateOn)
       )
             button->changed_state = JSButtonChangedStateOffToOn;

       /* Record time stamp (in ms) */
       button->last_time = button->time;

       /* Set new time stamp (in ms) */
       button->time = t;
}

/*
 *    Updates the information in jsd, returns JSGotEvent if there
 *    was some change or JSNoEvent if there was no change.
 *
 *    jsd needs to be previously initialized by a call to
 *    JSInit().
 */
int JSUpdate(js_data_struct *jsd)
{
      int n;
      int status = JSNoEvent;
#if defined(__linux__)
      int keep_handling = 1;
      int bytes_read, cycles;
      struct js_event event;
#elif defined(__FreeBSD__)
      struct joystick js;
#endif
#if defined(__linux__) || defined(__FreeBSD__)
      js_axis_struct **axis;
      js_button_struct **button;
#endif  /* __linux__ || __FreeBSD__ */


      if(jsd == NULL)
          return(status);

      if(jsd->fd < 0)
          return(status);

#if defined(__linux__) || defined(__FreeBSD__)
      /* Reset all button state change value on all buttons */
      for(n = 0, button = jsd->button;
          n < jsd->total_buttons;
          n++, button++
      )
      {
          if(*button != NULL)
            (*button)->changed_state = JSButtonChangedStateNone;
      }

      /* Reset current and previous axis values */
      for(n = 0, axis = jsd->axis;
          n < jsd->total_axises;
          n++, axis++
      )
      {
          if(*axis != NULL)
            (*axis)->prev = (*axis)->cur;
      }
#endif /* __linux__ || __FreeBSD__ */


#if defined(__linux__)
      /* Linux joystick device fetching
       *
       * Handle up to 16 events from joystick driver
       */
      cycles = 0;
      while(keep_handling &&
            (cycles < 16)
      )
      {
          /* Get event */
          bytes_read = read(
              jsd->fd,
              &event,
              sizeof(struct js_event)
          );
          /* No more events to be read? */
          if(bytes_read != sizeof(struct js_event))
              return(status);

          /* Handle by event type */
          switch(event.type & ~JS_EVENT_INIT)
          {
            /* Axis event */
            case JS_EVENT_AXIS:
              /* Get axis number */
            n = event.number;

            /* Does axis exist? */
            if(JSIsAxisAllocated(jsd, n))
                SetAxisValue(
                  jsd->axis[n],
                  (int)event.value,
                  (time_t)event.time
                );
            jsd->events_received++; /* Increment events recv count */
              status = JSGotEvent;  /* Mark that we got event */
              break;

            /* Button event */
            case JS_EVENT_BUTTON:
              /* Get button number */
              n = event.number;

              /* Does button exist? */
            if(JSIsButtonAllocated(jsd, n))
                SetButtonValue(
                  jsd->button[n],
                  (int)event.value, (time_t)event.time
                );
            jsd->events_received++; /* Increment events recv count */
              status = JSGotEvent;  /* Mark that we got event */
              break;

            /* Other event */
            default:
            keep_handling = 0;      /* Stop handling events */
            break;
          }

          cycles++;
      }
#elif defined(__FreeBSD__)
      /* FreeBSD joystick device fetching */
      if(read(jsd->fd, &js, sizeof(struct joystick)) == sizeof(struct joystick))
      {
          status = JSGotEvent;
          if(JSIsAxisAllocated(jsd, 0))
            SetAxisValue(jsd->axis[0], js.x, time(NULL));
          if(JSIsAxisAllocated(jsd, 1))
            SetAxisValue(jsd->axis[1], js.y, time(NULL));
          if(JSIsButtonAllocated(jsd, 0))
            SetButtonValue(jsd->button[0], js.b1, time(NULL));
          if(JSIsButtonAllocated(jsd, 1))
            SetButtonValue(jsd->button[1], js.b2, time(NULL));
      }
#endif

      return(status);
}

/*
 *    Closes the joystick and deallocates all resources on the given
 *    jsd structure. The jsd structure itself is not deallocated however
 *    its values will be reset to defaults.
 */
void JSClose(js_data_struct *jsd)
{
      int i;


      if(jsd == NULL)
          return;

      /* Deallocate force feedback resources and deallocate the
       * force_feedback structure.
       */
      JSFFDelete(jsd->force_feedback);
      jsd->force_feedback = NULL;

      /* Close descriptor to joystick */
      if(jsd->fd > -1)
      {
          close(jsd->fd);
          jsd->fd = -1;
      }

      /* Free name */
      free(jsd->name);
      jsd->name = NULL;


      /* Free all axises */
      for(i = 0; i < jsd->total_axises; i++)
          free(jsd->axis[i]);
      free(jsd->axis);
      jsd->axis = NULL;
      jsd->total_axises = 0;


      /* Free all buttons */
      for(i = 0; i < jsd->total_buttons; i++)
          free(jsd->button[i]);
      free(jsd->button);
      jsd->button = NULL;
      jsd->total_buttons = 0;


      /* Free device name */
      free(jsd->device_name);
      jsd->device_name = NULL;

      /* Free calibration file name */
      free(jsd->calibration_file);
      jsd->calibration_file = NULL;


      /* Reset rest of the values */
      jsd->flags = 0;
      jsd->driver_version = 0;
      jsd->last_calibrated = 0;

      /* Do not deallocate the jsd structure */
}

Generated by  Doxygen 1.6.0   Back to index