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 "../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 # include <machine/joystick.h> /* For analog joysticks */
84 # if __FreeBSD_version >= 500000
85 # include <sys/joystick.h>
88 * XXX NetBSD/amd64 systems may find that they have to steal the
89 * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
90 * XXX I cannot comment whether that works for the interface, but
91 * XXX it lets you compile...(^& I do not think that we can do away
92 * XXX with this header.
94 # include <machine/joystick.h>
96 # define JS_DATA_TYPE joystick
97 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
104 # if defined(__linux__)
105 # include <sys/ioctl.h>
106 # include <linux/joystick.h>
108 /* check the joystick driver version */
109 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
112 # else /* Not BSD or Linux */
116 * We'll put these values in and that should
117 * allow the code to at least compile when there is
118 * no support. The JS open routine should error out
119 * and shut off all the code downstream anyway and if
120 * the application doesn't use a joystick we'll be fine.
130 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
139 * BSD defines from "jsBSD.cxx" around lines 42-270
142 #if defined(__NetBSD__) || defined(__FreeBSD__)
145 # if defined(__NetBSD__)
147 * XXX The below hack is done until freeglut's autoconf is updated.
149 # define HAVE_USBHID_H 1
150 # ifdef HAVE_USBHID_H
155 # elif defined(__FreeBSD__)
156 # include <libusbhid.h>
158 # include <dev/usb/usb.h>
159 # include <dev/usb/usbhid.h>
161 /* Compatibility with older usb.h revisions */
162 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
163 # define USB_MAX_DEVNAMES MAXDEVNAMES
167 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
168 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
169 struct os_specific_s {
173 /* The following structure members are specific to analog joysticks */
176 /* The following structure members are specific to USB joysticks */
177 struct hid_item *hids;
181 int axes_usage [ _JS_MAX_AXES ];
183 /* We keep button and axes state ourselves, as they might not be updated
184 * on every read of a USB device
187 float cache_axes [ _JS_MAX_AXES ];
190 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
191 # define USB_IDENT_OFFSET 2
193 # define USBDEV "/dev/usb"
194 # define UHIDDEV "/dev/uhid"
195 # define AJSDEV "/dev/joy"
199 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
200 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
201 * return the uhidN device name. We warn the user of this situation once.
203 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
205 struct usb_device_info di;
209 for (a = 1; a < USB_MAX_DEVICES; a++) {
211 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
213 for (i = 0; i < USB_MAX_DEVNAMES; i++)
214 if (di.udi_devnames[i][0] &&
215 strcmp(di.udi_devnames[i], dev) == 0) {
216 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
217 strcpy(cp, di.udi_vendor);
219 strcat(cp, di.udi_product);
220 strncpy(out, cp, outlen - 1);
229 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
234 static int protection_warned = 0;
236 for (i = 0; i < 16; i++) {
237 sprintf(buf, "%s%d", USBDEV, i);
238 f = open(buf, O_RDONLY);
240 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
244 } else if (errno == EACCES) {
245 if (!protection_warned) {
246 fprintf(stderr, "Can't open %s for read!\n",
248 protection_warned = 1;
255 static int fghJoystickInitializeHID(struct os_specific_s *os,
256 int *num_axes, int *num_buttons)
258 int size, is_joystick;
259 # ifdef HAVE_USBHID_H
266 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
268 fprintf( stderr, "error: %s: %s", os->fname, strerror( errno ) );
274 # ifdef HAVE_USBHID_H
275 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
277 /*** XXX {report_id} may not be the right variable? ***/
278 fprintf( stderr, "error: %s%d: %s",
279 UHIDDEV, report_id, strerror( errno ) );
283 size = hid_report_size( rd, hid_input, report_id );
285 size = hid_report_size( rd, 0, hid_input );
287 os->hid_data_buf = calloc( 1, size );
291 # ifdef HAVE_USBHID_H
292 d = hid_start_parse( rd, 1 << hid_input, report_id );
294 d = hid_start_parse( rd, 1 << hid_input );
296 while( hid_get_item( d, &h ) )
298 int usage, page, interesting_hid;
300 page = HID_PAGE( h.usage );
301 usage = HID_USAGE( h.usage );
303 /* This test is somewhat too simplistic, but this is how MicroSoft
304 * does, so I guess it works for all joysticks/game pads. */
305 is_joystick = is_joystick ||
306 ( h.kind == hid_collection &&
307 page == HUP_GENERIC_DESKTOP &&
308 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
310 if( h.kind != hid_input )
316 interesting_hid = TRUE;
317 if( page == HUP_GENERIC_DESKTOP )
328 if( *num_axes < _JS_MAX_AXES )
330 os->axes_usage[ *num_axes ] = usage;
335 /* Allocate two axes for a hat */
336 if( *num_axes + 1 < _JS_MAX_AXES )
338 os->axes_usage[ *num_axes ] = usage;
340 os->axes_usage[ *num_axes ] = usage;
345 interesting_hid = FALSE;
349 else if( page == HUP_BUTTON )
351 interesting_hid = ( usage > 0 ) &&
352 ( usage <= _JS_MAX_BUTTONS );
354 if( interesting_hid && usage - 1 > *num_buttons )
355 *num_buttons = usage - 1;
358 if( interesting_hid )
361 os->hids = calloc( 1, sizeof ( struct hid_item ) );
367 return os->hids != NULL;
373 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
374 * See "js.h" lines 80-178.
376 typedef struct tagSFG_Joystick SFG_Joystick;
377 struct tagSFG_Joystick
379 #if TARGET_HOST_MACINTOSH
380 #define ISP_NUM_AXIS 9
381 #define ISP_NUM_NEEDS 41
382 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
383 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
386 #if TARGET_HOST_MAC_OSX
387 IOHIDDeviceInterface ** hidDev;
388 IOHIDElementCookie buttonCookies[41];
389 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
390 long minReport[_JS_MAX_AXES],
391 maxReport[_JS_MAX_AXES];
394 #if TARGET_HOST_WIN32
401 #if TARGET_HOST_UNIX_X11
402 # if defined(__FreeBSD__) || defined(__NetBSD__)
403 struct os_specific_s *os;
409 float tmp_axes [ _JS_MAX_AXES ];
411 struct JS_DATA_TYPE js;
424 float dead_band[ _JS_MAX_AXES ];
425 float saturate [ _JS_MAX_AXES ];
426 float center [ _JS_MAX_AXES ];
427 float max [ _JS_MAX_AXES ];
428 float min [ _JS_MAX_AXES ];
432 * Functions associated with the "jsJoystick" class in PLIB
434 #if TARGET_HOST_MAC_OSX
435 #define K_NUM_DEVICES 32
437 io_object_t ioDevices[K_NUM_DEVICES];
439 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
440 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
442 void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
443 /* callback for CFArrayApply */
444 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
445 void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
447 void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
448 void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
449 void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
454 * The static joystick structure pointer
456 #define MAX_NUM_JOYSTICKS 2
457 static int fgNumberOfJoysticks = 0;
458 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
462 * Read the raw joystick data
464 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
466 #if TARGET_HOST_WIN32
472 #if defined(__FreeBSD__) || defined(__NetBSD__)
483 for( i = 0; i < joy->num_axes; i++ )
489 #if TARGET_HOST_MACINTOSH
494 for ( i = 0; i < joy->num_buttons; i++ )
497 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
500 *buttons |= state << i;
506 for ( i = 0; i < joy->num_axes; i++ )
509 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
512 axes [i] = (float) state;
517 #if TARGET_HOST_MAC_OSX
518 if ( buttons != NULL )
522 for ( i = 0; i < joy->num_buttons; i++ )
524 IOHIDEventStruct hidEvent;
525 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
526 if ( hidEvent.value )
533 for ( i = 0; i < joy->num_axes; i++ )
535 IOHIDEventStruct hidEvent;
536 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
537 axes[i] = hidEvent.value;
542 #if TARGET_HOST_WIN32
543 status = joyGetPosEx( joy->js_id, &joy->js );
545 if ( status != JOYERR_NOERROR )
547 joy->error = GL_TRUE;
552 *buttons = joy->js.dwButtons;
557 * WARNING - Fall through case clauses!!
559 switch ( joy->num_axes )
562 /* Generate two POV axes from the POV hat angle.
563 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
564 * hundredths of a degree, or 0xFFFF when idle.
566 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
573 /* This is the contentious bit: how to convert angle to X/Y.
574 * wk: I know of no define for PI that we could use here:
575 * SG_PI would pull in sg, M_PI is undefined for MSVC
576 * But the accuracy of the value of PI is very unimportant at
579 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
580 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
582 /* Convert to coordinates on a square so that North-East
583 * is (1,1) not (.7,.7), etc.
584 * s and c cannot both be zero so we won't divide by zero.
586 if ( fabs ( s ) < fabs ( c ) )
588 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
589 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
593 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
594 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
598 case 6: axes[5] = (float) joy->js.dwVpos;
599 case 5: axes[4] = (float) joy->js.dwUpos;
600 case 4: axes[3] = (float) joy->js.dwRpos;
601 case 3: axes[2] = (float) joy->js.dwZpos;
602 case 2: axes[1] = (float) joy->js.dwYpos;
603 case 1: axes[0] = (float) joy->js.dwXpos;
608 #if TARGET_HOST_UNIX_X11
609 # if defined(__FreeBSD__) || defined(__NetBSD__)
610 if ( joy->os->is_analog )
612 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
613 if ( status != sizeof(joy->os->ajs) ) {
614 perror ( joy->os->fname );
615 joy->error = GL_TRUE;
618 if ( buttons != NULL )
619 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
623 axes[0] = (float) joy->os->ajs.x;
624 axes[1] = (float) joy->os->ajs.y;
631 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
635 for ( h = joy->os->hids; h; h = h->next )
637 int d = hid_get_data ( joy->os->hid_data_buf, h );
639 int page = HID_PAGE ( h->usage );
640 int usage = HID_USAGE ( h->usage );
642 if ( page == HUP_GENERIC_DESKTOP )
645 for ( i = 0; i < joy->num_axes; i++ )
646 if (joy->os->axes_usage[i] == usage)
648 if (usage == HUG_HAT_SWITCH)
652 joy->os->cache_axes[i] = (float)hatmap_x[d];
653 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
657 joy->os->cache_axes[i] = (float)d;
662 else if (page == HUP_BUTTON)
664 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
667 joy->os->cache_buttons |= (1 << usage - 1);
669 joy->os->cache_buttons &= ~(1 << usage - 1);
674 if ( len < 0 && errno != EAGAIN )
676 perror( joy->os->fname );
679 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
681 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
689 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
691 if ( status != sizeof( struct js_event ) )
693 if ( errno == EAGAIN )
695 /* Use the old values */
697 *buttons = joy->tmp_buttons;
699 memcpy( axes, joy->tmp_axes,
700 sizeof( float ) * joy->num_axes );
704 fgWarning ( "%s", joy->fname );
705 joy->error = GL_TRUE;
709 switch ( joy->js.type & ~JS_EVENT_INIT )
711 case JS_EVENT_BUTTON:
712 if( joy->js.value == 0 ) /* clear the flag */
713 joy->tmp_buttons &= ~( 1 << joy->js.number );
715 joy->tmp_buttons |= ( 1 << joy->js.number );
719 if ( joy->js.number < joy->num_axes )
721 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
724 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
729 fgWarning ( "%s", "PLIB_JS: Unrecognised /dev/js return!?!" );
731 /* use the old values */
733 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
735 memcpy ( axes, joy->tmp_axes, sizeof(float) * num_axes );
741 *buttons = joy->tmp_buttons;
745 status = read( joy->fd, &joy->js, JS_RETURN );
747 if ( status != JS_RETURN )
749 fgWarning( "%s", joy->fname );
750 joy->error = GL_TRUE;
755 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
756 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
758 *buttons = joy->js.buttons;
763 axes[ 0 ] = (float) joy->js.x;
764 axes[ 1 ] = (float) joy->js.y;
771 * Correct the joystick axis data
773 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
775 if( value < joy->center[ axis ] )
777 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
780 if( xx < -joy->saturate[ axis ] )
783 if( xx > -joy->dead_band [ axis ] )
786 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
787 joy->dead_band[ axis ] );
789 return ( xx < -1.0f ) ? -1.0f : xx;
793 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
794 joy->center[ axis ] );
796 if( xx > joy->saturate[ axis ] )
799 if( xx < joy->dead_band[ axis ] )
802 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
803 joy->dead_band[ axis ] );
805 return ( xx > 1.0f ) ? 1.0f : xx;
810 * Read the corrected joystick data
812 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
814 float raw_axes[ _JS_MAX_AXES ];
823 for ( i=0; i<joy->num_axes; i++ )
827 fghJoystickRawRead( joy, buttons, raw_axes );
830 for( i=0; i<joy->num_axes; i++ )
831 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
835 * Happy happy happy joy joy joy (happy new year toudi :D)
839 #if TARGET_HOST_MAC_OSX
840 /** open the IOKit connection, enumerate all the HID devices, add their
841 interface references to the static array. We then use the array index
842 as the device number when we come to open() the joystick. */
843 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
845 CFMutableDictionaryRef hidMatch = NULL;
846 IOReturn rv = kIOReturnSuccess;
848 io_iterator_t hidIterator;
851 /* build a dictionary matching HID devices */
852 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
854 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
855 if (rv != kIOReturnSuccess || !hidIterator) {
856 fgWarning ( "%s", "no joystick (HID) devices found");
861 while ((ioDev = IOIteratorNext(hidIterator))) {
862 /* filter out keyboard and mouse devices */
863 CFDictionaryRef properties = getCFProperties(ioDev);
866 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
867 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
868 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
869 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
871 /* keep only joystick devices */
872 if ( ( page == kHIDPage_GenericDesktop ) && (
873 (usage == kHIDUsage_GD_Joystick)
874 || (usage == kHIDUsage_GD_GamePad)
875 || (usage == kHIDUsage_GD_MultiAxisController)
876 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
877 /* add it to the array */
878 ioDevices[numDevices++] = ioDev;
881 IOObjectRelease(hidIterator);
884 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
887 CFMutableDictionaryRef cfProperties;
890 /* comment copied from darwin/SDL_sysjoystick.c */
891 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
892 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
895 io_registry_entry_t parent1, parent2;
897 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
898 if (rv != kIOReturnSuccess) {
899 fgWarning ( "%s", "error getting device entry parent");
903 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
904 if (rv != kIOReturnSuccess) {
905 fgWarning ( "%s", "error getting device entry parent 2");
910 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
911 &cfProperties, kCFAllocatorDefault, kNilOptions);
912 if (rv != kIOReturnSuccess || !cfProperties) {
913 fgWarning ( "%s", "error getting device properties");
920 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
922 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
923 fgError ( "%s", "element enumerator passed non-dictionary value");
927 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
930 /** element enumerator function : pass NULL for top-level*/
931 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
933 assert(CFGetTypeID(element) == CFArrayGetTypeID());
935 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
936 CFArrayApplyFunction((CFArrayRef) element, range,
937 &fghJoystickElementEnumerator, joy );
940 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
942 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
943 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
945 long type, page, usage;
947 CFNumberGetValue((CFNumberRef)
948 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
949 kCFNumberLongType, &type);
952 case kIOHIDElementTypeInput_Misc:
953 case kIOHIDElementTypeInput_Axis:
954 case kIOHIDElementTypeInput_Button:
955 printf("got input element...");
956 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
957 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
959 if (page == kHIDPage_GenericDesktop) {
960 switch ( usage ) /* look at usage to determine function */
965 case kHIDUsage_GD_Rx:
966 case kHIDUsage_GD_Ry:
967 case kHIDUsage_GD_Rz:
968 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
970 fghJoystickAddAxisElement((CFDictionaryRef) element);
973 case kHIDUsage_GD_Hatswitch:
975 fghJoystickAddHatElement((CFDictionaryRef) element);
979 printf("input type element has weird usage (%x)\n", usage);
982 } else if (page == kHIDPage_Button) {
984 fghJoystickAddButtonElement((CFDictionaryRef) element);
986 printf("input type element has weird page (%x)\n", page);
989 case kIOHIDElementTypeCollection:
990 fghJoystickEnumerateElements (
991 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
1000 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
1002 long cookie, lmin, lmax;
1003 int index = joy->num_axes++;
1005 CFNumberGetValue ((CFNumberRef)
1006 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1007 kCFNumberLongType, &cookie);
1009 axisCookies[index] = (IOHIDElementCookie) cookie;
1011 CFNumberGetValue ((CFNumberRef)
1012 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1013 kCFNumberLongType, &lmin);
1015 CFNumberGetValue ((CFNumberRef)
1016 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1017 kCFNumberLongType, &lmax);
1019 joy->min[index] = lmin;
1020 joy->max[index] = lmax;
1021 joy->dead_band[index] = 0.0;
1022 joy->saturate[index] = 1.0;
1023 joy->center[index] = (lmax + lmin) * 0.5;
1026 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1029 CFNumberGetValue ((CFNumberRef)
1030 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1031 kCFNumberLongType, &cookie);
1033 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1034 /* anything else for buttons? */
1037 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1039 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1040 /* do we map hats to axes or buttons? */
1044 #if TARGET_HOST_WIN32
1046 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1048 # pragma comment (lib, "advapi32.lib")
1050 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1052 char buffer [ 256 ];
1054 char OEMKey [ 256 ];
1063 /* Open .. MediaResources\CurrentJoystickSettings */
1064 sprintf ( buffer, "%s\\%s\\%s",
1065 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1066 REGSTR_KEY_JOYCURR );
1068 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1070 if ( lr != ERROR_SUCCESS ) return 0;
1072 /* Get OEM Key name */
1073 dwcb = sizeof(OEMKey);
1075 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1076 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1078 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1079 RegCloseKey ( hKey );
1081 if ( lr != ERROR_SUCCESS ) return 0;
1083 /* Open OEM Key from ...MediaProperties */
1084 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1086 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1088 if ( lr != ERROR_SUCCESS ) return 0;
1093 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1095 RegCloseKey ( hKey );
1097 if ( lr != ERROR_SUCCESS ) return 0;
1104 static void fghJoystickOpen( SFG_Joystick* joy )
1107 #if TARGET_HOST_MACINTOSH
1110 #if TARGET_HOST_MAC_OSX
1113 IOCFPlugInInterface **plugin;
1115 HRESULT pluginResult;
1117 CFDictionaryRef props;
1118 CFTypeRef topLevelElement;
1120 #if TARGET_HOST_UNIX_X11
1121 # if defined(__FreeBSD__) || defined(__NetBSD__)
1131 /* Default values (for no joystick -- each conditional will reset the error flag) */
1133 joy->num_axes = joy->num_buttons = 0;
1134 joy->name [0] = '\0';
1136 #if TARGET_HOST_MACINTOSH
1138 * XXX FIXME: get joystick name in Mac
1141 err = ISpStartup ();
1145 #define ISP_CHECK_ERR(x) if ( x != noErr ) { joy->error = GL_TRUE; return; }
1147 joy->error = GL_TRUE;
1149 /* initialize the needs structure */
1150 ISpNeed temp_isp_needs[isp_num_needs] =
1152 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1153 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1154 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1155 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1156 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1157 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1158 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1159 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1160 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1162 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1190 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1191 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1192 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1193 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1196 memcpy ( joy->isp_needs, temp_isp_needs, sizeof(temp_isp_needs) );
1199 /* next two calls allow keyboard and mouse to emulate other input
1200 * devices (gamepads, joysticks, etc)
1203 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1207 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1211 err = ISpElement_NewVirtualFromNeeds ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 0 );
1214 err = ISpInit ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 'freeglut', nil, 0, 128, 0 );
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) {
1237 fgWarning ( "%s", "device index out of range in fgJoystickOpen()");
1241 /* create device interface */
1242 rv = IOCreatePlugInInterfaceForService ( ioDevices[joy->id],
1243 kIOHIDDeviceUserClientTypeID,
1244 kIOCFPlugInInterfaceID,
1247 if (rv != kIOReturnSuccess) {
1248 fgWarning ( "%s", "error creating plugin for io device");
1252 pluginResult = (*plugin)->QueryInterface ( plugin,
1253 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), &(LPVOID) joy->hidDev );
1255 if ( pluginResult != S_OK )
1256 fgWarning ( "%s", "QI-ing IO plugin to HID Device interface failed");
1258 (*plugin)->Release(plugin); /* don't leak a ref */
1259 if (joy->hidDev == NULL) return;
1261 /* store the interface in this instance */
1262 rv = (*(joy->hidDev))->open(joy->hidDev, 0);
1263 if (rv != kIOReturnSuccess) {
1264 fgWarning ( "%s", "error opening device interface");
1268 props = getCFProperties(ioDevices[joy->id]);
1270 /* recursively enumerate all the bits */
1271 CFTypeRef topLevelElement =
1272 CFDictionaryGetValue ( props, CFSTR ( kIOHIDElementKey ) );
1273 enumerateElements ( topLevelElement );
1275 CFRelease ( props );
1278 #if TARGET_HOST_WIN32
1279 joy->js.dwFlags = JOY_RETURNALL;
1280 joy->js.dwSize = sizeof( joy->js );
1282 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1285 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1288 if ( joy->jsCaps.wNumAxes == 0 )
1291 joy->error = GL_TRUE;
1295 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1296 * at least for USB. Try to get the real name from the registry.
1298 if ( ! fghJoystickGetOEMProductName ( joy, joy->name, sizeof(joy->name) ) )
1300 fgWarning ( "%s", "JS: Failed to read joystick name from registry" );
1301 strncpy ( joy->name, joy->jsCaps.szPname, sizeof(joy->name) );
1304 /* Windows joystick drivers may provide any combination of
1305 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1307 if ( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1309 joy->num_axes = _JS_MAX_AXES;
1310 joy->min [ 7 ] = -1.0; joy->max [ 7 ] = 1.0; /* POV Y */
1311 joy->min [ 6 ] = -1.0; joy->max [ 6 ] = 1.0; /* POV X */
1316 joy->min[ 5 ] = (float) joy->jsCaps.wVmin;
1317 joy->max[ 5 ] = (float) joy->jsCaps.wVmax;
1318 joy->min[ 4 ] = (float) joy->jsCaps.wUmin;
1319 joy->max[ 4 ] = (float) joy->jsCaps.wUmax;
1320 joy->min[ 3 ] = (float) joy->jsCaps.wRmin;
1321 joy->max[ 3 ] = (float) joy->jsCaps.wRmax;
1322 joy->min[ 2 ] = (float) joy->jsCaps.wZmin;
1323 joy->max[ 2 ] = (float) joy->jsCaps.wZmax;
1324 joy->min[ 1 ] = (float) joy->jsCaps.wYmin;
1325 joy->max[ 1 ] = (float) joy->jsCaps.wYmax;
1326 joy->min[ 0 ] = (float) joy->jsCaps.wXmin;
1327 joy->max[ 0 ] = (float) joy->jsCaps.wXmax;
1331 * Guess all the rest judging on the axes extremals
1333 for( i = 0; i < joy->num_axes; i++ )
1335 joy->center [ i ] = (joy->max[i] + joy->min[i]) * 0.5f;
1336 joy->dead_band[ i ] = 0.0f;
1337 joy->saturate [ i ] = 1.0f;
1341 #if TARGET_HOST_UNIX_X11
1342 #if defined(__FreeBSD__) || defined(__NetBSD__)
1343 for ( i = 0; i < _JS_MAX_AXES; i++ )
1344 joy->os->cache_axes [ i ] = 0.0f;
1346 joy->os->cache_buttons = 0;
1348 joy->os->fd = open ( joy->os->fname, O_RDONLY | O_NONBLOCK);
1350 if ( joy->os->fd < 0 && errno == EACCES)
1351 fgWarning ( "%s exists but is not readable by you\n", joy->os->fname );
1353 joy->error = ( joy->os->fd < 0 );
1359 joy->num_buttons = 0;
1360 if ( joy->os->is_analog )
1363 char joyfname [ 1024 ];
1364 int noargs, in_no_axes;
1366 float axes [ _JS_MAX_AXES ];
1367 int buttons [ _JS_MAX_AXES ];
1370 joy->num_buttons = 32;
1372 fghJoystickRawRead ( joy, buttons, axes );
1373 joy->error = axes[0] < -1000000000.0f;
1377 sprintf( joyfname, "%s/.joy%drc", getenv ( "HOME" ), joy->id );
1379 joyfile = fopen ( joyfname, "r" );
1380 joy->error = ( joyfile == NULL );
1384 noargs = fscanf ( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1385 &joy->min [ 0 ], &joy->center [ 0 ], &joy->max [ 0 ],
1386 &joy->min [ 1 ], &joy->center [ 1 ], &joy->max [ 1 ] );
1387 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1392 for ( i = 0; i < _JS_MAX_AXES; i++ )
1394 joy->dead_band [ i ] = 0.0f;
1395 joy->saturate [ i ] = 1.0f;
1398 return; /* End of analog code */
1402 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1403 &joy->num_buttons ) )
1405 close ( joy->os->fd );
1406 joy->error = GL_TRUE;
1410 cp = strrchr(joy->os->fname, '/');
1412 if ( fghJoystickFindUSBdev ( &cp[1], joy->name, sizeof(joy->name) ) == 0 )
1413 strcpy ( joy->name, &cp[1] );
1416 if ( joy->num_axes > _JS_MAX_AXES )
1417 joy->num_axes = _JS_MAX_AXES;
1419 for ( i = 0; i < _JS_MAX_AXES; i++ )
1421 /* We really should get this from the HID, but that data seems
1422 * to be quite unreliable for analog-to-USB converters. Punt for
1425 if ( joy->os->axes_usage [ i ] == HUG_HAT_SWITCH )
1427 joy->max [ i ] = 1.0f;
1428 joy->center [ i ] = 0.0f;
1429 joy->min [ i ] = -1.0f;
1433 joy->max [ i ] = 255.0f;
1434 joy->center [ i ] = 127.0f;
1435 joy->min [ i ] = 0.0f;
1438 joy->dead_band [ i ] = 0.0f;
1439 joy->saturate [ i ] = 1.0f;
1444 #if defined(__linux__)
1446 * Default for older Linux systems.
1449 joy->num_buttons = 32;
1452 for( i = 0; i < _JS_MAX_AXES; i++ )
1453 joy->tmp_axes[ i ] = 0.0f;
1455 joy->tmp_buttons = 0;
1458 joy->fd = open( joy->fname, O_RDONLY );
1460 joy->error = (joy->fd < 0);
1466 * Set the correct number of axes for the linux driver
1469 /* Melchior Franz's fixes for big-endian Linuxes since writing
1470 * to the upper byte of an uninitialized word doesn't work.
1473 ioctl ( joy->fd, JSIOCGAXES , &u );
1475 ioctl ( joy->fd, JSIOCGBUTTONS, &u );
1476 joy->num_buttons = u;
1477 ioctl ( joy->fd, JSIOCGNAME ( sizeof(joy->name) ), joy->name );
1478 fcntl ( joy->fd, F_SETFL , O_NONBLOCK );
1482 * The Linux driver seems to return 512 for all axes
1483 * when no stick is present - but there is a chance
1484 * that could happen by accident - so it's gotta happen
1485 * on both axes for at least 100 attempts.
1487 * PWO: shouldn't be that done somehow wiser on the kernel level?
1494 fghJoystickRawRead( joy, NULL, joy->center );
1496 } while( !joy->error &&
1498 joy->center[ 0 ] == 512.0f &&
1499 joy->center[ 1 ] == 512.0f );
1501 if ( counter >= 100 )
1502 joy->error = GL_TRUE;
1505 for ( i = 0; i < _JS_MAX_AXES; i++ )
1508 joy->max [ i ] = 32767.0f;
1509 joy->center[ i ] = 0.0f;
1510 joy->min [ i ] = -32767.0f;
1512 joy->max[ i ] = joy->center[ i ] * 2.0f;
1513 joy->min[ i ] = 0.0f;
1515 joy->dead_band[ i ] = 0.0f;
1516 joy->saturate [ i ] = 1.0f;
1523 * This function replaces the constructor method in the JS library.
1525 void fgJoystickInit( int ident )
1527 if ( ident >= MAX_NUM_JOYSTICKS )
1528 fgError( "Too large a joystick number" );
1530 if( fgJoystick[ident] )
1531 fgError( "illegal attempt to initialize joystick device" );
1533 fgJoystick[ident] = ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1536 fgJoystick[ident]->num_axes = fgJoystick[ident]->num_buttons = 0;
1537 fgJoystick[ident]->error = GL_TRUE;
1539 #if TARGET_HOST_MACINTOSH
1540 fgJoystick[ident]->id = ident;
1541 sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident ); /* FIXME */
1542 fgJoystick[ident]->error = GL_FALSE;
1545 #if TARGET_HOST_MAC_OSX
1546 fgJoystick[ident]->id = ident;
1547 fgJoystick[ident]->error = GL_FALSE;
1548 fgJoystick[ident]->num_axes = 0;
1549 fgJoystick[ident]->num_buttons = 0;
1551 if (numDevices < 0) {
1552 /* do first-time init (since we can't over-ride jsInit, hmm */
1555 mach_port_t masterPort;
1556 IOReturn rv = IOMasterPort ( bootstrap_port, &masterPort );
1557 if ( rv != kIOReturnSuccess ) {
1558 fgWarning ( "%s", "error getting master Mach port");
1561 fghJoystickFindDevices ( masterPort );
1564 if ( ident >= numDevices ) {
1565 fgJoystick[ident]->error = GL_TRUE;
1569 /* get the name now too */
1570 CFDictionaryRef properties = getCFProperties(ioDevices[ident]);
1571 CFTypeRef ref = CFDictionaryGetValue (properties, CFSTR(kIOHIDProductKey));
1573 ref = CFDictionaryGetValue (properties, CFSTR("USB Product Name"));
1575 if (!ref || !CFStringGetCString ((CFStringRef) ref, name, 128, CFStringGetSystemEncoding ())) {
1576 fgWarning ( "%s", "error getting device name");
1581 #if TARGET_HOST_WIN32
1585 fgJoystick[ident]->js_id = JOYSTICKID1;
1586 fgJoystick[ident]->error = GL_FALSE;
1589 fgJoystick[ident]->js_id = JOYSTICKID2;
1590 fgJoystick[ident]->error = GL_FALSE;
1593 fgJoystick[ident]->num_axes = 0;
1594 fgJoystick[ident]->error = GL_TRUE;
1599 #if TARGET_HOST_UNIX_X11
1600 # if defined(__FreeBSD__) || defined(__NetBSD__)
1601 fgJoystick[ident]->id = ident;
1602 fgJoystick[ident]->error = GL_FALSE;
1604 fgJoystick[ident]->os = calloc (1, sizeof (struct os_specific_s));
1605 memset ( fgJoystick[ident]->os, 0, sizeof(struct os_specific_s) );
1606 if (ident < USB_IDENT_OFFSET)
1607 fgJoystick[ident]->os->is_analog = 1;
1608 if (fgJoystick[ident]->os->is_analog)
1609 sprintf ( fgJoystick[ident]->os->fname, "%s%d", AJSDEV, ident );
1611 sprintf ( fgJoystick[ident]->os->fname, "%s%d", UHIDDEV, ident - USB_IDENT_OFFSET );
1612 # elif defined(__linux__)
1613 fgJoystick[ident]->id = ident;
1614 fgJoystick[ident]->error = GL_FALSE;
1616 sprintf ( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1618 if ( access ( fgJoystick[ident]->fname, F_OK ) != 0 )
1619 sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident );
1623 fghJoystickOpen ( fgJoystick[ident] );
1629 void fgJoystickClose( void )
1632 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1634 if ( fgJoystick[ident] )
1637 #if TARGET_HOST_MACINTOSH
1643 #if TARGET_HOST_MAC_OSX
1644 (*(fgJoystick[ident]->hidDev))->close(fgJoystick[ident]->hidDev);
1647 #if TARGET_HOST_WIN32
1648 /* Do nothing special */
1651 #if TARGET_HOST_UNIX_X11
1652 #if defined(__FreeBSD__) || defined(__NetBSD__)
1653 if ( fgJoystick[ident]->os )
1655 if ( ! fgJoystick[ident]->error )
1656 close ( fgJoystick[ident]->os->fd );
1658 if ( fgJoystick[ident]->os->hids )
1659 free (fgJoystick[ident]->os->hids);
1660 if ( fgJoystick[ident]->os->hid_data_buf )
1661 free (fgJoystick[ident]->os->hid_data_buf);
1663 free (fgJoystick[ident]->os);
1667 if( ! fgJoystick[ident]->error )
1668 close( fgJoystick[ident]->fd );
1671 free( fgJoystick[ident] );
1672 fgJoystick[ident] = NULL; /* show joystick has been deinitialized */
1678 * Polls the joystick and executes the joystick callback hooked to the
1679 * window specified in the function's parameter:
1681 void fgJoystickPollWindow( SFG_Window* window )
1683 float axes[ _JS_MAX_AXES ];
1687 freeglut_return_if_fail( window );
1688 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1690 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1692 if ( fgJoystick[ident] )
1694 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1696 if ( !fgJoystick[ident]->error )
1697 INVOKE_WCB( *window, Joystick,
1699 (int) (axes[ 0 ] * 1000.0f ),
1700 (int) (axes[ 1 ] * 1000.0f ),
1701 (int) (axes[ 2 ] * 1000.0f ) )
1708 * PWO: These jsJoystick class methods have not been implemented.
1709 * We might consider adding such functions to freeglut-2.0.
1711 int getNumAxes ( int ident )
1712 { return fgJoystick[ident]->num_axes; }
1713 int notWorking ( int ident )
1714 { return fgJoystick[ident]->error; }
1716 float getDeadBand ( int ident, int axis )
1717 { return fgJoystick[ident]->dead_band [ axis ]; }
1718 void setDeadBand ( int ident, int axis, float db )
1719 { fgJoystick[ident]->dead_band [ axis ] = db; }
1721 float getSaturation ( int ident, int axis )
1722 { return fgJoystick[ident]->saturate [ axis ]; }
1723 void setSaturation ( int ident, int axis, float st )
1724 { fgJoystick[ident]->saturate [ axis ] = st; }
1726 void setMinRange ( int ident, float *axes )
1727 { memcpy ( fgJoystick[ident]->min , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1728 void setMaxRange ( int ident, float *axes )
1729 { memcpy ( fgJoystick[ident]->max , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1730 void setCenter ( int ident, float *axes )
1731 { memcpy ( fgJoystick[ident]->center, axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1733 void getMinRange ( int ident, float *axes )
1734 { memcpy ( axes, fgJoystick[ident]->min , fgJoystick[ident]->num_axes * sizeof(float) ); }
1735 void getMaxRange ( int ident, float *axes )
1736 { memcpy ( axes, fgJoystick[ident]->max , fgJoystick[ident]->num_axes * sizeof(float) ); }
1737 void getCenter ( int ident, float *axes )
1738 { memcpy ( axes, fgJoystick[ident]->center, fgJoystick[ident]->num_axes * sizeof(float) ); }
1740 /*** END OF FILE ***/