X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Ffreeglut_joystick.c;h=58c594d465568b2bc0ed49de61126016a190a7a3;hb=d6ccf7c81bdad11e6902a258e8e21fdbe6b9f968;hp=5bb29702df1d264c80c9127c5f862b0b085b0e8e;hpb=2e5dc871b7c1df472978af9811d0e97b6684707c;p=freeglut diff --git a/src/freeglut_joystick.c b/src/freeglut_joystick.c index 5bb2970..58c594d 100644 --- a/src/freeglut_joystick.c +++ b/src/freeglut_joystick.c @@ -25,123 +25,407 @@ */ /* - * 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 + * + * 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 +#if defined( __FreeBSD__ ) || defined( __NetBSD__ ) +# include #endif #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif -#include "../include/GL/freeglut.h" +#include #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 +#endif + +#if TARGET_HOST_MAC_OSX +# define _JS_MAX_AXES 16 +# include +# include +# include +#endif + +#if TARGET_HOST_WIN32 +# define _JS_MAX_AXES 8 +# include +# include +# include +# include + +#endif + +#if TARGET_HOST_UNIX_X11 +# define _JS_MAX_AXES 16 +# if defined(__FreeBSD__) || defined(__NetBSD__) +/* + * XXX The below hack is done until freeglut's autoconf is updated. + */ +# define HAVE_USB_JS 1 + +# include +# if defined(__FreeBSD__) && __FreeBSD_version >= 500000 +# include +# else +/* + * XXX NetBSD/amd64 systems may find that they have to steal the + * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system. + * XXX I cannot comment whether that works for the interface, but + * XXX it lets you compile...(^& I do not think that we can do away + * XXX with this header. + */ +# include /* For analog joysticks */ +# endif +# define JS_DATA_TYPE joystick +# define JS_RETURN (sizeof(struct JS_DATA_TYPE)) +# endif + +# include +# include +# include + +# if defined(__linux__) +# include +# include + +/* 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 */ -#ifdef WIN32 -# include -# if defined( __CYGWIN32__ ) || defined( __CYGWIN__ ) -# define NEAR /* */ -# define FAR /* */ + +#if defined(__NetBSD__) || defined(__FreeBSD__) + +# ifdef HAVE_USB_JS +# if defined(__NetBSD__) +/* + * XXX The below hack is done until freeglut's autoconf is updated. + */ +# define HAVE_USBHID_H 1 +# ifdef HAVE_USBHID_H +# include +# else +# include +# endif +# elif defined(__FreeBSD__) +# if __FreeBSD_version < 500000 +# include +# else +/* + * XXX The below hack is done until freeglut's autoconf is updated. + */ +# define HAVE_USBHID_H 1 +# include +# endif +# endif +# include +# include + +/* 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 -# include -#else -# include -# include -# if defined(__FreeBSD__) || defined(__NetBSD__) -# if __FreeBSD_version >= 500000 -# include + 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 + 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 -# include -# include + 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; + } -#ifdef 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 -#ifdef WIN32 +#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 ]; @@ -151,54 +435,172 @@ struct tagSFG_Joystick }; /* + * 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 */ -static void fghJoystickRawRead ( SFG_Joystick* joy, int* buttons, float* axes ) +static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes ) { -#ifdef WIN32 +#if TARGET_HOST_WIN32 MMRESULT status; #else 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; inum_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 -#ifdef WIN32 +#if TARGET_HOST_WIN32 status = joyGetPosEx( joy->js_id, &joy->js ); - if( status != JOYERR_NOERROR ) + if ( status != JOYERR_NOERROR ) { - joy->error = TRUE; + 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; @@ -207,77 +609,167 @@ static void fghJoystickRawRead ( SFG_Joystick* joy, int* buttons, float* axes ) 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 ); + sizeof( float ) * joy->num_axes ); return; } - fgWarning( "%s", joy->fname ); - joy->error = TRUE; + 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 */ - joy->tmp_buttons &= ~(1 << joy->js.number); - else - joy->tmp_buttons |= (1 << joy->js.number); + if( joy->js.value == 0 ) /* clear the flag */ + joy->tmp_buttons &= ~( 1 << joy->js.number ); + else + joy->tmp_buttons |= ( 1 << joy->js.number ); 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) * joy->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 = TRUE; + 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 } @@ -288,8 +780,8 @@ static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis ) { if( value < joy->center[ axis ] ) { - float xx = (value - joy->center[ axis ]) / (joy->center[ axis ] - - joy->min[ axis ]); + float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] - + joy->min[ axis ] ); if( xx < -joy->saturate[ axis ] ) return -1.0f; @@ -297,24 +789,24 @@ static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis ) if( xx > -joy->dead_band [ axis ] ) return 0.0f; - xx = (xx + joy->dead_band[ axis ]) / (joy->saturate[ axis ] - - joy->dead_band[ axis ]); + xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] - + joy->dead_band[ axis ] ); return ( xx < -1.0f ) ? -1.0f : xx; } else { - float xx = (value - joy->center [ axis ]) / (joy->max[ axis ] - - joy->center[ axis ]); + float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] - + joy->center[ axis ] ); if( xx > joy->saturate[ axis ] ) - return 1.0f ; + return 1.0f; if( xx < joy->dead_band[ axis ] ) - return 0.0f ; + return 0.0f; - xx = (xx - joy->dead_band[ axis ]) / (joy->saturate[ axis ] - - joy->dead_band[ axis ]); + xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] - + joy->dead_band[ axis ] ); return ( xx > 1.0f ) ? 1.0f : xx; } @@ -334,158 +826,681 @@ static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes ) *buttons = 0; if( axes ) - for ( i=0; inum_axes ; i++ ) - axes[ i ] = 0.0f ; + for ( i=0; inum_axes; i++ ) + axes[ i ] = 0.0f; } fghJoystickRawRead( joy, buttons, raw_axes ); if( axes ) - for( i=0 ; inum_axes ; i++ ) + for( i=0; inum_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 ) { -#ifdef WIN32 - JOYCAPS jsCaps; - int i; + CFMutableDictionaryRef hidMatch = NULL; + IOReturn rv = kIOReturnSuccess; - joy->js.dwFlags = JOY_RETURNALL; - joy->js.dwSize = sizeof( joy->js ); + io_iterator_t hidIterator; + io_object_t ioDev; - memset( &jsCaps, 0, sizeof(jsCaps) ); + /* build a dictionary matching HID devices */ + hidMatch = IOServiceMatching(kIOHIDDeviceKey); - joy->error = - (joyGetDevCaps( joy->js_id, &jsCaps, sizeof(jsCaps) ) != - JOYERR_NOERROR); - joy->num_axes = - (jsCaps.wNumAxes < _JS_MAX_AXES ) ? jsCaps.wNumAxes : _JS_MAX_AXES; + rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator); + if (rv != kIOReturnSuccess || !hidIterator) { + fgWarning( "%s", "no joystick (HID) devices found" ); + return; + } - /* - * WARNING - Fall through case clauses!! + /* 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 */ - switch( joy->num_axes ) - { - 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; + + 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(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; - /* - * I guess we have no axes at all - */ default: - joy->error = TRUE; 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 +/* Inspired by + http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp + */ +# if defined(_MSC_VER) +# pragma comment (lib, "advapi32.lib") +# endif + +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 /* - * Guess all the rest judging on the axes extremals + * Default values (for no joystick -- each conditional will reset the + * error flag) */ - for( i=0 ; inum_axes ; i++ ) + 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 ) { - joy->center [ i ] = (joy->max[i] + joy->min[i]) * 0.5f; - joy->dead_band[ i ] = 0.0f; - joy->saturate [ i ] = 1.0f; +#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 -#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_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( "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( &joy->jsCaps, 0, sizeof( joy->jsCaps ) ); + + joy->error = + ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) != + JOYERR_NOERROR ); + + if( joy->jsCaps.wNumAxes == 0 ) + { + joy->num_axes = 0; + joy->error = GL_TRUE; + } + 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( "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; + } /* - * Default for older Linux systems. + * Guess all the rest judging on the axes extremals */ - joy->num_axes = 2; - joy->num_buttons = 32; + 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 -# ifdef JS_NEW - for( i=0 ; i<_JS_MAX_AXES ; i++ ) - joy->tmp_axes[ i ] = 0.0f ; +#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; - joy->tmp_buttons = 0 ; -# endif + joy->os->cache_buttons = 0; - joy->fd = open( joy->fname, O_RDONLY ); + joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK); + + if( joy->os->fd < 0 && errno == EACCES ) + fgWarning ( "%s exists but is not readable by you\n", joy->os->fname ); - joy->error = (joy->fd < 0); + joy->error =( joy->os->fd < 0 ); 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 ; - sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id ); + joy->num_axes = 0; + joy->num_buttons = 0; + if( joy->os->is_analog ) + { + FILE *joyfile; + char joyfname[ 1024 ]; + int noargs, in_no_axes; - joyfile = fopen( joyfname, "r" ); - joy->error = (joyfile == NULL); - if( joy->error ) - return; + float axes [ _JS_MAX_AXES ]; + int buttons[ _JS_MAX_AXES ]; - 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->num_axes = 2; + joy->num_buttons = 32; - joy->error = (noargs != 7) || (in_no_axes != _JS_MAX_AXES); - fclose( joyfile ); - if( joy->error ) + fghJoystickRawRead( joy, buttons, axes ); + joy->error = axes[ 0 ] < -1000000000.0f; + if( joy->error ) + return; + + 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; + } + + cp = strrchr( joy->os->fname, '/' ); + if( cp ) + { + if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) == + 0 ) + strcpy( joy->name, &cp[1] ); + } - for( i=0 ; i<_JS_MAX_AXES ; i++ ) + if( joy->num_axes > _JS_MAX_AXES ) + joy->num_axes = _JS_MAX_AXES; + + for( i = 0; i < _JS_MAX_AXES; i++ ) { + /* 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; + 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 -# endif + joy->fd = open( joy->fname, O_RDONLY ); + + joy->error =( joy->fd < 0 ); + + if( joy->error ) + return; + + /* + * 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 @@ -495,79 +1510,150 @@ static void fghJoystickOpen( SFG_Joystick* joy ) * * PWO: shouldn't be that done somehow wiser on the kernel level? */ -# ifndef JS_NEW - counter = 0 ; +# ifndef JS_NEW + counter = 0; do - { + { fghJoystickRawRead( joy, NULL, joy->center ); counter++; } while( !joy->error && counter < 100 && joy->center[ 0 ] == 512.0f && joy->center[ 1 ] == 512.0f ); - - if( counter >= 100 ) - joy->error = TRUE; -# endif - for( i=0 ; i<_JS_MAX_AXES ; i++ ) + if ( counter >= 100 ) + joy->error = GL_TRUE; +# endif + + 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 != NULL ) - 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; + +#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; - fgJoystick = (SFG_Joystick *)calloc( sizeof(SFG_Joystick), 1 ); + 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 -#ifdef WIN32 +#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 = 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; + + sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident ); - fghJoystickOpen( fgJoystick ); + if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 ) + sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); +# endif #endif + + fghJoystickOpen( fgJoystick[ ident ] ); } /* @@ -575,16 +1661,52 @@ void fgJoystickInit( int ident ) */ void fgJoystickClose( void ) { - if( fgJoystick == NULL ) - 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 -#ifndef WIN32 - if( fgJoystick->error != TRUE ) - close( fgJoystick->fd ); +#if TARGET_HOST_MAC_OSX + ( *( fgJoystick[ ident ]->hidDev ) )-> + close( fgJoystick[ ident ]->hidDev ); #endif - free ( fgJoystick ) ; - fgJoystick = NULL ; /* show joystick has been deinitialized */ +#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( ! fgJoystick[ident]->error ) + close( fgJoystick[ ident ]->fd ); +#endif + + free( fgJoystick[ ident ] ); + fgJoystick[ ident ] = NULL; + /* show joystick has been deinitialized */ + } + } } /* @@ -595,54 +1717,89 @@ void fgJoystickPollWindow( SFG_Window* window ) { float axes[ _JS_MAX_AXES ]; int buttons; + int ident; - freeglut_return_if_fail( fgJoystick != NULL && window != NULL ); - freeglut_return_if_fail( window->Callbacks.Joystick != NULL ); + freeglut_return_if_fail( window ); + freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) ); - fghJoystickRead( fgJoystick, &buttons, axes ); - - 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 glutJoystickGetNumAxes( int ident ) +{ + return fgJoystick[ ident ]->num_axes; +} +int glutJoystickNotWorking( int ident ) +{ + return fgJoystick[ ident ]->error; +} + +float glutJoystickGetDeadBand( int ident, int axis ) +{ + return fgJoystick[ ident ]->dead_band [ axis ]; +} +void glutJoystickSetDeadBand( int ident, int axis, float db ) +{ + fgJoystick[ ident ]->dead_band[ axis ] = db; +} + +float glutJoystickGetSaturation( int ident, int axis ) +{ + return fgJoystick[ ident ]->saturate[ axis ]; +} +void glutJoystickSetSaturation( int ident, int axis, float st ) +{ + fgJoystick[ ident ]->saturate [ axis ] = st; +} + +void glutJoystickSetMinRange( int ident, float *axes ) +{ + memcpy( fgJoystick[ ident ]->min, axes, + fgJoystick[ ident ]->num_axes * sizeof( float ) ); +} +void glutJoystickSetMaxRange( int ident, float *axes ) +{ + memcpy( fgJoystick[ ident ]->max, axes, + fgJoystick[ ident ]->num_axes * sizeof( float ) ); +} +void glutJoystickSetCenter( int ident, float *axes ) +{ + memcpy( fgJoystick[ ident ]->center, axes, + fgJoystick[ ident ]->num_axes * sizeof( float ) ); +} + +void glutJoystickGetMinRange( int ident, float *axes ) +{ + memcpy( axes, fgJoystick[ ident ]->min, + fgJoystick[ ident ]->num_axes * sizeof( float ) ); +} +void glutJoystickGetMaxRange( int ident, float *axes ) +{ + memcpy( axes, fgJoystick[ ident ]->max, + fgJoystick[ ident ]->num_axes * sizeof( float ) ); +} +void glutJoystickGetCenter( int ident, float *axes ) +{ + memcpy( axes, fgJoystick[ ident ]->center, + fgJoystick[ ident ]->num_axes * sizeof( float ) ); +} /*** END OF FILE ***/