4 * Joystick handling code
6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7 * Written by Steve Baker, <sjbaker1@airmail.net>
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
23 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu>
30 * Redone by John Fay 2/4/04 with another look from the PLIB "js" library.
31 * Many thanks for Steve Baker for permission to pull from that library.
34 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
35 # include <sys/param.h>
38 #include <GL/freeglut.h>
39 #include "freeglut_internal.h"
42 * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
45 #define _JS_MAX_BUTTONS 32
48 #if TARGET_HOST_MACINTOSH
49 # define _JS_MAX_AXES 9
50 # include <InputSprocket.h>
53 #if TARGET_HOST_MAC_OSX
54 # define _JS_MAX_AXES 16
55 # include <mach/mach.h>
56 # include <IOKit/IOkitLib.h>
57 # include <IOKit/hid/IOHIDLib.h>
61 # define _JS_MAX_AXES 8
63 # include <mmsystem.h>
69 #if TARGET_HOST_UNIX_X11
70 # define _JS_MAX_AXES 16
71 # if defined(__FreeBSD__) || defined(__NetBSD__)
72 /* XXX The below hack is done until freeglut's autoconf is updated. */
73 # define HAVE_USB_JS 1
75 # include <sys/ioctl.h>
76 # if defined(__FreeBSD__) && __FreeBSD_version >= 500000
77 # include <sys/joystick.h>
80 * XXX NetBSD/amd64 systems may find that they have to steal the
81 * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
82 * XXX I cannot comment whether that works for the interface, but
83 * XXX it lets you compile...(^& I do not think that we can do away
84 * XXX with this header.
86 # include <machine/joystick.h> /* For analog joysticks */
88 # define JS_DATA_TYPE joystick
89 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
96 # if defined(__linux__)
97 # include <sys/ioctl.h>
98 # include <linux/joystick.h>
100 /* check the joystick driver version */
101 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
104 # else /* Not BSD or Linux */
108 * We'll put these values in and that should
109 * allow the code to at least compile when there is
110 * no support. The JS open routine should error out
111 * and shut off all the code downstream anyway and if
112 * the application doesn't use a joystick we'll be fine.
122 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
130 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
132 #if defined(__NetBSD__) || defined(__FreeBSD__)
135 # if defined(__NetBSD__)
136 /* XXX The below hack is done until freeglut's autoconf is updated. */
137 # define HAVE_USBHID_H 1
138 # ifdef HAVE_USBHID_H
143 # elif defined(__FreeBSD__)
144 # if __FreeBSD_version < 500000
145 # include <libusbhid.h>
147 /* XXX The below hack is done until freeglut's autoconf is updated. */
148 # define HAVE_USBHID_H 1
152 # include <dev/usb/usb.h>
153 # include <dev/usb/usbhid.h>
155 /* Compatibility with older usb.h revisions */
156 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
157 # define USB_MAX_DEVNAMES MAXDEVNAMES
161 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
162 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
163 struct os_specific_s {
167 /* The following structure members are specific to analog joysticks */
170 /* The following structure members are specific to USB joysticks */
171 struct hid_item *hids;
175 int axes_usage [ _JS_MAX_AXES ];
177 /* We keep button and axes state ourselves, as they might not be updated
178 * on every read of a USB device
181 float cache_axes [ _JS_MAX_AXES ];
184 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
185 # define USB_IDENT_OFFSET 2
187 # define USBDEV "/dev/usb"
188 # define UHIDDEV "/dev/uhid"
189 # define AJSDEV "/dev/joy"
193 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
194 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
195 * return the uhidN device name. We warn the user of this situation once.
197 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
199 struct usb_device_info di;
203 for (a = 1; a < USB_MAX_DEVICES; a++) {
205 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
207 for (i = 0; i < USB_MAX_DEVNAMES; i++)
208 if (di.udi_devnames[i][0] &&
209 strcmp(di.udi_devnames[i], dev) == 0) {
210 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
211 strcpy(cp, di.udi_vendor);
213 strcat(cp, di.udi_product);
214 strncpy(out, cp, outlen - 1);
223 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
228 static int protection_warned = 0;
230 for (i = 0; i < 16; i++) {
231 sprintf(buf, "%s%d", USBDEV, i);
232 f = open(buf, O_RDONLY);
234 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
238 } else if (errno == EACCES) {
239 if (!protection_warned) {
240 fgWarning ( "Can't open %s for read!", buf );
241 protection_warned = 1;
248 static int fghJoystickInitializeHID(struct os_specific_s *os,
249 int *num_axes, int *num_buttons)
251 int size, is_joystick;
252 # ifdef HAVE_USBHID_H
259 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
261 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
267 # ifdef HAVE_USBHID_H
268 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
270 /*** XXX {report_id} may not be the right variable? ***/
271 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
275 size = hid_report_size( rd, hid_input, report_id );
277 size = hid_report_size( rd, 0, hid_input );
279 os->hid_data_buf = calloc( 1, size );
283 # ifdef HAVE_USBHID_H
284 d = hid_start_parse( rd, 1 << hid_input, report_id );
286 d = hid_start_parse( rd, 1 << hid_input );
288 while( hid_get_item( d, &h ) )
290 int usage, page, interesting_hid;
292 page = HID_PAGE( h.usage );
293 usage = HID_USAGE( h.usage );
295 /* This test is somewhat too simplistic, but this is how MicroSoft
296 * does, so I guess it works for all joysticks/game pads. */
297 is_joystick = is_joystick ||
298 ( h.kind == hid_collection &&
299 page == HUP_GENERIC_DESKTOP &&
300 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
302 if( h.kind != hid_input )
308 interesting_hid = TRUE;
309 if( page == HUP_GENERIC_DESKTOP )
320 if( *num_axes < _JS_MAX_AXES )
322 os->axes_usage[ *num_axes ] = usage;
327 /* Allocate two axes for a hat */
328 if( *num_axes + 1 < _JS_MAX_AXES )
330 os->axes_usage[ *num_axes ] = usage;
332 os->axes_usage[ *num_axes ] = usage;
337 interesting_hid = FALSE;
341 else if( page == HUP_BUTTON )
343 interesting_hid = ( usage > 0 ) &&
344 ( usage <= _JS_MAX_BUTTONS );
346 if( interesting_hid && usage - 1 > *num_buttons )
347 *num_buttons = usage - 1;
350 if( interesting_hid )
353 os->hids = calloc( 1, sizeof ( struct hid_item ) );
359 return os->hids != NULL;
365 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
366 * See "js.h" lines 80-178.
368 typedef struct tagSFG_Joystick SFG_Joystick;
369 struct tagSFG_Joystick
371 #if TARGET_HOST_MACINTOSH
372 #define ISP_NUM_AXIS 9
373 #define ISP_NUM_NEEDS 41
374 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
375 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
378 #if TARGET_HOST_MAC_OSX
379 IOHIDDeviceInterface ** hidDev;
380 IOHIDElementCookie buttonCookies[41];
381 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
382 long minReport[_JS_MAX_AXES],
383 maxReport[_JS_MAX_AXES];
386 #if TARGET_HOST_WIN32
393 #if TARGET_HOST_UNIX_X11
394 # if defined(__FreeBSD__) || defined(__NetBSD__)
395 struct os_specific_s *os;
401 float tmp_axes [ _JS_MAX_AXES ];
403 struct JS_DATA_TYPE js;
416 float dead_band[ _JS_MAX_AXES ];
417 float saturate [ _JS_MAX_AXES ];
418 float center [ _JS_MAX_AXES ];
419 float max [ _JS_MAX_AXES ];
420 float min [ _JS_MAX_AXES ];
424 * Functions associated with the "jsJoystick" class in PLIB
426 #if TARGET_HOST_MAC_OSX
427 #define K_NUM_DEVICES 32
429 io_object_t ioDevices[K_NUM_DEVICES];
431 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
432 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
434 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
435 /* callback for CFArrayApply */
436 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
437 static void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
439 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
440 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
441 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
446 * The static joystick structure pointer
448 #define MAX_NUM_JOYSTICKS 2
449 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
453 * Read the raw joystick data
455 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
457 #if TARGET_HOST_WIN32
463 #if defined(__FreeBSD__) || defined(__NetBSD__)
474 for( i = 0; i < joy->num_axes; i++ )
480 #if TARGET_HOST_MACINTOSH
485 for ( i = 0; i < joy->num_buttons; i++ )
488 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
491 *buttons |= state << i;
497 for ( i = 0; i < joy->num_axes; i++ )
500 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
503 axes [i] = (float) state;
508 #if TARGET_HOST_MAC_OSX
509 if ( buttons != NULL )
513 for ( i = 0; i < joy->num_buttons; i++ )
515 IOHIDEventStruct hidEvent;
516 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
517 if ( hidEvent.value )
524 for ( i = 0; i < joy->num_axes; i++ )
526 IOHIDEventStruct hidEvent;
527 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
528 axes[i] = hidEvent.value;
533 #if TARGET_HOST_WIN32
534 status = joyGetPosEx( joy->js_id, &joy->js );
536 if ( status != JOYERR_NOERROR )
538 joy->error = GL_TRUE;
543 *buttons = joy->js.dwButtons;
548 * WARNING - Fall through case clauses!!
550 switch ( joy->num_axes )
553 /* Generate two POV axes from the POV hat angle.
554 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
555 * hundredths of a degree, or 0xFFFF when idle.
557 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
564 /* This is the contentious bit: how to convert angle to X/Y.
565 * wk: I know of no define for PI that we could use here:
566 * SG_PI would pull in sg, M_PI is undefined for MSVC
567 * But the accuracy of the value of PI is very unimportant at
570 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
571 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
573 /* Convert to coordinates on a square so that North-East
574 * is (1,1) not (.7,.7), etc.
575 * s and c cannot both be zero so we won't divide by zero.
577 if ( fabs ( s ) < fabs ( c ) )
579 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
580 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
584 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
585 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
589 case 6: axes[5] = (float) joy->js.dwVpos;
590 case 5: axes[4] = (float) joy->js.dwUpos;
591 case 4: axes[3] = (float) joy->js.dwRpos;
592 case 3: axes[2] = (float) joy->js.dwZpos;
593 case 2: axes[1] = (float) joy->js.dwYpos;
594 case 1: axes[0] = (float) joy->js.dwXpos;
599 #if TARGET_HOST_UNIX_X11
600 # if defined(__FreeBSD__) || defined(__NetBSD__)
601 if ( joy->os->is_analog )
603 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
604 if ( status != sizeof(joy->os->ajs) ) {
605 perror ( joy->os->fname );
606 joy->error = GL_TRUE;
609 if ( buttons != NULL )
610 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
614 axes[0] = (float) joy->os->ajs.x;
615 axes[1] = (float) joy->os->ajs.y;
622 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
626 for ( h = joy->os->hids; h; h = h->next )
628 int d = hid_get_data ( joy->os->hid_data_buf, h );
630 int page = HID_PAGE ( h->usage );
631 int usage = HID_USAGE ( h->usage );
633 if ( page == HUP_GENERIC_DESKTOP )
636 for ( i = 0; i < joy->num_axes; i++ )
637 if (joy->os->axes_usage[i] == usage)
639 if (usage == HUG_HAT_SWITCH)
643 joy->os->cache_axes[i] = (float)hatmap_x[d];
644 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
648 joy->os->cache_axes[i] = (float)d;
653 else if (page == HUP_BUTTON)
655 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
658 joy->os->cache_buttons |= (1 << usage - 1);
660 joy->os->cache_buttons &= ~(1 << usage - 1);
665 if ( len < 0 && errno != EAGAIN )
667 perror( joy->os->fname );
670 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
672 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
680 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
682 if ( status != sizeof( struct js_event ) )
684 if ( errno == EAGAIN )
686 /* Use the old values */
688 *buttons = joy->tmp_buttons;
690 memcpy( axes, joy->tmp_axes,
691 sizeof( float ) * joy->num_axes );
695 fgWarning ( "%s", joy->fname );
696 joy->error = GL_TRUE;
700 switch ( joy->js.type & ~JS_EVENT_INIT )
702 case JS_EVENT_BUTTON:
703 if( joy->js.value == 0 ) /* clear the flag */
704 joy->tmp_buttons &= ~( 1 << joy->js.number );
706 joy->tmp_buttons |= ( 1 << joy->js.number );
710 if ( joy->js.number < joy->num_axes )
712 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
715 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
720 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
722 /* use the old values */
724 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
726 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
732 *buttons = joy->tmp_buttons;
736 status = read( joy->fd, &joy->js, JS_RETURN );
738 if ( status != JS_RETURN )
740 fgWarning( "%s", joy->fname );
741 joy->error = GL_TRUE;
746 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
747 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
749 *buttons = joy->js.buttons;
754 axes[ 0 ] = (float) joy->js.x;
755 axes[ 1 ] = (float) joy->js.y;
762 * Correct the joystick axis data
764 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
766 if( value < joy->center[ axis ] )
768 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
771 if( xx < -joy->saturate[ axis ] )
774 if( xx > -joy->dead_band [ axis ] )
777 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
778 joy->dead_band[ axis ] );
780 return ( xx < -1.0f ) ? -1.0f : xx;
784 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
785 joy->center[ axis ] );
787 if( xx > joy->saturate[ axis ] )
790 if( xx < joy->dead_band[ axis ] )
793 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
794 joy->dead_band[ axis ] );
796 return ( xx > 1.0f ) ? 1.0f : xx;
801 * Read the corrected joystick data
803 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
805 float raw_axes[ _JS_MAX_AXES ];
814 for ( i=0; i<joy->num_axes; i++ )
818 fghJoystickRawRead( joy, buttons, raw_axes );
821 for( i=0; i<joy->num_axes; i++ )
822 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
826 * Happy happy happy joy joy joy (happy new year toudi :D)
830 #if TARGET_HOST_MAC_OSX
831 /** open the IOKit connection, enumerate all the HID devices, add their
832 interface references to the static array. We then use the array index
833 as the device number when we come to open() the joystick. */
834 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
836 CFMutableDictionaryRef hidMatch = NULL;
837 IOReturn rv = kIOReturnSuccess;
839 io_iterator_t hidIterator;
842 /* build a dictionary matching HID devices */
843 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
845 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
846 if (rv != kIOReturnSuccess || !hidIterator) {
847 fgWarning( "no joystick (HID) devices found" );
852 while ((ioDev = IOIteratorNext(hidIterator))) {
853 /* filter out keyboard and mouse devices */
854 CFDictionaryRef properties = getCFProperties(ioDev);
857 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
858 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
859 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
860 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
862 /* keep only joystick devices */
863 if ( ( page == kHIDPage_GenericDesktop ) && (
864 (usage == kHIDUsage_GD_Joystick)
865 || (usage == kHIDUsage_GD_GamePad)
866 || (usage == kHIDUsage_GD_MultiAxisController)
867 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
868 /* add it to the array */
869 ioDevices[numDevices++] = ioDev;
872 IOObjectRelease(hidIterator);
875 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
878 CFMutableDictionaryRef cfProperties;
881 /* comment copied from darwin/SDL_sysjoystick.c */
882 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
883 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
886 io_registry_entry_t parent1, parent2;
888 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
889 if (rv != kIOReturnSuccess) {
890 fgWarning ( "error getting device entry parent");
894 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
895 if (rv != kIOReturnSuccess) {
896 fgWarning ( "error getting device entry parent 2");
901 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
902 &cfProperties, kCFAllocatorDefault, kNilOptions);
903 if (rv != kIOReturnSuccess || !cfProperties) {
904 fgWarning ( "error getting device properties");
911 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
913 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
914 fgError ( "%s", "element enumerator passed non-dictionary value");
918 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
921 /** element enumerator function : pass NULL for top-level*/
922 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
924 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
925 "Joystick element type mismatch",
926 "fghJoystickEnumerateElements" );
928 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
929 CFArrayApplyFunction((CFArrayRef) element, range,
930 &fghJoystickElementEnumerator, joy );
933 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
935 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
936 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
938 long type, page, usage;
940 CFNumberGetValue((CFNumberRef)
941 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
942 kCFNumberLongType, &type);
945 case kIOHIDElementTypeInput_Misc:
946 case kIOHIDElementTypeInput_Axis:
947 case kIOHIDElementTypeInput_Button:
948 printf("got input element...");
949 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
950 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
952 if (page == kHIDPage_GenericDesktop) {
953 switch ( usage ) /* look at usage to determine function */
958 case kHIDUsage_GD_Rx:
959 case kHIDUsage_GD_Ry:
960 case kHIDUsage_GD_Rz:
961 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
963 fghJoystickAddAxisElement((CFDictionaryRef) element);
966 case kHIDUsage_GD_Hatswitch:
968 fghJoystickAddHatElement((CFDictionaryRef) element);
972 fgWarning ( "input type element has weird usage (%x)", usage);
975 } else if (page == kHIDPage_Button) {
977 fghJoystickAddButtonElement((CFDictionaryRef) element);
979 fgWarning ( "input type element has weird page (%x)", page);
982 case kIOHIDElementTypeCollection:
983 fghJoystickEnumerateElements (
984 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
993 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
995 long cookie, lmin, lmax;
996 int index = joy->num_axes++;
998 CFNumberGetValue ((CFNumberRef)
999 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1000 kCFNumberLongType, &cookie);
1002 axisCookies[index] = (IOHIDElementCookie) cookie;
1004 CFNumberGetValue ((CFNumberRef)
1005 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1006 kCFNumberLongType, &lmin);
1008 CFNumberGetValue ((CFNumberRef)
1009 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1010 kCFNumberLongType, &lmax);
1012 joy->min[index] = lmin;
1013 joy->max[index] = lmax;
1014 joy->dead_band[index] = 0.0;
1015 joy->saturate[index] = 1.0;
1016 joy->center[index] = (lmax + lmin) * 0.5;
1019 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1022 CFNumberGetValue ((CFNumberRef)
1023 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1024 kCFNumberLongType, &cookie);
1026 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1027 /* anything else for buttons? */
1030 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1032 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1033 /* do we map hats to axes or buttons? */
1037 #if TARGET_HOST_WIN32
1039 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1041 # if defined(_MSC_VER)
1042 # pragma comment (lib, "advapi32.lib")
1045 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1047 char buffer [ 256 ];
1049 char OEMKey [ 256 ];
1058 /* Open .. MediaResources\CurrentJoystickSettings */
1059 sprintf ( buffer, "%s\\%s\\%s",
1060 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1061 REGSTR_KEY_JOYCURR );
1063 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1065 if ( lr != ERROR_SUCCESS ) return 0;
1067 /* Get OEM Key name */
1068 dwcb = sizeof(OEMKey);
1070 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1071 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1073 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1074 RegCloseKey ( hKey );
1076 if ( lr != ERROR_SUCCESS ) return 0;
1078 /* Open OEM Key from ...MediaProperties */
1079 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1081 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1083 if ( lr != ERROR_SUCCESS ) return 0;
1088 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1090 RegCloseKey ( hKey );
1092 if ( lr != ERROR_SUCCESS ) return 0;
1099 static void fghJoystickOpen( SFG_Joystick* joy )
1102 #if TARGET_HOST_MACINTOSH
1105 #if TARGET_HOST_MAC_OSX
1108 IOCFPlugInInterface **plugin;
1110 HRESULT pluginResult;
1112 CFDictionaryRef props;
1113 CFTypeRef topLevelElement;
1115 #if TARGET_HOST_UNIX_X11
1116 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1127 * Default values (for no joystick -- each conditional will reset the
1131 joy->num_axes = joy->num_buttons = 0;
1132 joy->name[ 0 ] = '\0';
1134 #if TARGET_HOST_MACINTOSH
1135 /* XXX FIXME: get joystick name in Mac */
1137 err = ISpStartup( );
1141 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1143 joy->error = GL_TRUE;
1145 /* initialize the needs structure */
1146 ISpNeed temp_isp_needs[ isp_num_needs ] =
1148 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1149 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1150 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1151 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1152 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1153 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1154 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1155 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1156 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1158 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1159 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1160 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1161 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1192 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1195 /* next two calls allow keyboard and mouse to emulate other input
1196 * devices (gamepads, joysticks, etc)
1199 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1203 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1207 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1208 joy->isp_needs, joy->isp_elem,
1210 ISP_CHECK_ERR( err )
1212 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1213 'freeglut', nil, 0, 128, 0 );
1214 ISP_CHECK_ERR( err )
1216 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1217 joy->num_axes = joy->isp_num_axis;
1219 for( i = 0; i < joy->num_axes; i++ )
1221 joy->dead_band[ i ] = 0;
1222 joy->saturate [ i ] = 1;
1223 joy->center [ i ] = kISpAxisMiddle;
1224 joy->max [ i ] = kISpAxisMaximum;
1225 joy->min [ i ] = kISpAxisMinimum;
1228 joy->error = GL_FALSE;
1231 joy->num_buttons = joy->num_axes = 0;
1234 #if TARGET_HOST_MAC_OSX
1235 if( joy->id >= numDevices )
1237 fgWarning( "device index out of range in fgJoystickOpen()" );
1241 /* create device interface */
1242 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1243 kIOHIDDeviceUserClientTypeID,
1244 kIOCFPlugInInterfaceID,
1247 if( rv != kIOReturnSuccess )
1249 fgWarning( "error creating plugin for io device" );
1253 pluginResult = ( *plugin )->QueryInterface(
1255 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1256 &( LPVOID )joy->hidDev
1259 if( pluginResult != S_OK )
1260 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1262 ( *plugin )->Release( plugin ); /* don't leak a ref */
1263 if( joy->hidDev == NULL )
1266 /* store the interface in this instance */
1267 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1268 if( rv != kIOReturnSuccess )
1270 fgWarning( "error opening device interface");
1274 props = getCFProperties( ioDevices[ joy->id ] );
1276 /* recursively enumerate all the bits */
1277 CFTypeRef topLevelElement =
1278 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1279 enumerateElements( topLevelElement );
1284 #if TARGET_HOST_WIN32
1285 joy->js.dwFlags = JOY_RETURNALL;
1286 joy->js.dwSize = sizeof( joy->js );
1288 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1291 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1294 if( joy->jsCaps.wNumAxes == 0 )
1297 joy->error = GL_TRUE;
1301 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1302 * at least for USB. Try to get the real name from the registry.
1304 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1305 sizeof( joy->name ) ) )
1307 fgWarning( "JS: Failed to read joystick name from registry" );
1308 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1311 /* Windows joystick drivers may provide any combination of
1312 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1314 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1316 joy->num_axes = _JS_MAX_AXES;
1317 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1318 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1323 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1324 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1325 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1326 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1327 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1328 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1329 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1330 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1331 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1332 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1333 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1334 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1337 /* Guess all the rest judging on the axes extremals */
1338 for( i = 0; i < joy->num_axes; i++ )
1340 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1341 joy->dead_band[ i ] = 0.0f;
1342 joy->saturate [ i ] = 1.0f;
1346 #if TARGET_HOST_UNIX_X11
1347 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1348 for( i = 0; i < _JS_MAX_AXES; i++ )
1349 joy->os->cache_axes[ i ] = 0.0f;
1351 joy->os->cache_buttons = 0;
1353 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1355 if( joy->os->fd < 0 && errno == EACCES )
1356 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1358 joy->error =( joy->os->fd < 0 );
1364 joy->num_buttons = 0;
1365 if( joy->os->is_analog )
1368 char joyfname[ 1024 ];
1369 int noargs, in_no_axes;
1371 float axes [ _JS_MAX_AXES ];
1372 int buttons[ _JS_MAX_AXES ];
1375 joy->num_buttons = 32;
1377 fghJoystickRawRead( joy, buttons, axes );
1378 joy->error = axes[ 0 ] < -1000000000.0f;
1382 sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
1384 joyfile = fopen( joyfname, "r" );
1385 joy->error =( joyfile == NULL );
1389 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1390 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1391 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1392 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1397 for( i = 0; i < _JS_MAX_AXES; i++ )
1399 joy->dead_band[ i ] = 0.0f;
1400 joy->saturate [ i ] = 1.0f;
1403 return; /* End of analog code */
1407 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1408 &joy->num_buttons ) )
1410 close( joy->os->fd );
1411 joy->error = GL_TRUE;
1415 cp = strrchr( joy->os->fname, '/' );
1418 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1420 strcpy( joy->name, &cp[1] );
1423 if( joy->num_axes > _JS_MAX_AXES )
1424 joy->num_axes = _JS_MAX_AXES;
1426 for( i = 0; i < _JS_MAX_AXES; i++ )
1428 /* We really should get this from the HID, but that data seems
1429 * to be quite unreliable for analog-to-USB converters. Punt for
1432 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1434 joy->max [ i ] = 1.0f;
1435 joy->center[ i ] = 0.0f;
1436 joy->min [ i ] = -1.0f;
1440 joy->max [ i ] = 255.0f;
1441 joy->center[ i ] = 127.0f;
1442 joy->min [ i ] = 0.0f;
1445 joy->dead_band[ i ] = 0.0f;
1446 joy->saturate[ i ] = 1.0f;
1451 #if defined( __linux__ )
1452 /* Default for older Linux systems. */
1454 joy->num_buttons = 32;
1457 for( i = 0; i < _JS_MAX_AXES; i++ )
1458 joy->tmp_axes[ i ] = 0.0f;
1460 joy->tmp_buttons = 0;
1463 joy->fd = open( joy->fname, O_RDONLY );
1465 joy->error =( joy->fd < 0 );
1470 /* Set the correct number of axes for the linux driver */
1472 /* Melchior Franz's fixes for big-endian Linuxes since writing
1473 * to the upper byte of an uninitialized word doesn't work.
1476 ioctl( joy->fd, JSIOCGAXES, &u );
1478 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1479 joy->num_buttons = u;
1480 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1481 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1485 * The Linux driver seems to return 512 for all axes
1486 * when no stick is present - but there is a chance
1487 * that could happen by accident - so it's gotta happen
1488 * on both axes for at least 100 attempts.
1490 * PWO: shouldn't be that done somehow wiser on the kernel level?
1497 fghJoystickRawRead( joy, NULL, joy->center );
1499 } while( !joy->error &&
1501 joy->center[ 0 ] == 512.0f &&
1502 joy->center[ 1 ] == 512.0f );
1504 if ( counter >= 100 )
1505 joy->error = GL_TRUE;
1508 for( i = 0; i < _JS_MAX_AXES; i++ )
1511 joy->max [ i ] = 32767.0f;
1512 joy->center[ i ] = 0.0f;
1513 joy->min [ i ] = -32767.0f;
1515 joy->max[ i ] = joy->center[ i ] * 2.0f;
1516 joy->min[ i ] = 0.0f;
1518 joy->dead_band[ i ] = 0.0f;
1519 joy->saturate [ i ] = 1.0f;
1526 * This function replaces the constructor method in the JS library.
1528 static void fghJoystickInit( int ident )
1530 if( ident >= MAX_NUM_JOYSTICKS )
1531 fgError( "Too large a joystick number: %d", ident );
1533 if( fgJoystick[ ident ] )
1534 fgError( "illegal attempt to initialize joystick device again" );
1536 fgJoystick[ ident ] =
1537 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1540 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1541 fgJoystick[ ident ]->error = GL_TRUE;
1543 #if TARGET_HOST_MACINTOSH
1544 fgJoystick[ ident ]->id = ident;
1545 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
1546 fgJoystick[ ident ]->error = GL_FALSE;
1549 #if TARGET_HOST_MAC_OSX
1550 fgJoystick[ ident ]->id = ident;
1551 fgJoystick[ ident ]->error = GL_FALSE;
1552 fgJoystick[ ident ]->num_axes = 0;
1553 fgJoystick[ ident ]->num_buttons = 0;
1555 if( numDevices < 0 )
1557 /* do first-time init (since we can't over-ride jsInit, hmm */
1560 mach_port_t masterPort;
1561 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1562 if( rv != kIOReturnSuccess )
1564 fgWarning( "error getting master Mach port" );
1567 fghJoystickFindDevices( masterPort );
1570 if ( ident >= numDevices )
1572 fgJoystick[ ident ]->error = GL_TRUE;
1576 /* get the name now too */
1577 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1578 CFTypeRef ref = CFDictionaryGetValue( properties,
1579 CFSTR( kIOHIDProductKey ) );
1581 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1584 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1585 CFStringGetSystemEncoding( ) ) )
1587 fgWarning( "error getting device name" );
1592 #if TARGET_HOST_WIN32
1596 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1597 fgJoystick[ ident ]->error = GL_FALSE;
1600 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1601 fgJoystick[ ident ]->error = GL_FALSE;
1604 fgJoystick[ ident ]->num_axes = 0;
1605 fgJoystick[ ident ]->error = GL_TRUE;
1610 #if TARGET_HOST_UNIX_X11
1611 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1612 fgJoystick[ ident ]->id = ident;
1613 fgJoystick[ ident ]->error = GL_FALSE;
1615 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1616 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1617 if( ident < USB_IDENT_OFFSET )
1618 fgJoystick[ ident ]->os->is_analog = 1;
1619 if( fgJoystick[ ident ]->os->is_analog )
1620 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
1622 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
1623 ident - USB_IDENT_OFFSET );
1624 # elif defined( __linux__ )
1625 fgJoystick[ ident ]->id = ident;
1626 fgJoystick[ ident ]->error = GL_FALSE;
1628 sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1630 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1631 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
1635 fghJoystickOpen( fgJoystick[ ident ] );
1639 * Try initializing all the joysticks (well, both of them)
1641 void fgInitialiseJoysticks ( void )
1643 /* Initialization courtesy of OpenGLUT -- do we want it? */
1644 if( !fgState.JoysticksInitialised )
1647 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1648 fghJoystickInit( ident );
1650 fgState.JoysticksInitialised = GL_TRUE;
1657 void fgJoystickClose( void )
1660 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1662 if( fgJoystick[ ident ] )
1665 #if TARGET_HOST_MACINTOSH
1671 #if TARGET_HOST_MAC_OSX
1672 ( *( fgJoystick[ ident ]->hidDev ) )->
1673 close( fgJoystick[ ident ]->hidDev );
1676 #if TARGET_HOST_WIN32
1677 /* Do nothing special */
1680 #if TARGET_HOST_UNIX_X11
1681 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1682 if( fgJoystick[ident]->os )
1684 if( ! fgJoystick[ ident ]->error )
1685 close( fgJoystick[ ident ]->os->fd );
1687 if( fgJoystick[ ident ]->os->hids )
1688 free (fgJoystick[ ident ]->os->hids);
1689 if( fgJoystick[ ident ]->os->hid_data_buf )
1690 free( fgJoystick[ ident ]->os->hid_data_buf );
1692 free( fgJoystick[ident]->os );
1696 if( ! fgJoystick[ident]->error )
1697 close( fgJoystick[ ident ]->fd );
1700 free( fgJoystick[ ident ] );
1701 fgJoystick[ ident ] = NULL;
1702 /* show joystick has been deinitialized */
1708 * Polls the joystick and executes the joystick callback hooked to the
1709 * window specified in the function's parameter:
1711 void fgJoystickPollWindow( SFG_Window* window )
1713 float axes[ _JS_MAX_AXES ];
1717 freeglut_return_if_fail( window );
1718 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1720 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1722 if( fgJoystick[ident] )
1724 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1726 if( !fgJoystick[ident]->error )
1727 INVOKE_WCB( *window, Joystick,
1729 (int) ( axes[ 0 ] * 1000.0f ),
1730 (int) ( axes[ 1 ] * 1000.0f ),
1731 (int) ( axes[ 2 ] * 1000.0f ) )
1738 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1740 int fgJoystickDetect( void )
1744 fgInitialiseJoysticks ();
1749 if ( !fgState.JoysticksInitialised )
1752 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1753 if( fgJoystick[ident] && !fgJoystick[ident]->error )
1760 * Joystick information functions
1762 int glutJoystickGetNumAxes( int ident )
1764 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
1765 return fgJoystick[ ident ]->num_axes;
1767 int glutJoystickGetNumButtons( int ident )
1769 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
1770 return fgJoystick[ ident ]->num_buttons;
1772 int glutJoystickNotWorking( int ident )
1774 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
1775 return fgJoystick[ ident ]->error;
1778 float glutJoystickGetDeadBand( int ident, int axis )
1780 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1781 return fgJoystick[ ident ]->dead_band [ axis ];
1783 void glutJoystickSetDeadBand( int ident, int axis, float db )
1785 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1786 fgJoystick[ ident ]->dead_band[ axis ] = db;
1789 float glutJoystickGetSaturation( int ident, int axis )
1791 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1792 return fgJoystick[ ident ]->saturate[ axis ];
1794 void glutJoystickSetSaturation( int ident, int axis, float st )
1796 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1797 fgJoystick[ ident ]->saturate [ axis ] = st;
1800 void glutJoystickSetMinRange( int ident, float *axes )
1802 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1803 memcpy( fgJoystick[ ident ]->min, axes,
1804 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1806 void glutJoystickSetMaxRange( int ident, float *axes )
1808 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1809 memcpy( fgJoystick[ ident ]->max, axes,
1810 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1812 void glutJoystickSetCenter( int ident, float *axes )
1814 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1815 memcpy( fgJoystick[ ident ]->center, axes,
1816 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1819 void glutJoystickGetMinRange( int ident, float *axes )
1821 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1822 memcpy( axes, fgJoystick[ ident ]->min,
1823 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1825 void glutJoystickGetMaxRange( int ident, float *axes )
1827 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1828 memcpy( axes, fgJoystick[ ident ]->max,
1829 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1831 void glutJoystickGetCenter( int ident, float *axes )
1833 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1834 memcpy( axes, fgJoystick[ ident ]->center,
1835 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1838 /*** END OF FILE ***/