Adding further comments to the removal of the "atexit" call from the Windows build...
[freeglut] / src / freeglut_joystick.c
index a61a708..f48dc3f 100644 (file)
  *  Many thanks for Steve Baker for permission to pull from that library.
  */
 
-#if defined( __FreeBSD__ ) || defined( __NetBSD__ )
-#    include <sys/param.h>
-#endif
-
-#ifdef HAVE_CONFIG_H
-#    include "config.h"
-#endif
-
 #include <GL/freeglut.h>
 #include "freeglut_internal.h"
+#ifdef HAVE_SYS_PARAM_H
+#    include <sys/param.h>
+#endif
 
 /*
  * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
  * interspersed
  */
-#define _JS_MAX_BUTTONS 32
 
+/* XXX It might be better to poll the operating system for the numbers of buttons and
+ * XXX axes and then dynamically allocate the arrays.
+ */
+#define _JS_MAX_BUTTONS 32
 
 #if TARGET_HOST_MACINTOSH
 #    define _JS_MAX_AXES  9
 #    include <IOKit/hid/IOHIDLib.h>
 #endif
 
-#if TARGET_HOST_WIN32
+#if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
 #    define _JS_MAX_AXES  8
 #    include <windows.h>
 #    include <mmsystem.h>
-#    include <string.h>
 #    include <regstr.h>
 
 #endif
 
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
 #    define _JS_MAX_AXES 16
-#    if defined(__FreeBSD__) || defined(__NetBSD__)
+#    ifdef HAVE_SYS_IOCTL_H
+#        include <sys/ioctl.h>
+#    endif
+#    ifdef HAVE_FCNTL_H
+#        include <fcntl.h>
+#    endif
+#    ifdef HAVE_ERRNO_H
+#        include <errno.h>
+#        include <string.h>
+#    endif
+#    if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
 /* XXX The below hack is done until freeglut's autoconf is updated. */
 #        define HAVE_USB_JS    1
 
-#        include <sys/ioctl.h>
-#        if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+#        if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 #            include <sys/joystick.h>
 #        else
 /*
 #        define JS_RETURN (sizeof(struct JS_DATA_TYPE))
 #    endif
 
-#    include <unistd.h>
-#    include <fcntl.h>
-#    include <errno.h>
-
 #    if defined(__linux__)
-#        include <sys/ioctl.h>
 #        include <linux/joystick.h>
 
 /* check the joystick driver version */
 
 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
 
-#if defined(__NetBSD__) || defined(__FreeBSD__)
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 
 #    ifdef HAVE_USB_JS
 #        if defined(__NetBSD__)
 #            else
 #                include <usb.h>
 #            endif
-#        elif defined(__FreeBSD__)
-#            if __FreeBSD_version < 500000
-#                include <libusbhid.h>
-#            else
-/* XXX The below hack is done until freeglut's autoconf is updated. */
-#                define HAVE_USBHID_H 1
+#        elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#            ifdef HAVE_USBHID_H
 #                include <usbhid.h>
+#            else
+#                include <libusbhid.h>
 #            endif
 #        endif
-#        include <dev/usb/usb.h>
+#        include <legacy/dev/usb/usb.h>
 #        include <dev/usb/usbhid.h>
 
 /* Compatibility with older usb.h revisions */
@@ -232,19 +231,22 @@ static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
   static int protection_warned = 0;
 
   for (i = 0; i < 16; i++) {
-    sprintf(buf, "%s%d", USBDEV, i);
+    snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
     f = open(buf, O_RDONLY);
     if (f >= 0) {
       cp = fghJoystickWalkUSBdev(f, name, out, outlen);
       close(f);
       if (cp)
         return 1;
-    } else if (errno == EACCES) {
+    }
+#ifdef HAVE_ERRNO_H
+    else if (errno == EACCES) {
       if (!protection_warned) {
         fgWarning ( "Can't open %s for read!", buf );
         protection_warned = 1;
       }
     }
+#endif
   }
   return 0;
 }
@@ -262,7 +264,11 @@ static int fghJoystickInitializeHID(struct os_specific_s *os,
 
     if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
     {
+#ifdef HAVE_ERRNO_H
         fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
+#else
+        fgWarning ( "error: %s", os->fname );
+#endif
         return FALSE;
     }
 
@@ -272,7 +278,11 @@ static int fghJoystickInitializeHID(struct os_specific_s *os,
         if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
         {
             /*** XXX {report_id} may not be the right variable? ***/
+#ifdef HAVE_ERRNO_H
             fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
+#else
+            fgWarning ( "error: %s%d", UHIDDEV, report_id );
+#endif
             return FALSE;
         }
 
@@ -387,15 +397,15 @@ struct tagSFG_Joystick
          maxReport[_JS_MAX_AXES];
 #endif
 
-#if TARGET_HOST_WIN32
+#if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
     JOYCAPS     jsCaps;
     JOYINFOEX   js;
     UINT        js_id;
 #endif
 
 
-#if TARGET_HOST_UNIX_X11
-#   if defined(__FreeBSD__) || defined(__NetBSD__)
+#if TARGET_HOST_POSIX_X11
+#   if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
        struct os_specific_s *os;
 #   endif
 
@@ -438,7 +448,6 @@ static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object
 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
 /* callback for CFArrayApply */
 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
-static void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
 
 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
@@ -450,7 +459,6 @@ static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
  * The static joystick structure pointer
  */
 #define MAX_NUM_JOYSTICKS  2
-static int fgNumberOfJoysticks = 0;
 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
 
 
@@ -459,13 +467,13 @@ static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
  */
 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
 {
-#if TARGET_HOST_WIN32
+#if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
     MMRESULT status;
 #else
     int status;
 #endif
 
-#if defined(__FreeBSD__) || defined(__NetBSD__)
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
     int len;
 #endif
 
@@ -535,7 +543,7 @@ static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
     }
 #endif
 
-#if TARGET_HOST_WIN32
+#if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
     status = joyGetPosEx( joy->js_id, &joy->js );
 
     if ( status != JOYERR_NOERROR )
@@ -601,8 +609,8 @@ static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
     }
 #endif
 
-#if TARGET_HOST_UNIX_X11
-#    if defined(__FreeBSD__) || defined(__NetBSD__)
+#if TARGET_HOST_POSIX_X11
+#    if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
     if ( joy->os->is_analog )
     {
         int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
@@ -660,14 +668,18 @@ static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
                if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
                {
                    if (d)
-                       joy->os->cache_buttons |= (1 << usage - 1);
+                       joy->os->cache_buttons |=  (1 << ( usage - 1 ));
                    else
-                       joy->os->cache_buttons &= ~(1 << usage - 1);
+                       joy->os->cache_buttons &= ~(1 << ( usage - 1 ));
                }
             }
         }
     }
+#ifdef HAVE_ERRNO_H
     if ( len < 0 && errno != EAGAIN )
+#else
+    if ( len < 0 )
+#endif
     {
         perror( joy->os->fname );
         joy->error = 1;
@@ -686,6 +698,7 @@ static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
 
         if ( status != sizeof( struct js_event ) )
         {
+#ifdef HAVE_ERRNO_H
             if ( errno == EAGAIN )
             {
                 /* Use the old values */
@@ -696,6 +709,7 @@ static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
                             sizeof( float ) * joy->num_axes );
                 return;
             }
+#endif
 
             fgWarning ( "%s", joy->fname );
             joy->error = GL_TRUE;
@@ -748,7 +762,7 @@ static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
     }
 
     if ( buttons )
-#        if defined( __FreeBSD__ ) || defined( __NetBSD__ )
+#        if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
         *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 );  /* XXX Should not be here -- BSD is handled earlier */
 #        else
         *buttons = joy->js.buttons;
@@ -926,73 +940,15 @@ static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, voi
 /** element enumerator function : pass NULL for top-level*/
 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
 {
-      assert(CFGetTypeID(element) == CFArrayGetTypeID());
+      FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
+                                    "Joystick element type mismatch",
+                                    "fghJoystickEnumerateElements" );
 
       CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
       CFArrayApplyFunction((CFArrayRef) element, range,
             &fghJoystickElementEnumerator, joy );
 }
 
-static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
-{
-    CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
-    CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
-
-    long type, page, usage;
-
-    CFNumberGetValue((CFNumberRef)
-        CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
-        kCFNumberLongType, &type);
-
-    switch ( typ e) {
-    case kIOHIDElementTypeInput_Misc:
-    case kIOHIDElementTypeInput_Axis:
-    case kIOHIDElementTypeInput_Button:
-        printf("got input element...");
-        CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
-        CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
-
-        if (page == kHIDPage_GenericDesktop) {
-            switch ( usage ) /* look at usage to determine function */
-            {
-            case kHIDUsage_GD_X:
-            case kHIDUsage_GD_Y:
-            case kHIDUsage_GD_Z:
-            case kHIDUsage_GD_Rx:
-            case kHIDUsage_GD_Ry:
-            case kHIDUsage_GD_Rz:
-            case kHIDUsage_GD_Slider: /* for throttle / trim controls */
-                printf(" axis\n");
-                fghJoystickAddAxisElement((CFDictionaryRef) element);
-                break;
-
-            case kHIDUsage_GD_Hatswitch:
-                printf(" hat\n");
-                fghJoystickAddHatElement((CFDictionaryRef) element);
-                break;
-
-            default:
-                fgWarning ( "input type element has weird usage (%x)", usage);
-                break;
-            }
-        } else if (page == kHIDPage_Button) {
-            printf(" button\n");
-            fghJoystickAddButtonElement((CFDictionaryRef) element);
-        } else
-            fgWarning ( "input type element has weird page (%x)", page);
-        break;
-
-    case kIOHIDElementTypeCollection:
-        fghJoystickEnumerateElements (
-            CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
-        );
-        break;
-
-    default:
-        break;
-    }
-}
-
 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
 {
     long cookie, lmin, lmax;
@@ -1037,11 +993,11 @@ static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button
 }
 #endif
 
-#if TARGET_HOST_WIN32
+#if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
 /* Inspired by
    http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
  */
-#    if defined(_MSC_VER)
+#    if FREEGLUT_LIB_PRAGMAS
 #        pragma comment (lib, "advapi32.lib")
 #    endif
 
@@ -1059,9 +1015,9 @@ static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_
         return 0;
 
     /* Open .. MediaResources\CurrentJoystickSettings */
-    sprintf ( buffer, "%s\\%s\\%s",
-              REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
-              REGSTR_KEY_JOYCURR );
+    _snprintf ( buffer, sizeof(buffer), "%s\\%s\\%s",
+                REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
+                REGSTR_KEY_JOYCURR );
 
     lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
 
@@ -1071,7 +1027,7 @@ static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_
     dwcb = sizeof(OEMKey);
 
     /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
-    sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
+    _snprintf ( buffer, sizeof(buffer), "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
 
     lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
     RegCloseKey ( hKey );
@@ -1079,7 +1035,7 @@ static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_
     if ( lr != ERROR_SUCCESS ) return 0;
 
     /* Open OEM Key from ...MediaProperties */
-    sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
+    _snprintf ( buffer, sizeof(buffer), "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
 
     lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
 
@@ -1101,7 +1057,7 @@ static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_
 
 static void fghJoystickOpen( SFG_Joystick* joy )
 {
-    int i;
+    int i = 0;
 #if TARGET_HOST_MACINTOSH
     OSStatus err;
 #endif
@@ -1115,17 +1071,22 @@ static void fghJoystickOpen( SFG_Joystick* joy )
         CFDictionaryRef props;
     CFTypeRef topLevelElement;
 #endif
-#if TARGET_HOST_UNIX_X11
-#    if defined( __FreeBSD__ ) || defined( __NetBSD__ )
+#if TARGET_HOST_POSIX_X11
+#    if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
        char *cp;
 #    endif
 #    ifdef JS_NEW
        unsigned char u;
 #    else
-       int counter;
+#      if defined( __linux__ ) || TARGET_HOST_SOLARIS
+         int counter = 0;
+#      endif
 #    endif
 #endif
 
+    /* Silence gcc, the correct #ifdefs would be too fragile... */
+    (void)i;
+
     /*
      * Default values (for no joystick -- each conditional will reset the
      * error flag)
@@ -1284,7 +1245,7 @@ static void fghJoystickOpen( SFG_Joystick* joy )
     CFRelease( props );
 #endif
 
-#if TARGET_HOST_WIN32
+#if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
     joy->js.dwFlags = JOY_RETURNALL;
     joy->js.dwSize  = sizeof( joy->js );
 
@@ -1346,8 +1307,8 @@ static void fghJoystickOpen( SFG_Joystick* joy )
     }
 #endif
 
-#if TARGET_HOST_UNIX_X11
-#if defined( __FreeBSD__ ) || defined( __NetBSD__ )
+#if TARGET_HOST_POSIX_X11
+#if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
     for( i = 0; i < _JS_MAX_AXES; i++ )
         joy->os->cache_axes[ i ] = 0.0f;
 
@@ -1355,8 +1316,10 @@ static void fghJoystickOpen( SFG_Joystick* joy )
 
     joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
 
+#ifdef HAVE_ERRNO_H
     if( joy->os->fd < 0 && errno == EACCES )
         fgWarning ( "%s exists but is not readable by you", joy->os->fname );
+#endif
 
     joy->error =( joy->os->fd < 0 );
 
@@ -1382,7 +1345,7 @@ static void fghJoystickOpen( SFG_Joystick* joy )
         if( joy->error )
             return;
 
-        sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
+        snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
 
         joyfile = fopen( joyfname, "r" );
         joy->error =( joyfile == NULL );
@@ -1451,7 +1414,7 @@ static void fghJoystickOpen( SFG_Joystick* joy )
 #    endif
 #endif
 
-#if defined( __linux__ )
+#if defined( __linux__ ) || TARGET_HOST_SOLARIS
     /* Default for older Linux systems. */
     joy->num_axes    =  2;
     joy->num_buttons = 32;
@@ -1528,7 +1491,7 @@ static void fghJoystickOpen( SFG_Joystick* joy )
 /*
  * This function replaces the constructor method in the JS library.
  */
-void fgJoystickInit( int ident )
+static void fghJoystickInit( int ident )
 {
     if( ident >= MAX_NUM_JOYSTICKS )
       fgError( "Too large a joystick number: %d", ident );
@@ -1545,7 +1508,7 @@ void fgJoystickInit( int ident )
 
 #if TARGET_HOST_MACINTOSH
     fgJoystick[ ident ]->id = ident;
-    sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
+    snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident ); /* FIXME */
     fgJoystick[ ident ]->error = GL_FALSE;
 #endif
 
@@ -1592,7 +1555,7 @@ void fgJoystickInit( int ident )
     }
 #endif
 
-#if TARGET_HOST_WIN32
+#if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
     switch( ident )
     {
     case 0:
@@ -1610,8 +1573,8 @@ void fgJoystickInit( int ident )
     }
 #endif
 
-#if TARGET_HOST_UNIX_X11
-#    if defined( __FreeBSD__ ) || defined( __NetBSD__ )
+#if TARGET_HOST_POSIX_X11
+#    if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
     fgJoystick[ ident ]->id = ident;
     fgJoystick[ ident ]->error = GL_FALSE;
 
@@ -1620,18 +1583,18 @@ void fgJoystickInit( int ident )
     if( ident < USB_IDENT_OFFSET )
         fgJoystick[ ident ]->os->is_analog = 1;
     if( fgJoystick[ ident ]->os->is_analog )
-        sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
+        snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", AJSDEV, ident );
     else
-        sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
+        snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", UHIDDEV,
                  ident - USB_IDENT_OFFSET );
 #    elif defined( __linux__ )
     fgJoystick[ ident ]->id = ident;
     fgJoystick[ ident ]->error = GL_FALSE;
 
-    sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
+    snprintf( fgJoystick[ident]->fname, sizeof(fgJoystick[ident]->fname), "/dev/input/js%d", ident );
 
     if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
-        sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
+        snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident );
 #    endif
 #endif
 
@@ -1639,6 +1602,21 @@ void fgJoystickInit( int ident )
 }
 
 /*
+ * Try initializing all the joysticks (well, both of them)
+ */
+void fgInitialiseJoysticks ( void )
+{
+    if( !fgState.JoysticksInitialised )
+    {
+        int ident ;
+        for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
+            fghJoystickInit( ident );
+
+        fgState.JoysticksInitialised = GL_TRUE;
+    }
+}
+
+/*
  *
  */
 void fgJoystickClose( void )
@@ -1660,12 +1638,12 @@ void fgJoystickClose( void )
                 close( fgJoystick[ ident ]->hidDev );
 #endif
 
-#if TARGET_HOST_WIN32
+#if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
             /* Do nothing special */
 #endif
 
-#if TARGET_HOST_UNIX_X11
-#if defined( __FreeBSD__ ) || defined( __NetBSD__ )
+#if TARGET_HOST_POSIX_X11
+#if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
             if( fgJoystick[ident]->os )
             {
                 if( ! fgJoystick[ ident ]->error )
@@ -1722,63 +1700,99 @@ void fgJoystickPollWindow( SFG_Window* window )
 }
 
 /*
- * PWO: These jsJoystick class methods have not been implemented.
+ * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
+ */
+int fgJoystickDetect( void )
+{
+    int ident;
+
+    fgInitialiseJoysticks ();
+
+    if ( !fgState.JoysticksInitialised )
+        return 0;
+
+    for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
+        if( fgJoystick[ident] && !fgJoystick[ident]->error )
+            return 1;
+
+    return 0;
+}
+
+/*
+ * Joystick information functions
  */
 int  glutJoystickGetNumAxes( int ident )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
     return fgJoystick[ ident ]->num_axes;
 }
+int  glutJoystickGetNumButtons( int ident )
+{
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
+    return fgJoystick[ ident ]->num_buttons;
+}
 int  glutJoystickNotWorking( int ident )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
     return fgJoystick[ ident ]->error;
 }
 
 float glutJoystickGetDeadBand( int ident, int axis )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
     return fgJoystick[ ident ]->dead_band [ axis ];
 }
 void  glutJoystickSetDeadBand( int ident, int axis, float db )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
     fgJoystick[ ident ]->dead_band[ axis ] = db;
 }
 
 float glutJoystickGetSaturation( int ident, int axis )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
     return fgJoystick[ ident ]->saturate[ axis ];
 }
 void  glutJoystickSetSaturation( int ident, int axis, float st )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
     fgJoystick[ ident ]->saturate [ axis ] = st;
 }
 
 void glutJoystickSetMinRange( int ident, float *axes )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
     memcpy( fgJoystick[ ident ]->min, axes,
             fgJoystick[ ident ]->num_axes * sizeof( float ) );
 }
 void glutJoystickSetMaxRange( int ident, float *axes )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
     memcpy( fgJoystick[ ident ]->max, axes,
             fgJoystick[ ident ]->num_axes * sizeof( float ) );
 }
 void glutJoystickSetCenter( int ident, float *axes )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
     memcpy( fgJoystick[ ident ]->center, axes,
             fgJoystick[ ident ]->num_axes * sizeof( float ) );
 }
 
 void glutJoystickGetMinRange( int ident, float *axes )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
     memcpy( axes, fgJoystick[ ident ]->min,
             fgJoystick[ ident ]->num_axes * sizeof( float ) );
 }
 void glutJoystickGetMaxRange( int ident, float *axes )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
     memcpy( axes, fgJoystick[ ident ]->max,
             fgJoystick[ ident ]->num_axes * sizeof( float ) );
 }
 void glutJoystickGetCenter( int ident, float *axes )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
     memcpy( axes, fgJoystick[ ident ]->center,
             fgJoystick[ ident ]->num_axes * sizeof( float ) );
 }