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 <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__)
76 /* XXX The below hack is done until freeglut's autoconf is updated. */
77 # define HAVE_USB_JS 1
79 # include <sys/ioctl.h>
80 # if defined(__FreeBSD__) && __FreeBSD_version >= 500000
81 # include <sys/joystick.h>
84 * XXX NetBSD/amd64 systems may find that they have to steal the
85 * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
86 * XXX I cannot comment whether that works for the interface, but
87 * XXX it lets you compile...(^& I do not think that we can do away
88 * XXX with this header.
90 # include <machine/joystick.h> /* For analog joysticks */
92 # define JS_DATA_TYPE joystick
93 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
100 # if defined(__linux__)
101 # include <sys/ioctl.h>
102 # include <linux/joystick.h>
104 /* check the joystick driver version */
105 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
108 # else /* Not BSD or Linux */
112 * We'll put these values in and that should
113 * allow the code to at least compile when there is
114 * no support. The JS open routine should error out
115 * and shut off all the code downstream anyway and if
116 * the application doesn't use a joystick we'll be fine.
126 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
134 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
136 #if defined(__NetBSD__) || defined(__FreeBSD__)
139 # if defined(__NetBSD__)
140 /* XXX The below hack is done until freeglut's autoconf is updated. */
141 # define HAVE_USBHID_H 1
142 # ifdef HAVE_USBHID_H
147 # elif defined(__FreeBSD__)
148 # if __FreeBSD_version < 500000
149 # include <libusbhid.h>
151 /* XXX The below hack is done until freeglut's autoconf is updated. */
152 # define HAVE_USBHID_H 1
156 # include <dev/usb/usb.h>
157 # include <dev/usb/usbhid.h>
159 /* Compatibility with older usb.h revisions */
160 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
161 # define USB_MAX_DEVNAMES MAXDEVNAMES
165 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
166 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
167 struct os_specific_s {
171 /* The following structure members are specific to analog joysticks */
174 /* The following structure members are specific to USB joysticks */
175 struct hid_item *hids;
179 int axes_usage [ _JS_MAX_AXES ];
181 /* We keep button and axes state ourselves, as they might not be updated
182 * on every read of a USB device
185 float cache_axes [ _JS_MAX_AXES ];
188 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
189 # define USB_IDENT_OFFSET 2
191 # define USBDEV "/dev/usb"
192 # define UHIDDEV "/dev/uhid"
193 # define AJSDEV "/dev/joy"
197 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
198 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
199 * return the uhidN device name. We warn the user of this situation once.
201 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
203 struct usb_device_info di;
207 for (a = 1; a < USB_MAX_DEVICES; a++) {
209 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
211 for (i = 0; i < USB_MAX_DEVNAMES; i++)
212 if (di.udi_devnames[i][0] &&
213 strcmp(di.udi_devnames[i], dev) == 0) {
214 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
215 strcpy(cp, di.udi_vendor);
217 strcat(cp, di.udi_product);
218 strncpy(out, cp, outlen - 1);
227 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
232 static int protection_warned = 0;
234 for (i = 0; i < 16; i++) {
235 sprintf(buf, "%s%d", USBDEV, i);
236 f = open(buf, O_RDONLY);
238 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
242 } else if (errno == EACCES) {
243 if (!protection_warned) {
244 fprintf(stderr, "Can't open %s for read!\n",
246 protection_warned = 1;
253 static int fghJoystickInitializeHID(struct os_specific_s *os,
254 int *num_axes, int *num_buttons)
256 int size, is_joystick;
257 # ifdef HAVE_USBHID_H
264 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
266 fprintf( stderr, "error: %s: %s", os->fname, strerror( errno ) );
272 # ifdef HAVE_USBHID_H
273 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
275 /*** XXX {report_id} may not be the right variable? ***/
276 fprintf( stderr, "error: %s%d: %s",
277 UHIDDEV, report_id, strerror( errno ) );
281 size = hid_report_size( rd, hid_input, report_id );
283 size = hid_report_size( rd, 0, hid_input );
285 os->hid_data_buf = calloc( 1, size );
289 # ifdef HAVE_USBHID_H
290 d = hid_start_parse( rd, 1 << hid_input, report_id );
292 d = hid_start_parse( rd, 1 << hid_input );
294 while( hid_get_item( d, &h ) )
296 int usage, page, interesting_hid;
298 page = HID_PAGE( h.usage );
299 usage = HID_USAGE( h.usage );
301 /* This test is somewhat too simplistic, but this is how MicroSoft
302 * does, so I guess it works for all joysticks/game pads. */
303 is_joystick = is_joystick ||
304 ( h.kind == hid_collection &&
305 page == HUP_GENERIC_DESKTOP &&
306 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
308 if( h.kind != hid_input )
314 interesting_hid = TRUE;
315 if( page == HUP_GENERIC_DESKTOP )
326 if( *num_axes < _JS_MAX_AXES )
328 os->axes_usage[ *num_axes ] = usage;
333 /* Allocate two axes for a hat */
334 if( *num_axes + 1 < _JS_MAX_AXES )
336 os->axes_usage[ *num_axes ] = usage;
338 os->axes_usage[ *num_axes ] = usage;
343 interesting_hid = FALSE;
347 else if( page == HUP_BUTTON )
349 interesting_hid = ( usage > 0 ) &&
350 ( usage <= _JS_MAX_BUTTONS );
352 if( interesting_hid && usage - 1 > *num_buttons )
353 *num_buttons = usage - 1;
356 if( interesting_hid )
359 os->hids = calloc( 1, sizeof ( struct hid_item ) );
365 return os->hids != NULL;
371 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
372 * See "js.h" lines 80-178.
374 typedef struct tagSFG_Joystick SFG_Joystick;
375 struct tagSFG_Joystick
377 #if TARGET_HOST_MACINTOSH
378 #define ISP_NUM_AXIS 9
379 #define ISP_NUM_NEEDS 41
380 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
381 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
384 #if TARGET_HOST_MAC_OSX
385 IOHIDDeviceInterface ** hidDev;
386 IOHIDElementCookie buttonCookies[41];
387 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
388 long minReport[_JS_MAX_AXES],
389 maxReport[_JS_MAX_AXES];
392 #if TARGET_HOST_WIN32
399 #if TARGET_HOST_UNIX_X11
400 # if defined(__FreeBSD__) || defined(__NetBSD__)
401 struct os_specific_s *os;
407 float tmp_axes [ _JS_MAX_AXES ];
409 struct JS_DATA_TYPE js;
422 float dead_band[ _JS_MAX_AXES ];
423 float saturate [ _JS_MAX_AXES ];
424 float center [ _JS_MAX_AXES ];
425 float max [ _JS_MAX_AXES ];
426 float min [ _JS_MAX_AXES ];
430 * Functions associated with the "jsJoystick" class in PLIB
432 #if TARGET_HOST_MAC_OSX
433 #define K_NUM_DEVICES 32
435 io_object_t ioDevices[K_NUM_DEVICES];
437 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
438 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
440 void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
441 /* callback for CFArrayApply */
442 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
443 void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
445 void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
446 void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
447 void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
452 * The static joystick structure pointer
454 #define MAX_NUM_JOYSTICKS 2
455 static int fgNumberOfJoysticks = 0;
456 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
460 * Read the raw joystick data
462 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
464 #if TARGET_HOST_WIN32
470 #if defined(__FreeBSD__) || defined(__NetBSD__)
481 for( i = 0; i < joy->num_axes; i++ )
487 #if TARGET_HOST_MACINTOSH
492 for ( i = 0; i < joy->num_buttons; i++ )
495 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
498 *buttons |= state << i;
504 for ( i = 0; i < joy->num_axes; i++ )
507 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
510 axes [i] = (float) state;
515 #if TARGET_HOST_MAC_OSX
516 if ( buttons != NULL )
520 for ( i = 0; i < joy->num_buttons; i++ )
522 IOHIDEventStruct hidEvent;
523 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
524 if ( hidEvent.value )
531 for ( i = 0; i < joy->num_axes; i++ )
533 IOHIDEventStruct hidEvent;
534 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
535 axes[i] = hidEvent.value;
540 #if TARGET_HOST_WIN32
541 status = joyGetPosEx( joy->js_id, &joy->js );
543 if ( status != JOYERR_NOERROR )
545 joy->error = GL_TRUE;
550 *buttons = joy->js.dwButtons;
555 * WARNING - Fall through case clauses!!
557 switch ( joy->num_axes )
560 /* Generate two POV axes from the POV hat angle.
561 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
562 * hundredths of a degree, or 0xFFFF when idle.
564 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
571 /* This is the contentious bit: how to convert angle to X/Y.
572 * wk: I know of no define for PI that we could use here:
573 * SG_PI would pull in sg, M_PI is undefined for MSVC
574 * But the accuracy of the value of PI is very unimportant at
577 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
578 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
580 /* Convert to coordinates on a square so that North-East
581 * is (1,1) not (.7,.7), etc.
582 * s and c cannot both be zero so we won't divide by zero.
584 if ( fabs ( s ) < fabs ( c ) )
586 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
587 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
591 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
592 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
596 case 6: axes[5] = (float) joy->js.dwVpos;
597 case 5: axes[4] = (float) joy->js.dwUpos;
598 case 4: axes[3] = (float) joy->js.dwRpos;
599 case 3: axes[2] = (float) joy->js.dwZpos;
600 case 2: axes[1] = (float) joy->js.dwYpos;
601 case 1: axes[0] = (float) joy->js.dwXpos;
606 #if TARGET_HOST_UNIX_X11
607 # if defined(__FreeBSD__) || defined(__NetBSD__)
608 if ( joy->os->is_analog )
610 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
611 if ( status != sizeof(joy->os->ajs) ) {
612 perror ( joy->os->fname );
613 joy->error = GL_TRUE;
616 if ( buttons != NULL )
617 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
621 axes[0] = (float) joy->os->ajs.x;
622 axes[1] = (float) joy->os->ajs.y;
629 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
633 for ( h = joy->os->hids; h; h = h->next )
635 int d = hid_get_data ( joy->os->hid_data_buf, h );
637 int page = HID_PAGE ( h->usage );
638 int usage = HID_USAGE ( h->usage );
640 if ( page == HUP_GENERIC_DESKTOP )
643 for ( i = 0; i < joy->num_axes; i++ )
644 if (joy->os->axes_usage[i] == usage)
646 if (usage == HUG_HAT_SWITCH)
650 joy->os->cache_axes[i] = (float)hatmap_x[d];
651 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
655 joy->os->cache_axes[i] = (float)d;
660 else if (page == HUP_BUTTON)
662 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
665 joy->os->cache_buttons |= (1 << usage - 1);
667 joy->os->cache_buttons &= ~(1 << usage - 1);
672 if ( len < 0 && errno != EAGAIN )
674 perror( joy->os->fname );
677 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
679 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
687 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
689 if ( status != sizeof( struct js_event ) )
691 if ( errno == EAGAIN )
693 /* Use the old values */
695 *buttons = joy->tmp_buttons;
697 memcpy( axes, joy->tmp_axes,
698 sizeof( float ) * joy->num_axes );
702 fgWarning ( "%s", joy->fname );
703 joy->error = GL_TRUE;
707 switch ( joy->js.type & ~JS_EVENT_INIT )
709 case JS_EVENT_BUTTON:
710 if( joy->js.value == 0 ) /* clear the flag */
711 joy->tmp_buttons &= ~( 1 << joy->js.number );
713 joy->tmp_buttons |= ( 1 << joy->js.number );
717 if ( joy->js.number < joy->num_axes )
719 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
722 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
727 fgWarning ( "%s", "PLIB_JS: Unrecognised /dev/js return!?!" );
729 /* use the old values */
731 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
733 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
739 *buttons = joy->tmp_buttons;
743 status = read( joy->fd, &joy->js, JS_RETURN );
745 if ( status != JS_RETURN )
747 fgWarning( "%s", joy->fname );
748 joy->error = GL_TRUE;
753 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
754 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
756 *buttons = joy->js.buttons;
761 axes[ 0 ] = (float) joy->js.x;
762 axes[ 1 ] = (float) joy->js.y;
769 * Correct the joystick axis data
771 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
773 if( value < joy->center[ axis ] )
775 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
778 if( xx < -joy->saturate[ axis ] )
781 if( xx > -joy->dead_band [ axis ] )
784 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
785 joy->dead_band[ axis ] );
787 return ( xx < -1.0f ) ? -1.0f : xx;
791 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
792 joy->center[ axis ] );
794 if( xx > joy->saturate[ axis ] )
797 if( xx < joy->dead_band[ axis ] )
800 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
801 joy->dead_band[ axis ] );
803 return ( xx > 1.0f ) ? 1.0f : xx;
808 * Read the corrected joystick data
810 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
812 float raw_axes[ _JS_MAX_AXES ];
821 for ( i=0; i<joy->num_axes; i++ )
825 fghJoystickRawRead( joy, buttons, raw_axes );
828 for( i=0; i<joy->num_axes; i++ )
829 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
833 * Happy happy happy joy joy joy (happy new year toudi :D)
837 #if TARGET_HOST_MAC_OSX
838 /** open the IOKit connection, enumerate all the HID devices, add their
839 interface references to the static array. We then use the array index
840 as the device number when we come to open() the joystick. */
841 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
843 CFMutableDictionaryRef hidMatch = NULL;
844 IOReturn rv = kIOReturnSuccess;
846 io_iterator_t hidIterator;
849 /* build a dictionary matching HID devices */
850 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
852 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
853 if (rv != kIOReturnSuccess || !hidIterator) {
854 fgWarning( "%s", "no joystick (HID) devices found" );
859 while ((ioDev = IOIteratorNext(hidIterator))) {
860 /* filter out keyboard and mouse devices */
861 CFDictionaryRef properties = getCFProperties(ioDev);
864 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
865 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
866 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
867 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
869 /* keep only joystick devices */
870 if ( ( page == kHIDPage_GenericDesktop ) && (
871 (usage == kHIDUsage_GD_Joystick)
872 || (usage == kHIDUsage_GD_GamePad)
873 || (usage == kHIDUsage_GD_MultiAxisController)
874 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
875 /* add it to the array */
876 ioDevices[numDevices++] = ioDev;
879 IOObjectRelease(hidIterator);
882 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
885 CFMutableDictionaryRef cfProperties;
888 /* comment copied from darwin/SDL_sysjoystick.c */
889 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
890 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
893 io_registry_entry_t parent1, parent2;
895 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
896 if (rv != kIOReturnSuccess) {
897 fgWarning ( "%s", "error getting device entry parent");
901 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
902 if (rv != kIOReturnSuccess) {
903 fgWarning ( "%s", "error getting device entry parent 2");
908 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
909 &cfProperties, kCFAllocatorDefault, kNilOptions);
910 if (rv != kIOReturnSuccess || !cfProperties) {
911 fgWarning ( "%s", "error getting device properties");
918 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
920 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
921 fgError ( "%s", "element enumerator passed non-dictionary value");
925 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
928 /** element enumerator function : pass NULL for top-level*/
929 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
931 assert(CFGetTypeID(element) == CFArrayGetTypeID());
933 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
934 CFArrayApplyFunction((CFArrayRef) element, range,
935 &fghJoystickElementEnumerator, joy );
938 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
940 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
941 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
943 long type, page, usage;
945 CFNumberGetValue((CFNumberRef)
946 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
947 kCFNumberLongType, &type);
950 case kIOHIDElementTypeInput_Misc:
951 case kIOHIDElementTypeInput_Axis:
952 case kIOHIDElementTypeInput_Button:
953 printf("got input element...");
954 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
955 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
957 if (page == kHIDPage_GenericDesktop) {
958 switch ( usage ) /* look at usage to determine function */
963 case kHIDUsage_GD_Rx:
964 case kHIDUsage_GD_Ry:
965 case kHIDUsage_GD_Rz:
966 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
968 fghJoystickAddAxisElement((CFDictionaryRef) element);
971 case kHIDUsage_GD_Hatswitch:
973 fghJoystickAddHatElement((CFDictionaryRef) element);
977 printf("input type element has weird usage (%x)\n", usage);
980 } else if (page == kHIDPage_Button) {
982 fghJoystickAddButtonElement((CFDictionaryRef) element);
984 printf("input type element has weird page (%x)\n", page);
987 case kIOHIDElementTypeCollection:
988 fghJoystickEnumerateElements (
989 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
998 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
1000 long cookie, lmin, lmax;
1001 int index = joy->num_axes++;
1003 CFNumberGetValue ((CFNumberRef)
1004 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1005 kCFNumberLongType, &cookie);
1007 axisCookies[index] = (IOHIDElementCookie) cookie;
1009 CFNumberGetValue ((CFNumberRef)
1010 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1011 kCFNumberLongType, &lmin);
1013 CFNumberGetValue ((CFNumberRef)
1014 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1015 kCFNumberLongType, &lmax);
1017 joy->min[index] = lmin;
1018 joy->max[index] = lmax;
1019 joy->dead_band[index] = 0.0;
1020 joy->saturate[index] = 1.0;
1021 joy->center[index] = (lmax + lmin) * 0.5;
1024 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1027 CFNumberGetValue ((CFNumberRef)
1028 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1029 kCFNumberLongType, &cookie);
1031 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1032 /* anything else for buttons? */
1035 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1037 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1038 /* do we map hats to axes or buttons? */
1042 #if TARGET_HOST_WIN32
1044 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1046 # if defined(_MSC_VER)
1047 # pragma comment (lib, "advapi32.lib")
1050 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1052 char buffer [ 256 ];
1054 char OEMKey [ 256 ];
1063 /* Open .. MediaResources\CurrentJoystickSettings */
1064 sprintf ( buffer, "%s\\%s\\%s",
1065 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1066 REGSTR_KEY_JOYCURR );
1068 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1070 if ( lr != ERROR_SUCCESS ) return 0;
1072 /* Get OEM Key name */
1073 dwcb = sizeof(OEMKey);
1075 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1076 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1078 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1079 RegCloseKey ( hKey );
1081 if ( lr != ERROR_SUCCESS ) return 0;
1083 /* Open OEM Key from ...MediaProperties */
1084 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1086 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1088 if ( lr != ERROR_SUCCESS ) return 0;
1093 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1095 RegCloseKey ( hKey );
1097 if ( lr != ERROR_SUCCESS ) return 0;
1104 static void fghJoystickOpen( SFG_Joystick* joy )
1107 #if TARGET_HOST_MACINTOSH
1110 #if TARGET_HOST_MAC_OSX
1113 IOCFPlugInInterface **plugin;
1115 HRESULT pluginResult;
1117 CFDictionaryRef props;
1118 CFTypeRef topLevelElement;
1120 #if TARGET_HOST_UNIX_X11
1121 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1132 * Default values (for no joystick -- each conditional will reset the
1136 joy->num_axes = joy->num_buttons = 0;
1137 joy->name[ 0 ] = '\0';
1139 #if TARGET_HOST_MACINTOSH
1140 /* XXX FIXME: get joystick name in Mac */
1142 err = ISpStartup( );
1146 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1148 joy->error = GL_TRUE;
1150 /* initialize the needs structure */
1151 ISpNeed temp_isp_needs[ isp_num_needs ] =
1153 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1154 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1155 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1156 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1157 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1158 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1159 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1160 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1161 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1163 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1190 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1191 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1192 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1193 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1194 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1197 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1200 /* next two calls allow keyboard and mouse to emulate other input
1201 * devices (gamepads, joysticks, etc)
1204 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1208 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1212 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1213 joy->isp_needs, joy->isp_elem,
1215 ISP_CHECK_ERR( err )
1217 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1218 'freeglut', nil, 0, 128, 0 );
1219 ISP_CHECK_ERR( err )
1221 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1222 joy->num_axes = joy->isp_num_axis;
1224 for( i = 0; i < joy->num_axes; i++ )
1226 joy->dead_band[ i ] = 0;
1227 joy->saturate [ i ] = 1;
1228 joy->center [ i ] = kISpAxisMiddle;
1229 joy->max [ i ] = kISpAxisMaximum;
1230 joy->min [ i ] = kISpAxisMinimum;
1233 joy->error = GL_FALSE;
1236 joy->num_buttons = joy->num_axes = 0;
1239 #if TARGET_HOST_MAC_OSX
1240 if( joy->id >= numDevices )
1242 fgWarning( "%s", "device index out of range in fgJoystickOpen()" );
1246 /* create device interface */
1247 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1248 kIOHIDDeviceUserClientTypeID,
1249 kIOCFPlugInInterfaceID,
1252 if( rv != kIOReturnSuccess )
1254 fgWarning( "%s", "error creating plugin for io device" );
1258 pluginResult = ( *plugin )->QueryInterface(
1260 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1261 &( LPVOID )joy->hidDev
1264 if( pluginResult != S_OK )
1265 fgWarning ( "%s", "QI-ing IO plugin to HID Device interface failed" );
1267 ( *plugin )->Release( plugin ); /* don't leak a ref */
1268 if( joy->hidDev == NULL )
1271 /* store the interface in this instance */
1272 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1273 if( rv != kIOReturnSuccess )
1275 fgWarning( "error opening device interface");
1279 props = getCFProperties( ioDevices[ joy->id ] );
1281 /* recursively enumerate all the bits */
1282 CFTypeRef topLevelElement =
1283 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1284 enumerateElements( topLevelElement );
1289 #if TARGET_HOST_WIN32
1290 joy->js.dwFlags = JOY_RETURNALL;
1291 joy->js.dwSize = sizeof( joy->js );
1293 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1296 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1299 if( joy->jsCaps.wNumAxes == 0 )
1302 joy->error = GL_TRUE;
1306 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1307 * at least for USB. Try to get the real name from the registry.
1309 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1310 sizeof( joy->name ) ) )
1312 fgWarning( "JS: Failed to read joystick name from registry" );
1313 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1316 /* Windows joystick drivers may provide any combination of
1317 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1319 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1321 joy->num_axes = _JS_MAX_AXES;
1322 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1323 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1328 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1329 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1330 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1331 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1332 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1333 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1334 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1335 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1336 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1337 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1338 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1339 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1342 /* Guess all the rest judging on the axes extremals */
1343 for( i = 0; i < joy->num_axes; i++ )
1345 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1346 joy->dead_band[ i ] = 0.0f;
1347 joy->saturate [ i ] = 1.0f;
1351 #if TARGET_HOST_UNIX_X11
1352 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1353 for( i = 0; i < _JS_MAX_AXES; i++ )
1354 joy->os->cache_axes[ i ] = 0.0f;
1356 joy->os->cache_buttons = 0;
1358 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1360 if( joy->os->fd < 0 && errno == EACCES )
1361 fgWarning ( "%s exists but is not readable by you\n", joy->os->fname );
1363 joy->error =( joy->os->fd < 0 );
1369 joy->num_buttons = 0;
1370 if( joy->os->is_analog )
1373 char joyfname[ 1024 ];
1374 int noargs, in_no_axes;
1376 float axes [ _JS_MAX_AXES ];
1377 int buttons[ _JS_MAX_AXES ];
1380 joy->num_buttons = 32;
1382 fghJoystickRawRead( joy, buttons, axes );
1383 joy->error = axes[ 0 ] < -1000000000.0f;
1387 sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
1389 joyfile = fopen( joyfname, "r" );
1390 joy->error =( joyfile == NULL );
1394 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1395 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1396 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1397 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1402 for( i = 0; i < _JS_MAX_AXES; i++ )
1404 joy->dead_band[ i ] = 0.0f;
1405 joy->saturate [ i ] = 1.0f;
1408 return; /* End of analog code */
1412 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1413 &joy->num_buttons ) )
1415 close( joy->os->fd );
1416 joy->error = GL_TRUE;
1420 cp = strrchr( joy->os->fname, '/' );
1423 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1425 strcpy( joy->name, &cp[1] );
1428 if( joy->num_axes > _JS_MAX_AXES )
1429 joy->num_axes = _JS_MAX_AXES;
1431 for( i = 0; i < _JS_MAX_AXES; i++ )
1433 /* We really should get this from the HID, but that data seems
1434 * to be quite unreliable for analog-to-USB converters. Punt for
1437 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1439 joy->max [ i ] = 1.0f;
1440 joy->center[ i ] = 0.0f;
1441 joy->min [ i ] = -1.0f;
1445 joy->max [ i ] = 255.0f;
1446 joy->center[ i ] = 127.0f;
1447 joy->min [ i ] = 0.0f;
1450 joy->dead_band[ i ] = 0.0f;
1451 joy->saturate[ i ] = 1.0f;
1456 #if defined( __linux__ )
1457 /* Default for older Linux systems. */
1459 joy->num_buttons = 32;
1462 for( i = 0; i < _JS_MAX_AXES; i++ )
1463 joy->tmp_axes[ i ] = 0.0f;
1465 joy->tmp_buttons = 0;
1468 joy->fd = open( joy->fname, O_RDONLY );
1470 joy->error =( joy->fd < 0 );
1475 /* 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 ] =
1542 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1545 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1546 fgJoystick[ ident ]->error = GL_TRUE;
1548 #if TARGET_HOST_MACINTOSH
1549 fgJoystick[ ident ]->id = ident;
1550 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
1551 fgJoystick[ ident ]->error = GL_FALSE;
1554 #if TARGET_HOST_MAC_OSX
1555 fgJoystick[ ident ]->id = ident;
1556 fgJoystick[ ident ]->error = GL_FALSE;
1557 fgJoystick[ ident ]->num_axes = 0;
1558 fgJoystick[ ident ]->num_buttons = 0;
1560 if( numDevices < 0 )
1562 /* do first-time init (since we can't over-ride jsInit, hmm */
1565 mach_port_t masterPort;
1566 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1567 if( rv != kIOReturnSuccess )
1569 fgWarning( "%s", "error getting master Mach port" );
1572 fghJoystickFindDevices( masterPort );
1575 if ( ident >= numDevices )
1577 fgJoystick[ ident ]->error = GL_TRUE;
1581 /* get the name now too */
1582 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1583 CFTypeRef ref = CFDictionaryGetValue( properties,
1584 CFSTR( kIOHIDProductKey ) );
1586 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1589 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1590 CFStringGetSystemEncoding( ) ) )
1592 fgWarning( "%s", "error getting device name" );
1597 #if TARGET_HOST_WIN32
1601 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1602 fgJoystick[ ident ]->error = GL_FALSE;
1605 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1606 fgJoystick[ ident ]->error = GL_FALSE;
1609 fgJoystick[ ident ]->num_axes = 0;
1610 fgJoystick[ ident ]->error = GL_TRUE;
1615 #if TARGET_HOST_UNIX_X11
1616 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1617 fgJoystick[ ident ]->id = ident;
1618 fgJoystick[ ident ]->error = GL_FALSE;
1620 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1621 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1622 if( ident < USB_IDENT_OFFSET )
1623 fgJoystick[ ident ]->os->is_analog = 1;
1624 if( fgJoystick[ ident ]->os->is_analog )
1625 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
1627 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
1628 ident - USB_IDENT_OFFSET );
1629 # elif defined( __linux__ )
1630 fgJoystick[ ident ]->id = ident;
1631 fgJoystick[ ident ]->error = GL_FALSE;
1633 sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1635 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1636 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
1640 fghJoystickOpen( fgJoystick[ ident ] );
1646 void fgJoystickClose( void )
1649 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1651 if( fgJoystick[ ident ] )
1654 #if TARGET_HOST_MACINTOSH
1660 #if TARGET_HOST_MAC_OSX
1661 ( *( fgJoystick[ ident ]->hidDev ) )->
1662 close( fgJoystick[ ident ]->hidDev );
1665 #if TARGET_HOST_WIN32
1666 /* Do nothing special */
1669 #if TARGET_HOST_UNIX_X11
1670 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1671 if( fgJoystick[ident]->os )
1673 if( ! fgJoystick[ ident ]->error )
1674 close( fgJoystick[ ident ]->os->fd );
1676 if( fgJoystick[ ident ]->os->hids )
1677 free (fgJoystick[ ident ]->os->hids);
1678 if( fgJoystick[ ident ]->os->hid_data_buf )
1679 free( fgJoystick[ ident ]->os->hid_data_buf );
1681 free( fgJoystick[ident]->os );
1685 if( ! fgJoystick[ident]->error )
1686 close( fgJoystick[ ident ]->fd );
1689 free( fgJoystick[ ident ] );
1690 fgJoystick[ ident ] = NULL;
1691 /* show joystick has been deinitialized */
1697 * Polls the joystick and executes the joystick callback hooked to the
1698 * window specified in the function's parameter:
1700 void fgJoystickPollWindow( SFG_Window* window )
1702 float axes[ _JS_MAX_AXES ];
1706 freeglut_return_if_fail( window );
1707 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1709 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1711 if( fgJoystick[ident] )
1713 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1715 if( !fgJoystick[ident]->error )
1716 INVOKE_WCB( *window, Joystick,
1718 (int) ( axes[ 0 ] * 1000.0f ),
1719 (int) ( axes[ 1 ] * 1000.0f ),
1720 (int) ( axes[ 2 ] * 1000.0f ) )
1727 * PWO: These jsJoystick class methods have not been implemented.
1729 int glutJoystickGetNumAxes( int ident )
1731 return fgJoystick[ ident ]->num_axes;
1733 int glutJoystickNotWorking( int ident )
1735 return fgJoystick[ ident ]->error;
1738 float glutJoystickGetDeadBand( int ident, int axis )
1740 return fgJoystick[ ident ]->dead_band [ axis ];
1742 void glutJoystickSetDeadBand( int ident, int axis, float db )
1744 fgJoystick[ ident ]->dead_band[ axis ] = db;
1747 float glutJoystickGetSaturation( int ident, int axis )
1749 return fgJoystick[ ident ]->saturate[ axis ];
1751 void glutJoystickSetSaturation( int ident, int axis, float st )
1753 fgJoystick[ ident ]->saturate [ axis ] = st;
1756 void glutJoystickSetMinRange( int ident, float *axes )
1758 memcpy( fgJoystick[ ident ]->min, axes,
1759 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1761 void glutJoystickSetMaxRange( int ident, float *axes )
1763 memcpy( fgJoystick[ ident ]->max, axes,
1764 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1766 void glutJoystickSetCenter( int ident, float *axes )
1768 memcpy( fgJoystick[ ident ]->center, axes,
1769 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1772 void glutJoystickGetMinRange( int ident, float *axes )
1774 memcpy( axes, fgJoystick[ ident ]->min,
1775 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1777 void glutJoystickGetMaxRange( int ident, float *axes )
1779 memcpy( axes, fgJoystick[ ident ]->max,
1780 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1782 void glutJoystickGetCenter( int ident, float *axes )
1784 memcpy( axes, fgJoystick[ ident ]->center,
1785 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1788 /*** END OF FILE ***/