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 fgWarning ( "Can't open %s for read!", buf );
245 protection_warned = 1;
252 static int fghJoystickInitializeHID(struct os_specific_s *os,
253 int *num_axes, int *num_buttons)
255 int size, is_joystick;
256 # ifdef HAVE_USBHID_H
263 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
265 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
271 # ifdef HAVE_USBHID_H
272 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
274 /*** XXX {report_id} may not be the right variable? ***/
275 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
279 size = hid_report_size( rd, hid_input, report_id );
281 size = hid_report_size( rd, 0, hid_input );
283 os->hid_data_buf = calloc( 1, size );
287 # ifdef HAVE_USBHID_H
288 d = hid_start_parse( rd, 1 << hid_input, report_id );
290 d = hid_start_parse( rd, 1 << hid_input );
292 while( hid_get_item( d, &h ) )
294 int usage, page, interesting_hid;
296 page = HID_PAGE( h.usage );
297 usage = HID_USAGE( h.usage );
299 /* This test is somewhat too simplistic, but this is how MicroSoft
300 * does, so I guess it works for all joysticks/game pads. */
301 is_joystick = is_joystick ||
302 ( h.kind == hid_collection &&
303 page == HUP_GENERIC_DESKTOP &&
304 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
306 if( h.kind != hid_input )
312 interesting_hid = TRUE;
313 if( page == HUP_GENERIC_DESKTOP )
324 if( *num_axes < _JS_MAX_AXES )
326 os->axes_usage[ *num_axes ] = usage;
331 /* Allocate two axes for a hat */
332 if( *num_axes + 1 < _JS_MAX_AXES )
334 os->axes_usage[ *num_axes ] = usage;
336 os->axes_usage[ *num_axes ] = usage;
341 interesting_hid = FALSE;
345 else if( page == HUP_BUTTON )
347 interesting_hid = ( usage > 0 ) &&
348 ( usage <= _JS_MAX_BUTTONS );
350 if( interesting_hid && usage - 1 > *num_buttons )
351 *num_buttons = usage - 1;
354 if( interesting_hid )
357 os->hids = calloc( 1, sizeof ( struct hid_item ) );
363 return os->hids != NULL;
369 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
370 * See "js.h" lines 80-178.
372 typedef struct tagSFG_Joystick SFG_Joystick;
373 struct tagSFG_Joystick
375 #if TARGET_HOST_MACINTOSH
376 #define ISP_NUM_AXIS 9
377 #define ISP_NUM_NEEDS 41
378 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
379 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
382 #if TARGET_HOST_MAC_OSX
383 IOHIDDeviceInterface ** hidDev;
384 IOHIDElementCookie buttonCookies[41];
385 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
386 long minReport[_JS_MAX_AXES],
387 maxReport[_JS_MAX_AXES];
390 #if TARGET_HOST_WIN32
397 #if TARGET_HOST_UNIX_X11
398 # if defined(__FreeBSD__) || defined(__NetBSD__)
399 struct os_specific_s *os;
405 float tmp_axes [ _JS_MAX_AXES ];
407 struct JS_DATA_TYPE js;
420 float dead_band[ _JS_MAX_AXES ];
421 float saturate [ _JS_MAX_AXES ];
422 float center [ _JS_MAX_AXES ];
423 float max [ _JS_MAX_AXES ];
424 float min [ _JS_MAX_AXES ];
428 * Functions associated with the "jsJoystick" class in PLIB
430 #if TARGET_HOST_MAC_OSX
431 #define K_NUM_DEVICES 32
433 io_object_t ioDevices[K_NUM_DEVICES];
435 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
436 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
438 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
439 /* callback for CFArrayApply */
440 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
441 static void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
443 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
444 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
445 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
450 * The static joystick structure pointer
452 #define MAX_NUM_JOYSTICKS 2
453 static int fgNumberOfJoysticks = 0;
454 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
458 * Read the raw joystick data
460 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
462 #if TARGET_HOST_WIN32
468 #if defined(__FreeBSD__) || defined(__NetBSD__)
479 for( i = 0; i < joy->num_axes; i++ )
485 #if TARGET_HOST_MACINTOSH
490 for ( i = 0; i < joy->num_buttons; i++ )
493 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
496 *buttons |= state << i;
502 for ( i = 0; i < joy->num_axes; i++ )
505 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
508 axes [i] = (float) state;
513 #if TARGET_HOST_MAC_OSX
514 if ( buttons != NULL )
518 for ( i = 0; i < joy->num_buttons; i++ )
520 IOHIDEventStruct hidEvent;
521 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
522 if ( hidEvent.value )
529 for ( i = 0; i < joy->num_axes; i++ )
531 IOHIDEventStruct hidEvent;
532 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
533 axes[i] = hidEvent.value;
538 #if TARGET_HOST_WIN32
539 status = joyGetPosEx( joy->js_id, &joy->js );
541 if ( status != JOYERR_NOERROR )
543 joy->error = GL_TRUE;
548 *buttons = joy->js.dwButtons;
553 * WARNING - Fall through case clauses!!
555 switch ( joy->num_axes )
558 /* Generate two POV axes from the POV hat angle.
559 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
560 * hundredths of a degree, or 0xFFFF when idle.
562 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
569 /* This is the contentious bit: how to convert angle to X/Y.
570 * wk: I know of no define for PI that we could use here:
571 * SG_PI would pull in sg, M_PI is undefined for MSVC
572 * But the accuracy of the value of PI is very unimportant at
575 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
576 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
578 /* Convert to coordinates on a square so that North-East
579 * is (1,1) not (.7,.7), etc.
580 * s and c cannot both be zero so we won't divide by zero.
582 if ( fabs ( s ) < fabs ( c ) )
584 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
585 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
589 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
590 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
594 case 6: axes[5] = (float) joy->js.dwVpos;
595 case 5: axes[4] = (float) joy->js.dwUpos;
596 case 4: axes[3] = (float) joy->js.dwRpos;
597 case 3: axes[2] = (float) joy->js.dwZpos;
598 case 2: axes[1] = (float) joy->js.dwYpos;
599 case 1: axes[0] = (float) joy->js.dwXpos;
604 #if TARGET_HOST_UNIX_X11
605 # if defined(__FreeBSD__) || defined(__NetBSD__)
606 if ( joy->os->is_analog )
608 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
609 if ( status != sizeof(joy->os->ajs) ) {
610 perror ( joy->os->fname );
611 joy->error = GL_TRUE;
614 if ( buttons != NULL )
615 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
619 axes[0] = (float) joy->os->ajs.x;
620 axes[1] = (float) joy->os->ajs.y;
627 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
631 for ( h = joy->os->hids; h; h = h->next )
633 int d = hid_get_data ( joy->os->hid_data_buf, h );
635 int page = HID_PAGE ( h->usage );
636 int usage = HID_USAGE ( h->usage );
638 if ( page == HUP_GENERIC_DESKTOP )
641 for ( i = 0; i < joy->num_axes; i++ )
642 if (joy->os->axes_usage[i] == usage)
644 if (usage == HUG_HAT_SWITCH)
648 joy->os->cache_axes[i] = (float)hatmap_x[d];
649 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
653 joy->os->cache_axes[i] = (float)d;
658 else if (page == HUP_BUTTON)
660 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
663 joy->os->cache_buttons |= (1 << usage - 1);
665 joy->os->cache_buttons &= ~(1 << usage - 1);
670 if ( len < 0 && errno != EAGAIN )
672 perror( joy->os->fname );
675 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
677 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
685 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
687 if ( status != sizeof( struct js_event ) )
689 if ( errno == EAGAIN )
691 /* Use the old values */
693 *buttons = joy->tmp_buttons;
695 memcpy( axes, joy->tmp_axes,
696 sizeof( float ) * joy->num_axes );
700 fgWarning ( "%s", joy->fname );
701 joy->error = GL_TRUE;
705 switch ( joy->js.type & ~JS_EVENT_INIT )
707 case JS_EVENT_BUTTON:
708 if( joy->js.value == 0 ) /* clear the flag */
709 joy->tmp_buttons &= ~( 1 << joy->js.number );
711 joy->tmp_buttons |= ( 1 << joy->js.number );
715 if ( joy->js.number < joy->num_axes )
717 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
720 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
725 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
727 /* use the old values */
729 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
731 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
737 *buttons = joy->tmp_buttons;
741 status = read( joy->fd, &joy->js, JS_RETURN );
743 if ( status != JS_RETURN )
745 fgWarning( "%s", joy->fname );
746 joy->error = GL_TRUE;
751 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
752 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
754 *buttons = joy->js.buttons;
759 axes[ 0 ] = (float) joy->js.x;
760 axes[ 1 ] = (float) joy->js.y;
767 * Correct the joystick axis data
769 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
771 if( value < joy->center[ axis ] )
773 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
776 if( xx < -joy->saturate[ axis ] )
779 if( xx > -joy->dead_band [ axis ] )
782 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
783 joy->dead_band[ axis ] );
785 return ( xx < -1.0f ) ? -1.0f : xx;
789 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
790 joy->center[ axis ] );
792 if( xx > joy->saturate[ axis ] )
795 if( xx < joy->dead_band[ axis ] )
798 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
799 joy->dead_band[ axis ] );
801 return ( xx > 1.0f ) ? 1.0f : xx;
806 * Read the corrected joystick data
808 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
810 float raw_axes[ _JS_MAX_AXES ];
819 for ( i=0; i<joy->num_axes; i++ )
823 fghJoystickRawRead( joy, buttons, raw_axes );
826 for( i=0; i<joy->num_axes; i++ )
827 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
831 * Happy happy happy joy joy joy (happy new year toudi :D)
835 #if TARGET_HOST_MAC_OSX
836 /** open the IOKit connection, enumerate all the HID devices, add their
837 interface references to the static array. We then use the array index
838 as the device number when we come to open() the joystick. */
839 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
841 CFMutableDictionaryRef hidMatch = NULL;
842 IOReturn rv = kIOReturnSuccess;
844 io_iterator_t hidIterator;
847 /* build a dictionary matching HID devices */
848 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
850 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
851 if (rv != kIOReturnSuccess || !hidIterator) {
852 fgWarning( "no joystick (HID) devices found" );
857 while ((ioDev = IOIteratorNext(hidIterator))) {
858 /* filter out keyboard and mouse devices */
859 CFDictionaryRef properties = getCFProperties(ioDev);
862 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
863 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
864 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
865 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
867 /* keep only joystick devices */
868 if ( ( page == kHIDPage_GenericDesktop ) && (
869 (usage == kHIDUsage_GD_Joystick)
870 || (usage == kHIDUsage_GD_GamePad)
871 || (usage == kHIDUsage_GD_MultiAxisController)
872 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
873 /* add it to the array */
874 ioDevices[numDevices++] = ioDev;
877 IOObjectRelease(hidIterator);
880 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
883 CFMutableDictionaryRef cfProperties;
886 /* comment copied from darwin/SDL_sysjoystick.c */
887 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
888 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
891 io_registry_entry_t parent1, parent2;
893 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
894 if (rv != kIOReturnSuccess) {
895 fgWarning ( "error getting device entry parent");
899 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
900 if (rv != kIOReturnSuccess) {
901 fgWarning ( "error getting device entry parent 2");
906 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
907 &cfProperties, kCFAllocatorDefault, kNilOptions);
908 if (rv != kIOReturnSuccess || !cfProperties) {
909 fgWarning ( "error getting device properties");
916 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
918 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
919 fgError ( "%s", "element enumerator passed non-dictionary value");
923 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
926 /** element enumerator function : pass NULL for top-level*/
927 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
929 assert(CFGetTypeID(element) == CFArrayGetTypeID());
931 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
932 CFArrayApplyFunction((CFArrayRef) element, range,
933 &fghJoystickElementEnumerator, joy );
936 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
938 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
939 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
941 long type, page, usage;
943 CFNumberGetValue((CFNumberRef)
944 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
945 kCFNumberLongType, &type);
948 case kIOHIDElementTypeInput_Misc:
949 case kIOHIDElementTypeInput_Axis:
950 case kIOHIDElementTypeInput_Button:
951 printf("got input element...");
952 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
953 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
955 if (page == kHIDPage_GenericDesktop) {
956 switch ( usage ) /* look at usage to determine function */
961 case kHIDUsage_GD_Rx:
962 case kHIDUsage_GD_Ry:
963 case kHIDUsage_GD_Rz:
964 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
966 fghJoystickAddAxisElement((CFDictionaryRef) element);
969 case kHIDUsage_GD_Hatswitch:
971 fghJoystickAddHatElement((CFDictionaryRef) element);
975 fgWarning ( "input type element has weird usage (%x)", usage);
978 } else if (page == kHIDPage_Button) {
980 fghJoystickAddButtonElement((CFDictionaryRef) element);
982 fgWarning ( "input type element has weird page (%x)", page);
985 case kIOHIDElementTypeCollection:
986 fghJoystickEnumerateElements (
987 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
996 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
998 long cookie, lmin, lmax;
999 int index = joy->num_axes++;
1001 CFNumberGetValue ((CFNumberRef)
1002 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1003 kCFNumberLongType, &cookie);
1005 axisCookies[index] = (IOHIDElementCookie) cookie;
1007 CFNumberGetValue ((CFNumberRef)
1008 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1009 kCFNumberLongType, &lmin);
1011 CFNumberGetValue ((CFNumberRef)
1012 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1013 kCFNumberLongType, &lmax);
1015 joy->min[index] = lmin;
1016 joy->max[index] = lmax;
1017 joy->dead_band[index] = 0.0;
1018 joy->saturate[index] = 1.0;
1019 joy->center[index] = (lmax + lmin) * 0.5;
1022 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1025 CFNumberGetValue ((CFNumberRef)
1026 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1027 kCFNumberLongType, &cookie);
1029 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1030 /* anything else for buttons? */
1033 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1035 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1036 /* do we map hats to axes or buttons? */
1040 #if TARGET_HOST_WIN32
1042 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1044 # if defined(_MSC_VER)
1045 # pragma comment (lib, "advapi32.lib")
1048 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1050 char buffer [ 256 ];
1052 char OEMKey [ 256 ];
1061 /* Open .. MediaResources\CurrentJoystickSettings */
1062 sprintf ( buffer, "%s\\%s\\%s",
1063 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1064 REGSTR_KEY_JOYCURR );
1066 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1068 if ( lr != ERROR_SUCCESS ) return 0;
1070 /* Get OEM Key name */
1071 dwcb = sizeof(OEMKey);
1073 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1074 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1076 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1077 RegCloseKey ( hKey );
1079 if ( lr != ERROR_SUCCESS ) return 0;
1081 /* Open OEM Key from ...MediaProperties */
1082 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1084 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1086 if ( lr != ERROR_SUCCESS ) return 0;
1091 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1093 RegCloseKey ( hKey );
1095 if ( lr != ERROR_SUCCESS ) return 0;
1102 static void fghJoystickOpen( SFG_Joystick* joy )
1105 #if TARGET_HOST_MACINTOSH
1108 #if TARGET_HOST_MAC_OSX
1111 IOCFPlugInInterface **plugin;
1113 HRESULT pluginResult;
1115 CFDictionaryRef props;
1116 CFTypeRef topLevelElement;
1118 #if TARGET_HOST_UNIX_X11
1119 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1130 * Default values (for no joystick -- each conditional will reset the
1134 joy->num_axes = joy->num_buttons = 0;
1135 joy->name[ 0 ] = '\0';
1137 #if TARGET_HOST_MACINTOSH
1138 /* XXX FIXME: get joystick name in Mac */
1140 err = ISpStartup( );
1144 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1146 joy->error = GL_TRUE;
1148 /* initialize the needs structure */
1149 ISpNeed temp_isp_needs[ isp_num_needs ] =
1151 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1152 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1153 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1154 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1155 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1156 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1157 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1158 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1159 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1161 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1190 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1191 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1192 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1195 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1198 /* next two calls allow keyboard and mouse to emulate other input
1199 * devices (gamepads, joysticks, etc)
1202 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1206 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1210 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1211 joy->isp_needs, joy->isp_elem,
1213 ISP_CHECK_ERR( err )
1215 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1216 'freeglut', nil, 0, 128, 0 );
1217 ISP_CHECK_ERR( err )
1219 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1220 joy->num_axes = joy->isp_num_axis;
1222 for( i = 0; i < joy->num_axes; i++ )
1224 joy->dead_band[ i ] = 0;
1225 joy->saturate [ i ] = 1;
1226 joy->center [ i ] = kISpAxisMiddle;
1227 joy->max [ i ] = kISpAxisMaximum;
1228 joy->min [ i ] = kISpAxisMinimum;
1231 joy->error = GL_FALSE;
1234 joy->num_buttons = joy->num_axes = 0;
1237 #if TARGET_HOST_MAC_OSX
1238 if( joy->id >= numDevices )
1240 fgWarning( "device index out of range in fgJoystickOpen()" );
1244 /* create device interface */
1245 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1246 kIOHIDDeviceUserClientTypeID,
1247 kIOCFPlugInInterfaceID,
1250 if( rv != kIOReturnSuccess )
1252 fgWarning( "error creating plugin for io device" );
1256 pluginResult = ( *plugin )->QueryInterface(
1258 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1259 &( LPVOID )joy->hidDev
1262 if( pluginResult != S_OK )
1263 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1265 ( *plugin )->Release( plugin ); /* don't leak a ref */
1266 if( joy->hidDev == NULL )
1269 /* store the interface in this instance */
1270 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1271 if( rv != kIOReturnSuccess )
1273 fgWarning( "error opening device interface");
1277 props = getCFProperties( ioDevices[ joy->id ] );
1279 /* recursively enumerate all the bits */
1280 CFTypeRef topLevelElement =
1281 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1282 enumerateElements( topLevelElement );
1287 #if TARGET_HOST_WIN32
1288 joy->js.dwFlags = JOY_RETURNALL;
1289 joy->js.dwSize = sizeof( joy->js );
1291 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1294 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1297 if( joy->jsCaps.wNumAxes == 0 )
1300 joy->error = GL_TRUE;
1304 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1305 * at least for USB. Try to get the real name from the registry.
1307 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1308 sizeof( joy->name ) ) )
1310 fgWarning( "JS: Failed to read joystick name from registry" );
1311 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1314 /* Windows joystick drivers may provide any combination of
1315 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1317 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1319 joy->num_axes = _JS_MAX_AXES;
1320 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1321 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1326 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1327 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1328 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1329 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1330 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1331 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1332 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1333 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1334 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1335 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1336 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1337 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1340 /* 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", 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, '/' );
1421 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1423 strcpy( joy->name, &cp[1] );
1426 if( joy->num_axes > _JS_MAX_AXES )
1427 joy->num_axes = _JS_MAX_AXES;
1429 for( i = 0; i < _JS_MAX_AXES; i++ )
1431 /* We really should get this from the HID, but that data seems
1432 * to be quite unreliable for analog-to-USB converters. Punt for
1435 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1437 joy->max [ i ] = 1.0f;
1438 joy->center[ i ] = 0.0f;
1439 joy->min [ i ] = -1.0f;
1443 joy->max [ i ] = 255.0f;
1444 joy->center[ i ] = 127.0f;
1445 joy->min [ i ] = 0.0f;
1448 joy->dead_band[ i ] = 0.0f;
1449 joy->saturate[ i ] = 1.0f;
1454 #if defined( __linux__ )
1455 /* 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 );
1473 /* Set the correct number of axes for the linux driver */
1475 /* Melchior Franz's fixes for big-endian Linuxes since writing
1476 * to the upper byte of an uninitialized word doesn't work.
1479 ioctl( joy->fd, JSIOCGAXES, &u );
1481 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1482 joy->num_buttons = u;
1483 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1484 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1488 * The Linux driver seems to return 512 for all axes
1489 * when no stick is present - but there is a chance
1490 * that could happen by accident - so it's gotta happen
1491 * on both axes for at least 100 attempts.
1493 * PWO: shouldn't be that done somehow wiser on the kernel level?
1500 fghJoystickRawRead( joy, NULL, joy->center );
1502 } while( !joy->error &&
1504 joy->center[ 0 ] == 512.0f &&
1505 joy->center[ 1 ] == 512.0f );
1507 if ( counter >= 100 )
1508 joy->error = GL_TRUE;
1511 for( i = 0; i < _JS_MAX_AXES; i++ )
1514 joy->max [ i ] = 32767.0f;
1515 joy->center[ i ] = 0.0f;
1516 joy->min [ i ] = -32767.0f;
1518 joy->max[ i ] = joy->center[ i ] * 2.0f;
1519 joy->min[ i ] = 0.0f;
1521 joy->dead_band[ i ] = 0.0f;
1522 joy->saturate [ i ] = 1.0f;
1529 * This function replaces the constructor method in the JS library.
1531 void fgJoystickInit( int ident )
1533 if( ident >= MAX_NUM_JOYSTICKS )
1534 fgError( "Too large a joystick number: %d", ident );
1536 if( fgJoystick[ ident ] )
1537 fgError( "illegal attempt to initialize joystick device again" );
1539 fgJoystick[ ident ] =
1540 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1543 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1544 fgJoystick[ ident ]->error = GL_TRUE;
1546 #if TARGET_HOST_MACINTOSH
1547 fgJoystick[ ident ]->id = ident;
1548 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
1549 fgJoystick[ ident ]->error = GL_FALSE;
1552 #if TARGET_HOST_MAC_OSX
1553 fgJoystick[ ident ]->id = ident;
1554 fgJoystick[ ident ]->error = GL_FALSE;
1555 fgJoystick[ ident ]->num_axes = 0;
1556 fgJoystick[ ident ]->num_buttons = 0;
1558 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 )
1567 fgWarning( "error getting master Mach port" );
1570 fghJoystickFindDevices( masterPort );
1573 if ( ident >= numDevices )
1575 fgJoystick[ ident ]->error = GL_TRUE;
1579 /* get the name now too */
1580 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1581 CFTypeRef ref = CFDictionaryGetValue( properties,
1582 CFSTR( kIOHIDProductKey ) );
1584 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1587 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1588 CFStringGetSystemEncoding( ) ) )
1590 fgWarning( "error getting device name" );
1595 #if TARGET_HOST_WIN32
1599 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1600 fgJoystick[ ident ]->error = GL_FALSE;
1603 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1604 fgJoystick[ ident ]->error = GL_FALSE;
1607 fgJoystick[ ident ]->num_axes = 0;
1608 fgJoystick[ ident ]->error = GL_TRUE;
1613 #if TARGET_HOST_UNIX_X11
1614 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1615 fgJoystick[ ident ]->id = ident;
1616 fgJoystick[ ident ]->error = GL_FALSE;
1618 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1619 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1620 if( ident < USB_IDENT_OFFSET )
1621 fgJoystick[ ident ]->os->is_analog = 1;
1622 if( fgJoystick[ ident ]->os->is_analog )
1623 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
1625 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
1626 ident - USB_IDENT_OFFSET );
1627 # elif defined( __linux__ )
1628 fgJoystick[ ident ]->id = ident;
1629 fgJoystick[ ident ]->error = GL_FALSE;
1631 sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1633 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1634 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
1638 fghJoystickOpen( fgJoystick[ ident ] );
1644 void fgJoystickClose( void )
1647 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1649 if( fgJoystick[ ident ] )
1652 #if TARGET_HOST_MACINTOSH
1658 #if TARGET_HOST_MAC_OSX
1659 ( *( fgJoystick[ ident ]->hidDev ) )->
1660 close( fgJoystick[ ident ]->hidDev );
1663 #if TARGET_HOST_WIN32
1664 /* Do nothing special */
1667 #if TARGET_HOST_UNIX_X11
1668 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1669 if( fgJoystick[ident]->os )
1671 if( ! fgJoystick[ ident ]->error )
1672 close( fgJoystick[ ident ]->os->fd );
1674 if( fgJoystick[ ident ]->os->hids )
1675 free (fgJoystick[ ident ]->os->hids);
1676 if( fgJoystick[ ident ]->os->hid_data_buf )
1677 free( fgJoystick[ ident ]->os->hid_data_buf );
1679 free( fgJoystick[ident]->os );
1683 if( ! fgJoystick[ident]->error )
1684 close( fgJoystick[ ident ]->fd );
1687 free( fgJoystick[ ident ] );
1688 fgJoystick[ ident ] = NULL;
1689 /* show joystick has been deinitialized */
1695 * Polls the joystick and executes the joystick callback hooked to the
1696 * window specified in the function's parameter:
1698 void fgJoystickPollWindow( SFG_Window* window )
1700 float axes[ _JS_MAX_AXES ];
1704 freeglut_return_if_fail( window );
1705 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1707 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1709 if( fgJoystick[ident] )
1711 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1713 if( !fgJoystick[ident]->error )
1714 INVOKE_WCB( *window, Joystick,
1716 (int) ( axes[ 0 ] * 1000.0f ),
1717 (int) ( axes[ 1 ] * 1000.0f ),
1718 (int) ( axes[ 2 ] * 1000.0f ) )
1725 * PWO: These jsJoystick class methods have not been implemented.
1727 int glutJoystickGetNumAxes( int ident )
1729 return fgJoystick[ ident ]->num_axes;
1731 int glutJoystickNotWorking( int ident )
1733 return fgJoystick[ ident ]->error;
1736 float glutJoystickGetDeadBand( int ident, int axis )
1738 return fgJoystick[ ident ]->dead_band [ axis ];
1740 void glutJoystickSetDeadBand( int ident, int axis, float db )
1742 fgJoystick[ ident ]->dead_band[ axis ] = db;
1745 float glutJoystickGetSaturation( int ident, int axis )
1747 return fgJoystick[ ident ]->saturate[ axis ];
1749 void glutJoystickSetSaturation( int ident, int axis, float st )
1751 fgJoystick[ ident ]->saturate [ axis ] = st;
1754 void glutJoystickSetMinRange( int ident, float *axes )
1756 memcpy( fgJoystick[ ident ]->min, axes,
1757 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1759 void glutJoystickSetMaxRange( int ident, float *axes )
1761 memcpy( fgJoystick[ ident ]->max, axes,
1762 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1764 void glutJoystickSetCenter( int ident, float *axes )
1766 memcpy( fgJoystick[ ident ]->center, axes,
1767 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1770 void glutJoystickGetMinRange( int ident, float *axes )
1772 memcpy( axes, fgJoystick[ ident ]->min,
1773 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1775 void glutJoystickGetMaxRange( int ident, float *axes )
1777 memcpy( axes, fgJoystick[ ident ]->max,
1778 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1780 void glutJoystickGetCenter( int ident, float *axes )
1782 memcpy( axes, fgJoystick[ ident ]->center,
1783 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1786 /*** END OF FILE ***/