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"
45 /* XXX It might be better to poll the operating system for the numbers of buttons and
46 * XXX axes and then dynamically allocate the arrays.
48 #define _JS_MAX_BUTTONS 32
50 #if TARGET_HOST_MACINTOSH
51 # define _JS_MAX_AXES 9
52 # include <InputSprocket.h>
55 #if TARGET_HOST_MAC_OSX
56 # define _JS_MAX_AXES 16
57 # include <mach/mach.h>
58 # include <IOKit/IOkitLib.h>
59 # include <IOKit/hid/IOHIDLib.h>
63 # define _JS_MAX_AXES 8
65 # include <mmsystem.h>
70 #if TARGET_HOST_UNIX_X11
71 # define _JS_MAX_AXES 16
73 # include <sys/ioctl.h>
79 # if defined(__FreeBSD__) || defined(__NetBSD__)
80 /* XXX The below hack is done until freeglut's autoconf is updated. */
81 # define HAVE_USB_JS 1
83 # if defined(__FreeBSD__) && __FreeBSD_version >= 500000
84 # include <sys/joystick.h>
87 * XXX NetBSD/amd64 systems may find that they have to steal the
88 * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
89 * XXX I cannot comment whether that works for the interface, but
90 * XXX it lets you compile...(^& I do not think that we can do away
91 * XXX with this header.
93 # include <machine/joystick.h> /* For analog joysticks */
95 # define JS_DATA_TYPE joystick
96 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
99 # if defined(__linux__)
100 # include <linux/joystick.h>
102 /* check the joystick driver version */
103 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
106 # else /* Not BSD or Linux */
110 * We'll put these values in and that should
111 * allow the code to at least compile when there is
112 * no support. The JS open routine should error out
113 * and shut off all the code downstream anyway and if
114 * the application doesn't use a joystick we'll be fine.
124 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
132 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
134 #if defined(__NetBSD__) || defined(__FreeBSD__)
137 # if defined(__NetBSD__)
138 /* XXX The below hack is done until freeglut's autoconf is updated. */
139 # define HAVE_USBHID_H 1
140 # ifdef HAVE_USBHID_H
145 # elif defined(__FreeBSD__)
146 # if __FreeBSD_version < 500000
147 # include <libusbhid.h>
149 /* XXX The below hack is done until freeglut's autoconf is updated. */
150 # define HAVE_USBHID_H 1
154 # include <dev/usb/usb.h>
155 # include <dev/usb/usbhid.h>
157 /* Compatibility with older usb.h revisions */
158 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
159 # define USB_MAX_DEVNAMES MAXDEVNAMES
163 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
164 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
165 struct os_specific_s {
169 /* The following structure members are specific to analog joysticks */
172 /* The following structure members are specific to USB joysticks */
173 struct hid_item *hids;
177 int axes_usage [ _JS_MAX_AXES ];
179 /* We keep button and axes state ourselves, as they might not be updated
180 * on every read of a USB device
183 float cache_axes [ _JS_MAX_AXES ];
186 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
187 # define USB_IDENT_OFFSET 2
189 # define USBDEV "/dev/usb"
190 # define UHIDDEV "/dev/uhid"
191 # define AJSDEV "/dev/joy"
195 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
196 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
197 * return the uhidN device name. We warn the user of this situation once.
199 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
201 struct usb_device_info di;
205 for (a = 1; a < USB_MAX_DEVICES; a++) {
207 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
209 for (i = 0; i < USB_MAX_DEVNAMES; i++)
210 if (di.udi_devnames[i][0] &&
211 strcmp(di.udi_devnames[i], dev) == 0) {
212 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
213 strcpy(cp, di.udi_vendor);
215 strcat(cp, di.udi_product);
216 strncpy(out, cp, outlen - 1);
225 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
230 static int protection_warned = 0;
232 for (i = 0; i < 16; i++) {
233 sprintf(buf, "%s%d", USBDEV, i);
234 f = open(buf, O_RDONLY);
236 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
240 } else if (errno == EACCES) {
241 if (!protection_warned) {
242 fgWarning ( "Can't open %s for read!", buf );
243 protection_warned = 1;
250 static int fghJoystickInitializeHID(struct os_specific_s *os,
251 int *num_axes, int *num_buttons)
253 int size, is_joystick;
254 # ifdef HAVE_USBHID_H
261 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
263 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
269 # ifdef HAVE_USBHID_H
270 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
272 /*** XXX {report_id} may not be the right variable? ***/
273 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
277 size = hid_report_size( rd, hid_input, report_id );
279 size = hid_report_size( rd, 0, hid_input );
281 os->hid_data_buf = calloc( 1, size );
285 # ifdef HAVE_USBHID_H
286 d = hid_start_parse( rd, 1 << hid_input, report_id );
288 d = hid_start_parse( rd, 1 << hid_input );
290 while( hid_get_item( d, &h ) )
292 int usage, page, interesting_hid;
294 page = HID_PAGE( h.usage );
295 usage = HID_USAGE( h.usage );
297 /* This test is somewhat too simplistic, but this is how MicroSoft
298 * does, so I guess it works for all joysticks/game pads. */
299 is_joystick = is_joystick ||
300 ( h.kind == hid_collection &&
301 page == HUP_GENERIC_DESKTOP &&
302 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
304 if( h.kind != hid_input )
310 interesting_hid = TRUE;
311 if( page == HUP_GENERIC_DESKTOP )
322 if( *num_axes < _JS_MAX_AXES )
324 os->axes_usage[ *num_axes ] = usage;
329 /* Allocate two axes for a hat */
330 if( *num_axes + 1 < _JS_MAX_AXES )
332 os->axes_usage[ *num_axes ] = usage;
334 os->axes_usage[ *num_axes ] = usage;
339 interesting_hid = FALSE;
343 else if( page == HUP_BUTTON )
345 interesting_hid = ( usage > 0 ) &&
346 ( usage <= _JS_MAX_BUTTONS );
348 if( interesting_hid && usage - 1 > *num_buttons )
349 *num_buttons = usage - 1;
352 if( interesting_hid )
355 os->hids = calloc( 1, sizeof ( struct hid_item ) );
361 return os->hids != NULL;
367 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
368 * See "js.h" lines 80-178.
370 typedef struct tagSFG_Joystick SFG_Joystick;
371 struct tagSFG_Joystick
373 #if TARGET_HOST_MACINTOSH
374 #define ISP_NUM_AXIS 9
375 #define ISP_NUM_NEEDS 41
376 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
377 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
380 #if TARGET_HOST_MAC_OSX
381 IOHIDDeviceInterface ** hidDev;
382 IOHIDElementCookie buttonCookies[41];
383 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
384 long minReport[_JS_MAX_AXES],
385 maxReport[_JS_MAX_AXES];
388 #if TARGET_HOST_WIN32
395 #if TARGET_HOST_UNIX_X11
396 # if defined(__FreeBSD__) || defined(__NetBSD__)
397 struct os_specific_s *os;
403 float tmp_axes [ _JS_MAX_AXES ];
405 struct JS_DATA_TYPE js;
418 float dead_band[ _JS_MAX_AXES ];
419 float saturate [ _JS_MAX_AXES ];
420 float center [ _JS_MAX_AXES ];
421 float max [ _JS_MAX_AXES ];
422 float min [ _JS_MAX_AXES ];
426 * Functions associated with the "jsJoystick" class in PLIB
428 #if TARGET_HOST_MAC_OSX
429 #define K_NUM_DEVICES 32
431 io_object_t ioDevices[K_NUM_DEVICES];
433 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
434 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
436 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
437 /* callback for CFArrayApply */
438 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
439 static void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
441 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
442 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
443 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
448 * The static joystick structure pointer
450 #define MAX_NUM_JOYSTICKS 2
451 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
455 * Read the raw joystick data
457 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
459 #if TARGET_HOST_WIN32
465 #if defined(__FreeBSD__) || defined(__NetBSD__)
476 for( i = 0; i < joy->num_axes; i++ )
482 #if TARGET_HOST_MACINTOSH
487 for ( i = 0; i < joy->num_buttons; i++ )
490 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
493 *buttons |= state << i;
499 for ( i = 0; i < joy->num_axes; i++ )
502 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
505 axes [i] = (float) state;
510 #if TARGET_HOST_MAC_OSX
511 if ( buttons != NULL )
515 for ( i = 0; i < joy->num_buttons; i++ )
517 IOHIDEventStruct hidEvent;
518 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
519 if ( hidEvent.value )
526 for ( i = 0; i < joy->num_axes; i++ )
528 IOHIDEventStruct hidEvent;
529 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
530 axes[i] = hidEvent.value;
535 #if TARGET_HOST_WIN32
536 status = joyGetPosEx( joy->js_id, &joy->js );
538 if ( status != JOYERR_NOERROR )
540 joy->error = GL_TRUE;
545 *buttons = joy->js.dwButtons;
550 * WARNING - Fall through case clauses!!
552 switch ( joy->num_axes )
555 /* Generate two POV axes from the POV hat angle.
556 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
557 * hundredths of a degree, or 0xFFFF when idle.
559 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
566 /* This is the contentious bit: how to convert angle to X/Y.
567 * wk: I know of no define for PI that we could use here:
568 * SG_PI would pull in sg, M_PI is undefined for MSVC
569 * But the accuracy of the value of PI is very unimportant at
572 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
573 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
575 /* Convert to coordinates on a square so that North-East
576 * is (1,1) not (.7,.7), etc.
577 * s and c cannot both be zero so we won't divide by zero.
579 if ( fabs ( s ) < fabs ( c ) )
581 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
582 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
586 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
587 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
591 case 6: axes[5] = (float) joy->js.dwVpos;
592 case 5: axes[4] = (float) joy->js.dwUpos;
593 case 4: axes[3] = (float) joy->js.dwRpos;
594 case 3: axes[2] = (float) joy->js.dwZpos;
595 case 2: axes[1] = (float) joy->js.dwYpos;
596 case 1: axes[0] = (float) joy->js.dwXpos;
601 #if TARGET_HOST_UNIX_X11
602 # if defined(__FreeBSD__) || defined(__NetBSD__)
603 if ( joy->os->is_analog )
605 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
606 if ( status != sizeof(joy->os->ajs) ) {
607 perror ( joy->os->fname );
608 joy->error = GL_TRUE;
611 if ( buttons != NULL )
612 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
616 axes[0] = (float) joy->os->ajs.x;
617 axes[1] = (float) joy->os->ajs.y;
624 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
628 for ( h = joy->os->hids; h; h = h->next )
630 int d = hid_get_data ( joy->os->hid_data_buf, h );
632 int page = HID_PAGE ( h->usage );
633 int usage = HID_USAGE ( h->usage );
635 if ( page == HUP_GENERIC_DESKTOP )
638 for ( i = 0; i < joy->num_axes; i++ )
639 if (joy->os->axes_usage[i] == usage)
641 if (usage == HUG_HAT_SWITCH)
645 joy->os->cache_axes[i] = (float)hatmap_x[d];
646 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
650 joy->os->cache_axes[i] = (float)d;
655 else if (page == HUP_BUTTON)
657 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
660 joy->os->cache_buttons |= (1 << usage - 1);
662 joy->os->cache_buttons &= ~(1 << usage - 1);
667 if ( len < 0 && errno != EAGAIN )
669 perror( joy->os->fname );
672 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
674 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
682 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
684 if ( status != sizeof( struct js_event ) )
686 if ( errno == EAGAIN )
688 /* Use the old values */
690 *buttons = joy->tmp_buttons;
692 memcpy( axes, joy->tmp_axes,
693 sizeof( float ) * joy->num_axes );
697 fgWarning ( "%s", joy->fname );
698 joy->error = GL_TRUE;
702 switch ( joy->js.type & ~JS_EVENT_INIT )
704 case JS_EVENT_BUTTON:
705 if( joy->js.value == 0 ) /* clear the flag */
706 joy->tmp_buttons &= ~( 1 << joy->js.number );
708 joy->tmp_buttons |= ( 1 << joy->js.number );
712 if ( joy->js.number < joy->num_axes )
714 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
717 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
722 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
724 /* use the old values */
726 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
728 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
734 *buttons = joy->tmp_buttons;
738 status = read( joy->fd, &joy->js, JS_RETURN );
740 if ( status != JS_RETURN )
742 fgWarning( "%s", joy->fname );
743 joy->error = GL_TRUE;
748 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
749 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
751 *buttons = joy->js.buttons;
756 axes[ 0 ] = (float) joy->js.x;
757 axes[ 1 ] = (float) joy->js.y;
764 * Correct the joystick axis data
766 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
768 if( value < joy->center[ axis ] )
770 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
773 if( xx < -joy->saturate[ axis ] )
776 if( xx > -joy->dead_band [ axis ] )
779 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
780 joy->dead_band[ axis ] );
782 return ( xx < -1.0f ) ? -1.0f : xx;
786 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
787 joy->center[ axis ] );
789 if( xx > joy->saturate[ axis ] )
792 if( xx < joy->dead_band[ axis ] )
795 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
796 joy->dead_band[ axis ] );
798 return ( xx > 1.0f ) ? 1.0f : xx;
803 * Read the corrected joystick data
805 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
807 float raw_axes[ _JS_MAX_AXES ];
816 for ( i=0; i<joy->num_axes; i++ )
820 fghJoystickRawRead( joy, buttons, raw_axes );
823 for( i=0; i<joy->num_axes; i++ )
824 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
828 * Happy happy happy joy joy joy (happy new year toudi :D)
832 #if TARGET_HOST_MAC_OSX
833 /** open the IOKit connection, enumerate all the HID devices, add their
834 interface references to the static array. We then use the array index
835 as the device number when we come to open() the joystick. */
836 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
838 CFMutableDictionaryRef hidMatch = NULL;
839 IOReturn rv = kIOReturnSuccess;
841 io_iterator_t hidIterator;
844 /* build a dictionary matching HID devices */
845 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
847 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
848 if (rv != kIOReturnSuccess || !hidIterator) {
849 fgWarning( "no joystick (HID) devices found" );
854 while ((ioDev = IOIteratorNext(hidIterator))) {
855 /* filter out keyboard and mouse devices */
856 CFDictionaryRef properties = getCFProperties(ioDev);
859 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
860 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
861 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
862 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
864 /* keep only joystick devices */
865 if ( ( page == kHIDPage_GenericDesktop ) && (
866 (usage == kHIDUsage_GD_Joystick)
867 || (usage == kHIDUsage_GD_GamePad)
868 || (usage == kHIDUsage_GD_MultiAxisController)
869 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
870 /* add it to the array */
871 ioDevices[numDevices++] = ioDev;
874 IOObjectRelease(hidIterator);
877 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
880 CFMutableDictionaryRef cfProperties;
883 /* comment copied from darwin/SDL_sysjoystick.c */
884 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
885 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
888 io_registry_entry_t parent1, parent2;
890 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
891 if (rv != kIOReturnSuccess) {
892 fgWarning ( "error getting device entry parent");
896 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
897 if (rv != kIOReturnSuccess) {
898 fgWarning ( "error getting device entry parent 2");
903 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
904 &cfProperties, kCFAllocatorDefault, kNilOptions);
905 if (rv != kIOReturnSuccess || !cfProperties) {
906 fgWarning ( "error getting device properties");
913 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
915 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
916 fgError ( "%s", "element enumerator passed non-dictionary value");
920 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
923 /** element enumerator function : pass NULL for top-level*/
924 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
926 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
927 "Joystick element type mismatch",
928 "fghJoystickEnumerateElements" );
930 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
931 CFArrayApplyFunction((CFArrayRef) element, range,
932 &fghJoystickElementEnumerator, joy );
935 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
937 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
938 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
940 long type, page, usage;
942 CFNumberGetValue((CFNumberRef)
943 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
944 kCFNumberLongType, &type);
947 case kIOHIDElementTypeInput_Misc:
948 case kIOHIDElementTypeInput_Axis:
949 case kIOHIDElementTypeInput_Button:
950 printf("got input element...");
951 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
952 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
954 if (page == kHIDPage_GenericDesktop) {
955 switch ( usage ) /* look at usage to determine function */
960 case kHIDUsage_GD_Rx:
961 case kHIDUsage_GD_Ry:
962 case kHIDUsage_GD_Rz:
963 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
965 fghJoystickAddAxisElement((CFDictionaryRef) element);
968 case kHIDUsage_GD_Hatswitch:
970 fghJoystickAddHatElement((CFDictionaryRef) element);
974 fgWarning ( "input type element has weird usage (%x)", usage);
977 } else if (page == kHIDPage_Button) {
979 fghJoystickAddButtonElement((CFDictionaryRef) element);
981 fgWarning ( "input type element has weird page (%x)", page);
984 case kIOHIDElementTypeCollection:
985 fghJoystickEnumerateElements (
986 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
995 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
997 long cookie, lmin, lmax;
998 int index = joy->num_axes++;
1000 CFNumberGetValue ((CFNumberRef)
1001 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1002 kCFNumberLongType, &cookie);
1004 axisCookies[index] = (IOHIDElementCookie) cookie;
1006 CFNumberGetValue ((CFNumberRef)
1007 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1008 kCFNumberLongType, &lmin);
1010 CFNumberGetValue ((CFNumberRef)
1011 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1012 kCFNumberLongType, &lmax);
1014 joy->min[index] = lmin;
1015 joy->max[index] = lmax;
1016 joy->dead_band[index] = 0.0;
1017 joy->saturate[index] = 1.0;
1018 joy->center[index] = (lmax + lmin) * 0.5;
1021 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1024 CFNumberGetValue ((CFNumberRef)
1025 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1026 kCFNumberLongType, &cookie);
1028 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1029 /* anything else for buttons? */
1032 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1034 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1035 /* do we map hats to axes or buttons? */
1039 #if TARGET_HOST_WIN32
1041 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1043 # if defined(_MSC_VER)
1044 # pragma comment (lib, "advapi32.lib")
1047 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1049 char buffer [ 256 ];
1051 char OEMKey [ 256 ];
1060 /* Open .. MediaResources\CurrentJoystickSettings */
1061 sprintf ( buffer, "%s\\%s\\%s",
1062 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1063 REGSTR_KEY_JOYCURR );
1065 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1067 if ( lr != ERROR_SUCCESS ) return 0;
1069 /* Get OEM Key name */
1070 dwcb = sizeof(OEMKey);
1072 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1073 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1075 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1076 RegCloseKey ( hKey );
1078 if ( lr != ERROR_SUCCESS ) return 0;
1080 /* Open OEM Key from ...MediaProperties */
1081 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1083 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1085 if ( lr != ERROR_SUCCESS ) return 0;
1090 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1092 RegCloseKey ( hKey );
1094 if ( lr != ERROR_SUCCESS ) return 0;
1101 static void fghJoystickOpen( SFG_Joystick* joy )
1104 #if TARGET_HOST_MACINTOSH
1107 #if TARGET_HOST_MAC_OSX
1110 IOCFPlugInInterface **plugin;
1112 HRESULT pluginResult;
1114 CFDictionaryRef props;
1115 CFTypeRef topLevelElement;
1117 #if TARGET_HOST_UNIX_X11
1118 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1129 * Default values (for no joystick -- each conditional will reset the
1133 joy->num_axes = joy->num_buttons = 0;
1134 joy->name[ 0 ] = '\0';
1136 #if TARGET_HOST_MACINTOSH
1137 /* XXX FIXME: get joystick name in Mac */
1139 err = ISpStartup( );
1143 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1145 joy->error = GL_TRUE;
1147 /* initialize the needs structure */
1148 ISpNeed temp_isp_needs[ isp_num_needs ] =
1150 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1151 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1152 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1153 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1154 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1155 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1156 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1157 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1158 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1160 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1161 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1190 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1191 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1194 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1197 /* next two calls allow keyboard and mouse to emulate other input
1198 * devices (gamepads, joysticks, etc)
1201 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1205 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1209 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1210 joy->isp_needs, joy->isp_elem,
1212 ISP_CHECK_ERR( err )
1214 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1215 'freeglut', nil, 0, 128, 0 );
1216 ISP_CHECK_ERR( err )
1218 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1219 joy->num_axes = joy->isp_num_axis;
1221 for( i = 0; i < joy->num_axes; i++ )
1223 joy->dead_band[ i ] = 0;
1224 joy->saturate [ i ] = 1;
1225 joy->center [ i ] = kISpAxisMiddle;
1226 joy->max [ i ] = kISpAxisMaximum;
1227 joy->min [ i ] = kISpAxisMinimum;
1230 joy->error = GL_FALSE;
1233 joy->num_buttons = joy->num_axes = 0;
1236 #if TARGET_HOST_MAC_OSX
1237 if( joy->id >= numDevices )
1239 fgWarning( "device index out of range in fgJoystickOpen()" );
1243 /* create device interface */
1244 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1245 kIOHIDDeviceUserClientTypeID,
1246 kIOCFPlugInInterfaceID,
1249 if( rv != kIOReturnSuccess )
1251 fgWarning( "error creating plugin for io device" );
1255 pluginResult = ( *plugin )->QueryInterface(
1257 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1258 &( LPVOID )joy->hidDev
1261 if( pluginResult != S_OK )
1262 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1264 ( *plugin )->Release( plugin ); /* don't leak a ref */
1265 if( joy->hidDev == NULL )
1268 /* store the interface in this instance */
1269 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1270 if( rv != kIOReturnSuccess )
1272 fgWarning( "error opening device interface");
1276 props = getCFProperties( ioDevices[ joy->id ] );
1278 /* recursively enumerate all the bits */
1279 CFTypeRef topLevelElement =
1280 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1281 enumerateElements( topLevelElement );
1286 #if TARGET_HOST_WIN32
1287 joy->js.dwFlags = JOY_RETURNALL;
1288 joy->js.dwSize = sizeof( joy->js );
1290 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1293 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1296 if( joy->jsCaps.wNumAxes == 0 )
1299 joy->error = GL_TRUE;
1303 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1304 * at least for USB. Try to get the real name from the registry.
1306 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1307 sizeof( joy->name ) ) )
1309 fgWarning( "JS: Failed to read joystick name from registry" );
1310 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1313 /* Windows joystick drivers may provide any combination of
1314 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1316 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1318 joy->num_axes = _JS_MAX_AXES;
1319 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1320 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1325 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1326 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1327 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1328 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1329 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1330 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1331 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1332 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1333 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1334 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1335 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1336 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1339 /* Guess all the rest judging on the axes extremals */
1340 for( i = 0; i < joy->num_axes; i++ )
1342 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1343 joy->dead_band[ i ] = 0.0f;
1344 joy->saturate [ i ] = 1.0f;
1348 #if TARGET_HOST_UNIX_X11
1349 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1350 for( i = 0; i < _JS_MAX_AXES; i++ )
1351 joy->os->cache_axes[ i ] = 0.0f;
1353 joy->os->cache_buttons = 0;
1355 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1357 if( joy->os->fd < 0 && errno == EACCES )
1358 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1360 joy->error =( joy->os->fd < 0 );
1366 joy->num_buttons = 0;
1367 if( joy->os->is_analog )
1370 char joyfname[ 1024 ];
1371 int noargs, in_no_axes;
1373 float axes [ _JS_MAX_AXES ];
1374 int buttons[ _JS_MAX_AXES ];
1377 joy->num_buttons = 32;
1379 fghJoystickRawRead( joy, buttons, axes );
1380 joy->error = axes[ 0 ] < -1000000000.0f;
1384 sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
1386 joyfile = fopen( joyfname, "r" );
1387 joy->error =( joyfile == NULL );
1391 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1392 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1393 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1394 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1399 for( i = 0; i < _JS_MAX_AXES; i++ )
1401 joy->dead_band[ i ] = 0.0f;
1402 joy->saturate [ i ] = 1.0f;
1405 return; /* End of analog code */
1409 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1410 &joy->num_buttons ) )
1412 close( joy->os->fd );
1413 joy->error = GL_TRUE;
1417 cp = strrchr( joy->os->fname, '/' );
1420 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1422 strcpy( joy->name, &cp[1] );
1425 if( joy->num_axes > _JS_MAX_AXES )
1426 joy->num_axes = _JS_MAX_AXES;
1428 for( i = 0; i < _JS_MAX_AXES; i++ )
1430 /* We really should get this from the HID, but that data seems
1431 * to be quite unreliable for analog-to-USB converters. Punt for
1434 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1436 joy->max [ i ] = 1.0f;
1437 joy->center[ i ] = 0.0f;
1438 joy->min [ i ] = -1.0f;
1442 joy->max [ i ] = 255.0f;
1443 joy->center[ i ] = 127.0f;
1444 joy->min [ i ] = 0.0f;
1447 joy->dead_band[ i ] = 0.0f;
1448 joy->saturate[ i ] = 1.0f;
1453 #if defined( __linux__ )
1454 /* Default for older Linux systems. */
1456 joy->num_buttons = 32;
1459 for( i = 0; i < _JS_MAX_AXES; i++ )
1460 joy->tmp_axes[ i ] = 0.0f;
1462 joy->tmp_buttons = 0;
1465 joy->fd = open( joy->fname, O_RDONLY );
1467 joy->error =( joy->fd < 0 );
1472 /* Set the correct number of axes for the linux driver */
1474 /* Melchior Franz's fixes for big-endian Linuxes since writing
1475 * to the upper byte of an uninitialized word doesn't work.
1478 ioctl( joy->fd, JSIOCGAXES, &u );
1480 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1481 joy->num_buttons = u;
1482 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1483 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1487 * The Linux driver seems to return 512 for all axes
1488 * when no stick is present - but there is a chance
1489 * that could happen by accident - so it's gotta happen
1490 * on both axes for at least 100 attempts.
1492 * PWO: shouldn't be that done somehow wiser on the kernel level?
1499 fghJoystickRawRead( joy, NULL, joy->center );
1501 } while( !joy->error &&
1503 joy->center[ 0 ] == 512.0f &&
1504 joy->center[ 1 ] == 512.0f );
1506 if ( counter >= 100 )
1507 joy->error = GL_TRUE;
1510 for( i = 0; i < _JS_MAX_AXES; i++ )
1513 joy->max [ i ] = 32767.0f;
1514 joy->center[ i ] = 0.0f;
1515 joy->min [ i ] = -32767.0f;
1517 joy->max[ i ] = joy->center[ i ] * 2.0f;
1518 joy->min[ i ] = 0.0f;
1520 joy->dead_band[ i ] = 0.0f;
1521 joy->saturate [ i ] = 1.0f;
1528 * This function replaces the constructor method in the JS library.
1530 static void fghJoystickInit( int ident )
1532 if( ident >= MAX_NUM_JOYSTICKS )
1533 fgError( "Too large a joystick number: %d", ident );
1535 if( fgJoystick[ ident ] )
1536 fgError( "illegal attempt to initialize joystick device again" );
1538 fgJoystick[ ident ] =
1539 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1542 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1543 fgJoystick[ ident ]->error = GL_TRUE;
1545 #if TARGET_HOST_MACINTOSH
1546 fgJoystick[ ident ]->id = ident;
1547 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
1548 fgJoystick[ ident ]->error = GL_FALSE;
1551 #if TARGET_HOST_MAC_OSX
1552 fgJoystick[ ident ]->id = ident;
1553 fgJoystick[ ident ]->error = GL_FALSE;
1554 fgJoystick[ ident ]->num_axes = 0;
1555 fgJoystick[ ident ]->num_buttons = 0;
1557 if( numDevices < 0 )
1559 /* do first-time init (since we can't over-ride jsInit, hmm */
1562 mach_port_t masterPort;
1563 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1564 if( rv != kIOReturnSuccess )
1566 fgWarning( "error getting master Mach port" );
1569 fghJoystickFindDevices( masterPort );
1572 if ( ident >= numDevices )
1574 fgJoystick[ ident ]->error = GL_TRUE;
1578 /* get the name now too */
1579 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1580 CFTypeRef ref = CFDictionaryGetValue( properties,
1581 CFSTR( kIOHIDProductKey ) );
1583 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1586 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1587 CFStringGetSystemEncoding( ) ) )
1589 fgWarning( "error getting device name" );
1594 #if TARGET_HOST_WIN32
1598 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1599 fgJoystick[ ident ]->error = GL_FALSE;
1602 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1603 fgJoystick[ ident ]->error = GL_FALSE;
1606 fgJoystick[ ident ]->num_axes = 0;
1607 fgJoystick[ ident ]->error = GL_TRUE;
1612 #if TARGET_HOST_UNIX_X11
1613 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1614 fgJoystick[ ident ]->id = ident;
1615 fgJoystick[ ident ]->error = GL_FALSE;
1617 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1618 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1619 if( ident < USB_IDENT_OFFSET )
1620 fgJoystick[ ident ]->os->is_analog = 1;
1621 if( fgJoystick[ ident ]->os->is_analog )
1622 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
1624 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
1625 ident - USB_IDENT_OFFSET );
1626 # elif defined( __linux__ )
1627 fgJoystick[ ident ]->id = ident;
1628 fgJoystick[ ident ]->error = GL_FALSE;
1630 sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1632 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1633 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
1637 fghJoystickOpen( fgJoystick[ ident ] );
1641 * Try initializing all the joysticks (well, both of them)
1643 void fgInitialiseJoysticks ( void )
1645 /* Initialization courtesy of OpenGLUT -- do we want it? */
1646 if( !fgState.JoysticksInitialised )
1649 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1650 fghJoystickInit( ident );
1652 fgState.JoysticksInitialised = GL_TRUE;
1659 void fgJoystickClose( void )
1662 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1664 if( fgJoystick[ ident ] )
1667 #if TARGET_HOST_MACINTOSH
1673 #if TARGET_HOST_MAC_OSX
1674 ( *( fgJoystick[ ident ]->hidDev ) )->
1675 close( fgJoystick[ ident ]->hidDev );
1678 #if TARGET_HOST_WIN32
1679 /* Do nothing special */
1682 #if TARGET_HOST_UNIX_X11
1683 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1684 if( fgJoystick[ident]->os )
1686 if( ! fgJoystick[ ident ]->error )
1687 close( fgJoystick[ ident ]->os->fd );
1689 if( fgJoystick[ ident ]->os->hids )
1690 free (fgJoystick[ ident ]->os->hids);
1691 if( fgJoystick[ ident ]->os->hid_data_buf )
1692 free( fgJoystick[ ident ]->os->hid_data_buf );
1694 free( fgJoystick[ident]->os );
1698 if( ! fgJoystick[ident]->error )
1699 close( fgJoystick[ ident ]->fd );
1702 free( fgJoystick[ ident ] );
1703 fgJoystick[ ident ] = NULL;
1704 /* show joystick has been deinitialized */
1710 * Polls the joystick and executes the joystick callback hooked to the
1711 * window specified in the function's parameter:
1713 void fgJoystickPollWindow( SFG_Window* window )
1715 float axes[ _JS_MAX_AXES ];
1719 freeglut_return_if_fail( window );
1720 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1722 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1724 if( fgJoystick[ident] )
1726 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1728 if( !fgJoystick[ident]->error )
1729 INVOKE_WCB( *window, Joystick,
1731 (int) ( axes[ 0 ] * 1000.0f ),
1732 (int) ( axes[ 1 ] * 1000.0f ),
1733 (int) ( axes[ 2 ] * 1000.0f ) )
1740 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1742 int fgJoystickDetect( void )
1746 fgInitialiseJoysticks ();
1751 if ( !fgState.JoysticksInitialised )
1754 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1755 if( fgJoystick[ident] && !fgJoystick[ident]->error )
1762 * Joystick information functions
1764 int glutJoystickGetNumAxes( int ident )
1766 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
1767 return fgJoystick[ ident ]->num_axes;
1769 int glutJoystickGetNumButtons( int ident )
1771 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
1772 return fgJoystick[ ident ]->num_buttons;
1774 int glutJoystickNotWorking( int ident )
1776 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
1777 return fgJoystick[ ident ]->error;
1780 float glutJoystickGetDeadBand( int ident, int axis )
1782 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1783 return fgJoystick[ ident ]->dead_band [ axis ];
1785 void glutJoystickSetDeadBand( int ident, int axis, float db )
1787 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1788 fgJoystick[ ident ]->dead_band[ axis ] = db;
1791 float glutJoystickGetSaturation( int ident, int axis )
1793 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1794 return fgJoystick[ ident ]->saturate[ axis ];
1796 void glutJoystickSetSaturation( int ident, int axis, float st )
1798 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1799 fgJoystick[ ident ]->saturate [ axis ] = st;
1802 void glutJoystickSetMinRange( int ident, float *axes )
1804 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1805 memcpy( fgJoystick[ ident ]->min, axes,
1806 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1808 void glutJoystickSetMaxRange( int ident, float *axes )
1810 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1811 memcpy( fgJoystick[ ident ]->max, axes,
1812 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1814 void glutJoystickSetCenter( int ident, float *axes )
1816 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1817 memcpy( fgJoystick[ ident ]->center, axes,
1818 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1821 void glutJoystickGetMinRange( int ident, float *axes )
1823 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1824 memcpy( axes, fgJoystick[ ident ]->min,
1825 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1827 void glutJoystickGetMaxRange( int ident, float *axes )
1829 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1830 memcpy( axes, fgJoystick[ ident ]->max,
1831 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1833 void glutJoystickGetCenter( int ident, float *axes )
1835 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1836 memcpy( axes, fgJoystick[ ident ]->center,
1837 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1840 /*** END OF FILE ***/