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