freeglut_joystick.c
上传用户:gb3593
上传日期:2022-01-07
资源大小:3028k
文件大小:54k
源码类别:

游戏引擎

开发平台:

Visual C++

  1. /*
  2.  * freeglut_joystick.c
  3.  *
  4.  * Joystick handling code
  5.  *
  6.  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
  7.  * Written by Steve Baker, <sjbaker1@airmail.net>
  8.  *
  9.  * Permission is hereby granted, free of charge, to any person obtaining a
  10.  * copy of this software and associated documentation files (the "Software"),
  11.  * to deal in the Software without restriction, including without limitation
  12.  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  13.  * and/or sell copies of the Software, and to permit persons to whom the
  14.  * Software is furnished to do so, subject to the following conditions:
  15.  *
  16.  * The above copyright notice and this permission notice shall be included
  17.  * in all copies or substantial portions of the Software.
  18.  *
  19.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  20.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  22.  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  23.  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  24.  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  25.  */
  26. /*
  27.  * FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu>
  28.  *
  29.  * Redone by John Fay 2/4/04 with another look from the PLIB "js" library.
  30.  *  Many thanks for Steve Baker for permission to pull from that library.
  31.  */
  32. #include <GL/freeglut.h>
  33. #include "freeglut_internal.h"
  34. #if HAVE_SYS_PARAM_H
  35. #    include <sys/param.h>
  36. #endif
  37. /*
  38.  * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
  39.  * interspersed
  40.  */
  41. /* XXX It might be better to poll the operating system for the numbers of buttons and
  42.  * XXX axes and then dynamically allocate the arrays.
  43.  */
  44. #define _JS_MAX_BUTTONS 32
  45. #if TARGET_HOST_MACINTOSH
  46. #    define _JS_MAX_AXES  9
  47. #    include <InputSprocket.h>
  48. #endif
  49. #if TARGET_HOST_MAC_OSX
  50. #    define _JS_MAX_AXES 16
  51. #    include <mach/mach.h>
  52. #    include <IOKit/IOkitLib.h>
  53. #    include <IOKit/hid/IOHIDLib.h>
  54. #endif
  55. #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
  56. #    define _JS_MAX_AXES  8
  57. #    include <windows.h>
  58. #    include <mmsystem.h>
  59. #    include <regstr.h>
  60. #endif
  61. #if TARGET_HOST_POSIX_X11
  62. #    define _JS_MAX_AXES 16
  63. #    if HAVE_SYS_IOCTL_H
  64. #        include <sys/ioctl.h>
  65. #    endif
  66. #    if HAVE_FCNTL_H
  67. #        include <fcntl.h>
  68. #    endif
  69. #    if HAVE_ERRNO
  70. #        include <errno.h>
  71. #    endif
  72. #    if defined(__FreeBSD__) || defined(__NetBSD__)
  73. /* XXX The below hack is done until freeglut's autoconf is updated. */
  74. #        define HAVE_USB_JS    1
  75. #        if defined(__FreeBSD__)
  76. #            include <sys/joystick.h>
  77. #        else
  78. /*
  79.  * XXX NetBSD/amd64 systems may find that they have to steal the
  80.  * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
  81.  * XXX I cannot comment whether that works for the interface, but
  82.  * XXX it lets you compile...(^&  I do not think that we can do away
  83.  * XXX with this header.
  84.  */
  85. #            include <machine/joystick.h>         /* For analog joysticks */
  86. #        endif
  87. #        define JS_DATA_TYPE joystick
  88. #        define JS_RETURN (sizeof(struct JS_DATA_TYPE))
  89. #    endif
  90. #    if defined(__linux__)
  91. #        include <linux/joystick.h>
  92. /* check the joystick driver version */
  93. #        if defined(JS_VERSION) && JS_VERSION >= 0x010000
  94. #            define JS_NEW
  95. #        endif
  96. #    else  /* Not BSD or Linux */
  97. #        ifndef JS_RETURN
  98.   /*
  99.    * We'll put these values in and that should
  100.    * allow the code to at least compile when there is
  101.    * no support. The JS open routine should error out
  102.    * and shut off all the code downstream anyway and if
  103.    * the application doesn't use a joystick we'll be fine.
  104.    */
  105.   struct JS_DATA_TYPE
  106.   {
  107.     int buttons;
  108.     int x;
  109.     int y;
  110.   };
  111. #            define JS_RETURN (sizeof(struct JS_DATA_TYPE))
  112. #        endif
  113. #    endif
  114. #endif
  115. #define JS_TRUE  1
  116. #define JS_FALSE 0
  117. /* BSD defines from "jsBSD.cxx" around lines 42-270 */
  118. #if defined(__NetBSD__) || defined(__FreeBSD__)
  119. #    ifdef HAVE_USB_JS
  120. #        if defined(__NetBSD__)
  121. /* XXX The below hack is done until freeglut's autoconf is updated. */
  122. #            define HAVE_USBHID_H 1
  123. #            ifdef HAVE_USBHID_H
  124. #                include <usbhid.h>
  125. #            else
  126. #                include <usb.h>
  127. #            endif
  128. #        elif defined(__FreeBSD__)
  129. #            if __FreeBSD_version < 500000
  130. #                include <libusbhid.h>
  131. #            else
  132. /* XXX The below hack is done until freeglut's autoconf is updated. */
  133. #                define HAVE_USBHID_H 1
  134. #                include <usbhid.h>
  135. #            endif
  136. #        endif
  137. #        include <dev/usb/usb.h>
  138. #        include <dev/usb/usbhid.h>
  139. /* Compatibility with older usb.h revisions */
  140. #        if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
  141. #            define USB_MAX_DEVNAMES MAXDEVNAMES
  142. #        endif
  143. #    endif
  144. static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
  145. static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
  146. struct os_specific_s {
  147.   char             fname [128 ];
  148.   int              fd;
  149.   int              is_analog;
  150.   /* The following structure members are specific to analog joysticks */
  151.   struct joystick  ajs;
  152. #    ifdef HAVE_USB_JS
  153.   /* The following structure members are specific to USB joysticks */
  154.   struct hid_item *hids;
  155.   int              hid_dlen;
  156.   int              hid_offset;
  157.   char            *hid_data_buf;
  158.   int              axes_usage [ _JS_MAX_AXES ];
  159. #    endif
  160.   /* We keep button and axes state ourselves, as they might not be updated
  161.    * on every read of a USB device
  162.    */
  163.   int              cache_buttons;
  164.   float            cache_axes [ _JS_MAX_AXES ];
  165. };
  166. /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
  167. #    define USB_IDENT_OFFSET    2
  168. #    define USBDEV "/dev/usb"
  169. #    define UHIDDEV "/dev/uhid"
  170. #    define AJSDEV "/dev/joy"
  171. #    ifdef HAVE_USB_JS
  172. /*
  173.  * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
  174.  * the full name of a USB device. If /dev/usbN isn't readable, we punt and
  175.  * return the uhidN device name. We warn the user of this situation once.
  176.  */
  177. static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
  178. {
  179.   struct usb_device_info di;
  180.   int i, a;
  181.   char *cp;
  182.   for (a = 1; a < USB_MAX_DEVICES; a++) {
  183.     di.udi_addr = a;
  184.     if (ioctl(f, USB_DEVICEINFO, &di) != 0)
  185.       return NULL;
  186.     for (i = 0; i < USB_MAX_DEVNAMES; i++)
  187.       if (di.udi_devnames[i][0] &&
  188.           strcmp(di.udi_devnames[i], dev) == 0) {
  189.         cp =  calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
  190.         strcpy(cp, di.udi_vendor);
  191.         strcat(cp, " ");
  192.         strcat(cp, di.udi_product);
  193.         strncpy(out, cp, outlen - 1);
  194.         out[outlen - 1] = 0;
  195.         free( cp );
  196.         return out;
  197.       }
  198.   }
  199.   return NULL;
  200. }
  201. static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
  202. {
  203.   int i, f;
  204.   char buf[50];
  205.   char *cp;
  206.   static int protection_warned = 0;
  207.   for (i = 0; i < 16; i++) {
  208.     snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
  209.     f = open(buf, O_RDONLY);
  210.     if (f >= 0) {
  211.       cp = fghJoystickWalkUSBdev(f, name, out, outlen);
  212.       close(f);
  213.       if (cp)
  214.         return 1;
  215.     }
  216. #if HAVE_ERRNO
  217.     else if (errno == EACCES) {
  218.       if (!protection_warned) {
  219.         fgWarning ( "Can't open %s for read!", buf );
  220.         protection_warned = 1;
  221.       }
  222.     }
  223. #endif
  224.   }
  225.   return 0;
  226. }
  227. static int fghJoystickInitializeHID(struct os_specific_s *os,
  228.        int *num_axes, int *num_buttons)
  229. {
  230.     int size, is_joystick;
  231. #   ifdef HAVE_USBHID_H
  232.         int report_id = 0;
  233. #   endif
  234.     struct hid_data *d;
  235.     struct hid_item h;
  236.     report_desc_t rd;
  237.     if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
  238.     {
  239. #if HAVE_ERRNO
  240.         fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
  241. #else
  242.         fgWarning ( "error: %s", os->fname );
  243. #endif
  244.         return FALSE;
  245.     }
  246.     os->hids = NULL;
  247. #   ifdef HAVE_USBHID_H
  248.         if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
  249.         {
  250.             /*** XXX {report_id} may not be the right variable? ***/
  251. #if HAVE_ERRNO
  252.             fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
  253. #else
  254.             fgWarning ( "error: %s%d", UHIDDEV, report_id );
  255. #endif
  256.             return FALSE;
  257.         }
  258.         size = hid_report_size( rd, hid_input, report_id );
  259. #   else
  260.         size = hid_report_size( rd, 0, hid_input );
  261. #   endif
  262.     os->hid_data_buf = calloc( 1, size );
  263.     os->hid_dlen = size;
  264.     is_joystick = 0;
  265. #   ifdef HAVE_USBHID_H
  266.         d = hid_start_parse( rd, 1 << hid_input, report_id );
  267. #   else
  268.         d = hid_start_parse( rd, 1 << hid_input );
  269. #   endif
  270.         while( hid_get_item( d, &h ) )
  271.         {
  272.             int usage, page, interesting_hid;
  273.             page = HID_PAGE( h.usage );
  274.             usage = HID_USAGE( h.usage );
  275.             /* This test is somewhat too simplistic, but this is how MicroSoft
  276.              * does, so I guess it works for all joysticks/game pads. */
  277.             is_joystick = is_joystick ||
  278.                 ( h.kind == hid_collection &&
  279.                   page == HUP_GENERIC_DESKTOP &&
  280.                   ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
  281.             if( h.kind != hid_input )
  282.                 continue;
  283.             if( !is_joystick )
  284.                 continue;
  285.             interesting_hid = TRUE;
  286.             if( page == HUP_GENERIC_DESKTOP )
  287.             {
  288.                 switch( usage )
  289.                 {
  290.                 case HUG_X:
  291.                 case HUG_RX:
  292.                 case HUG_Y:
  293.                 case HUG_RY:
  294.                 case HUG_Z:
  295.                 case HUG_RZ:
  296.                 case HUG_SLIDER:
  297.                     if( *num_axes < _JS_MAX_AXES )
  298.                     {
  299.                         os->axes_usage[ *num_axes ] = usage;
  300.                         ( *num_axes )++;
  301.                     }
  302.                     break;
  303.                 case HUG_HAT_SWITCH:
  304.                     /* Allocate two axes for a hat */
  305.                     if( *num_axes + 1 < _JS_MAX_AXES )
  306.                     {
  307.                         os->axes_usage[ *num_axes ] = usage;
  308.                         (*num_axes)++;
  309.                         os->axes_usage[ *num_axes ] = usage;
  310.                         (*num_axes)++;
  311.                     }
  312.                     break;
  313.                 default:
  314.                     interesting_hid = FALSE;
  315.                     break;
  316.                 }
  317.             }
  318.             else if( page == HUP_BUTTON )
  319.             {
  320.                 interesting_hid = ( usage > 0 ) &&
  321.                     ( usage <= _JS_MAX_BUTTONS );
  322.                 if( interesting_hid && usage - 1 > *num_buttons )
  323.                     *num_buttons = usage - 1;
  324.             }
  325.             if( interesting_hid )
  326.             {
  327.                 h.next = os->hids;
  328.                 os->hids = calloc( 1, sizeof ( struct hid_item ) );
  329.                 *os->hids = h;
  330.             }
  331.         }
  332.         hid_end_parse( d );
  333.         return os->hids != NULL;
  334. }
  335. #    endif
  336. #endif
  337. /*
  338.  * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
  339.  * See "js.h" lines 80-178.
  340.  */
  341. typedef struct tagSFG_Joystick SFG_Joystick;
  342. struct tagSFG_Joystick
  343. {
  344. #if TARGET_HOST_MACINTOSH
  345. #define  ISP_NUM_AXIS    9
  346. #define  ISP_NUM_NEEDS  41
  347.     ISpElementReference isp_elem  [ ISP_NUM_NEEDS ];
  348.     ISpNeed             isp_needs [ ISP_NUM_NEEDS ];
  349. #endif
  350. #if TARGET_HOST_MAC_OSX
  351.     IOHIDDeviceInterface ** hidDev;
  352.     IOHIDElementCookie buttonCookies[41];
  353.     IOHIDElementCookie axisCookies[_JS_MAX_AXES];
  354.     long minReport[_JS_MAX_AXES],
  355.          maxReport[_JS_MAX_AXES];
  356. #endif
  357. #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
  358.     JOYCAPS     jsCaps;
  359.     JOYINFOEX   js;
  360.     UINT        js_id;
  361. #endif
  362. #if TARGET_HOST_POSIX_X11
  363. #   if defined(__FreeBSD__) || defined(__NetBSD__)
  364.        struct os_specific_s *os;
  365. #   endif
  366. #   ifdef JS_NEW
  367.        struct js_event     js;
  368.        int          tmp_buttons;
  369.        float        tmp_axes [ _JS_MAX_AXES ];
  370. #   else
  371.        struct JS_DATA_TYPE js;
  372. #   endif
  373.     char         fname [ 128 ];
  374.     int          fd;
  375. #endif
  376.     int          id;
  377.     GLboolean    error;
  378.     char         name [ 128 ];
  379.     int          num_axes;
  380.     int          num_buttons;
  381.     float dead_band[ _JS_MAX_AXES ];
  382.     float saturate [ _JS_MAX_AXES ];
  383.     float center   [ _JS_MAX_AXES ];
  384.     float max      [ _JS_MAX_AXES ];
  385.     float min      [ _JS_MAX_AXES ];
  386. };
  387. /*
  388.  * Functions associated with the "jsJoystick" class in PLIB
  389.  */
  390. #if TARGET_HOST_MAC_OSX
  391. #define K_NUM_DEVICES   32
  392. int numDevices;
  393. io_object_t ioDevices[K_NUM_DEVICES];
  394. static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
  395. static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
  396. static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
  397. /* callback for CFArrayApply */
  398. static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
  399. static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
  400. static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
  401. static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
  402. #endif
  403. /*
  404.  * The static joystick structure pointer
  405.  */
  406. #define MAX_NUM_JOYSTICKS  2
  407. static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
  408. /*
  409.  * Read the raw joystick data
  410.  */
  411. static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
  412. {
  413. #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
  414.     MMRESULT status;
  415. #else
  416.     int status;
  417. #endif
  418. #if defined(__FreeBSD__) || defined(__NetBSD__)
  419.     int len;
  420. #endif
  421.     int i;
  422.     /* Defaults */
  423.     if( buttons )
  424.         *buttons = 0;
  425.     if( axes )
  426.         for( i = 0; i < joy->num_axes; i++ )
  427.             axes[ i ] = 1500.0f;
  428.     if( joy->error )
  429.         return;
  430. #if TARGET_HOST_MACINTOSH
  431.     if ( buttons )
  432.     {
  433.         *buttons = 0;
  434.         for ( i = 0; i < joy->num_buttons; i++ )
  435.         {
  436.             UInt32 state;
  437.             int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
  438.             ISP_CHECK_ERR(err)
  439.             *buttons |= state << i;
  440.         }
  441.     }
  442.     if ( axes )
  443.     {
  444.         for ( i = 0; i < joy->num_axes; i++ )
  445.         {
  446.             UInt32 state;
  447.             int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
  448.             ISP_CHECK_ERR(err)
  449.             axes [i] = (float) state;
  450.         }
  451.     }
  452. #endif
  453. #if TARGET_HOST_MAC_OSX
  454.     if ( buttons != NULL )
  455.     {
  456.         *buttons = 0;
  457.         for ( i = 0; i < joy->num_buttons; i++ )
  458.         {
  459.             IOHIDEventStruct hidEvent;
  460.             (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
  461.             if ( hidEvent.value )
  462.                 *buttons |= 1 << i;
  463.         }
  464.     }
  465.     if ( axes != NULL )
  466.     {
  467.         for ( i = 0; i < joy->num_axes; i++ )
  468.         {
  469.             IOHIDEventStruct hidEvent;
  470.             (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
  471.             axes[i] = hidEvent.value;
  472.         }
  473.     }
  474. #endif
  475. #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
  476.     status = joyGetPosEx( joy->js_id, &joy->js );
  477.     if ( status != JOYERR_NOERROR )
  478.     {
  479.         joy->error = GL_TRUE;
  480.         return;
  481.     }
  482.     if ( buttons )
  483.         *buttons = joy->js.dwButtons;
  484.     if ( axes )
  485.     {
  486.         /*
  487.          * WARNING - Fall through case clauses!!
  488.          */
  489.         switch ( joy->num_axes )
  490.         {
  491.         case 8:
  492.             /* Generate two POV axes from the POV hat angle.
  493.              * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
  494.              *   hundredths of a degree, or 0xFFFF when idle.
  495.              */
  496.             if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
  497.             {
  498.               axes [ 6 ] = 0.0;
  499.               axes [ 7 ] = 0.0;
  500.             }
  501.             else
  502.             {
  503.               /* This is the contentious bit: how to convert angle to X/Y.
  504.                *    wk: I know of no define for PI that we could use here:
  505.                *    SG_PI would pull in sg, M_PI is undefined for MSVC
  506.                * But the accuracy of the value of PI is very unimportant at
  507.                * this point.
  508.                */
  509.               float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
  510.               float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
  511.               /* Convert to coordinates on a square so that North-East
  512.                * is (1,1) not (.7,.7), etc.
  513.                * s and c cannot both be zero so we won't divide by zero.
  514.                */
  515.               if ( fabs ( s ) < fabs ( c ) )
  516.               {
  517.                 axes [ 6 ] = ( c < 0.0 ) ? -s/c  : s/c ;
  518.                 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
  519.               }
  520.               else
  521.               {
  522.                 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
  523.                 axes [ 7 ] = ( s < 0.0 ) ? -c/s  : c/s ;
  524.               }
  525.             }
  526.         case 6: axes[5] = (float) joy->js.dwVpos;
  527.         case 5: axes[4] = (float) joy->js.dwUpos;
  528.         case 4: axes[3] = (float) joy->js.dwRpos;
  529.         case 3: axes[2] = (float) joy->js.dwZpos;
  530.         case 2: axes[1] = (float) joy->js.dwYpos;
  531.         case 1: axes[0] = (float) joy->js.dwXpos;
  532.         }
  533.     }
  534. #endif
  535. #if TARGET_HOST_POSIX_X11
  536. #    if defined(__FreeBSD__) || defined(__NetBSD__)
  537.     if ( joy->os->is_analog )
  538.     {
  539.         int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
  540.         if ( status != sizeof(joy->os->ajs) ) {
  541.             perror ( joy->os->fname );
  542.             joy->error = GL_TRUE;
  543.             return;
  544.         }
  545.         if ( buttons != NULL )
  546.             *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
  547.         if ( axes != NULL )
  548.         {
  549.             axes[0] = (float) joy->os->ajs.x;
  550.             axes[1] = (float) joy->os->ajs.y;
  551.         }
  552.         return;
  553.     }
  554. #        ifdef HAVE_USB_JS
  555.     while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
  556.     {
  557.         struct hid_item *h;
  558.         for  ( h = joy->os->hids; h; h = h->next )
  559.         {
  560.             int d = hid_get_data ( joy->os->hid_data_buf, h );
  561.             int page = HID_PAGE ( h->usage );
  562.             int usage = HID_USAGE ( h->usage );
  563.             if ( page == HUP_GENERIC_DESKTOP )
  564.             {
  565.                 int i;
  566.                 for ( i = 0; i < joy->num_axes; i++ )
  567.                     if (joy->os->axes_usage[i] == usage)
  568.                     {
  569.                         if (usage == HUG_HAT_SWITCH)
  570.                         {
  571.                             if (d < 0 || d > 8)
  572.                                 d = 0;  /* safety */
  573.                             joy->os->cache_axes[i] = (float)hatmap_x[d];
  574.                             joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
  575.                         }
  576.                         else
  577.                         {
  578.                             joy->os->cache_axes[i] = (float)d;
  579.                         }
  580.                         break;
  581.                     }
  582.             }
  583.             else if (page == HUP_BUTTON)
  584.             {
  585.                if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
  586.                {
  587.                    if (d)
  588.                        joy->os->cache_buttons |=  (1 << ( usage - 1 ));
  589.                    else
  590.                        joy->os->cache_buttons &= ~(1 << ( usage - 1 ));
  591.                }
  592.             }
  593.         }
  594.     }
  595. #if HAVE_ERRNO
  596.     if ( len < 0 && errno != EAGAIN )
  597. #else
  598.     if ( len < 0 )
  599. #endif
  600.     {
  601.         perror( joy->os->fname );
  602.         joy->error = 1;
  603.     }
  604.     if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
  605.     if ( axes    != NULL )
  606.         memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
  607. #        endif
  608. #    endif
  609. #    ifdef JS_NEW
  610.     while ( 1 )
  611.     {
  612.         status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
  613.         if ( status != sizeof( struct js_event ) )
  614.         {
  615. #if HAVE_ERRNO
  616.             if ( errno == EAGAIN )
  617.             {
  618.                 /* Use the old values */
  619.                 if ( buttons )
  620.                     *buttons = joy->tmp_buttons;
  621.                 if ( axes )
  622.                     memcpy( axes, joy->tmp_axes,
  623.                             sizeof( float ) * joy->num_axes );
  624.                 return;
  625.             }
  626. #endif
  627.             fgWarning ( "%s", joy->fname );
  628.             joy->error = GL_TRUE;
  629.             return;
  630.         }
  631.         switch ( joy->js.type & ~JS_EVENT_INIT )
  632.         {
  633.         case JS_EVENT_BUTTON:
  634.             if( joy->js.value == 0 ) /* clear the flag */
  635.                 joy->tmp_buttons &= ~( 1 << joy->js.number );
  636.             else
  637.                 joy->tmp_buttons |= ( 1 << joy->js.number );
  638.             break;
  639.         case JS_EVENT_AXIS:
  640.             if ( joy->js.number < joy->num_axes )
  641.             {
  642.                 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
  643.                 if( axes )
  644.                     memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
  645.             }
  646.             break;
  647.         default:
  648.             fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
  649.             /* use the old values */
  650.             if ( buttons != NULL ) *buttons = joy->tmp_buttons;
  651.             if ( axes    != NULL )
  652.                 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
  653.             return;
  654.         }
  655.         if( buttons )
  656.             *buttons = joy->tmp_buttons;
  657.     }
  658. #    else
  659.     status = read( joy->fd, &joy->js, JS_RETURN );
  660.     if ( status != JS_RETURN )
  661.     {
  662.         fgWarning( "%s", joy->fname );
  663.         joy->error = GL_TRUE;
  664.         return;
  665.     }
  666.     if ( buttons )
  667. #        if defined( __FreeBSD__ ) || defined( __NetBSD__ )
  668.         *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 );  /* XXX Should not be here -- BSD is handled earlier */
  669. #        else
  670.         *buttons = joy->js.buttons;
  671. #        endif
  672.     if ( axes )
  673.     {
  674.         axes[ 0 ] = (float) joy->js.x;
  675.         axes[ 1 ] = (float) joy->js.y;
  676.     }
  677. #    endif
  678. #endif
  679. }
  680. /*
  681.  * Correct the joystick axis data
  682.  */
  683. static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
  684. {
  685.     if( value < joy->center[ axis ] )
  686.     {
  687.         float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
  688.                                                        joy->min[ axis ] );
  689.         if( xx < -joy->saturate[ axis ] )
  690.             return -1.0f;
  691.         if( xx > -joy->dead_band [ axis ] )
  692.             return 0.0f;
  693.         xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
  694.                                                  joy->dead_band[ axis ] );
  695.         return ( xx < -1.0f ) ? -1.0f : xx;
  696.     }
  697.     else
  698.     {
  699.         float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
  700.                                                         joy->center[ axis ] );
  701.         if( xx > joy->saturate[ axis ] )
  702.             return 1.0f;
  703.         if( xx < joy->dead_band[ axis ] )
  704.             return 0.0f;
  705.         xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
  706.                                                  joy->dead_band[ axis ] );
  707.         return ( xx > 1.0f ) ? 1.0f : xx;
  708.     }
  709. }
  710. /*
  711.  * Read the corrected joystick data
  712.  */
  713. static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
  714. {
  715.     float raw_axes[ _JS_MAX_AXES ];
  716.     int  i;
  717.     if( joy->error )
  718.     {
  719.         if( buttons )
  720.             *buttons = 0;
  721.         if( axes )
  722.             for ( i=0; i<joy->num_axes; i++ )
  723.                 axes[ i ] = 0.0f;
  724.     }
  725.     fghJoystickRawRead( joy, buttons, raw_axes );
  726.     if( axes )
  727.         for( i=0; i<joy->num_axes; i++ )
  728.             axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
  729. }
  730. /*
  731.  * Happy happy happy joy joy joy (happy new year toudi :D)
  732.  */
  733. #if TARGET_HOST_MAC_OSX
  734. /** open the IOKit connection, enumerate all the HID devices, add their
  735. interface references to the static array. We then use the array index
  736. as the device number when we come to open() the joystick. */
  737. static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
  738. {
  739.     CFMutableDictionaryRef hidMatch = NULL;
  740.     IOReturn rv = kIOReturnSuccess;
  741.     io_iterator_t hidIterator;
  742.     io_object_t ioDev;
  743.     /* build a dictionary matching HID devices */
  744.     hidMatch = IOServiceMatching(kIOHIDDeviceKey);
  745.     rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
  746.     if (rv != kIOReturnSuccess || !hidIterator) {
  747.       fgWarning( "no joystick (HID) devices found" );
  748.       return;
  749.     }
  750.     /* iterate */
  751.     while ((ioDev = IOIteratorNext(hidIterator))) {
  752.         /* filter out keyboard and mouse devices */
  753.         CFDictionaryRef properties = getCFProperties(ioDev);
  754.         long usage, page;
  755.         CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
  756.         CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
  757.         CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
  758.         CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
  759.         /* keep only joystick devices */
  760.         if ( ( page == kHIDPage_GenericDesktop ) && (
  761.                             (usage == kHIDUsage_GD_Joystick)
  762.                          || (usage == kHIDUsage_GD_GamePad)
  763.                          || (usage == kHIDUsage_GD_MultiAxisController)
  764.                          || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
  765.             /* add it to the array */
  766.             ioDevices[numDevices++] = ioDev;
  767.     }
  768.     IOObjectRelease(hidIterator);
  769. }
  770. static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
  771. {
  772.     IOReturn rv;
  773.     CFMutableDictionaryRef cfProperties;
  774. #if 0
  775.     /* comment copied from darwin/SDL_sysjoystick.c */
  776.     /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
  777.      * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
  778.      */
  779.     io_registry_entry_t parent1, parent2;
  780.     rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
  781.     if (rv != kIOReturnSuccess) {
  782.         fgWarning ( "error getting device entry parent");
  783.         return NULL;
  784.     }
  785.     rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
  786.     if (rv != kIOReturnSuccess) {
  787.         fgWarning ( "error getting device entry parent 2");
  788.         return NULL;
  789.     }
  790. #endif
  791.     rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
  792.         &cfProperties, kCFAllocatorDefault, kNilOptions);
  793.     if (rv != kIOReturnSuccess || !cfProperties) {
  794.         fgWarning ( "error getting device properties");
  795.         return NULL;
  796.     }
  797.     return cfProperties;
  798. }
  799. static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
  800. {
  801.       if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
  802.             fgError ( "%s", "element enumerator passed non-dictionary value");
  803.             return;
  804.     }
  805.       static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
  806. }
  807. /** element enumerator function : pass NULL for top-level*/
  808. static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
  809. {
  810.       FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
  811.                                     "Joystick element type mismatch",
  812.                                     "fghJoystickEnumerateElements" );
  813.       CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
  814.       CFArrayApplyFunction((CFArrayRef) element, range,
  815.             &fghJoystickElementEnumerator, joy );
  816. }
  817. static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
  818. {
  819.     long cookie, lmin, lmax;
  820.     int index = joy->num_axes++;
  821.     CFNumberGetValue ((CFNumberRef)
  822.         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
  823.         kCFNumberLongType, &cookie);
  824.     axisCookies[index] = (IOHIDElementCookie) cookie;
  825.     CFNumberGetValue ((CFNumberRef)
  826.         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
  827.         kCFNumberLongType, &lmin);
  828.     CFNumberGetValue ((CFNumberRef)
  829.         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
  830.         kCFNumberLongType, &lmax);
  831.     joy->min[index] = lmin;
  832.     joy->max[index] = lmax;
  833.     joy->dead_band[index] = 0.0;
  834.     joy->saturate[index] = 1.0;
  835.     joy->center[index] = (lmax + lmin) * 0.5;
  836. }
  837. static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
  838. {
  839.     long cookie;
  840.     CFNumberGetValue ((CFNumberRef)
  841.             CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
  842.             kCFNumberLongType, &cookie);
  843.     joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
  844.     /* anything else for buttons? */
  845. }
  846. static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
  847. {
  848.     /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
  849.     /* do we map hats to axes or buttons? */
  850. }
  851. #endif
  852. #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
  853. /* Inspired by
  854.    http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
  855.  */
  856. #    if FREEGLUT_LIB_PRAGMAS
  857. #        pragma comment (lib, "advapi32.lib")
  858. #    endif
  859. static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
  860. {
  861.     char buffer [ 256 ];
  862.     char OEMKey [ 256 ];
  863.     HKEY  hKey;
  864.     DWORD dwcb;
  865.     LONG  lr;
  866.     if ( joy->error )
  867.         return 0;
  868.     /* Open .. MediaResourcesCurrentJoystickSettings */
  869.     _snprintf ( buffer, sizeof(buffer), "%s\%s\%s",
  870.                 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
  871.                 REGSTR_KEY_JOYCURR );
  872.     lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
  873.     if ( lr != ERROR_SUCCESS ) return 0;
  874.     /* Get OEM Key name */
  875.     dwcb = sizeof(OEMKey);
  876.     /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
  877.     _snprintf ( buffer, sizeof(buffer), "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
  878.     lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
  879.     RegCloseKey ( hKey );
  880.     if ( lr != ERROR_SUCCESS ) return 0;
  881.     /* Open OEM Key from ...MediaProperties */
  882.     _snprintf ( buffer, sizeof(buffer), "%s\%s", REGSTR_PATH_JOYOEM, OEMKey );
  883.     lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
  884.     if ( lr != ERROR_SUCCESS ) return 0;
  885.     /* Get OEM Name */
  886.     dwcb = buf_sz;
  887.     lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
  888.                              &dwcb );
  889.     RegCloseKey ( hKey );
  890.     if ( lr != ERROR_SUCCESS ) return 0;
  891.     return 1;
  892. }
  893. #endif
  894. static void fghJoystickOpen( SFG_Joystick* joy )
  895. {
  896.     int i = 0;
  897. #if TARGET_HOST_MACINTOSH
  898.     OSStatus err;
  899. #endif
  900. #if TARGET_HOST_MAC_OSX
  901.         IOReturn rv;
  902.         SInt32 score;
  903.         IOCFPlugInInterface **plugin;
  904.         HRESULT pluginResult;
  905.         CFDictionaryRef props;
  906.     CFTypeRef topLevelElement;
  907. #endif
  908. #if TARGET_HOST_POSIX_X11
  909. #    if defined( __FreeBSD__ ) || defined( __NetBSD__ )
  910.        char *cp;
  911. #    endif
  912. #    ifdef JS_NEW
  913.        unsigned char u;
  914. #    else
  915. #      if defined( __linux__ ) || TARGET_HOST_SOLARIS
  916.          int counter = 0;
  917. #      endif
  918. #    endif
  919. #endif
  920.     /* Silence gcc, the correct #ifdefs would be too fragile... */
  921.     (void)i;
  922.     /*
  923.      * Default values (for no joystick -- each conditional will reset the
  924.      * error flag)
  925.      */
  926.     joy->error = TRUE;
  927.     joy->num_axes = joy->num_buttons = 0;
  928.     joy->name[ 0 ] = '';
  929. #if TARGET_HOST_MACINTOSH
  930.     /* XXX FIXME: get joystick name in Mac */
  931.     err = ISpStartup( );
  932.     if( err == noErr )
  933.     {
  934. #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
  935.         joy->error = GL_TRUE;
  936.         /* initialize the needs structure */
  937.         ISpNeed temp_isp_needs[ isp_num_needs ] =
  938.         {
  939.           { "pX-Axis",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
  940.           { "pY-Axis",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
  941.           { "pZ-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
  942.           { "pR-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
  943.           { "pAxis   4",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
  944.           { "pAxis   5",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
  945.           { "pAxis   6",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
  946.           { "pAxis   7",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
  947.           { "pAxis   8",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
  948.           { "pButton 0",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  949.           { "pButton 1",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  950.           { "pButton 2",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  951.           { "pButton 3",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  952.           { "pButton 4",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  953.           { "pButton 5",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  954.           { "pButton 6",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  955.           { "pButton 7",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  956.           { "pButton 8",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  957.           { "pButton 9",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  958.           { "pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  959.           { "pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  960.           { "pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  961.           { "pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  962.           { "pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  963.           { "pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  964.           { "pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  965.           { "pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  966.           { "pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  967.           { "pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  968.           { "pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  969.           { "pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  970.           { "pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  971.           { "pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  972.           { "pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  973.           { "pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  974.           { "pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  975.           { "pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  976.           { "pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  977.           { "pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  978.           { "pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  979.           { "pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
  980.         };
  981.         memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
  982.         /* next two calls allow keyboard and mouse to emulate other input
  983.          * devices (gamepads, joysticks, etc)
  984.          */
  985.         /*
  986.           err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
  987.           ISP_CHECK_ERR(err)
  988.           err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
  989.           ISP_CHECK_ERR(err)
  990.         */
  991.         err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
  992.                                               joy->isp_needs, joy->isp_elem,
  993.                                               0 );
  994.         ISP_CHECK_ERR( err )
  995.         err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
  996.                        'freeglut', nil, 0, 128, 0 );
  997.         ISP_CHECK_ERR( err )
  998.         joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
  999.         joy->num_axes    = joy->isp_num_axis;
  1000.         for( i = 0; i < joy->num_axes; i++ )
  1001.         {
  1002.             joy->dead_band[ i ] = 0;
  1003.             joy->saturate [ i ] = 1;
  1004.             joy->center   [ i ] = kISpAxisMiddle;
  1005.             joy->max      [ i ] = kISpAxisMaximum;
  1006.             joy->min      [ i ] = kISpAxisMinimum;
  1007.         }
  1008.         joy->error = GL_FALSE;
  1009.     }
  1010.     else
  1011.         joy->num_buttons = joy->num_axes = 0;
  1012. #endif
  1013. #if TARGET_HOST_MAC_OSX
  1014.     if( joy->id >= numDevices )
  1015.     {
  1016.         fgWarning( "device index out of range in fgJoystickOpen()" );
  1017.         return;
  1018.     }
  1019.     /* create device interface */
  1020.     rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
  1021.                                             kIOHIDDeviceUserClientTypeID,
  1022.                                             kIOCFPlugInInterfaceID,
  1023.                                             &plugin, &score );
  1024.     if( rv != kIOReturnSuccess )
  1025.     {
  1026.         fgWarning( "error creating plugin for io device" );
  1027.         return;
  1028.     }
  1029.     pluginResult = ( *plugin )->QueryInterface(
  1030.         plugin,
  1031.         CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
  1032.         &( LPVOID )joy->hidDev
  1033.     );
  1034.     if( pluginResult != S_OK )
  1035.         fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
  1036.     ( *plugin )->Release( plugin ); /* don't leak a ref */
  1037.     if( joy->hidDev == NULL )
  1038.         return;
  1039.     /* store the interface in this instance */
  1040.     rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
  1041.     if( rv != kIOReturnSuccess )
  1042.     {
  1043.         fgWarning( "error opening device interface");
  1044.         return;
  1045.     }
  1046.     props = getCFProperties( ioDevices[ joy->id ] );
  1047.     /* recursively enumerate all the bits */
  1048.     CFTypeRef topLevelElement =
  1049.         CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
  1050.     enumerateElements( topLevelElement );
  1051.     CFRelease( props );
  1052. #endif
  1053. #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
  1054.     joy->js.dwFlags = JOY_RETURNALL;
  1055.     joy->js.dwSize  = sizeof( joy->js );
  1056.     memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
  1057.     joy->error =
  1058.         ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
  1059.           JOYERR_NOERROR );
  1060.     if( joy->jsCaps.wNumAxes == 0 )
  1061.     {
  1062.         joy->num_axes = 0;
  1063.         joy->error = GL_TRUE;
  1064.     }
  1065.     else
  1066.     {
  1067.         /* Device name from jsCaps is often "Microsoft PC-joystick driver",
  1068.          * at least for USB.  Try to get the real name from the registry.
  1069.          */
  1070.         if ( ! fghJoystickGetOEMProductName( joy, joy->name,
  1071.                                              sizeof( joy->name ) ) )
  1072.         {
  1073.             fgWarning( "JS: Failed to read joystick name from registry" );
  1074.             strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
  1075.         }
  1076.         /* Windows joystick drivers may provide any combination of
  1077.          * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
  1078.          */
  1079.         if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
  1080.         {
  1081.             joy->num_axes = _JS_MAX_AXES;
  1082.             joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0;  /* POV Y */
  1083.             joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0;  /* POV X */
  1084.         }
  1085.         else
  1086.             joy->num_axes = 6;
  1087.         joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
  1088.         joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
  1089.         joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
  1090.         joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
  1091.         joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
  1092.         joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
  1093.         joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
  1094.         joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
  1095.         joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
  1096.         joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
  1097.         joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
  1098.         joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
  1099.     }
  1100.     /* Guess all the rest judging on the axes extremals */
  1101.     for( i = 0; i < joy->num_axes; i++ )
  1102.     {
  1103.         joy->center   [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
  1104.         joy->dead_band[ i ] = 0.0f;
  1105.         joy->saturate [ i ] = 1.0f;
  1106.     }
  1107. #endif
  1108. #if TARGET_HOST_POSIX_X11
  1109. #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
  1110.     for( i = 0; i < _JS_MAX_AXES; i++ )
  1111.         joy->os->cache_axes[ i ] = 0.0f;
  1112.     joy->os->cache_buttons = 0;
  1113.     joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
  1114. #if HAVE_ERRNO
  1115.     if( joy->os->fd < 0 && errno == EACCES )
  1116.         fgWarning ( "%s exists but is not readable by you", joy->os->fname );
  1117. #endif
  1118.     joy->error =( joy->os->fd < 0 );
  1119.     if( joy->error )
  1120.         return;
  1121.     joy->num_axes = 0;
  1122.     joy->num_buttons = 0;
  1123.     if( joy->os->is_analog )
  1124.     {
  1125.         FILE *joyfile;
  1126.         char joyfname[ 1024 ];
  1127.         int noargs, in_no_axes;
  1128.         float axes [ _JS_MAX_AXES ];
  1129.         int buttons[ _JS_MAX_AXES ];
  1130.         joy->num_axes    =  2;
  1131.         joy->num_buttons = 32;
  1132.         fghJoystickRawRead( joy, buttons, axes );
  1133.         joy->error = axes[ 0 ] < -1000000000.0f;
  1134.         if( joy->error )
  1135.             return;
  1136.         snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
  1137.         joyfile = fopen( joyfname, "r" );
  1138.         joy->error =( joyfile == NULL );
  1139.         if( joy->error )
  1140.             return;
  1141.         noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
  1142.                          &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
  1143.                          &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
  1144.         joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
  1145.         fclose( joyfile );
  1146.         if( joy->error )
  1147.             return;
  1148.         for( i = 0; i < _JS_MAX_AXES; i++ )
  1149.         {
  1150.             joy->dead_band[ i ] = 0.0f;
  1151.             joy->saturate [ i ] = 1.0f;
  1152.         }
  1153.         return;    /* End of analog code */
  1154.     }
  1155. #    ifdef HAVE_USB_JS
  1156.     if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
  1157.                                     &joy->num_buttons ) )
  1158.     {
  1159.         close( joy->os->fd );
  1160.         joy->error = GL_TRUE;
  1161.         return;
  1162.     }
  1163.     cp = strrchr( joy->os->fname, '/' );
  1164.     if( cp )
  1165.     {
  1166.         if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
  1167.             0 )
  1168.             strcpy( joy->name, &cp[1] );
  1169.     }
  1170.     if( joy->num_axes > _JS_MAX_AXES )
  1171.         joy->num_axes = _JS_MAX_AXES;
  1172.     for( i = 0; i < _JS_MAX_AXES; i++ )
  1173.     {
  1174.         /* We really should get this from the HID, but that data seems
  1175.          * to be quite unreliable for analog-to-USB converters. Punt for
  1176.          * now.
  1177.          */
  1178.         if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
  1179.         {
  1180.             joy->max   [ i ] = 1.0f;
  1181.             joy->center[ i ] = 0.0f;
  1182.             joy->min   [ i ] = -1.0f;
  1183.         }
  1184.         else
  1185.         {
  1186.             joy->max   [ i ] = 255.0f;
  1187.             joy->center[ i ] = 127.0f;
  1188.             joy->min   [ i ] = 0.0f;
  1189.         }
  1190.         joy->dead_band[ i ] = 0.0f;
  1191.         joy->saturate[ i ] = 1.0f;
  1192.     }
  1193. #    endif
  1194. #endif
  1195. #if defined( __linux__ ) || TARGET_HOST_SOLARIS
  1196.     /* Default for older Linux systems. */
  1197.     joy->num_axes    =  2;
  1198.     joy->num_buttons = 32;
  1199. #    ifdef JS_NEW
  1200.     for( i = 0; i < _JS_MAX_AXES; i++ )
  1201.         joy->tmp_axes[ i ] = 0.0f;
  1202.     joy->tmp_buttons = 0;
  1203. #    endif
  1204.     joy->fd = open( joy->fname, O_RDONLY );
  1205.     joy->error =( joy->fd < 0 );
  1206.     if( joy->error )
  1207.         return;
  1208.     /* Set the correct number of axes for the linux driver */
  1209. #    ifdef JS_NEW
  1210.     /* Melchior Franz's fixes for big-endian Linuxes since writing
  1211.      *  to the upper byte of an uninitialized word doesn't work.
  1212.      *  9 April 2003
  1213.      */
  1214.     ioctl( joy->fd, JSIOCGAXES, &u );
  1215.     joy->num_axes = u;
  1216.     ioctl( joy->fd, JSIOCGBUTTONS, &u );
  1217.     joy->num_buttons = u;
  1218.     ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
  1219.     fcntl( joy->fd, F_SETFL, O_NONBLOCK );
  1220. #    endif
  1221.     /*
  1222.      * The Linux driver seems to return 512 for all axes
  1223.      * when no stick is present - but there is a chance
  1224.      * that could happen by accident - so it's gotta happen
  1225.      * on both axes for at least 100 attempts.
  1226.      *
  1227.      * PWO: shouldn't be that done somehow wiser on the kernel level?
  1228.      */
  1229. #    ifndef JS_NEW
  1230.     counter = 0;
  1231.     do
  1232.     {
  1233.         fghJoystickRawRead( joy, NULL, joy->center );
  1234.         counter++;
  1235.     } while( !joy->error &&
  1236.              counter < 100 &&
  1237.              joy->center[ 0 ] == 512.0f &&
  1238.              joy->center[ 1 ] == 512.0f );
  1239.     if ( counter >= 100 )
  1240.         joy->error = GL_TRUE;
  1241. #    endif
  1242.     for( i = 0; i < _JS_MAX_AXES; i++ )
  1243.     {
  1244. #    ifdef JS_NEW
  1245.         joy->max   [ i ] =  32767.0f;
  1246.         joy->center[ i ] =      0.0f;
  1247.         joy->min   [ i ] = -32767.0f;
  1248. #    else
  1249.         joy->max[ i ] = joy->center[ i ] * 2.0f;
  1250.         joy->min[ i ] = 0.0f;
  1251. #    endif
  1252.         joy->dead_band[ i ] = 0.0f;
  1253.         joy->saturate [ i ] = 1.0f;
  1254.     }
  1255. #endif
  1256. #endif
  1257. }
  1258. /*
  1259.  * This function replaces the constructor method in the JS library.
  1260.  */
  1261. static void fghJoystickInit( int ident )
  1262. {
  1263.     if( ident >= MAX_NUM_JOYSTICKS )
  1264.       fgError( "Too large a joystick number: %d", ident );
  1265.     if( fgJoystick[ ident ] )
  1266.         fgError( "illegal attempt to initialize joystick device again" );
  1267.     fgJoystick[ ident ] =
  1268.         ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
  1269.     /* Set defaults */
  1270.     fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
  1271.     fgJoystick[ ident ]->error = GL_TRUE;
  1272. #if TARGET_HOST_MACINTOSH
  1273.     fgJoystick[ ident ]->id = ident;
  1274.     snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident ); /* FIXME */
  1275.     fgJoystick[ ident ]->error = GL_FALSE;
  1276. #endif
  1277. #if TARGET_HOST_MAC_OSX
  1278.     fgJoystick[ ident ]->id = ident;
  1279.     fgJoystick[ ident ]->error = GL_FALSE;
  1280.     fgJoystick[ ident ]->num_axes = 0;
  1281.     fgJoystick[ ident ]->num_buttons = 0;
  1282.     if( numDevices < 0 )
  1283.     {
  1284.         /* do first-time init (since we can't over-ride jsInit, hmm */
  1285.         numDevices = 0;
  1286.         mach_port_t masterPort;
  1287.         IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
  1288.         if( rv != kIOReturnSuccess )
  1289.         {
  1290.             fgWarning( "error getting master Mach port" );
  1291.             return;
  1292.         }
  1293.         fghJoystickFindDevices( masterPort );
  1294.     }
  1295.     if ( ident >= numDevices )
  1296.     {
  1297.         fgJoystick[ ident ]->error = GL_TRUE;
  1298.         return;
  1299.     }
  1300.     /* get the name now too */
  1301.     CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
  1302.     CFTypeRef ref = CFDictionaryGetValue( properties,
  1303.                                           CFSTR( kIOHIDProductKey ) );
  1304.     if (!ref)
  1305.         ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
  1306.     if( !ref ||
  1307.         !CFStringGetCString( ( CFStringRef )ref, name, 128,
  1308.                              CFStringGetSystemEncoding( ) ) )
  1309.     {
  1310.         fgWarning( "error getting device name" );
  1311.         name[ 0 ] = '';
  1312.     }
  1313. #endif
  1314. #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
  1315.     switch( ident )
  1316.     {
  1317.     case 0:
  1318.         fgJoystick[ ident ]->js_id = JOYSTICKID1;
  1319.         fgJoystick[ ident ]->error = GL_FALSE;
  1320.         break;
  1321.     case 1:
  1322.         fgJoystick[ ident ]->js_id = JOYSTICKID2;
  1323.         fgJoystick[ ident ]->error = GL_FALSE;
  1324.         break;
  1325.     default:
  1326.         fgJoystick[ ident ]->num_axes = 0;
  1327.         fgJoystick[ ident ]->error = GL_TRUE;
  1328.         return;
  1329.     }
  1330. #endif
  1331. #if TARGET_HOST_POSIX_X11
  1332. #    if defined( __FreeBSD__ ) || defined( __NetBSD__ )
  1333.     fgJoystick[ ident ]->id = ident;
  1334.     fgJoystick[ ident ]->error = GL_FALSE;
  1335.     fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
  1336.     memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
  1337.     if( ident < USB_IDENT_OFFSET )
  1338.         fgJoystick[ ident ]->os->is_analog = 1;
  1339.     if( fgJoystick[ ident ]->os->is_analog )
  1340.         snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", AJSDEV, ident );
  1341.     else
  1342.         snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", UHIDDEV,
  1343.                  ident - USB_IDENT_OFFSET );
  1344. #    elif defined( __linux__ )
  1345.     fgJoystick[ ident ]->id = ident;
  1346.     fgJoystick[ ident ]->error = GL_FALSE;
  1347.     snprintf( fgJoystick[ident]->fname, sizeof(fgJoystick[ident]->fname), "/dev/input/js%d", ident );
  1348.     if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
  1349.         snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident );
  1350. #    endif
  1351. #endif
  1352.     fghJoystickOpen( fgJoystick[ ident  ] );
  1353. }
  1354. /*
  1355.  * Try initializing all the joysticks (well, both of them)
  1356.  */
  1357. void fgInitialiseJoysticks ( void )
  1358. {
  1359.     if( !fgState.JoysticksInitialised )
  1360.     {
  1361.         int ident ;
  1362.         for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
  1363.             fghJoystickInit( ident );
  1364.         fgState.JoysticksInitialised = GL_TRUE;
  1365.     }
  1366. }
  1367. /*
  1368.  *
  1369.  */
  1370. void fgJoystickClose( void )
  1371. {
  1372.     int ident ;
  1373.     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
  1374.     {
  1375.         if( fgJoystick[ ident ] )
  1376.         {
  1377. #if TARGET_HOST_MACINTOSH
  1378.             ISpSuspend( );
  1379.             ISpStop( );
  1380.             ISpShutdown( );
  1381. #endif
  1382. #if TARGET_HOST_MAC_OSX
  1383.             ( *( fgJoystick[ ident ]->hidDev ) )->
  1384.                 close( fgJoystick[ ident ]->hidDev );
  1385. #endif
  1386. #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
  1387.             /* Do nothing special */
  1388. #endif
  1389. #if TARGET_HOST_POSIX_X11
  1390. #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
  1391.             if( fgJoystick[ident]->os )
  1392.             {
  1393.                 if( ! fgJoystick[ ident ]->error )
  1394.                     close( fgJoystick[ ident ]->os->fd );
  1395. #ifdef HAVE_USB_JS
  1396.                 if( fgJoystick[ ident ]->os->hids )
  1397.                     free (fgJoystick[ ident ]->os->hids);
  1398.                 if( fgJoystick[ ident ]->os->hid_data_buf )
  1399.                     free( fgJoystick[ ident ]->os->hid_data_buf );
  1400. #endif
  1401.                 free( fgJoystick[ident]->os );
  1402.             }
  1403. #endif
  1404.             if( ! fgJoystick[ident]->error )
  1405.                 close( fgJoystick[ ident ]->fd );
  1406. #endif
  1407.             free( fgJoystick[ ident ] );
  1408.             fgJoystick[ ident ] = NULL;
  1409.             /* show joystick has been deinitialized */
  1410.         }
  1411.     }
  1412. }
  1413. /*
  1414.  * Polls the joystick and executes the joystick callback hooked to the
  1415.  * window specified in the function's parameter:
  1416.  */
  1417. void fgJoystickPollWindow( SFG_Window* window )
  1418. {
  1419.     float axes[ _JS_MAX_AXES ];
  1420.     int buttons;
  1421.     int ident;
  1422.     freeglut_return_if_fail( window );
  1423.     freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
  1424.     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
  1425.     {
  1426.         if( fgJoystick[ident] )
  1427.         {
  1428.             fghJoystickRead( fgJoystick[ident], &buttons, axes );
  1429.             if( !fgJoystick[ident]->error )
  1430.                 INVOKE_WCB( *window, Joystick,
  1431.                             ( buttons,
  1432.                               (int) ( axes[ 0 ] * 1000.0f ),
  1433.                               (int) ( axes[ 1 ] * 1000.0f ),
  1434.                               (int) ( axes[ 2 ] * 1000.0f ) )
  1435.                 );
  1436.         }
  1437.     }
  1438. }
  1439. /*
  1440.  * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
  1441.  */
  1442. int fgJoystickDetect( void )
  1443. {
  1444.     int ident;
  1445.     fgInitialiseJoysticks ();
  1446.     if ( !fgState.JoysticksInitialised )
  1447.         return 0;
  1448.     for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
  1449.         if( fgJoystick[ident] && !fgJoystick[ident]->error )
  1450.             return 1;
  1451.     return 0;
  1452. }
  1453. /*
  1454.  * Joystick information functions
  1455.  */
  1456. int  glutJoystickGetNumAxes( int ident )
  1457. {
  1458.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
  1459.     return fgJoystick[ ident ]->num_axes;
  1460. }
  1461. int  glutJoystickGetNumButtons( int ident )
  1462. {
  1463.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
  1464.     return fgJoystick[ ident ]->num_buttons;
  1465. }
  1466. int  glutJoystickNotWorking( int ident )
  1467. {
  1468.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
  1469.     return fgJoystick[ ident ]->error;
  1470. }
  1471. float glutJoystickGetDeadBand( int ident, int axis )
  1472. {
  1473.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
  1474.     return fgJoystick[ ident ]->dead_band [ axis ];
  1475. }
  1476. void  glutJoystickSetDeadBand( int ident, int axis, float db )
  1477. {
  1478.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
  1479.     fgJoystick[ ident ]->dead_band[ axis ] = db;
  1480. }
  1481. float glutJoystickGetSaturation( int ident, int axis )
  1482. {
  1483.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
  1484.     return fgJoystick[ ident ]->saturate[ axis ];
  1485. }
  1486. void  glutJoystickSetSaturation( int ident, int axis, float st )
  1487. {
  1488.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
  1489.     fgJoystick[ ident ]->saturate [ axis ] = st;
  1490. }
  1491. void glutJoystickSetMinRange( int ident, float *axes )
  1492. {
  1493.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
  1494.     memcpy( fgJoystick[ ident ]->min, axes,
  1495.             fgJoystick[ ident ]->num_axes * sizeof( float ) );
  1496. }
  1497. void glutJoystickSetMaxRange( int ident, float *axes )
  1498. {
  1499.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
  1500.     memcpy( fgJoystick[ ident ]->max, axes,
  1501.             fgJoystick[ ident ]->num_axes * sizeof( float ) );
  1502. }
  1503. void glutJoystickSetCenter( int ident, float *axes )
  1504. {
  1505.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
  1506.     memcpy( fgJoystick[ ident ]->center, axes,
  1507.             fgJoystick[ ident ]->num_axes * sizeof( float ) );
  1508. }
  1509. void glutJoystickGetMinRange( int ident, float *axes )
  1510. {
  1511.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
  1512.     memcpy( axes, fgJoystick[ ident ]->min,
  1513.             fgJoystick[ ident ]->num_axes * sizeof( float ) );
  1514. }
  1515. void glutJoystickGetMaxRange( int ident, float *axes )
  1516. {
  1517.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
  1518.     memcpy( axes, fgJoystick[ ident ]->max,
  1519.             fgJoystick[ ident ]->num_axes * sizeof( float ) );
  1520. }
  1521. void glutJoystickGetCenter( int ident, float *axes )
  1522. {
  1523.     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
  1524.     memcpy( axes, fgJoystick[ ident ]->center,
  1525.             fgJoystick[ ident ]->num_axes * sizeof( float ) );
  1526. }
  1527. /*** END OF FILE ***/