*/
/*
- * PWO: This is not exactly what Steve Baker has done for PLIB, as I had to
- * convert it from C++ to C. And I've also reformatted it a bit (that's
- * my little personal deviation :]) I don't really know if it is still
- * portable...
- * Steve: could you please add some comments to the code? :)
- *
* FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu>
+ *
+ * Redone by John Fay 2/4/04 with another look from the PLIB "js" library.
+ * Many thanks for Steve Baker for permission to pull from that library.
*/
#if defined( __FreeBSD__ ) || defined( __NetBSD__ )
-#include <sys/param.h>
+# include <sys/param.h>
#endif
#ifdef HAVE_CONFIG_H
-#include "config.h"
+# include "config.h"
#endif
#include "../include/GL/freeglut.h"
#include "freeglut_internal.h"
/*
- * PWO: I don't like it at all. It's a mess. Could it be cleared?
+ * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
+ * interspersed
*/
+#define _JS_MAX_BUTTONS 32
+
+
+#if TARGET_HOST_MACINTOSH
+# define _JS_MAX_AXES 9
+# include <InputSprocket.h>
+#endif
+
+#if TARGET_HOST_MAC_OSX
+# define _JS_MAX_AXES 16
+# include <mach/mach.h>
+# include <IOKit/IOkitLib.h>
+# include <IOKit/hid/IOHIDLib.h>
+#endif
+
#if TARGET_HOST_WIN32
-# include <windows.h>
-# if defined( __CYGWIN32__ ) || defined( __CYGWIN__ )
-# define NEAR /* */
-# define FAR /* */
+# define _JS_MAX_AXES 8
+# include <windows.h>
+# include <mmsystem.h>
+# include <string.h>
+# include <regstr.h>
+
+#endif
+
+#if TARGET_HOST_UNIX_X11
+# define _JS_MAX_AXES 16
+# if defined(__FreeBSD__) || defined(__NetBSD__)
+/* XXX The next line is an unjustified hack which needs to be changed by someone who
+ * XXX is familiar with *nix, BSD, and USB joysticks.
+ */
+# define HAVE_USB_JS 1
+
+# include <sys/ioctl.h>
+# include <machine/joystick.h> /* For analog joysticks */
+
+# if __FreeBSD_version >= 500000
+# include <sys/joystick.h>
+# else
+# include <machine/joystick.h>
+# endif
+# define JS_DATA_TYPE joystick
+# 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 */
+# if defined(JS_VERSION) && JS_VERSION >= 0x010000
+# define JS_NEW
+# endif
+# else /* Not BSD or Linux */
+# ifndef JS_RETURN
+
+ /*
+ * We'll put these values in and that should
+ * allow the code to at least compile when there is
+ * no support. The JS open routine should error out
+ * and shut off all the code downstream anyway and if
+ * the application doesn't use a joystick we'll be fine.
+ */
+
+ struct JS_DATA_TYPE
+ {
+ int buttons;
+ int x;
+ int y;
+ };
+
+# define JS_RETURN (sizeof(struct JS_DATA_TYPE))
+# endif
+# endif
+#endif
+
+#define JS_TRUE 1
+#define JS_FALSE 0
+
+/*
+ * BSD defines from "jsBSD.cxx" around lines 42-270
+ */
+
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+
+# ifdef HAVE_USB_JS
+# if defined(__NetBSD__)
+/*
+ * XXX Apparently another ugly hack which someone who knows BSD and USBHID needs to solve
+ */
+# define HAVE_USBHID_H 1
+# ifdef HAVE_USBHID_H
+# include <usbhid.h>
+# else
+# include <usb.h>
+# endif
+# elif defined(__FreeBSD__)
+# include <libusbhid.h>
+# endif
+# include <dev/usb/usb.h>
+# include <dev/usb/usbhid.h>
+
+/* Compatibility with older usb.h revisions */
+# if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
+# define USB_MAX_DEVNAMES MAXDEVNAMES
+# endif
+# endif
+
+static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
+static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
+struct os_specific_s {
+ char fname [128 ];
+ int fd;
+ int is_analog;
+ /* The following structure members are specific to analog joysticks */
+ struct joystick ajs;
+# ifdef HAVE_USB_JS
+ /* The following structure members are specific to USB joysticks */
+ struct hid_item *hids;
+ int hid_dlen;
+ int hid_offset;
+ char *hid_data_buf;
+ int axes_usage [ _JS_MAX_AXES ];
+# endif
+ /* We keep button and axes state ourselves, as they might not be updated
+ * on every read of a USB device
+ */
+ int cache_buttons;
+ float cache_axes [ _JS_MAX_AXES ];
+};
+
+/* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
+# define USB_IDENT_OFFSET 2
+
+# define USBDEV "/dev/usb"
+# define UHIDDEV "/dev/uhid"
+# define AJSDEV "/dev/joy"
+
+# ifdef HAVE_USB_JS
+/*
+ * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
+ * the full name of a USB device. If /dev/usbN isn't readable, we punt and
+ * return the uhidN device name. We warn the user of this situation once.
+ */
+static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
+{
+ struct usb_device_info di;
+ int i, a;
+ char *cp;
+
+ for (a = 1; a < USB_MAX_DEVICES; a++) {
+ di.udi_addr = a;
+ if (ioctl(f, USB_DEVICEINFO, &di) != 0)
+ return NULL;
+ for (i = 0; i < USB_MAX_DEVNAMES; i++)
+ if (di.udi_devnames[i][0] &&
+ strcmp(di.udi_devnames[i], dev) == 0) {
+ cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
+ strcpy(cp, di.udi_vendor);
+ strcat(cp, " ");
+ strcat(cp, di.udi_product);
+ strncpy(out, cp, outlen - 1);
+ out[outlen - 1] = 0;
+ free( cp );
+ return out;
+ }
+ }
+ return NULL;
+}
+
+static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
+{
+ int i, f;
+ char buf[50];
+ char *cp;
+ static int protection_warned = 0;
+
+ for (i = 0; i < 16; i++) {
+ sprintf(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) {
+ if (!protection_warned) {
+ fprintf(stderr, "Can't open %s for read!\n",
+ buf);
+ protection_warned = 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int fghJoystickInitializeHID(struct os_specific_s *os,
+ int *num_axes, int *num_buttons)
+{
+ int size, is_joystick;
+# ifdef HAVE_USBHID_H
+ int report_id = 0;
# endif
-# include <mmsystem.h>
-# include <string.h>
-#else
-# include <unistd.h>
-# include <fcntl.h>
-# if defined(__FreeBSD__) || defined(__NetBSD__)
-# if __FreeBSD_version >= 500000
-# include <sys/joystick.h>
+ struct hid_data *d;
+ struct hid_item h;
+ report_desc_t rd;
+
+ if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
+ {
+ fprintf( stderr, "error: %s: %s", os->fname, strerror( errno ) );
+ return FALSE;
+ }
+
+ os->hids = NULL;
+
+# ifdef HAVE_USBHID_H
+ if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
+ {
+ /*** XXX {report_id} may not be the right variable? ***/
+ fprintf( stderr, "error: %s%d: %s",
+ UHIDDEV, report_id, strerror( errno ) );
+ return FALSE;
+ }
+
+ size = hid_report_size( rd, hid_input, report_id );
# else
-# include <machine/joystick.h>
+ size = hid_report_size( rd, 0, hid_input );
# endif
-# define JS_DATA_TYPE joystick
-# define JS_RETURN (sizeof(struct JS_DATA_TYPE))
-# elif defined(__linux__)
-# include <sys/ioctl.h>
-# include <linux/joystick.h>
-# include <errno.h>
+ os->hid_data_buf = calloc( 1, size );
+ os->hid_dlen = size;
- /*
- * Check the joystick driver version
- */
-# ifdef JS_VERSION
-# if JS_VERSION >= 0x010000
-# define JS_NEW
-# endif
-# endif
+ is_joystick = 0;
+# ifdef HAVE_USBHID_H
+ d = hid_start_parse( rd, 1 << hid_input, report_id );
# else
-# ifndef JS_DATA_TYPE
-
- /*
- * Not Windoze and no (known) joystick driver...
- *
- * Well - we'll put these values in and that should
- * allow the code to at least compile. The JS open
- * routine should error out and shut off all the code
- * downstream anyway
- */
- struct JS_DATA_TYPE
+ d = hid_start_parse( rd, 1 << hid_input );
+# endif
+ while( hid_get_item( d, &h ) )
+ {
+ int usage, page, interesting_hid;
+
+ page = HID_PAGE( h.usage );
+ usage = HID_USAGE( h.usage );
+
+ /* This test is somewhat too simplistic, but this is how MicroSoft
+ * does, so I guess it works for all joysticks/game pads. */
+ is_joystick = is_joystick ||
+ ( h.kind == hid_collection &&
+ page == HUP_GENERIC_DESKTOP &&
+ ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
+
+ if( h.kind != hid_input )
+ continue;
+
+ if( !is_joystick )
+ continue;
+
+ interesting_hid = TRUE;
+ if( page == HUP_GENERIC_DESKTOP )
+ {
+ switch( usage )
+ {
+ case HUG_X:
+ case HUG_RX:
+ case HUG_Y:
+ case HUG_RY:
+ case HUG_Z:
+ case HUG_RZ:
+ case HUG_SLIDER:
+ if( *num_axes < _JS_MAX_AXES )
+ {
+ os->axes_usage[ *num_axes ] = usage;
+ ( *num_axes )++;
+ }
+ break;
+ case HUG_HAT_SWITCH:
+ /* Allocate two axes for a hat */
+ if( *num_axes + 1 < _JS_MAX_AXES )
+ {
+ os->axes_usage[ *num_axes ] = usage;
+ (*num_axes)++;
+ os->axes_usage[ *num_axes ] = usage;
+ (*num_axes)++;
+ }
+ break;
+ default:
+ interesting_hid = FALSE;
+ break;
+ }
+ }
+ else if( page == HUP_BUTTON )
{
- int buttons;
- int x;
- int y;
- };
+ interesting_hid = ( usage > 0 ) &&
+ ( usage <= _JS_MAX_BUTTONS );
-# define JS_RETURN (sizeof(struct JS_DATA_TYPE))
-# endif
-# endif
-#endif
+ if( interesting_hid && usage - 1 > *num_buttons )
+ *num_buttons = usage - 1;
+ }
-#if TARGET_HOST_WIN32
-# define _JS_MAX_AXES 6
-#else
-# ifdef __FreeBSD__
-# define _JS_MAX_AXES 2
-# else
-# define _JS_MAX_AXES 6
-# endif
+ if( interesting_hid )
+ {
+ h.next = os->hids;
+ os->hids = calloc( 1, sizeof ( struct hid_item ) );
+ *os->hids = h;
+ }
+ }
+ hid_end_parse( d );
+
+ return os->hids != NULL;
+}
+# endif
#endif
+/*
+ * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
+ * See "js.h" lines 80-178.
+ */
typedef struct tagSFG_Joystick SFG_Joystick;
struct tagSFG_Joystick
{
-/*
- * XXX All BSDs might share this?
- */
-#ifdef __FreeBSD__
- int id;
+#if TARGET_HOST_MACINTOSH
+#define ISP_NUM_AXIS 9
+#define ISP_NUM_NEEDS 41
+ ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
+ ISpNeed isp_needs [ ISP_NUM_NEEDS ];
+#endif
+
+#if TARGET_HOST_MAC_OSX
+ IOHIDDeviceInterface ** hidDev;
+ IOHIDElementCookie buttonCookies[41];
+ IOHIDElementCookie axisCookies[_JS_MAX_AXES];
+ long minReport[_JS_MAX_AXES],
+ maxReport[_JS_MAX_AXES];
#endif
#if TARGET_HOST_WIN32
+ JOYCAPS jsCaps;
JOYINFOEX js;
UINT js_id;
-#else
+#endif
+
+
+#if TARGET_HOST_UNIX_X11
+# if defined(__FreeBSD__) || defined(__NetBSD__)
+ struct os_specific_s *os;
+# endif
+
# ifdef JS_NEW
- struct js_event js;
- int tmp_buttons;
- float tmp_axes[ _JS_MAX_AXES ];
+ struct js_event js ;
+ int tmp_buttons;
+ float tmp_axes [ _JS_MAX_AXES ];
# else
- struct JS_DATA_TYPE js;
+ struct JS_DATA_TYPE js;
# endif
- char fname[ 128 ];
- int fd;
+ char fname [ 128 ];
+ int fd;
#endif
- GLboolean error;
- int num_axes;
- int num_buttons;
+ int id;
+ GLboolean error;
+ char name [ 128 ];
+ int num_axes;
+ int num_buttons;
float dead_band[ _JS_MAX_AXES ];
float saturate [ _JS_MAX_AXES ];
};
/*
+ * Functions associated with the "jsJoystick" class in PLIB
+ */
+#if TARGET_HOST_MAC_OSX
+#define K_NUM_DEVICES 32
+int numDevices;
+io_object_t ioDevices[K_NUM_DEVICES];
+
+static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
+static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
+
+void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
+/* callback for CFArrayApply */
+static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
+void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
+
+void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
+void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
+void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
+#endif
+
+
+/*
* The static joystick structure pointer
*/
-static SFG_Joystick* fgJoystick = NULL;
+#define MAX_NUM_JOYSTICKS 2
+static int fgNumberOfJoysticks = 0;
+static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
+
/*
* Read the raw joystick data
int status;
#endif
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+ int len;
+#endif
+
int i;
+ /* Defaults */
+ if( buttons )
+ *buttons = 0;
+
+ if( axes )
+ for( i = 0; i < joy->num_axes; i++ )
+ axes[ i ] = 1500.0f;
+
if( joy->error )
+ return;
+
+#if TARGET_HOST_MACINTOSH
+ if ( buttons )
{
- if( buttons )
- *buttons = 0;
+ *buttons = 0;
- if( axes )
- for( i=0; i<joy->num_axes; i++ )
- axes[ i ] = 1500.0f;
+ for ( i = 0; i < joy->num_buttons; i++ )
+ {
+ UInt32 state;
+ int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
+ ISP_CHECK_ERR(err)
- return;
+ *buttons |= state << i;
+ }
+ }
+
+ if ( axes )
+ {
+ for ( i = 0; i < joy->num_axes; i++ )
+ {
+ UInt32 state;
+ int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
+ ISP_CHECK_ERR(err)
+
+ axes [i] = (float) state;
+ }
+ }
+#endif
+
+#if TARGET_HOST_MAC_OSX
+ if ( buttons != NULL )
+ {
+ *buttons = 0;
+
+ for ( i = 0; i < joy->num_buttons; i++ )
+ {
+ IOHIDEventStruct hidEvent;
+ (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
+ if ( hidEvent.value )
+ *buttons |= 1 << i;
+ }
+ }
+
+ if ( axes != NULL )
+ {
+ for ( i = 0; i < joy->num_axes; i++ )
+ {
+ IOHIDEventStruct hidEvent;
+ (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
+ axes[i] = hidEvent.value;
+ }
}
+#endif
#if TARGET_HOST_WIN32
status = joyGetPosEx( joy->js_id, &joy->js );
- if( status != JOYERR_NOERROR )
+ if ( status != JOYERR_NOERROR )
{
joy->error = GL_TRUE;
return;
}
- if( buttons )
+ if ( buttons )
*buttons = joy->js.dwButtons;
- if( axes )
+ if ( axes )
{
/*
* WARNING - Fall through case clauses!!
*/
- switch( joy->num_axes )
+ switch ( joy->num_axes )
{
+ case 8:
+ /* Generate two POV axes from the POV hat angle.
+ * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
+ * hundredths of a degree, or 0xFFFF when idle.
+ */
+ if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
+ {
+ axes [ 6 ] = 0.0;
+ axes [ 7 ] = 0.0;
+ }
+ else
+ {
+ /* This is the contentious bit: how to convert angle to X/Y.
+ * wk: I know of no define for PI that we could use here:
+ * SG_PI would pull in sg, M_PI is undefined for MSVC
+ * But the accuracy of the value of PI is very unimportant at
+ * this point.
+ */
+ float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
+ float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
+
+ /* Convert to coordinates on a square so that North-East
+ * is (1,1) not (.7,.7), etc.
+ * s and c cannot both be zero so we won't divide by zero.
+ */
+ if ( fabs ( s ) < fabs ( c ) )
+ {
+ axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
+ axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
+ }
+ else
+ {
+ axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
+ axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
+ }
+ }
+
case 6: axes[5] = (float) joy->js.dwVpos;
case 5: axes[4] = (float) joy->js.dwUpos;
case 4: axes[3] = (float) joy->js.dwRpos;
case 1: axes[0] = (float) joy->js.dwXpos;
}
}
-#else
-# ifdef JS_NEW
+#endif
+
+#if TARGET_HOST_UNIX_X11
+# if defined(__FreeBSD__) || defined(__NetBSD__)
+ if ( joy->os->is_analog )
+ {
+ int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
+ if ( status != sizeof(joy->os->ajs) ) {
+ perror ( joy->os->fname );
+ joy->error = GL_TRUE;
+ return;
+ }
+ if ( buttons != NULL )
+ *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
+
+ if ( axes != NULL )
+ {
+ axes[0] = (float) joy->os->ajs.x;
+ axes[1] = (float) joy->os->ajs.y;
+ }
+
+ return;
+ }
- while( 1 )
+# ifdef HAVE_USB_JS
+ while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
{
- status = read( joy->fd, &joy->js, sizeof(struct js_event) );
+ struct hid_item *h;
- if( status != sizeof( struct js_event ) )
+ for ( h = joy->os->hids; h; h = h->next )
{
- if( errno == EAGAIN )
+ int d = hid_get_data ( joy->os->hid_data_buf, h );
+
+ int page = HID_PAGE ( h->usage );
+ int usage = HID_USAGE ( h->usage );
+
+ if ( page == HUP_GENERIC_DESKTOP )
+ {
+ int i;
+ for ( i = 0; i < joy->num_axes; i++ )
+ if (joy->os->axes_usage[i] == usage)
+ {
+ if (usage == HUG_HAT_SWITCH)
+ {
+ if (d < 0 || d > 8)
+ d = 0; /* safety */
+ joy->os->cache_axes[i] = (float)hatmap_x[d];
+ joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
+ }
+ else
+ {
+ joy->os->cache_axes[i] = (float)d;
+ }
+ break;
+ }
+ }
+ else if (page == HUP_BUTTON)
{
- /*
- * Use the old values
- */
- if( buttons )
+ if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
+ {
+ if (d)
+ joy->os->cache_buttons |= (1 << usage - 1);
+ else
+ joy->os->cache_buttons &= ~(1 << usage - 1);
+ }
+ }
+ }
+ }
+ if ( len < 0 && errno != EAGAIN )
+ {
+ perror( joy->os->fname );
+ joy->error = 1;
+ }
+ if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
+ if ( axes != NULL )
+ memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
+# endif
+# endif
+
+# ifdef JS_NEW
+
+ while ( 1 )
+ {
+ status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
+
+ if ( status != sizeof( struct js_event ) )
+ {
+ if ( errno == EAGAIN )
+ {
+ /* Use the old values */
+ if ( buttons )
*buttons = joy->tmp_buttons;
- if( axes )
+ if ( axes )
memcpy( axes, joy->tmp_axes,
sizeof( float ) * joy->num_axes );
return;
}
- fgWarning( "%s", joy->fname );
+ fgWarning ( "%s", joy->fname );
joy->error = GL_TRUE;
return;
}
- switch( joy->js.type & ~JS_EVENT_INIT )
+ switch ( joy->js.type & ~JS_EVENT_INIT )
{
case JS_EVENT_BUTTON:
if( joy->js.value == 0 ) /* clear the flag */
break;
case JS_EVENT_AXIS:
- joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
-
- if( axes )
- memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
+ if ( joy->js.number < joy->num_axes )
+ {
+ joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
+
+ if( axes )
+ memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
+ }
break;
+
+ default:
+ fgWarning ( "%s", "PLIB_JS: Unrecognised /dev/js return!?!" );
+
+ /* use the old values */
+
+ if ( buttons != NULL ) *buttons = joy->tmp_buttons;
+ if ( axes != NULL )
+ memcpy ( axes, joy->tmp_axes, sizeof(float) * num_axes );
+
+ return;
}
if( buttons )
*buttons = joy->tmp_buttons;
}
-# else
+# else
status = read( joy->fd, &joy->js, JS_RETURN );
- if( status != JS_RETURN )
+ if ( status != JS_RETURN )
{
fgWarning( "%s", joy->fname );
joy->error = GL_TRUE;
return;
}
- if( buttons )
-# if defined( __FreeBSD__ ) || defined( __NetBSD__ )
- *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 );
-# else
+ if ( buttons )
+# if defined( __FreeBSD__ ) || 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;
-# endif
+# endif
- if( axes )
+ if ( axes )
{
axes[ 0 ] = (float) joy->js.x;
axes[ 1 ] = (float) joy->js.y;
}
-# endif
+# endif
#endif
}
*buttons = 0;
if( axes )
- for ( i=0; i<joy->num_axes ; i++ )
- axes[ i ] = 0.0f ;
+ for ( i=0; i<joy->num_axes; i++ )
+ axes[ i ] = 0.0f;
}
fghJoystickRawRead( joy, buttons, raw_axes );
if( axes )
- for( i=0 ; i<joy->num_axes ; i++ )
+ for( i=0; i<joy->num_axes; i++ )
axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
}
/*
* Happy happy happy joy joy joy (happy new year toudi :D)
*/
-static void fghJoystickOpen( SFG_Joystick* joy )
+
+
+#if TARGET_HOST_MAC_OSX
+/** open the IOKit connection, enumerate all the HID devices, add their
+interface references to the static array. We then use the array index
+as the device number when we come to open() the joystick. */
+static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
+{
+ CFMutableDictionaryRef hidMatch = NULL;
+ IOReturn rv = kIOReturnSuccess;
+
+ io_iterator_t hidIterator;
+ io_object_t ioDev;
+
+ /* build a dictionary matching HID devices */
+ hidMatch = IOServiceMatching(kIOHIDDeviceKey);
+
+ rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
+ if (rv != kIOReturnSuccess || !hidIterator) {
+ fgWarning ( "%s", "no joystick (HID) devices found");
+ return;
+ }
+
+ /* iterate */
+ while ((ioDev = IOIteratorNext(hidIterator))) {
+ /* filter out keyboard and mouse devices */
+ CFDictionaryRef properties = getCFProperties(ioDev);
+ long usage, page;
+
+ CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
+ CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
+ CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
+ CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
+
+ /* keep only joystick devices */
+ if ( ( page == kHIDPage_GenericDesktop ) && (
+ (usage == kHIDUsage_GD_Joystick)
+ || (usage == kHIDUsage_GD_GamePad)
+ || (usage == kHIDUsage_GD_MultiAxisController)
+ || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
+ /* add it to the array */
+ ioDevices[numDevices++] = ioDev;
+ }
+
+ IOObjectRelease(hidIterator);
+}
+
+static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
+{
+ IOReturn rv;
+ CFMutableDictionaryRef cfProperties;
+
+#if 0
+ /* comment copied from darwin/SDL_sysjoystick.c */
+ /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
+ * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
+ */
+
+ io_registry_entry_t parent1, parent2;
+
+ rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
+ if (rv != kIOReturnSuccess) {
+ fgWarning ( "%s", "error getting device entry parent");
+ return NULL;
+ }
+
+ rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
+ if (rv != kIOReturnSuccess) {
+ fgWarning ( "%s", "error getting device entry parent 2");
+ return NULL;
+ }
+#endif
+
+ rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
+ &cfProperties, kCFAllocatorDefault, kNilOptions);
+ if (rv != kIOReturnSuccess || !cfProperties) {
+ fgWarning ( "%s", "error getting device properties");
+ return NULL;
+ }
+
+ return cfProperties;
+}
+
+static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
{
+ if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
+ fgError ( "%s", "element enumerator passed non-dictionary value");
+ return;
+ }
+
+ static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
+}
+
+/** element enumerator function : pass NULL for top-level*/
+static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
+{
+ assert(CFGetTypeID(element) == CFArrayGetTypeID());
+
+ 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:
+ printf("input type element has weird usage (%x)\n", usage);
+ break;
+ }
+ } else if (page == kHIDPage_Button) {
+ printf(" button\n");
+ fghJoystickAddButtonElement((CFDictionaryRef) element);
+ } else
+ printf("input type element has weird page (%x)\n", page);
+ break;
+
+ case kIOHIDElementTypeCollection:
+ fghJoystickEnumerateElements (
+ CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
+ );
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
+{
+ long cookie, lmin, lmax;
+ int index = joy->num_axes++;
+
+ CFNumberGetValue ((CFNumberRef)
+ CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
+ kCFNumberLongType, &cookie);
+
+ axisCookies[index] = (IOHIDElementCookie) cookie;
+
+ CFNumberGetValue ((CFNumberRef)
+ CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
+ kCFNumberLongType, &lmin);
+
+ CFNumberGetValue ((CFNumberRef)
+ CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
+ kCFNumberLongType, &lmax);
+
+ joy->min[index] = lmin;
+ joy->max[index] = lmax;
+ joy->dead_band[index] = 0.0;
+ joy->saturate[index] = 1.0;
+ joy->center[index] = (lmax + lmin) * 0.5;
+}
+
+static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
+{
+ long cookie;
+ CFNumberGetValue ((CFNumberRef)
+ CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
+ kCFNumberLongType, &cookie);
+
+ joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
+ /* anything else for buttons? */
+}
+
+static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
+{
+ /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
+ /* do we map hats to axes or buttons? */
+}
+#endif
+
#if TARGET_HOST_WIN32
- JOYCAPS jsCaps;
- int i;
+/* Inspired by
+ http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
+ */
+
+static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
+{
+ char buffer [ 256 ];
+
+ char OEMKey [ 256 ];
+
+ HKEY hKey;
+ DWORD dwcb;
+ LONG lr;
+
+ if ( joy->error )
+ return 0;
+
+ /* Open .. MediaResources\CurrentJoystickSettings */
+ sprintf ( buffer, "%s\\%s\\%s",
+ REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
+ REGSTR_KEY_JOYCURR );
+
+ lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
+
+ if ( lr != ERROR_SUCCESS ) return 0;
+
+ /* Get OEM Key name */
+ 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 );
+
+ lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
+ RegCloseKey ( hKey );
+
+ if ( lr != ERROR_SUCCESS ) return 0;
+
+ /* Open OEM Key from ...MediaProperties */
+ sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
+
+ lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
+
+ if ( lr != ERROR_SUCCESS ) return 0;
+
+ /* Get OEM Name */
+ dwcb = buf_sz;
+
+ lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
+ &dwcb );
+ RegCloseKey ( hKey );
+
+ if ( lr != ERROR_SUCCESS ) return 0;
+
+ return 1;
+}
+#endif
+
+
+static void fghJoystickOpen( SFG_Joystick* joy )
+{
+ int i;
+#if TARGET_HOST_MACINTOSH
+ OSStatus err;
+#endif
+#if TARGET_HOST_MAC_OSX
+ IOReturn rv;
+ SInt32 score;
+ IOCFPlugInInterface **plugin;
+
+ HRESULT pluginResult;
+
+ CFDictionaryRef props;
+ CFTypeRef topLevelElement;
+#endif
+#if TARGET_HOST_UNIX_X11
+# if defined(__FreeBSD__) || defined(__NetBSD__)
+ char *cp;
+# endif
+# ifdef JS_NEW
+ unsigned char u;
+# else
+ int counter;
+# endif
+#endif
+
+ /* Default values (for no joystick -- each conditional will reset the error flag) */
+ joy->error = TRUE;
+ joy->num_axes = joy->num_buttons = 0;
+ joy->name [0] = '\0';
+
+#if TARGET_HOST_MACINTOSH
+ /*
+ * XXX FIXME: get joystick name in Mac
+ */
+
+ err = ISpStartup ();
+
+ if ( err == noErr )
+ {
+#define ISP_CHECK_ERR(x) if ( x != noErr ) { joy->error = GL_TRUE; return; }
+
+ joy->error = GL_TRUE;
+
+ /* initialize the needs structure */
+ ISpNeed temp_isp_needs[isp_num_needs] =
+ {
+ { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
+ { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
+ { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
+ { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
+ { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
+ { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
+ { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
+ { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
+ { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
+
+ { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
+ };
+
+ memcpy ( joy->isp_needs, temp_isp_needs, sizeof(temp_isp_needs) );
+
+
+ /* next two calls allow keyboard and mouse to emulate other input
+ * devices (gamepads, joysticks, etc)
+ */
+ /*
+ err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
+ ISP_CHECK_ERR(err)
+
+
+ err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
+ ISP_CHECK_ERR(err)
+ */
+ err = ISpElement_NewVirtualFromNeeds ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 0 );
+ ISP_CHECK_ERR(err)
+
+ err = ISpInit ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 'freeglut', nil, 0, 128, 0 );
+ ISP_CHECK_ERR(err)
+
+ joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
+ joy->num_axes = joy->isp_num_axis;
+
+ for ( i = 0; i < joy->num_axes; i++ )
+ {
+ joy->dead_band [ i ] = 0;
+ joy->saturate [ i ] = 1;
+ joy->center [ i ] = kISpAxisMiddle;
+ joy->max [ i ] = kISpAxisMaximum;
+ joy->min [ i ] = kISpAxisMinimum;
+ }
+
+ joy->error = GL_FALSE;
+ }
+ else
+ joy->num_buttons = joy->num_axes = 0;
+#endif
+
+#if TARGET_HOST_MAC_OSX
+ if (joy->id >= numDevices) {
+ fgWarning ( "%s", "device index out of range in fgJoystickOpen()");
+ return;
+ }
+
+ /* create device interface */
+ rv = IOCreatePlugInInterfaceForService ( ioDevices[joy->id],
+ kIOHIDDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugin, &score);
+
+ if (rv != kIOReturnSuccess) {
+ fgWarning ( "%s", "error creating plugin for io device");
+ return;
+ }
+
+ pluginResult = (*plugin)->QueryInterface ( plugin,
+ CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), &(LPVOID) joy->hidDev );
+
+ if ( pluginResult != S_OK )
+ fgWarning ( "%s", "QI-ing IO plugin to HID Device interface failed");
+
+ (*plugin)->Release(plugin); /* don't leak a ref */
+ if (joy->hidDev == NULL) return;
+
+ /* store the interface in this instance */
+ rv = (*(joy->hidDev))->open(joy->hidDev, 0);
+ if (rv != kIOReturnSuccess) {
+ fgWarning ( "%s", "error opening device interface");
+ return;
+ }
+
+ props = getCFProperties(ioDevices[joy->id]);
+
+ /* recursively enumerate all the bits */
+ CFTypeRef topLevelElement =
+ CFDictionaryGetValue ( props, CFSTR ( kIOHIDElementKey ) );
+ enumerateElements ( topLevelElement );
+
+ CFRelease ( props );
+#endif
+
+#if TARGET_HOST_WIN32
joy->js.dwFlags = JOY_RETURNALL;
joy->js.dwSize = sizeof( joy->js );
- memset( &jsCaps, 0, sizeof( jsCaps ) );
+ memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
joy->error =
- ( joyGetDevCaps( joy->js_id, &jsCaps, sizeof( jsCaps ) ) !=
+ ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
JOYERR_NOERROR );
- joy->num_axes =
- ( jsCaps.wNumAxes < _JS_MAX_AXES ) ? jsCaps.wNumAxes : _JS_MAX_AXES;
- /*
- * WARNING - Fall through case clauses!!
- */
- switch( joy->num_axes )
+ if ( joy->jsCaps.wNumAxes == 0 )
{
- case 6:
- joy->min[ 5 ] = (float) jsCaps.wVmin;
- joy->max[ 5 ] = (float) jsCaps.wVmax;
- case 5:
- joy->min[ 4 ] = (float) jsCaps.wUmin;
- joy->max[ 4 ] = (float) jsCaps.wUmax;
- case 4:
- joy->min[ 3 ] = (float) jsCaps.wRmin;
- joy->max[ 3 ] = (float) jsCaps.wRmax;
- case 3:
- joy->min[ 2 ] = (float) jsCaps.wZmin;
- joy->max[ 2 ] = (float) jsCaps.wZmax;
- case 2:
- joy->min[ 1 ] = (float) jsCaps.wYmin;
- joy->max[ 1 ] = (float) jsCaps.wYmax;
- case 1:
- joy->min[ 0 ] = (float) jsCaps.wXmin;
- joy->max[ 0 ] = (float) jsCaps.wXmax;
- break;
-
- /*
- * I guess we have no axes at all
- */
- default:
+ joy->num_axes = 0;
joy->error = GL_TRUE;
- break;
+ }
+ else
+ {
+ /* Device name from jsCaps is often "Microsoft PC-joystick driver",
+ * at least for USB. Try to get the real name from the registry.
+ */
+ if ( ! fghJoystickGetOEMProductName ( joy, joy->name, sizeof(joy->name) ) )
+ {
+ fgWarning ( "%s", "JS: Failed to read joystick name from registry" );
+ strncpy ( joy->name, joy->jsCaps.szPname, sizeof(joy->name) );
+ }
+
+ /* Windows joystick drivers may provide any combination of
+ * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
+ */
+ if ( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
+ {
+ joy->num_axes = _JS_MAX_AXES;
+ joy->min [ 7 ] = -1.0; joy->max [ 7 ] = 1.0; /* POV Y */
+ joy->min [ 6 ] = -1.0; joy->max [ 6 ] = 1.0; /* POV X */
+ }
+ else
+ joy->num_axes = 6;
+
+ joy->min[ 5 ] = (float) joy->jsCaps.wVmin;
+ joy->max[ 5 ] = (float) joy->jsCaps.wVmax;
+ joy->min[ 4 ] = (float) joy->jsCaps.wUmin;
+ joy->max[ 4 ] = (float) joy->jsCaps.wUmax;
+ joy->min[ 3 ] = (float) joy->jsCaps.wRmin;
+ joy->max[ 3 ] = (float) joy->jsCaps.wRmax;
+ joy->min[ 2 ] = (float) joy->jsCaps.wZmin;
+ joy->max[ 2 ] = (float) joy->jsCaps.wZmax;
+ joy->min[ 1 ] = (float) joy->jsCaps.wYmin;
+ joy->max[ 1 ] = (float) joy->jsCaps.wYmax;
+ joy->min[ 0 ] = (float) joy->jsCaps.wXmin;
+ joy->max[ 0 ] = (float) joy->jsCaps.wXmax;
}
/*
* Guess all the rest judging on the axes extremals
*/
- for( i=0 ; i<joy->num_axes ; i++ )
+ for( i = 0; i < joy->num_axes; i++ )
{
joy->center [ i ] = (joy->max[i] + joy->min[i]) * 0.5f;
joy->dead_band[ i ] = 0.0f;
joy->saturate [ i ] = 1.0f;
}
+#endif
-#else
-# ifdef __FreeBSD__
- int buttons[ _JS_MAX_AXES ];
- float axes[ _JS_MAX_AXES ];
- int noargs, in_no_axes;
- char joyfname[ 1024 ];
- FILE* joyfile;
-# else
-# ifndef JS_NEW
- int counter;
-# endif
-# endif
- int i;
+#if TARGET_HOST_UNIX_X11
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+ for ( i = 0; i < _JS_MAX_AXES; i++ )
+ joy->os->cache_axes [ i ] = 0.0f;
- /*
- * Default for older Linux systems.
- */
- joy->num_axes = 2;
- joy->num_buttons = 32;
+ joy->os->cache_buttons = 0;
-# ifdef JS_NEW
- for( i=0 ; i<_JS_MAX_AXES ; i++ )
- joy->tmp_axes[ i ] = 0.0f ;
+ joy->os->fd = open ( joy->os->fname, O_RDONLY | O_NONBLOCK);
- joy->tmp_buttons = 0 ;
-# endif
+ if ( joy->os->fd < 0 && errno == EACCES)
+ fgWarning ( "%s exists but is not readable by you\n", joy->os->fname );
- joy->fd = open( joy->fname, O_RDONLY );
+ joy->error = ( joy->os->fd < 0 );
- joy->error = (joy->fd < 0);
+ if ( joy->error )
+ return;
- if( joy->error )
- return;
-/*
- * XXX All BSDs should share this?
- */
-# ifdef __FreeBSD__
- fghJoystickRawRead(joy, buttons, axes );
- joy->error = axes[ 0 ] < -1000000000.0f;
- if( joy->error )
- return;
+ joy->num_axes = 0;
+ joy->num_buttons = 0;
+ if ( joy->os->is_analog )
+ {
+ FILE *joyfile;
+ char joyfname [ 1024 ];
+ int noargs, in_no_axes;
- sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
+ float axes [ _JS_MAX_AXES ];
+ int buttons [ _JS_MAX_AXES ];
- joyfile = fopen( joyfname, "r" );
- joy->error =( joyfile == NULL );
- if( joy->error )
- return;
+ joy->num_axes = 2;
+ joy->num_buttons = 32;
- noargs = fscanf(
- joyfile, "%d%f%f%f%f%f%f",
- &in_no_axes,
- &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
- &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ]
- );
+ fghJoystickRawRead ( joy, buttons, axes );
+ joy->error = axes[0] < -1000000000.0f;
+ if ( joy->error )
+ return;
- joy->error =( noargs != 7 ) || ( in_no_axes != _JS_MAX_AXES );
- fclose( joyfile );
- if( joy->error )
+ sprintf( joyfname, "%s/.joy%drc", getenv ( "HOME" ), joy->id );
+
+ joyfile = fopen ( joyfname, "r" );
+ joy->error = ( joyfile == NULL );
+ if ( joy->error )
+ return;
+
+ noargs = fscanf ( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
+ &joy->min [ 0 ], &joy->center [ 0 ], &joy->max [ 0 ],
+ &joy->min [ 1 ], &joy->center [ 1 ], &joy->max [ 1 ] );
+ joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
+ fclose ( joyfile );
+ if ( joy->error )
+ return;
+
+ for ( i = 0; i < _JS_MAX_AXES; i++ )
+ {
+ joy->dead_band [ i ] = 0.0f;
+ joy->saturate [ i ] = 1.0f;
+ }
+
+ return; /* End of analog code */
+ }
+
+# ifdef HAVE_USB_JS
+ if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
+ &joy->num_buttons ) )
+ {
+ close ( joy->os->fd );
+ joy->error = GL_TRUE;
return;
+ }
- for( i = 0; i < _JS_MAX_AXES; i++ )
+ cp = strrchr(joy->os->fname, '/');
+ if ( cp ) {
+ if ( fghJoystickFindUSBdev ( &cp[1], joy->name, sizeof(joy->name) ) == 0 )
+ strcpy ( joy->name, &cp[1] );
+ }
+
+ if ( joy->num_axes > _JS_MAX_AXES )
+ joy->num_axes = _JS_MAX_AXES;
+
+ for ( i = 0; i < _JS_MAX_AXES; i++ )
{
- joy->dead_band[ i ] = 0.0f;
- joy->saturate [ i ] = 1.0f;
+ /* We really should get this from the HID, but that data seems
+ * to be quite unreliable for analog-to-USB converters. Punt for
+ * now.
+ */
+ if ( joy->os->axes_usage [ i ] == HUG_HAT_SWITCH )
+ {
+ joy->max [ i ] = 1.0f;
+ joy->center [ i ] = 0.0f;
+ joy->min [ i ] = -1.0f;
+ }
+ else
+ {
+ joy->max [ i ] = 255.0f;
+ joy->center [ i ] = 127.0f;
+ joy->min [ i ] = 0.0f;
+ }
+
+ joy->dead_band [ i ] = 0.0f;
+ joy->saturate [ i ] = 1.0f;
}
-# else
+# endif
+#endif
+#if defined(__linux__)
/*
- * Set the correct number of axes for the linux driver
+ * Default for older Linux systems.
*/
-# ifdef JS_NEW
+ joy->num_axes = 2;
+ joy->num_buttons = 32;
- ioctl( joy->fd, JSIOCGAXES , &joy->num_axes );
- ioctl( joy->fd, JSIOCGBUTTONS, &joy->num_buttons );
- fcntl( joy->fd, F_SETFL, O_NONBLOCK );
+# ifdef JS_NEW
+ for( i = 0; i < _JS_MAX_AXES; i++ )
+ joy->tmp_axes[ i ] = 0.0f;
+
+ joy->tmp_buttons = 0;
+# endif
+
+ joy->fd = open( joy->fname, O_RDONLY );
+
+ joy->error = (joy->fd < 0);
+
+ if ( joy->error )
+ return;
-# endif
+ /*
+ * Set the correct number of axes for the linux driver
+ */
+# ifdef JS_NEW
+ /* Melchior Franz's fixes for big-endian Linuxes since writing
+ * to the upper byte of an uninitialized word doesn't work.
+ * 9 April 2003
+ */
+ ioctl ( joy->fd, JSIOCGAXES , &u );
+ joy->num_axes = u;
+ ioctl ( joy->fd, JSIOCGBUTTONS, &u );
+ joy->num_buttons = u;
+ ioctl ( joy->fd, JSIOCGNAME ( sizeof(joy->name) ), joy->name );
+ fcntl ( joy->fd, F_SETFL , O_NONBLOCK );
+# endif
/*
* The Linux driver seems to return 512 for all axes
*
* PWO: shouldn't be that done somehow wiser on the kernel level?
*/
-# ifndef JS_NEW
+# ifndef JS_NEW
counter = 0;
do
counter < 100 &&
joy->center[ 0 ] == 512.0f &&
joy->center[ 1 ] == 512.0f );
-
- if( counter >= 100 )
+
+ if ( counter >= 100 )
joy->error = GL_TRUE;
-# endif
+# endif
- for( i = 0; i < _JS_MAX_AXES; i++ )
+ for ( i = 0; i < _JS_MAX_AXES; i++ )
{
-# ifdef JS_NEW
+# ifdef JS_NEW
joy->max [ i ] = 32767.0f;
joy->center[ i ] = 0.0f;
joy->min [ i ] = -32767.0f;
-# else
+# else
joy->max[ i ] = joy->center[ i ] * 2.0f;
joy->min[ i ] = 0.0f;
-# endif
- joy->dead_band[ i ] = 0.0f ;
- joy->saturate [ i ] = 1.0f ;
+# endif
+ joy->dead_band[ i ] = 0.0f;
+ joy->saturate [ i ] = 1.0f;
}
-# endif
+#endif
#endif
}
/*
- *
+ * This function replaces the constructor method in the JS library.
*/
void fgJoystickInit( int ident )
{
- if( fgJoystick )
- fgError( "illegal attemp to initialize joystick device" );
+ if ( ident >= MAX_NUM_JOYSTICKS )
+ fgError( "Too large a joystick number" );
+
+ if( fgJoystick[ident] )
+ fgError( "illegal attempt to initialize joystick device" );
+
+ fgJoystick[ident] = ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
+
+ /* Set defaults */
+ fgJoystick[ident]->num_axes = fgJoystick[ident]->num_buttons = 0;
+ fgJoystick[ident]->error = GL_TRUE;
- fgJoystick = ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
+#if TARGET_HOST_MACINTOSH
+ fgJoystick[ident]->id = ident;
+ sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident ); /* FIXME */
+ fgJoystick[ident]->error = GL_FALSE;
+#endif
+
+#if TARGET_HOST_MAC_OSX
+ fgJoystick[ident]->id = ident;
+ fgJoystick[ident]->error = GL_FALSE;
+ fgJoystick[ident]->num_axes = 0;
+ fgJoystick[ident]->num_buttons = 0;
+
+ if (numDevices < 0) {
+ /* do first-time init (since we can't over-ride jsInit, hmm */
+ numDevices = 0;
+
+ mach_port_t masterPort;
+ IOReturn rv = IOMasterPort ( bootstrap_port, &masterPort );
+ if ( rv != kIOReturnSuccess ) {
+ fgWarning ( "%s", "error getting master Mach port");
+ return;
+ }
+ fghJoystickFindDevices ( masterPort );
+ }
+
+ if ( ident >= numDevices ) {
+ fgJoystick[ident]->error = GL_TRUE;
+ return;
+ }
+
+ /* get the name now too */
+ CFDictionaryRef properties = getCFProperties(ioDevices[ident]);
+ CFTypeRef ref = CFDictionaryGetValue (properties, CFSTR(kIOHIDProductKey));
+ if (!ref)
+ ref = CFDictionaryGetValue (properties, CFSTR("USB Product Name"));
+
+ if (!ref || !CFStringGetCString ((CFStringRef) ref, name, 128, CFStringGetSystemEncoding ())) {
+ fgWarning ( "%s", "error getting device name");
+ name[0] = '\0';
+ }
+#endif
#if TARGET_HOST_WIN32
switch( ident )
{
case 0:
- fgJoystick->js_id = JOYSTICKID1;
- fghJoystickOpen( fgJoystick );
+ fgJoystick[ident]->js_id = JOYSTICKID1;
+ fgJoystick[ident]->error = GL_FALSE;
break;
case 1:
- fgJoystick->js_id = JOYSTICKID2;
- fghJoystickOpen( fgJoystick );
+ fgJoystick[ident]->js_id = JOYSTICKID2;
+ fgJoystick[ident]->error = GL_FALSE;
break;
default:
- fgJoystick->num_axes = 0;
- fgJoystick->error = GL_TRUE;
- break;
+ fgJoystick[ident]->num_axes = 0;
+ fgJoystick[ident]->error = GL_TRUE;
+ return;
}
-#else
+#endif
-/*
- * XXX All BSDs should share this code?
- */
-# ifdef __FreeBSD__
- fgJoystick->id = ident;
- sprintf( fgJoystick->fname, "/dev/joy%d", ident );
-# else
- sprintf( fgJoystick->fname, "/dev/js%d", ident );
-# endif
+#if TARGET_HOST_UNIX_X11
+# if defined(__FreeBSD__) || defined(__NetBSD__)
+ fgJoystick[ident]->id = ident;
+ fgJoystick[ident]->error = GL_FALSE;
+
+ fgJoystick[ident]->os = calloc (1, sizeof (struct os_specific_s));
+ memset ( fgJoystick[ident]->os, 0, sizeof(struct os_specific_s) );
+ 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 );
+ else
+ sprintf ( fgJoystick[ident]->os->fname, "%s%d", UHIDDEV, ident - USB_IDENT_OFFSET );
+# elif defined(__linux__)
+ fgJoystick[ident]->id = ident;
+ fgJoystick[ident]->error = GL_FALSE;
- fghJoystickOpen( fgJoystick );
+ sprintf ( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
+
+ if ( access ( fgJoystick[ident]->fname, F_OK ) != 0 )
+ sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident );
+# endif
#endif
+
+ fghJoystickOpen ( fgJoystick[ident] );
}
/*
*/
void fgJoystickClose( void )
{
- if( !fgJoystick )
- fgError( "illegal attempt to deinitialize joystick device" );
+ int ident ;
+ for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
+ {
+ if ( fgJoystick[ident] )
+ {
+
+#if TARGET_HOST_MACINTOSH
+ ISpSuspend ();
+ ISpStop ();
+ ISpShutdown ();
+#endif
+
+#if TARGET_HOST_MAC_OSX
+ (*(fgJoystick[ident]->hidDev))->close(fgJoystick[ident]->hidDev);
+#endif
+
+#if TARGET_HOST_WIN32
+ /* Do nothing special */
+#endif
+
+#if TARGET_HOST_UNIX_X11
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+ if ( fgJoystick[ident]->os )
+ {
+ if ( ! fgJoystick[ident]->error )
+ close ( fgJoystick[ident]->os->fd );
+#ifdef HAVE_USB_JS
+ if ( fgJoystick[ident]->os->hids )
+ free (fgJoystick[ident]->os->hids);
+ if ( fgJoystick[ident]->os->hid_data_buf )
+ free (fgJoystick[ident]->os->hid_data_buf);
+#endif
+ free (fgJoystick[ident]->os);
+ }
+#endif
-#if !TARGET_HOST_WIN32
- if( ! fgJoystick->error )
- close( fgJoystick->fd );
+ if( ! fgJoystick[ident]->error )
+ close( fgJoystick[ident]->fd );
#endif
- free( fgJoystick );
- fgJoystick = NULL; /* show joystick has been deinitialized */
+ free( fgJoystick[ident] );
+ fgJoystick[ident] = NULL; /* show joystick has been deinitialized */
+ }
+ }
}
/*
{
float axes[ _JS_MAX_AXES ];
int buttons;
+ int ident;
- freeglut_return_if_fail( fgJoystick );
freeglut_return_if_fail( window );
freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
- fghJoystickRead( fgJoystick, &buttons, axes );
-
- INVOKE_WCB( *window, Joystick,
- ( buttons,
- (int) (axes[ 0 ] * 1000.0f ),
- (int) (axes[ 1 ] * 1000.0f ),
- (int) (axes[ 2 ] * 1000.0f ) )
- );
-
- /*
- * fgSetWindow (window);
- * window->Callbacks.Joystick(
- * buttons,
- * (int) (axes[ 0 ] * 1000.0f),
- * (int) (axes[ 1 ] * 1000.0f),
- * (int) (axes[ 2 ] * 1000.0f)
- * );
- */
+ for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
+ {
+ if ( fgJoystick[ident] )
+ {
+ fghJoystickRead( fgJoystick[ident], &buttons, axes );
+
+ if ( !fgJoystick[ident]->error )
+ INVOKE_WCB( *window, Joystick,
+ ( buttons,
+ (int) (axes[ 0 ] * 1000.0f ),
+ (int) (axes[ 1 ] * 1000.0f ),
+ (int) (axes[ 2 ] * 1000.0f ) )
+ );
+ }
+ }
}
/*
* PWO: These jsJoystick class methods have not been implemented.
* We might consider adding such functions to freeglut-2.0.
*/
-#if 0
-int getNumAxes ()
- { return num_axes; }
-int notWorking ()
- { return error; }
-
-float getDeadBand ( int axis )
- { return dead_band [ axis ]; }
-void setDeadBand ( int axis, float db )
- { dead_band [ axis ] = db; }
-
-float getSaturation ( int axis )
- { return saturate [ axis ]; }
-void setSaturation ( int axis, float st )
- { saturate [ axis ] = st; }
-
-void setMinRange ( float *axes )
- { memcpy ( min , axes, num_axes * sizeof(float) ); }
-void setMaxRange ( float *axes )
- { memcpy ( max , axes, num_axes * sizeof(float) ); }
-void setCenter ( float *axes )
- { memcpy ( center, axes, num_axes * sizeof(float) ); }
-
-void getMinRange ( float *axes )
- { memcpy ( axes, min , num_axes * sizeof(float) ); }
-void getMaxRange ( float *axes )
- { memcpy ( axes, max , num_axes * sizeof(float) ); }
-void getCenter ( float *axes )
- { memcpy ( axes, center, num_axes * sizeof(float) ); }
-#endif
+int getNumAxes ( int ident )
+ { return fgJoystick[ident]->num_axes; }
+int notWorking ( int ident )
+ { return fgJoystick[ident]->error; }
+
+float getDeadBand ( int ident, int axis )
+ { return fgJoystick[ident]->dead_band [ axis ]; }
+void setDeadBand ( int ident, int axis, float db )
+ { fgJoystick[ident]->dead_band [ axis ] = db; }
+
+float getSaturation ( int ident, int axis )
+ { return fgJoystick[ident]->saturate [ axis ]; }
+void setSaturation ( int ident, int axis, float st )
+ { fgJoystick[ident]->saturate [ axis ] = st; }
+
+void setMinRange ( int ident, float *axes )
+ { memcpy ( fgJoystick[ident]->min , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
+void setMaxRange ( int ident, float *axes )
+ { memcpy ( fgJoystick[ident]->max , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
+void setCenter ( int ident, float *axes )
+ { memcpy ( fgJoystick[ident]->center, axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
+
+void getMinRange ( int ident, float *axes )
+ { memcpy ( axes, fgJoystick[ident]->min , fgJoystick[ident]->num_axes * sizeof(float) ); }
+void getMaxRange ( int ident, float *axes )
+ { memcpy ( axes, fgJoystick[ident]->max , fgJoystick[ident]->num_axes * sizeof(float) ); }
+void getCenter ( int ident, float *axes )
+ { memcpy ( axes, fgJoystick[ident]->center, fgJoystick[ident]->num_axes * sizeof(float) ); }
/*** END OF FILE ***/