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>
42 #include "../include/GL/freeglut.h"
43 #include "freeglut_internal.h"
46 * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
49 #define _JS_MAX_BUTTONS 32
52 #if TARGET_HOST_MACINTOSH
53 # define _JS_MAX_AXES 9
54 # include <InputSprocket.h>
57 #if TARGET_HOST_MAC_OSX
58 # define _JS_MAX_AXES 16
59 # include <mach/mach.h>
60 # include <IOKit/IOkitLib.h>
61 # include <IOKit/hid/IOHIDLib.h>
65 # define _JS_MAX_AXES 8
67 # include <mmsystem.h>
73 #if TARGET_HOST_UNIX_X11
74 # define _JS_MAX_AXES 16
75 # if defined(__FreeBSD__) || defined(__NetBSD__)
77 * XXX The below hack is done until freeglut's autoconf is updated.
79 # define HAVE_USB_JS 1
81 # include <sys/ioctl.h>
82 # if defined(__FreeBSD__) && __FreeBSD_version >= 500000
83 # include <sys/joystick.h>
86 * XXX NetBSD/amd64 systems may find that they have to steal the
87 * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
88 * XXX I cannot comment whether that works for the interface, but
89 * XXX it lets you compile...(^& I do not think that we can do away
90 * XXX with this header.
92 # include <machine/joystick.h> /* For analog joysticks */
94 # define JS_DATA_TYPE joystick
95 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
102 # if defined(__linux__)
103 # include <sys/ioctl.h>
104 # include <linux/joystick.h>
106 /* check the joystick driver version */
107 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
110 # else /* Not BSD or Linux */
114 * We'll put these values in and that should
115 * allow the code to at least compile when there is
116 * no support. The JS open routine should error out
117 * and shut off all the code downstream anyway and if
118 * the application doesn't use a joystick we'll be fine.
128 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
137 * BSD defines from "jsBSD.cxx" around lines 42-270
140 #if defined(__NetBSD__) || defined(__FreeBSD__)
143 # if defined(__NetBSD__)
145 * XXX The below hack is done until freeglut's autoconf is updated.
147 # define HAVE_USBHID_H 1
148 # ifdef HAVE_USBHID_H
153 # elif defined(__FreeBSD__)
154 # if __FreeBSD_version < 500000
155 # include <libusbhid.h>
158 * XXX The below hack is done until freeglut's autoconf is updated.
160 # define HAVE_USBHID_H 1
164 # include <dev/usb/usb.h>
165 # include <dev/usb/usbhid.h>
167 /* Compatibility with older usb.h revisions */
168 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
169 # define USB_MAX_DEVNAMES MAXDEVNAMES
173 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
174 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
175 struct os_specific_s {
179 /* The following structure members are specific to analog joysticks */
182 /* The following structure members are specific to USB joysticks */
183 struct hid_item *hids;
187 int axes_usage [ _JS_MAX_AXES ];
189 /* We keep button and axes state ourselves, as they might not be updated
190 * on every read of a USB device
193 float cache_axes [ _JS_MAX_AXES ];
196 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
197 # define USB_IDENT_OFFSET 2
199 # define USBDEV "/dev/usb"
200 # define UHIDDEV "/dev/uhid"
201 # define AJSDEV "/dev/joy"
205 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
206 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
207 * return the uhidN device name. We warn the user of this situation once.
209 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
211 struct usb_device_info di;
215 for (a = 1; a < USB_MAX_DEVICES; a++) {
217 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
219 for (i = 0; i < USB_MAX_DEVNAMES; i++)
220 if (di.udi_devnames[i][0] &&
221 strcmp(di.udi_devnames[i], dev) == 0) {
222 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
223 strcpy(cp, di.udi_vendor);
225 strcat(cp, di.udi_product);
226 strncpy(out, cp, outlen - 1);
235 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
240 static int protection_warned = 0;
242 for (i = 0; i < 16; i++) {
243 sprintf(buf, "%s%d", USBDEV, i);
244 f = open(buf, O_RDONLY);
246 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
250 } else if (errno == EACCES) {
251 if (!protection_warned) {
252 fprintf(stderr, "Can't open %s for read!\n",
254 protection_warned = 1;
261 static int fghJoystickInitializeHID(struct os_specific_s *os,
262 int *num_axes, int *num_buttons)
264 int size, is_joystick;
265 # ifdef HAVE_USBHID_H
272 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
274 fprintf( stderr, "error: %s: %s", os->fname, strerror( errno ) );
280 # ifdef HAVE_USBHID_H
281 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
283 /*** XXX {report_id} may not be the right variable? ***/
284 fprintf( stderr, "error: %s%d: %s",
285 UHIDDEV, report_id, strerror( errno ) );
289 size = hid_report_size( rd, hid_input, report_id );
291 size = hid_report_size( rd, 0, hid_input );
293 os->hid_data_buf = calloc( 1, size );
297 # ifdef HAVE_USBHID_H
298 d = hid_start_parse( rd, 1 << hid_input, report_id );
300 d = hid_start_parse( rd, 1 << hid_input );
302 while( hid_get_item( d, &h ) )
304 int usage, page, interesting_hid;
306 page = HID_PAGE( h.usage );
307 usage = HID_USAGE( h.usage );
309 /* This test is somewhat too simplistic, but this is how MicroSoft
310 * does, so I guess it works for all joysticks/game pads. */
311 is_joystick = is_joystick ||
312 ( h.kind == hid_collection &&
313 page == HUP_GENERIC_DESKTOP &&
314 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
316 if( h.kind != hid_input )
322 interesting_hid = TRUE;
323 if( page == HUP_GENERIC_DESKTOP )
334 if( *num_axes < _JS_MAX_AXES )
336 os->axes_usage[ *num_axes ] = usage;
341 /* Allocate two axes for a hat */
342 if( *num_axes + 1 < _JS_MAX_AXES )
344 os->axes_usage[ *num_axes ] = usage;
346 os->axes_usage[ *num_axes ] = usage;
351 interesting_hid = FALSE;
355 else if( page == HUP_BUTTON )
357 interesting_hid = ( usage > 0 ) &&
358 ( usage <= _JS_MAX_BUTTONS );
360 if( interesting_hid && usage - 1 > *num_buttons )
361 *num_buttons = usage - 1;
364 if( interesting_hid )
367 os->hids = calloc( 1, sizeof ( struct hid_item ) );
373 return os->hids != NULL;
379 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
380 * See "js.h" lines 80-178.
382 typedef struct tagSFG_Joystick SFG_Joystick;
383 struct tagSFG_Joystick
385 #if TARGET_HOST_MACINTOSH
386 #define ISP_NUM_AXIS 9
387 #define ISP_NUM_NEEDS 41
388 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
389 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
392 #if TARGET_HOST_MAC_OSX
393 IOHIDDeviceInterface ** hidDev;
394 IOHIDElementCookie buttonCookies[41];
395 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
396 long minReport[_JS_MAX_AXES],
397 maxReport[_JS_MAX_AXES];
400 #if TARGET_HOST_WIN32
407 #if TARGET_HOST_UNIX_X11
408 # if defined(__FreeBSD__) || defined(__NetBSD__)
409 struct os_specific_s *os;
415 float tmp_axes [ _JS_MAX_AXES ];
417 struct JS_DATA_TYPE js;
430 float dead_band[ _JS_MAX_AXES ];
431 float saturate [ _JS_MAX_AXES ];
432 float center [ _JS_MAX_AXES ];
433 float max [ _JS_MAX_AXES ];
434 float min [ _JS_MAX_AXES ];
438 * Functions associated with the "jsJoystick" class in PLIB
440 #if TARGET_HOST_MAC_OSX
441 #define K_NUM_DEVICES 32
443 io_object_t ioDevices[K_NUM_DEVICES];
445 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
446 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
448 void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
449 /* callback for CFArrayApply */
450 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
451 void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
453 void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
454 void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
455 void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
460 * The static joystick structure pointer
462 #define MAX_NUM_JOYSTICKS 2
463 static int fgNumberOfJoysticks = 0;
464 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
468 * Read the raw joystick data
470 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
472 #if TARGET_HOST_WIN32
478 #if defined(__FreeBSD__) || defined(__NetBSD__)
489 for( i = 0; i < joy->num_axes; i++ )
495 #if TARGET_HOST_MACINTOSH
500 for ( i = 0; i < joy->num_buttons; i++ )
503 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
506 *buttons |= state << i;
512 for ( i = 0; i < joy->num_axes; i++ )
515 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
518 axes [i] = (float) state;
523 #if TARGET_HOST_MAC_OSX
524 if ( buttons != NULL )
528 for ( i = 0; i < joy->num_buttons; i++ )
530 IOHIDEventStruct hidEvent;
531 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
532 if ( hidEvent.value )
539 for ( i = 0; i < joy->num_axes; i++ )
541 IOHIDEventStruct hidEvent;
542 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
543 axes[i] = hidEvent.value;
548 #if TARGET_HOST_WIN32
549 status = joyGetPosEx( joy->js_id, &joy->js );
551 if ( status != JOYERR_NOERROR )
553 joy->error = GL_TRUE;
558 *buttons = joy->js.dwButtons;
563 * WARNING - Fall through case clauses!!
565 switch ( joy->num_axes )
568 /* Generate two POV axes from the POV hat angle.
569 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
570 * hundredths of a degree, or 0xFFFF when idle.
572 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
579 /* This is the contentious bit: how to convert angle to X/Y.
580 * wk: I know of no define for PI that we could use here:
581 * SG_PI would pull in sg, M_PI is undefined for MSVC
582 * But the accuracy of the value of PI is very unimportant at
585 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
586 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
588 /* Convert to coordinates on a square so that North-East
589 * is (1,1) not (.7,.7), etc.
590 * s and c cannot both be zero so we won't divide by zero.
592 if ( fabs ( s ) < fabs ( c ) )
594 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
595 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
599 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
600 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
604 case 6: axes[5] = (float) joy->js.dwVpos;
605 case 5: axes[4] = (float) joy->js.dwUpos;
606 case 4: axes[3] = (float) joy->js.dwRpos;
607 case 3: axes[2] = (float) joy->js.dwZpos;
608 case 2: axes[1] = (float) joy->js.dwYpos;
609 case 1: axes[0] = (float) joy->js.dwXpos;
614 #if TARGET_HOST_UNIX_X11
615 # if defined(__FreeBSD__) || defined(__NetBSD__)
616 if ( joy->os->is_analog )
618 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
619 if ( status != sizeof(joy->os->ajs) ) {
620 perror ( joy->os->fname );
621 joy->error = GL_TRUE;
624 if ( buttons != NULL )
625 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
629 axes[0] = (float) joy->os->ajs.x;
630 axes[1] = (float) joy->os->ajs.y;
637 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
641 for ( h = joy->os->hids; h; h = h->next )
643 int d = hid_get_data ( joy->os->hid_data_buf, h );
645 int page = HID_PAGE ( h->usage );
646 int usage = HID_USAGE ( h->usage );
648 if ( page == HUP_GENERIC_DESKTOP )
651 for ( i = 0; i < joy->num_axes; i++ )
652 if (joy->os->axes_usage[i] == usage)
654 if (usage == HUG_HAT_SWITCH)
658 joy->os->cache_axes[i] = (float)hatmap_x[d];
659 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
663 joy->os->cache_axes[i] = (float)d;
668 else if (page == HUP_BUTTON)
670 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
673 joy->os->cache_buttons |= (1 << usage - 1);
675 joy->os->cache_buttons &= ~(1 << usage - 1);
680 if ( len < 0 && errno != EAGAIN )
682 perror( joy->os->fname );
685 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
687 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
695 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
697 if ( status != sizeof( struct js_event ) )
699 if ( errno == EAGAIN )
701 /* Use the old values */
703 *buttons = joy->tmp_buttons;
705 memcpy( axes, joy->tmp_axes,
706 sizeof( float ) * joy->num_axes );
710 fgWarning ( "%s", joy->fname );
711 joy->error = GL_TRUE;
715 switch ( joy->js.type & ~JS_EVENT_INIT )
717 case JS_EVENT_BUTTON:
718 if( joy->js.value == 0 ) /* clear the flag */
719 joy->tmp_buttons &= ~( 1 << joy->js.number );
721 joy->tmp_buttons |= ( 1 << joy->js.number );
725 if ( joy->js.number < joy->num_axes )
727 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
730 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
735 fgWarning ( "%s", "PLIB_JS: Unrecognised /dev/js return!?!" );
737 /* use the old values */
739 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
741 memcpy ( axes, joy->tmp_axes, sizeof(float) * num_axes );
747 *buttons = joy->tmp_buttons;
751 status = read( joy->fd, &joy->js, JS_RETURN );
753 if ( status != JS_RETURN )
755 fgWarning( "%s", joy->fname );
756 joy->error = GL_TRUE;
761 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
762 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
764 *buttons = joy->js.buttons;
769 axes[ 0 ] = (float) joy->js.x;
770 axes[ 1 ] = (float) joy->js.y;
777 * Correct the joystick axis data
779 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
781 if( value < joy->center[ axis ] )
783 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
786 if( xx < -joy->saturate[ axis ] )
789 if( xx > -joy->dead_band [ axis ] )
792 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
793 joy->dead_band[ axis ] );
795 return ( xx < -1.0f ) ? -1.0f : xx;
799 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
800 joy->center[ axis ] );
802 if( xx > joy->saturate[ axis ] )
805 if( xx < joy->dead_band[ axis ] )
808 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
809 joy->dead_band[ axis ] );
811 return ( xx > 1.0f ) ? 1.0f : xx;
816 * Read the corrected joystick data
818 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
820 float raw_axes[ _JS_MAX_AXES ];
829 for ( i=0; i<joy->num_axes; i++ )
833 fghJoystickRawRead( joy, buttons, raw_axes );
836 for( i=0; i<joy->num_axes; i++ )
837 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
841 * Happy happy happy joy joy joy (happy new year toudi :D)
845 #if TARGET_HOST_MAC_OSX
846 /** open the IOKit connection, enumerate all the HID devices, add their
847 interface references to the static array. We then use the array index
848 as the device number when we come to open() the joystick. */
849 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
851 CFMutableDictionaryRef hidMatch = NULL;
852 IOReturn rv = kIOReturnSuccess;
854 io_iterator_t hidIterator;
857 /* build a dictionary matching HID devices */
858 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
860 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
861 if (rv != kIOReturnSuccess || !hidIterator) {
862 fgWarning( "%s", "no joystick (HID) devices found" );
867 while ((ioDev = IOIteratorNext(hidIterator))) {
868 /* filter out keyboard and mouse devices */
869 CFDictionaryRef properties = getCFProperties(ioDev);
872 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
873 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
874 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
875 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
877 /* keep only joystick devices */
878 if ( ( page == kHIDPage_GenericDesktop ) && (
879 (usage == kHIDUsage_GD_Joystick)
880 || (usage == kHIDUsage_GD_GamePad)
881 || (usage == kHIDUsage_GD_MultiAxisController)
882 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
883 /* add it to the array */
884 ioDevices[numDevices++] = ioDev;
887 IOObjectRelease(hidIterator);
890 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
893 CFMutableDictionaryRef cfProperties;
896 /* comment copied from darwin/SDL_sysjoystick.c */
897 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
898 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
901 io_registry_entry_t parent1, parent2;
903 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
904 if (rv != kIOReturnSuccess) {
905 fgWarning ( "%s", "error getting device entry parent");
909 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
910 if (rv != kIOReturnSuccess) {
911 fgWarning ( "%s", "error getting device entry parent 2");
916 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
917 &cfProperties, kCFAllocatorDefault, kNilOptions);
918 if (rv != kIOReturnSuccess || !cfProperties) {
919 fgWarning ( "%s", "error getting device properties");
926 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
928 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
929 fgError ( "%s", "element enumerator passed non-dictionary value");
933 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
936 /** element enumerator function : pass NULL for top-level*/
937 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
939 assert(CFGetTypeID(element) == CFArrayGetTypeID());
941 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
942 CFArrayApplyFunction((CFArrayRef) element, range,
943 &fghJoystickElementEnumerator, joy );
946 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
948 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
949 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
951 long type, page, usage;
953 CFNumberGetValue((CFNumberRef)
954 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
955 kCFNumberLongType, &type);
958 case kIOHIDElementTypeInput_Misc:
959 case kIOHIDElementTypeInput_Axis:
960 case kIOHIDElementTypeInput_Button:
961 printf("got input element...");
962 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
963 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
965 if (page == kHIDPage_GenericDesktop) {
966 switch ( usage ) /* look at usage to determine function */
971 case kHIDUsage_GD_Rx:
972 case kHIDUsage_GD_Ry:
973 case kHIDUsage_GD_Rz:
974 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
976 fghJoystickAddAxisElement((CFDictionaryRef) element);
979 case kHIDUsage_GD_Hatswitch:
981 fghJoystickAddHatElement((CFDictionaryRef) element);
985 printf("input type element has weird usage (%x)\n", usage);
988 } else if (page == kHIDPage_Button) {
990 fghJoystickAddButtonElement((CFDictionaryRef) element);
992 printf("input type element has weird page (%x)\n", page);
995 case kIOHIDElementTypeCollection:
996 fghJoystickEnumerateElements (
997 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
1006 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
1008 long cookie, lmin, lmax;
1009 int index = joy->num_axes++;
1011 CFNumberGetValue ((CFNumberRef)
1012 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1013 kCFNumberLongType, &cookie);
1015 axisCookies[index] = (IOHIDElementCookie) cookie;
1017 CFNumberGetValue ((CFNumberRef)
1018 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1019 kCFNumberLongType, &lmin);
1021 CFNumberGetValue ((CFNumberRef)
1022 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1023 kCFNumberLongType, &lmax);
1025 joy->min[index] = lmin;
1026 joy->max[index] = lmax;
1027 joy->dead_band[index] = 0.0;
1028 joy->saturate[index] = 1.0;
1029 joy->center[index] = (lmax + lmin) * 0.5;
1032 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1035 CFNumberGetValue ((CFNumberRef)
1036 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1037 kCFNumberLongType, &cookie);
1039 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1040 /* anything else for buttons? */
1043 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1045 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1046 /* do we map hats to axes or buttons? */
1050 #if TARGET_HOST_WIN32
1052 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1054 # if defined(_MSC_VER)
1055 # pragma comment (lib, "advapi32.lib")
1058 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1060 char buffer [ 256 ];
1062 char OEMKey [ 256 ];
1071 /* Open .. MediaResources\CurrentJoystickSettings */
1072 sprintf ( buffer, "%s\\%s\\%s",
1073 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1074 REGSTR_KEY_JOYCURR );
1076 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1078 if ( lr != ERROR_SUCCESS ) return 0;
1080 /* Get OEM Key name */
1081 dwcb = sizeof(OEMKey);
1083 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1084 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1086 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1087 RegCloseKey ( hKey );
1089 if ( lr != ERROR_SUCCESS ) return 0;
1091 /* Open OEM Key from ...MediaProperties */
1092 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1094 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1096 if ( lr != ERROR_SUCCESS ) return 0;
1101 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1103 RegCloseKey ( hKey );
1105 if ( lr != ERROR_SUCCESS ) return 0;
1112 static void fghJoystickOpen( SFG_Joystick* joy )
1115 #if TARGET_HOST_MACINTOSH
1118 #if TARGET_HOST_MAC_OSX
1121 IOCFPlugInInterface **plugin;
1123 HRESULT pluginResult;
1125 CFDictionaryRef props;
1126 CFTypeRef topLevelElement;
1128 #if TARGET_HOST_UNIX_X11
1129 # if defined(__FreeBSD__) || defined(__NetBSD__)
1139 /* Default values (for no joystick -- each conditional will reset the error flag) */
1141 joy->num_axes = joy->num_buttons = 0;
1142 joy->name [0] = '\0';
1144 #if TARGET_HOST_MACINTOSH
1146 * XXX FIXME: get joystick name in Mac
1149 err = ISpStartup ();
1153 #define ISP_CHECK_ERR(x) if ( x != noErr ) { joy->error = GL_TRUE; return; }
1155 joy->error = GL_TRUE;
1157 /* initialize the needs structure */
1158 ISpNeed temp_isp_needs[isp_num_needs] =
1160 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1161 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1162 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1163 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1164 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1165 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1166 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1167 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1168 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1170 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1190 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1191 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1192 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1193 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1194 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1195 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1196 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1197 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1198 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1199 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1200 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1201 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1204 memcpy ( joy->isp_needs, temp_isp_needs, sizeof(temp_isp_needs) );
1207 /* next two calls allow keyboard and mouse to emulate other input
1208 * devices (gamepads, joysticks, etc)
1211 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1215 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1219 err = ISpElement_NewVirtualFromNeeds ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 0 );
1222 err = ISpInit ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 'freeglut', nil, 0, 128, 0 );
1225 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1226 joy->num_axes = joy->isp_num_axis;
1228 for ( i = 0; i < joy->num_axes; i++ )
1230 joy->dead_band [ i ] = 0;
1231 joy->saturate [ i ] = 1;
1232 joy->center [ i ] = kISpAxisMiddle;
1233 joy->max [ i ] = kISpAxisMaximum;
1234 joy->min [ i ] = kISpAxisMinimum;
1237 joy->error = GL_FALSE;
1240 joy->num_buttons = joy->num_axes = 0;
1243 #if TARGET_HOST_MAC_OSX
1244 if (joy->id >= numDevices) {
1245 fgWarning ( "%s", "device index out of range in fgJoystickOpen()");
1249 /* create device interface */
1250 rv = IOCreatePlugInInterfaceForService ( ioDevices[joy->id],
1251 kIOHIDDeviceUserClientTypeID,
1252 kIOCFPlugInInterfaceID,
1255 if (rv != kIOReturnSuccess) {
1256 fgWarning ( "%s", "error creating plugin for io device");
1260 pluginResult = (*plugin)->QueryInterface ( plugin,
1261 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), &(LPVOID) joy->hidDev );
1263 if ( pluginResult != S_OK )
1264 fgWarning ( "%s", "QI-ing IO plugin to HID Device interface failed");
1266 (*plugin)->Release(plugin); /* don't leak a ref */
1267 if (joy->hidDev == NULL) return;
1269 /* store the interface in this instance */
1270 rv = (*(joy->hidDev))->open(joy->hidDev, 0);
1271 if (rv != kIOReturnSuccess) {
1272 fgWarning ( "%s", "error opening device interface");
1276 props = getCFProperties(ioDevices[joy->id]);
1278 /* recursively enumerate all the bits */
1279 CFTypeRef topLevelElement =
1280 CFDictionaryGetValue ( props, CFSTR ( kIOHIDElementKey ) );
1281 enumerateElements ( topLevelElement );
1283 CFRelease ( props );
1286 #if TARGET_HOST_WIN32
1287 joy->js.dwFlags = JOY_RETURNALL;
1288 joy->js.dwSize = sizeof( joy->js );
1290 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1293 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1296 if ( joy->jsCaps.wNumAxes == 0 )
1299 joy->error = GL_TRUE;
1303 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1304 * at least for USB. Try to get the real name from the registry.
1306 if ( ! fghJoystickGetOEMProductName ( joy, joy->name, sizeof(joy->name) ) )
1308 fgWarning ( "%s", "JS: Failed to read joystick name from registry" );
1309 strncpy ( joy->name, joy->jsCaps.szPname, sizeof(joy->name) );
1312 /* Windows joystick drivers may provide any combination of
1313 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1315 if ( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1317 joy->num_axes = _JS_MAX_AXES;
1318 joy->min [ 7 ] = -1.0; joy->max [ 7 ] = 1.0; /* POV Y */
1319 joy->min [ 6 ] = -1.0; joy->max [ 6 ] = 1.0; /* POV X */
1324 joy->min[ 5 ] = (float) joy->jsCaps.wVmin;
1325 joy->max[ 5 ] = (float) joy->jsCaps.wVmax;
1326 joy->min[ 4 ] = (float) joy->jsCaps.wUmin;
1327 joy->max[ 4 ] = (float) joy->jsCaps.wUmax;
1328 joy->min[ 3 ] = (float) joy->jsCaps.wRmin;
1329 joy->max[ 3 ] = (float) joy->jsCaps.wRmax;
1330 joy->min[ 2 ] = (float) joy->jsCaps.wZmin;
1331 joy->max[ 2 ] = (float) joy->jsCaps.wZmax;
1332 joy->min[ 1 ] = (float) joy->jsCaps.wYmin;
1333 joy->max[ 1 ] = (float) joy->jsCaps.wYmax;
1334 joy->min[ 0 ] = (float) joy->jsCaps.wXmin;
1335 joy->max[ 0 ] = (float) joy->jsCaps.wXmax;
1339 * Guess all the rest judging on the axes extremals
1341 for( i = 0; i < joy->num_axes; i++ )
1343 joy->center [ i ] = (joy->max[i] + joy->min[i]) * 0.5f;
1344 joy->dead_band[ i ] = 0.0f;
1345 joy->saturate [ i ] = 1.0f;
1349 #if TARGET_HOST_UNIX_X11
1350 #if defined(__FreeBSD__) || defined(__NetBSD__)
1351 for ( i = 0; i < _JS_MAX_AXES; i++ )
1352 joy->os->cache_axes [ i ] = 0.0f;
1354 joy->os->cache_buttons = 0;
1356 joy->os->fd = open ( joy->os->fname, O_RDONLY | O_NONBLOCK);
1358 if ( joy->os->fd < 0 && errno == EACCES)
1359 fgWarning ( "%s exists but is not readable by you\n", joy->os->fname );
1361 joy->error = ( joy->os->fd < 0 );
1367 joy->num_buttons = 0;
1368 if ( joy->os->is_analog )
1371 char joyfname [ 1024 ];
1372 int noargs, in_no_axes;
1374 float axes [ _JS_MAX_AXES ];
1375 int buttons [ _JS_MAX_AXES ];
1378 joy->num_buttons = 32;
1380 fghJoystickRawRead ( joy, buttons, axes );
1381 joy->error = axes[0] < -1000000000.0f;
1385 sprintf( joyfname, "%s/.joy%drc", getenv ( "HOME" ), joy->id );
1387 joyfile = fopen ( joyfname, "r" );
1388 joy->error = ( joyfile == NULL );
1392 noargs = fscanf ( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1393 &joy->min [ 0 ], &joy->center [ 0 ], &joy->max [ 0 ],
1394 &joy->min [ 1 ], &joy->center [ 1 ], &joy->max [ 1 ] );
1395 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1400 for ( i = 0; i < _JS_MAX_AXES; i++ )
1402 joy->dead_band [ i ] = 0.0f;
1403 joy->saturate [ i ] = 1.0f;
1406 return; /* End of analog code */
1410 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1411 &joy->num_buttons ) )
1413 close ( joy->os->fd );
1414 joy->error = GL_TRUE;
1418 cp = strrchr(joy->os->fname, '/');
1420 if ( fghJoystickFindUSBdev ( &cp[1], joy->name, sizeof(joy->name) ) == 0 )
1421 strcpy ( joy->name, &cp[1] );
1424 if ( joy->num_axes > _JS_MAX_AXES )
1425 joy->num_axes = _JS_MAX_AXES;
1427 for ( i = 0; i < _JS_MAX_AXES; i++ )
1429 /* We really should get this from the HID, but that data seems
1430 * to be quite unreliable for analog-to-USB converters. Punt for
1433 if ( joy->os->axes_usage [ i ] == HUG_HAT_SWITCH )
1435 joy->max [ i ] = 1.0f;
1436 joy->center [ i ] = 0.0f;
1437 joy->min [ i ] = -1.0f;
1441 joy->max [ i ] = 255.0f;
1442 joy->center [ i ] = 127.0f;
1443 joy->min [ i ] = 0.0f;
1446 joy->dead_band [ i ] = 0.0f;
1447 joy->saturate [ i ] = 1.0f;
1452 #if defined(__linux__)
1454 * Default for older Linux systems.
1457 joy->num_buttons = 32;
1460 for( i = 0; i < _JS_MAX_AXES; i++ )
1461 joy->tmp_axes[ i ] = 0.0f;
1463 joy->tmp_buttons = 0;
1466 joy->fd = open( joy->fname, O_RDONLY );
1468 joy->error = (joy->fd < 0);
1474 * Set the correct number of axes for the linux driver
1477 /* Melchior Franz's fixes for big-endian Linuxes since writing
1478 * to the upper byte of an uninitialized word doesn't work.
1481 ioctl ( joy->fd, JSIOCGAXES , &u );
1483 ioctl ( joy->fd, JSIOCGBUTTONS, &u );
1484 joy->num_buttons = u;
1485 ioctl ( joy->fd, JSIOCGNAME ( sizeof(joy->name) ), joy->name );
1486 fcntl ( joy->fd, F_SETFL , O_NONBLOCK );
1490 * The Linux driver seems to return 512 for all axes
1491 * when no stick is present - but there is a chance
1492 * that could happen by accident - so it's gotta happen
1493 * on both axes for at least 100 attempts.
1495 * PWO: shouldn't be that done somehow wiser on the kernel level?
1502 fghJoystickRawRead( joy, NULL, joy->center );
1504 } while( !joy->error &&
1506 joy->center[ 0 ] == 512.0f &&
1507 joy->center[ 1 ] == 512.0f );
1509 if ( counter >= 100 )
1510 joy->error = GL_TRUE;
1513 for ( i = 0; i < _JS_MAX_AXES; i++ )
1516 joy->max [ i ] = 32767.0f;
1517 joy->center[ i ] = 0.0f;
1518 joy->min [ i ] = -32767.0f;
1520 joy->max[ i ] = joy->center[ i ] * 2.0f;
1521 joy->min[ i ] = 0.0f;
1523 joy->dead_band[ i ] = 0.0f;
1524 joy->saturate [ i ] = 1.0f;
1531 * This function replaces the constructor method in the JS library.
1533 void fgJoystickInit( int ident )
1535 if ( ident >= MAX_NUM_JOYSTICKS )
1536 fgError( "Too large a joystick number" );
1538 if( fgJoystick[ident] )
1539 fgError( "illegal attempt to initialize joystick device" );
1541 fgJoystick[ident] = ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1544 fgJoystick[ident]->num_axes = fgJoystick[ident]->num_buttons = 0;
1545 fgJoystick[ident]->error = GL_TRUE;
1547 #if TARGET_HOST_MACINTOSH
1548 fgJoystick[ident]->id = ident;
1549 sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident ); /* FIXME */
1550 fgJoystick[ident]->error = GL_FALSE;
1553 #if TARGET_HOST_MAC_OSX
1554 fgJoystick[ident]->id = ident;
1555 fgJoystick[ident]->error = GL_FALSE;
1556 fgJoystick[ident]->num_axes = 0;
1557 fgJoystick[ident]->num_buttons = 0;
1559 if (numDevices < 0) {
1560 /* do first-time init (since we can't over-ride jsInit, hmm */
1563 mach_port_t masterPort;
1564 IOReturn rv = IOMasterPort ( bootstrap_port, &masterPort );
1565 if ( rv != kIOReturnSuccess ) {
1566 fgWarning ( "%s", "error getting master Mach port");
1569 fghJoystickFindDevices ( masterPort );
1572 if ( ident >= numDevices ) {
1573 fgJoystick[ident]->error = GL_TRUE;
1577 /* get the name now too */
1578 CFDictionaryRef properties = getCFProperties(ioDevices[ident]);
1579 CFTypeRef ref = CFDictionaryGetValue (properties, CFSTR(kIOHIDProductKey));
1581 ref = CFDictionaryGetValue (properties, CFSTR("USB Product Name"));
1583 if (!ref || !CFStringGetCString ((CFStringRef) ref, name, 128, CFStringGetSystemEncoding ())) {
1584 fgWarning( "%s", "error getting device name" );
1589 #if TARGET_HOST_WIN32
1593 fgJoystick[ident]->js_id = JOYSTICKID1;
1594 fgJoystick[ident]->error = GL_FALSE;
1597 fgJoystick[ident]->js_id = JOYSTICKID2;
1598 fgJoystick[ident]->error = GL_FALSE;
1601 fgJoystick[ident]->num_axes = 0;
1602 fgJoystick[ident]->error = GL_TRUE;
1607 #if TARGET_HOST_UNIX_X11
1608 # if defined(__FreeBSD__) || defined(__NetBSD__)
1609 fgJoystick[ident]->id = ident;
1610 fgJoystick[ident]->error = GL_FALSE;
1612 fgJoystick[ident]->os = calloc (1, sizeof (struct os_specific_s));
1613 memset ( fgJoystick[ident]->os, 0, sizeof(struct os_specific_s) );
1614 if (ident < USB_IDENT_OFFSET)
1615 fgJoystick[ident]->os->is_analog = 1;
1616 if (fgJoystick[ident]->os->is_analog)
1617 sprintf ( fgJoystick[ident]->os->fname, "%s%d", AJSDEV, ident );
1619 sprintf ( fgJoystick[ident]->os->fname, "%s%d", UHIDDEV, ident - USB_IDENT_OFFSET );
1620 # elif defined(__linux__)
1621 fgJoystick[ident]->id = ident;
1622 fgJoystick[ident]->error = GL_FALSE;
1624 sprintf ( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1626 if ( access ( fgJoystick[ident]->fname, F_OK ) != 0 )
1627 sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident );
1631 fghJoystickOpen ( fgJoystick[ident] );
1637 void fgJoystickClose( void )
1640 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1642 if ( fgJoystick[ident] )
1645 #if TARGET_HOST_MACINTOSH
1651 #if TARGET_HOST_MAC_OSX
1652 (*(fgJoystick[ident]->hidDev))->close(fgJoystick[ident]->hidDev);
1655 #if TARGET_HOST_WIN32
1656 /* Do nothing special */
1659 #if TARGET_HOST_UNIX_X11
1660 #if defined(__FreeBSD__) || defined(__NetBSD__)
1661 if ( fgJoystick[ident]->os )
1663 if ( ! fgJoystick[ident]->error )
1664 close ( fgJoystick[ident]->os->fd );
1666 if ( fgJoystick[ident]->os->hids )
1667 free (fgJoystick[ident]->os->hids);
1668 if ( fgJoystick[ident]->os->hid_data_buf )
1669 free (fgJoystick[ident]->os->hid_data_buf);
1671 free (fgJoystick[ident]->os);
1675 if( ! fgJoystick[ident]->error )
1676 close( fgJoystick[ident]->fd );
1679 free( fgJoystick[ident] );
1680 fgJoystick[ident] = NULL; /* show joystick has been deinitialized */
1686 * Polls the joystick and executes the joystick callback hooked to the
1687 * window specified in the function's parameter:
1689 void fgJoystickPollWindow( SFG_Window* window )
1691 float axes[ _JS_MAX_AXES ];
1695 freeglut_return_if_fail( window );
1696 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1698 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1700 if ( fgJoystick[ident] )
1702 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1704 if ( !fgJoystick[ident]->error )
1705 INVOKE_WCB( *window, Joystick,
1707 (int) (axes[ 0 ] * 1000.0f ),
1708 (int) (axes[ 1 ] * 1000.0f ),
1709 (int) (axes[ 2 ] * 1000.0f ) )
1716 * PWO: These jsJoystick class methods have not been implemented.
1717 * We might consider adding such functions to freeglut-2.0.
1719 int glutJoystickGetNumAxes ( int ident )
1720 { return fgJoystick[ident]->num_axes; }
1721 int glutJoystickNotWorking ( int ident )
1722 { return fgJoystick[ident]->error; }
1724 float glutJoystickGetDeadBand ( int ident, int axis )
1725 { return fgJoystick[ident]->dead_band [ axis ]; }
1726 void glutJoystickSetDeadBand ( int ident, int axis, float db )
1727 { fgJoystick[ident]->dead_band [ axis ] = db; }
1729 float glutJoystickGetSaturation ( int ident, int axis )
1730 { return fgJoystick[ident]->saturate [ axis ]; }
1731 void glutJoystickSetSaturation ( int ident, int axis, float st )
1732 { fgJoystick[ident]->saturate [ axis ] = st; }
1734 void glutJoystickSetMinRange ( int ident, float *axes )
1735 { memcpy ( fgJoystick[ident]->min , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1736 void glutJoystickSetMaxRange ( int ident, float *axes )
1737 { memcpy ( fgJoystick[ident]->max , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1738 void glutJoystickSetCenter ( int ident, float *axes )
1739 { memcpy ( fgJoystick[ident]->center, axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1741 void glutJoystickGetMinRange ( int ident, float *axes )
1742 { memcpy ( axes, fgJoystick[ident]->min , fgJoystick[ident]->num_axes * sizeof(float) ); }
1743 void glutJoystickGetMaxRange ( int ident, float *axes )
1744 { memcpy ( axes, fgJoystick[ident]->max , fgJoystick[ident]->num_axes * sizeof(float) ); }
1745 void glutJoystickGetCenter ( int ident, float *axes )
1746 { memcpy ( axes, fgJoystick[ident]->center, fgJoystick[ident]->num_axes * sizeof(float) ); }
1748 /*** END OF FILE ***/