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 #include <GL/freeglut.h>
35 #include "freeglut_internal.h"
37 # include <sys/param.h>
41 * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
44 #define _JS_MAX_BUTTONS 32
47 #if TARGET_HOST_MACINTOSH
48 # define _JS_MAX_AXES 9
49 # include <InputSprocket.h>
52 #if TARGET_HOST_MAC_OSX
53 # define _JS_MAX_AXES 16
54 # include <mach/mach.h>
55 # include <IOKit/IOkitLib.h>
56 # include <IOKit/hid/IOHIDLib.h>
60 # define _JS_MAX_AXES 8
62 # include <mmsystem.h>
67 #if TARGET_HOST_UNIX_X11
68 # define _JS_MAX_AXES 16
70 # include <sys/ioctl.h>
78 # if defined(__FreeBSD__) || defined(__NetBSD__)
79 /* XXX The below hack is done until freeglut's autoconf is updated. */
80 # define HAVE_USB_JS 1
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))
98 # if defined(__linux__)
99 # include <linux/joystick.h>
101 /* check the joystick driver version */
102 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
105 # else /* Not BSD or Linux */
109 * We'll put these values in and that should
110 * allow the code to at least compile when there is
111 * no support. The JS open routine should error out
112 * and shut off all the code downstream anyway and if
113 * the application doesn't use a joystick we'll be fine.
123 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
131 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
133 #if defined(__NetBSD__) || defined(__FreeBSD__)
136 # if defined(__NetBSD__)
137 /* XXX The below hack is done until freeglut's autoconf is updated. */
138 # define HAVE_USBHID_H 1
139 # ifdef HAVE_USBHID_H
144 # elif defined(__FreeBSD__)
145 # if __FreeBSD_version < 500000
146 # include <libusbhid.h>
148 /* XXX The below hack is done until freeglut's autoconf is updated. */
149 # define HAVE_USBHID_H 1
153 # include <dev/usb/usb.h>
154 # include <dev/usb/usbhid.h>
156 /* Compatibility with older usb.h revisions */
157 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
158 # define USB_MAX_DEVNAMES MAXDEVNAMES
162 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
163 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
164 struct os_specific_s {
168 /* The following structure members are specific to analog joysticks */
171 /* The following structure members are specific to USB joysticks */
172 struct hid_item *hids;
176 int axes_usage [ _JS_MAX_AXES ];
178 /* We keep button and axes state ourselves, as they might not be updated
179 * on every read of a USB device
182 float cache_axes [ _JS_MAX_AXES ];
185 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
186 # define USB_IDENT_OFFSET 2
188 # define USBDEV "/dev/usb"
189 # define UHIDDEV "/dev/uhid"
190 # define AJSDEV "/dev/joy"
194 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
195 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
196 * return the uhidN device name. We warn the user of this situation once.
198 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
200 struct usb_device_info di;
204 for (a = 1; a < USB_MAX_DEVICES; a++) {
206 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
208 for (i = 0; i < USB_MAX_DEVNAMES; i++)
209 if (di.udi_devnames[i][0] &&
210 strcmp(di.udi_devnames[i], dev) == 0) {
211 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
212 strcpy(cp, di.udi_vendor);
214 strcat(cp, di.udi_product);
215 strncpy(out, cp, outlen - 1);
224 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
229 static int protection_warned = 0;
231 for (i = 0; i < 16; i++) {
232 sprintf(buf, "%s%d", USBDEV, i);
233 f = open(buf, O_RDONLY);
235 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
239 } else if (errno == EACCES) {
240 if (!protection_warned) {
241 fgWarning ( "Can't open %s for read!", buf );
242 protection_warned = 1;
249 static int fghJoystickInitializeHID(struct os_specific_s *os,
250 int *num_axes, int *num_buttons)
252 int size, is_joystick;
253 # ifdef HAVE_USBHID_H
260 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
262 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
268 # ifdef HAVE_USBHID_H
269 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
271 /*** XXX {report_id} may not be the right variable? ***/
272 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
276 size = hid_report_size( rd, hid_input, report_id );
278 size = hid_report_size( rd, 0, hid_input );
280 os->hid_data_buf = calloc( 1, size );
284 # ifdef HAVE_USBHID_H
285 d = hid_start_parse( rd, 1 << hid_input, report_id );
287 d = hid_start_parse( rd, 1 << hid_input );
289 while( hid_get_item( d, &h ) )
291 int usage, page, interesting_hid;
293 page = HID_PAGE( h.usage );
294 usage = HID_USAGE( h.usage );
296 /* This test is somewhat too simplistic, but this is how MicroSoft
297 * does, so I guess it works for all joysticks/game pads. */
298 is_joystick = is_joystick ||
299 ( h.kind == hid_collection &&
300 page == HUP_GENERIC_DESKTOP &&
301 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
303 if( h.kind != hid_input )
309 interesting_hid = TRUE;
310 if( page == HUP_GENERIC_DESKTOP )
321 if( *num_axes < _JS_MAX_AXES )
323 os->axes_usage[ *num_axes ] = usage;
328 /* Allocate two axes for a hat */
329 if( *num_axes + 1 < _JS_MAX_AXES )
331 os->axes_usage[ *num_axes ] = usage;
333 os->axes_usage[ *num_axes ] = usage;
338 interesting_hid = FALSE;
342 else if( page == HUP_BUTTON )
344 interesting_hid = ( usage > 0 ) &&
345 ( usage <= _JS_MAX_BUTTONS );
347 if( interesting_hid && usage - 1 > *num_buttons )
348 *num_buttons = usage - 1;
351 if( interesting_hid )
354 os->hids = calloc( 1, sizeof ( struct hid_item ) );
360 return os->hids != NULL;
366 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
367 * See "js.h" lines 80-178.
369 typedef struct tagSFG_Joystick SFG_Joystick;
370 struct tagSFG_Joystick
372 #if TARGET_HOST_MACINTOSH
373 #define ISP_NUM_AXIS 9
374 #define ISP_NUM_NEEDS 41
375 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
376 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
379 #if TARGET_HOST_MAC_OSX
380 IOHIDDeviceInterface ** hidDev;
381 IOHIDElementCookie buttonCookies[41];
382 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
383 long minReport[_JS_MAX_AXES],
384 maxReport[_JS_MAX_AXES];
387 #if TARGET_HOST_WIN32
394 #if TARGET_HOST_UNIX_X11
395 # if defined(__FreeBSD__) || defined(__NetBSD__)
396 struct os_specific_s *os;
402 float tmp_axes [ _JS_MAX_AXES ];
404 struct JS_DATA_TYPE js;
417 float dead_band[ _JS_MAX_AXES ];
418 float saturate [ _JS_MAX_AXES ];
419 float center [ _JS_MAX_AXES ];
420 float max [ _JS_MAX_AXES ];
421 float min [ _JS_MAX_AXES ];
425 * Functions associated with the "jsJoystick" class in PLIB
427 #if TARGET_HOST_MAC_OSX
428 #define K_NUM_DEVICES 32
430 io_object_t ioDevices[K_NUM_DEVICES];
432 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
433 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
435 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
436 /* callback for CFArrayApply */
437 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
438 static void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
440 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
441 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
442 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
447 * The static joystick structure pointer
449 #define MAX_NUM_JOYSTICKS 2
450 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
454 * Read the raw joystick data
456 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
458 #if TARGET_HOST_WIN32
464 #if defined(__FreeBSD__) || defined(__NetBSD__)
475 for( i = 0; i < joy->num_axes; i++ )
481 #if TARGET_HOST_MACINTOSH
486 for ( i = 0; i < joy->num_buttons; i++ )
489 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
492 *buttons |= state << i;
498 for ( i = 0; i < joy->num_axes; i++ )
501 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
504 axes [i] = (float) state;
509 #if TARGET_HOST_MAC_OSX
510 if ( buttons != NULL )
514 for ( i = 0; i < joy->num_buttons; i++ )
516 IOHIDEventStruct hidEvent;
517 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
518 if ( hidEvent.value )
525 for ( i = 0; i < joy->num_axes; i++ )
527 IOHIDEventStruct hidEvent;
528 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
529 axes[i] = hidEvent.value;
534 #if TARGET_HOST_WIN32
535 status = joyGetPosEx( joy->js_id, &joy->js );
537 if ( status != JOYERR_NOERROR )
539 joy->error = GL_TRUE;
544 *buttons = joy->js.dwButtons;
549 * WARNING - Fall through case clauses!!
551 switch ( joy->num_axes )
554 /* Generate two POV axes from the POV hat angle.
555 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
556 * hundredths of a degree, or 0xFFFF when idle.
558 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
565 /* This is the contentious bit: how to convert angle to X/Y.
566 * wk: I know of no define for PI that we could use here:
567 * SG_PI would pull in sg, M_PI is undefined for MSVC
568 * But the accuracy of the value of PI is very unimportant at
571 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
572 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
574 /* Convert to coordinates on a square so that North-East
575 * is (1,1) not (.7,.7), etc.
576 * s and c cannot both be zero so we won't divide by zero.
578 if ( fabs ( s ) < fabs ( c ) )
580 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
581 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
585 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
586 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
590 case 6: axes[5] = (float) joy->js.dwVpos;
591 case 5: axes[4] = (float) joy->js.dwUpos;
592 case 4: axes[3] = (float) joy->js.dwRpos;
593 case 3: axes[2] = (float) joy->js.dwZpos;
594 case 2: axes[1] = (float) joy->js.dwYpos;
595 case 1: axes[0] = (float) joy->js.dwXpos;
600 #if TARGET_HOST_UNIX_X11
601 # if defined(__FreeBSD__) || defined(__NetBSD__)
602 if ( joy->os->is_analog )
604 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
605 if ( status != sizeof(joy->os->ajs) ) {
606 perror ( joy->os->fname );
607 joy->error = GL_TRUE;
610 if ( buttons != NULL )
611 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
615 axes[0] = (float) joy->os->ajs.x;
616 axes[1] = (float) joy->os->ajs.y;
623 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
627 for ( h = joy->os->hids; h; h = h->next )
629 int d = hid_get_data ( joy->os->hid_data_buf, h );
631 int page = HID_PAGE ( h->usage );
632 int usage = HID_USAGE ( h->usage );
634 if ( page == HUP_GENERIC_DESKTOP )
637 for ( i = 0; i < joy->num_axes; i++ )
638 if (joy->os->axes_usage[i] == usage)
640 if (usage == HUG_HAT_SWITCH)
644 joy->os->cache_axes[i] = (float)hatmap_x[d];
645 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
649 joy->os->cache_axes[i] = (float)d;
654 else if (page == HUP_BUTTON)
656 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
659 joy->os->cache_buttons |= (1 << usage - 1);
661 joy->os->cache_buttons &= ~(1 << usage - 1);
666 if ( len < 0 && errno != EAGAIN )
668 perror( joy->os->fname );
671 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
673 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
681 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
683 if ( status != sizeof( struct js_event ) )
685 if ( errno == EAGAIN )
687 /* Use the old values */
689 *buttons = joy->tmp_buttons;
691 memcpy( axes, joy->tmp_axes,
692 sizeof( float ) * joy->num_axes );
696 fgWarning ( "%s", joy->fname );
697 joy->error = GL_TRUE;
701 switch ( joy->js.type & ~JS_EVENT_INIT )
703 case JS_EVENT_BUTTON:
704 if( joy->js.value == 0 ) /* clear the flag */
705 joy->tmp_buttons &= ~( 1 << joy->js.number );
707 joy->tmp_buttons |= ( 1 << joy->js.number );
711 if ( joy->js.number < joy->num_axes )
713 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
716 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
721 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
723 /* use the old values */
725 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
727 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
733 *buttons = joy->tmp_buttons;
737 status = read( joy->fd, &joy->js, JS_RETURN );
739 if ( status != JS_RETURN )
741 fgWarning( "%s", joy->fname );
742 joy->error = GL_TRUE;
747 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
748 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
750 *buttons = joy->js.buttons;
755 axes[ 0 ] = (float) joy->js.x;
756 axes[ 1 ] = (float) joy->js.y;
763 * Correct the joystick axis data
765 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
767 if( value < joy->center[ axis ] )
769 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
772 if( xx < -joy->saturate[ axis ] )
775 if( xx > -joy->dead_band [ axis ] )
778 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
779 joy->dead_band[ axis ] );
781 return ( xx < -1.0f ) ? -1.0f : xx;
785 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
786 joy->center[ axis ] );
788 if( xx > joy->saturate[ axis ] )
791 if( xx < joy->dead_band[ axis ] )
794 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
795 joy->dead_band[ axis ] );
797 return ( xx > 1.0f ) ? 1.0f : xx;
802 * Read the corrected joystick data
804 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
806 float raw_axes[ _JS_MAX_AXES ];
815 for ( i=0; i<joy->num_axes; i++ )
819 fghJoystickRawRead( joy, buttons, raw_axes );
822 for( i=0; i<joy->num_axes; i++ )
823 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
827 * Happy happy happy joy joy joy (happy new year toudi :D)
831 #if TARGET_HOST_MAC_OSX
832 /** open the IOKit connection, enumerate all the HID devices, add their
833 interface references to the static array. We then use the array index
834 as the device number when we come to open() the joystick. */
835 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
837 CFMutableDictionaryRef hidMatch = NULL;
838 IOReturn rv = kIOReturnSuccess;
840 io_iterator_t hidIterator;
843 /* build a dictionary matching HID devices */
844 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
846 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
847 if (rv != kIOReturnSuccess || !hidIterator) {
848 fgWarning( "no joystick (HID) devices found" );
853 while ((ioDev = IOIteratorNext(hidIterator))) {
854 /* filter out keyboard and mouse devices */
855 CFDictionaryRef properties = getCFProperties(ioDev);
858 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
859 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
860 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
861 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
863 /* keep only joystick devices */
864 if ( ( page == kHIDPage_GenericDesktop ) && (
865 (usage == kHIDUsage_GD_Joystick)
866 || (usage == kHIDUsage_GD_GamePad)
867 || (usage == kHIDUsage_GD_MultiAxisController)
868 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
869 /* add it to the array */
870 ioDevices[numDevices++] = ioDev;
873 IOObjectRelease(hidIterator);
876 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
879 CFMutableDictionaryRef cfProperties;
882 /* comment copied from darwin/SDL_sysjoystick.c */
883 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
884 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
887 io_registry_entry_t parent1, parent2;
889 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
890 if (rv != kIOReturnSuccess) {
891 fgWarning ( "error getting device entry parent");
895 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
896 if (rv != kIOReturnSuccess) {
897 fgWarning ( "error getting device entry parent 2");
902 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
903 &cfProperties, kCFAllocatorDefault, kNilOptions);
904 if (rv != kIOReturnSuccess || !cfProperties) {
905 fgWarning ( "error getting device properties");
912 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
914 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
915 fgError ( "%s", "element enumerator passed non-dictionary value");
919 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
922 /** element enumerator function : pass NULL for top-level*/
923 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
925 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
926 "Joystick element type mismatch",
927 "fghJoystickEnumerateElements" );
929 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
930 CFArrayApplyFunction((CFArrayRef) element, range,
931 &fghJoystickElementEnumerator, joy );
934 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
936 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
937 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
939 long type, page, usage;
941 CFNumberGetValue((CFNumberRef)
942 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
943 kCFNumberLongType, &type);
946 case kIOHIDElementTypeInput_Misc:
947 case kIOHIDElementTypeInput_Axis:
948 case kIOHIDElementTypeInput_Button:
949 printf("got input element...");
950 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
951 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
953 if (page == kHIDPage_GenericDesktop) {
954 switch ( usage ) /* look at usage to determine function */
959 case kHIDUsage_GD_Rx:
960 case kHIDUsage_GD_Ry:
961 case kHIDUsage_GD_Rz:
962 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
964 fghJoystickAddAxisElement((CFDictionaryRef) element);
967 case kHIDUsage_GD_Hatswitch:
969 fghJoystickAddHatElement((CFDictionaryRef) element);
973 fgWarning ( "input type element has weird usage (%x)", usage);
976 } else if (page == kHIDPage_Button) {
978 fghJoystickAddButtonElement((CFDictionaryRef) element);
980 fgWarning ( "input type element has weird page (%x)", page);
983 case kIOHIDElementTypeCollection:
984 fghJoystickEnumerateElements (
985 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
994 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
996 long cookie, lmin, lmax;
997 int index = joy->num_axes++;
999 CFNumberGetValue ((CFNumberRef)
1000 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1001 kCFNumberLongType, &cookie);
1003 axisCookies[index] = (IOHIDElementCookie) cookie;
1005 CFNumberGetValue ((CFNumberRef)
1006 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1007 kCFNumberLongType, &lmin);
1009 CFNumberGetValue ((CFNumberRef)
1010 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1011 kCFNumberLongType, &lmax);
1013 joy->min[index] = lmin;
1014 joy->max[index] = lmax;
1015 joy->dead_band[index] = 0.0;
1016 joy->saturate[index] = 1.0;
1017 joy->center[index] = (lmax + lmin) * 0.5;
1020 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1023 CFNumberGetValue ((CFNumberRef)
1024 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1025 kCFNumberLongType, &cookie);
1027 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1028 /* anything else for buttons? */
1031 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1033 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1034 /* do we map hats to axes or buttons? */
1038 #if TARGET_HOST_WIN32
1040 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1042 # if defined(_MSC_VER)
1043 # pragma comment (lib, "advapi32.lib")
1046 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1048 char buffer [ 256 ];
1050 char OEMKey [ 256 ];
1059 /* Open .. MediaResources\CurrentJoystickSettings */
1060 sprintf ( buffer, "%s\\%s\\%s",
1061 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1062 REGSTR_KEY_JOYCURR );
1064 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1066 if ( lr != ERROR_SUCCESS ) return 0;
1068 /* Get OEM Key name */
1069 dwcb = sizeof(OEMKey);
1071 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1072 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1074 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1075 RegCloseKey ( hKey );
1077 if ( lr != ERROR_SUCCESS ) return 0;
1079 /* Open OEM Key from ...MediaProperties */
1080 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1082 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1084 if ( lr != ERROR_SUCCESS ) return 0;
1089 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1091 RegCloseKey ( hKey );
1093 if ( lr != ERROR_SUCCESS ) return 0;
1100 static void fghJoystickOpen( SFG_Joystick* joy )
1103 #if TARGET_HOST_MACINTOSH
1106 #if TARGET_HOST_MAC_OSX
1109 IOCFPlugInInterface **plugin;
1111 HRESULT pluginResult;
1113 CFDictionaryRef props;
1114 CFTypeRef topLevelElement;
1116 #if TARGET_HOST_UNIX_X11
1117 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1128 * Default values (for no joystick -- each conditional will reset the
1132 joy->num_axes = joy->num_buttons = 0;
1133 joy->name[ 0 ] = '\0';
1135 #if TARGET_HOST_MACINTOSH
1136 /* XXX FIXME: get joystick name in Mac */
1138 err = ISpStartup( );
1142 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1144 joy->error = GL_TRUE;
1146 /* initialize the needs structure */
1147 ISpNeed temp_isp_needs[ isp_num_needs ] =
1149 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1150 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1151 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1152 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1153 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1154 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1155 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1156 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1157 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1159 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1160 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1161 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1190 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1193 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1196 /* next two calls allow keyboard and mouse to emulate other input
1197 * devices (gamepads, joysticks, etc)
1200 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1204 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1208 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1209 joy->isp_needs, joy->isp_elem,
1211 ISP_CHECK_ERR( err )
1213 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1214 'freeglut', nil, 0, 128, 0 );
1215 ISP_CHECK_ERR( err )
1217 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1218 joy->num_axes = joy->isp_num_axis;
1220 for( i = 0; i < joy->num_axes; i++ )
1222 joy->dead_band[ i ] = 0;
1223 joy->saturate [ i ] = 1;
1224 joy->center [ i ] = kISpAxisMiddle;
1225 joy->max [ i ] = kISpAxisMaximum;
1226 joy->min [ i ] = kISpAxisMinimum;
1229 joy->error = GL_FALSE;
1232 joy->num_buttons = joy->num_axes = 0;
1235 #if TARGET_HOST_MAC_OSX
1236 if( joy->id >= numDevices )
1238 fgWarning( "device index out of range in fgJoystickOpen()" );
1242 /* create device interface */
1243 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1244 kIOHIDDeviceUserClientTypeID,
1245 kIOCFPlugInInterfaceID,
1248 if( rv != kIOReturnSuccess )
1250 fgWarning( "error creating plugin for io device" );
1254 pluginResult = ( *plugin )->QueryInterface(
1256 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1257 &( LPVOID )joy->hidDev
1260 if( pluginResult != S_OK )
1261 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1263 ( *plugin )->Release( plugin ); /* don't leak a ref */
1264 if( joy->hidDev == NULL )
1267 /* store the interface in this instance */
1268 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1269 if( rv != kIOReturnSuccess )
1271 fgWarning( "error opening device interface");
1275 props = getCFProperties( ioDevices[ joy->id ] );
1277 /* recursively enumerate all the bits */
1278 CFTypeRef topLevelElement =
1279 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1280 enumerateElements( topLevelElement );
1285 #if TARGET_HOST_WIN32
1286 joy->js.dwFlags = JOY_RETURNALL;
1287 joy->js.dwSize = sizeof( joy->js );
1289 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1292 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1295 if( joy->jsCaps.wNumAxes == 0 )
1298 joy->error = GL_TRUE;
1302 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1303 * at least for USB. Try to get the real name from the registry.
1305 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1306 sizeof( joy->name ) ) )
1308 fgWarning( "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;
1338 /* Guess all the rest judging on the axes extremals */
1339 for( i = 0; i < joy->num_axes; i++ )
1341 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1342 joy->dead_band[ i ] = 0.0f;
1343 joy->saturate [ i ] = 1.0f;
1347 #if TARGET_HOST_UNIX_X11
1348 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1349 for( i = 0; i < _JS_MAX_AXES; i++ )
1350 joy->os->cache_axes[ i ] = 0.0f;
1352 joy->os->cache_buttons = 0;
1354 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1356 if( joy->os->fd < 0 && errno == EACCES )
1357 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1359 joy->error =( joy->os->fd < 0 );
1365 joy->num_buttons = 0;
1366 if( joy->os->is_analog )
1369 char joyfname[ 1024 ];
1370 int noargs, in_no_axes;
1372 float axes [ _JS_MAX_AXES ];
1373 int buttons[ _JS_MAX_AXES ];
1376 joy->num_buttons = 32;
1378 fghJoystickRawRead( joy, buttons, axes );
1379 joy->error = axes[ 0 ] < -1000000000.0f;
1383 sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
1385 joyfile = fopen( joyfname, "r" );
1386 joy->error =( joyfile == NULL );
1390 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1391 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1392 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1393 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1398 for( i = 0; i < _JS_MAX_AXES; i++ )
1400 joy->dead_band[ i ] = 0.0f;
1401 joy->saturate [ i ] = 1.0f;
1404 return; /* End of analog code */
1408 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1409 &joy->num_buttons ) )
1411 close( joy->os->fd );
1412 joy->error = GL_TRUE;
1416 cp = strrchr( joy->os->fname, '/' );
1419 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
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__ )
1453 /* Default for older Linux systems. */
1455 joy->num_buttons = 32;
1458 for( i = 0; i < _JS_MAX_AXES; i++ )
1459 joy->tmp_axes[ i ] = 0.0f;
1461 joy->tmp_buttons = 0;
1464 joy->fd = open( joy->fname, O_RDONLY );
1466 joy->error =( joy->fd < 0 );
1471 /* Set the correct number of axes for the linux driver */
1473 /* Melchior Franz's fixes for big-endian Linuxes since writing
1474 * to the upper byte of an uninitialized word doesn't work.
1477 ioctl( joy->fd, JSIOCGAXES, &u );
1479 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1480 joy->num_buttons = u;
1481 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1482 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1486 * The Linux driver seems to return 512 for all axes
1487 * when no stick is present - but there is a chance
1488 * that could happen by accident - so it's gotta happen
1489 * on both axes for at least 100 attempts.
1491 * PWO: shouldn't be that done somehow wiser on the kernel level?
1498 fghJoystickRawRead( joy, NULL, joy->center );
1500 } while( !joy->error &&
1502 joy->center[ 0 ] == 512.0f &&
1503 joy->center[ 1 ] == 512.0f );
1505 if ( counter >= 100 )
1506 joy->error = GL_TRUE;
1509 for( i = 0; i < _JS_MAX_AXES; i++ )
1512 joy->max [ i ] = 32767.0f;
1513 joy->center[ i ] = 0.0f;
1514 joy->min [ i ] = -32767.0f;
1516 joy->max[ i ] = joy->center[ i ] * 2.0f;
1517 joy->min[ i ] = 0.0f;
1519 joy->dead_band[ i ] = 0.0f;
1520 joy->saturate [ i ] = 1.0f;
1527 * This function replaces the constructor method in the JS library.
1529 static void fghJoystickInit( int ident )
1531 if( ident >= MAX_NUM_JOYSTICKS )
1532 fgError( "Too large a joystick number: %d", ident );
1534 if( fgJoystick[ ident ] )
1535 fgError( "illegal attempt to initialize joystick device again" );
1537 fgJoystick[ ident ] =
1538 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1541 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1542 fgJoystick[ ident ]->error = GL_TRUE;
1544 #if TARGET_HOST_MACINTOSH
1545 fgJoystick[ ident ]->id = ident;
1546 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
1547 fgJoystick[ ident ]->error = GL_FALSE;
1550 #if TARGET_HOST_MAC_OSX
1551 fgJoystick[ ident ]->id = ident;
1552 fgJoystick[ ident ]->error = GL_FALSE;
1553 fgJoystick[ ident ]->num_axes = 0;
1554 fgJoystick[ ident ]->num_buttons = 0;
1556 if( numDevices < 0 )
1558 /* do first-time init (since we can't over-ride jsInit, hmm */
1561 mach_port_t masterPort;
1562 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1563 if( rv != kIOReturnSuccess )
1565 fgWarning( "error getting master Mach port" );
1568 fghJoystickFindDevices( masterPort );
1571 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,
1580 CFSTR( kIOHIDProductKey ) );
1582 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1585 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1586 CFStringGetSystemEncoding( ) ) )
1588 fgWarning( "error getting device name" );
1593 #if TARGET_HOST_WIN32
1597 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1598 fgJoystick[ ident ]->error = GL_FALSE;
1601 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1602 fgJoystick[ ident ]->error = GL_FALSE;
1605 fgJoystick[ ident ]->num_axes = 0;
1606 fgJoystick[ ident ]->error = GL_TRUE;
1611 #if TARGET_HOST_UNIX_X11
1612 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1613 fgJoystick[ ident ]->id = ident;
1614 fgJoystick[ ident ]->error = GL_FALSE;
1616 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1617 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1618 if( ident < USB_IDENT_OFFSET )
1619 fgJoystick[ ident ]->os->is_analog = 1;
1620 if( fgJoystick[ ident ]->os->is_analog )
1621 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
1623 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
1624 ident - USB_IDENT_OFFSET );
1625 # elif defined( __linux__ )
1626 fgJoystick[ ident ]->id = ident;
1627 fgJoystick[ ident ]->error = GL_FALSE;
1629 sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1631 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1632 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
1636 fghJoystickOpen( fgJoystick[ ident ] );
1640 * Try initializing all the joysticks (well, both of them)
1642 void fgInitialiseJoysticks ( void )
1644 /* Initialization courtesy of OpenGLUT -- do we want it? */
1645 if( !fgState.JoysticksInitialised )
1648 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1649 fghJoystickInit( ident );
1651 fgState.JoysticksInitialised = GL_TRUE;
1658 void fgJoystickClose( void )
1661 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1663 if( fgJoystick[ ident ] )
1666 #if TARGET_HOST_MACINTOSH
1672 #if TARGET_HOST_MAC_OSX
1673 ( *( fgJoystick[ ident ]->hidDev ) )->
1674 close( fgJoystick[ ident ]->hidDev );
1677 #if TARGET_HOST_WIN32
1678 /* Do nothing special */
1681 #if TARGET_HOST_UNIX_X11
1682 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1683 if( fgJoystick[ident]->os )
1685 if( ! fgJoystick[ ident ]->error )
1686 close( fgJoystick[ ident ]->os->fd );
1688 if( fgJoystick[ ident ]->os->hids )
1689 free (fgJoystick[ ident ]->os->hids);
1690 if( fgJoystick[ ident ]->os->hid_data_buf )
1691 free( fgJoystick[ ident ]->os->hid_data_buf );
1693 free( fgJoystick[ident]->os );
1697 if( ! fgJoystick[ident]->error )
1698 close( fgJoystick[ ident ]->fd );
1701 free( fgJoystick[ ident ] );
1702 fgJoystick[ ident ] = NULL;
1703 /* show joystick has been deinitialized */
1709 * Polls the joystick and executes the joystick callback hooked to the
1710 * window specified in the function's parameter:
1712 void fgJoystickPollWindow( SFG_Window* window )
1714 float axes[ _JS_MAX_AXES ];
1718 freeglut_return_if_fail( window );
1719 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1721 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1723 if( fgJoystick[ident] )
1725 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1727 if( !fgJoystick[ident]->error )
1728 INVOKE_WCB( *window, Joystick,
1730 (int) ( axes[ 0 ] * 1000.0f ),
1731 (int) ( axes[ 1 ] * 1000.0f ),
1732 (int) ( axes[ 2 ] * 1000.0f ) )
1739 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1741 int fgJoystickDetect( void )
1745 fgInitialiseJoysticks ();
1750 if ( !fgState.JoysticksInitialised )
1753 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1754 if( fgJoystick[ident] && !fgJoystick[ident]->error )
1761 * Joystick information functions
1763 int glutJoystickGetNumAxes( int ident )
1765 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
1766 return fgJoystick[ ident ]->num_axes;
1768 int glutJoystickGetNumButtons( int ident )
1770 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
1771 return fgJoystick[ ident ]->num_buttons;
1773 int glutJoystickNotWorking( int ident )
1775 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
1776 return fgJoystick[ ident ]->error;
1779 float glutJoystickGetDeadBand( int ident, int axis )
1781 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1782 return fgJoystick[ ident ]->dead_band [ axis ];
1784 void glutJoystickSetDeadBand( int ident, int axis, float db )
1786 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1787 fgJoystick[ ident ]->dead_band[ axis ] = db;
1790 float glutJoystickGetSaturation( int ident, int axis )
1792 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1793 return fgJoystick[ ident ]->saturate[ axis ];
1795 void glutJoystickSetSaturation( int ident, int axis, float st )
1797 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1798 fgJoystick[ ident ]->saturate [ axis ] = st;
1801 void glutJoystickSetMinRange( int ident, float *axes )
1803 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1804 memcpy( fgJoystick[ ident ]->min, axes,
1805 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1807 void glutJoystickSetMaxRange( int ident, float *axes )
1809 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1810 memcpy( fgJoystick[ ident ]->max, axes,
1811 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1813 void glutJoystickSetCenter( int ident, float *axes )
1815 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1816 memcpy( fgJoystick[ ident ]->center, axes,
1817 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1820 void glutJoystickGetMinRange( int ident, float *axes )
1822 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1823 memcpy( axes, fgJoystick[ ident ]->min,
1824 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1826 void glutJoystickGetMaxRange( int ident, float *axes )
1828 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1829 memcpy( axes, fgJoystick[ ident ]->max,
1830 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1832 void glutJoystickGetCenter( int ident, float *axes )
1834 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1835 memcpy( axes, fgJoystick[ ident ]->center,
1836 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1839 /*** END OF FILE ***/