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__)
76 /* XXX The next line is an unjustified hack which needs to be changed by someone who
77 * XXX is familiar with *nix, BSD, and USB joysticks.
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>
87 # include <machine/joystick.h>
89 # define JS_DATA_TYPE joystick
90 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
97 # if defined(__linux__)
98 # include <sys/ioctl.h>
99 # include <linux/joystick.h>
101 /* check the joystick driver version */
102 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
105 # else /* Not BSD or Linux */
109 * We'll put these values in and that should
110 * allow the code to at least compile when there is
111 * no support. The JS open routine should error out
112 * and shut off all the code downstream anyway and if
113 * the application doesn't use a joystick we'll be fine.
123 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
132 * BSD defines from "jsBSD.cxx" around lines 42-270
135 #if defined(__NetBSD__) || defined(__FreeBSD__)
138 # if defined(__NetBSD__)
140 * XXX Apparently another ugly hack which someone who knows BSD and USBHID needs to solve
142 # define HAVE_USBHID_H 1
143 # ifdef HAVE_USBHID_H
148 # elif defined(__FreeBSD__)
149 # include <libusbhid.h>
151 # include <dev/usb/usb.h>
152 # include <dev/usb/usbhid.h>
154 /* Compatibility with older usb.h revisions */
155 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
156 # define USB_MAX_DEVNAMES MAXDEVNAMES
160 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
161 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
162 struct os_specific_s {
166 /* The following structure members are specific to analog joysticks */
169 /* The following structure members are specific to USB joysticks */
170 struct hid_item *hids;
174 int axes_usage [ _JS_MAX_AXES ];
176 /* We keep button and axes state ourselves, as they might not be updated
177 * on every read of a USB device
180 float cache_axes [ _JS_MAX_AXES ];
183 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
184 # define USB_IDENT_OFFSET 2
186 # define USBDEV "/dev/usb"
187 # define UHIDDEV "/dev/uhid"
188 # define AJSDEV "/dev/joy"
192 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
193 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
194 * return the uhidN device name. We warn the user of this situation once.
196 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
198 struct usb_device_info di;
202 for (a = 1; a < USB_MAX_DEVICES; a++) {
204 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
206 for (i = 0; i < USB_MAX_DEVNAMES; i++)
207 if (di.udi_devnames[i][0] &&
208 strcmp(di.udi_devnames[i], dev) == 0) {
209 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
210 strcpy(cp, di.udi_vendor);
212 strcat(cp, di.udi_product);
213 strncpy(out, cp, outlen - 1);
222 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
227 static int protection_warned = 0;
229 for (i = 0; i < 16; i++) {
230 sprintf(buf, "%s%d", USBDEV, i);
231 f = open(buf, O_RDONLY);
233 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
237 } else if (errno == EACCES) {
238 if (!protection_warned) {
239 fprintf(stderr, "Can't open %s for read!\n",
241 protection_warned = 1;
248 static int fghJoystickInitializeHID(struct os_specific_s *os,
249 int *num_axes, int *num_buttons)
251 int size, is_joystick;
252 # ifdef HAVE_USBHID_H
259 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
261 fprintf( stderr, "error: %s: %s", os->fname, strerror( errno ) );
267 # ifdef HAVE_USBHID_H
268 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
270 /*** XXX {report_id} may not be the right variable? ***/
271 fprintf( stderr, "error: %s%d: %s",
272 UHIDDEV, report_id, strerror( errno ) );
276 size = hid_report_size( rd, hid_input, report_id );
278 size = hid_report_size( rd, 0, hid_input );
280 os->hid_data_buf = calloc( 1, size );
284 # ifdef HAVE_USBHID_H
285 d = hid_start_parse( rd, 1 << hid_input, report_id );
287 d = hid_start_parse( rd, 1 << hid_input );
289 while( hid_get_item( d, &h ) )
291 int usage, page, interesting_hid;
293 page = HID_PAGE( h.usage );
294 usage = HID_USAGE( h.usage );
296 /* This test is somewhat too simplistic, but this is how MicroSoft
297 * does, so I guess it works for all joysticks/game pads. */
298 is_joystick = is_joystick ||
299 ( h.kind == hid_collection &&
300 page == HUP_GENERIC_DESKTOP &&
301 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
303 if( h.kind != hid_input )
309 interesting_hid = TRUE;
310 if( page == HUP_GENERIC_DESKTOP )
321 if( *num_axes < _JS_MAX_AXES )
323 os->axes_usage[ *num_axes ] = usage;
328 /* Allocate two axes for a hat */
329 if( *num_axes + 1 < _JS_MAX_AXES )
331 os->axes_usage[ *num_axes ] = usage;
333 os->axes_usage[ *num_axes ] = usage;
338 interesting_hid = FALSE;
342 else if( page == HUP_BUTTON )
344 interesting_hid = ( usage > 0 ) &&
345 ( usage <= _JS_MAX_BUTTONS );
347 if( interesting_hid && usage - 1 > *num_buttons )
348 *num_buttons = usage - 1;
351 if( interesting_hid )
354 os->hids = calloc( 1, sizeof ( struct hid_item ) );
360 return os->hids != NULL;
366 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
367 * See "js.h" lines 80-178.
369 typedef struct tagSFG_Joystick SFG_Joystick;
370 struct tagSFG_Joystick
372 #if TARGET_HOST_MACINTOSH
373 #define ISP_NUM_AXIS 9
374 #define ISP_NUM_NEEDS 41
375 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
376 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
379 #if TARGET_HOST_MAC_OSX
380 IOHIDDeviceInterface ** hidDev;
381 IOHIDElementCookie buttonCookies[41];
382 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
383 long minReport[_JS_MAX_AXES],
384 maxReport[_JS_MAX_AXES];
387 #if TARGET_HOST_WIN32
394 #if TARGET_HOST_UNIX_X11
395 # if defined(__FreeBSD__) || defined(__NetBSD__)
396 struct os_specific_s *os;
402 float tmp_axes [ _JS_MAX_AXES ];
404 struct JS_DATA_TYPE js;
417 float dead_band[ _JS_MAX_AXES ];
418 float saturate [ _JS_MAX_AXES ];
419 float center [ _JS_MAX_AXES ];
420 float max [ _JS_MAX_AXES ];
421 float min [ _JS_MAX_AXES ];
425 * Functions associated with the "jsJoystick" class in PLIB
427 #if TARGET_HOST_MAC_OSX
428 #define K_NUM_DEVICES 32
430 io_object_t ioDevices[K_NUM_DEVICES];
432 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
433 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
435 void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
436 /* callback for CFArrayApply */
437 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
438 void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
440 void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
441 void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
442 void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
447 * The static joystick structure pointer
449 #define MAX_NUM_JOYSTICKS 2
450 static int fgNumberOfJoysticks = 0;
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 ( "%s", "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) * 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 ( "%s", "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 ( "%s", "error getting device entry parent");
896 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
897 if (rv != kIOReturnSuccess) {
898 fgWarning ( "%s", "error getting device entry parent 2");
903 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
904 &cfProperties, kCFAllocatorDefault, kNilOptions);
905 if (rv != kIOReturnSuccess || !cfProperties) {
906 fgWarning ( "%s", "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 assert(CFGetTypeID(element) == CFArrayGetTypeID());
928 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
929 CFArrayApplyFunction((CFArrayRef) element, range,
930 &fghJoystickElementEnumerator, joy );
933 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
935 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
936 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
938 long type, page, usage;
940 CFNumberGetValue((CFNumberRef)
941 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
942 kCFNumberLongType, &type);
945 case kIOHIDElementTypeInput_Misc:
946 case kIOHIDElementTypeInput_Axis:
947 case kIOHIDElementTypeInput_Button:
948 printf("got input element...");
949 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
950 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
952 if (page == kHIDPage_GenericDesktop) {
953 switch ( usage ) /* look at usage to determine function */
958 case kHIDUsage_GD_Rx:
959 case kHIDUsage_GD_Ry:
960 case kHIDUsage_GD_Rz:
961 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
963 fghJoystickAddAxisElement((CFDictionaryRef) element);
966 case kHIDUsage_GD_Hatswitch:
968 fghJoystickAddHatElement((CFDictionaryRef) element);
972 printf("input type element has weird usage (%x)\n", usage);
975 } else if (page == kHIDPage_Button) {
977 fghJoystickAddButtonElement((CFDictionaryRef) element);
979 printf("input type element has weird page (%x)\n", page);
982 case kIOHIDElementTypeCollection:
983 fghJoystickEnumerateElements (
984 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
993 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
995 long cookie, lmin, lmax;
996 int index = joy->num_axes++;
998 CFNumberGetValue ((CFNumberRef)
999 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1000 kCFNumberLongType, &cookie);
1002 axisCookies[index] = (IOHIDElementCookie) cookie;
1004 CFNumberGetValue ((CFNumberRef)
1005 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1006 kCFNumberLongType, &lmin);
1008 CFNumberGetValue ((CFNumberRef)
1009 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1010 kCFNumberLongType, &lmax);
1012 joy->min[index] = lmin;
1013 joy->max[index] = lmax;
1014 joy->dead_band[index] = 0.0;
1015 joy->saturate[index] = 1.0;
1016 joy->center[index] = (lmax + lmin) * 0.5;
1019 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1022 CFNumberGetValue ((CFNumberRef)
1023 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1024 kCFNumberLongType, &cookie);
1026 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1027 /* anything else for buttons? */
1030 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1032 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1033 /* do we map hats to axes or buttons? */
1037 #if TARGET_HOST_WIN32
1039 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1042 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1044 char buffer [ 256 ];
1046 char OEMKey [ 256 ];
1055 /* Open .. MediaResources\CurrentJoystickSettings */
1056 sprintf ( buffer, "%s\\%s\\%s",
1057 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1058 REGSTR_KEY_JOYCURR );
1060 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1062 if ( lr != ERROR_SUCCESS ) return 0;
1064 /* Get OEM Key name */
1065 dwcb = sizeof(OEMKey);
1067 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1068 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1070 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1071 RegCloseKey ( hKey );
1073 if ( lr != ERROR_SUCCESS ) return 0;
1075 /* Open OEM Key from ...MediaProperties */
1076 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1078 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1080 if ( lr != ERROR_SUCCESS ) return 0;
1085 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1087 RegCloseKey ( hKey );
1089 if ( lr != ERROR_SUCCESS ) return 0;
1096 static void fghJoystickOpen( SFG_Joystick* joy )
1099 #if TARGET_HOST_MACINTOSH
1102 #if TARGET_HOST_MAC_OSX
1105 IOCFPlugInInterface **plugin;
1107 HRESULT pluginResult;
1109 CFDictionaryRef props;
1110 CFTypeRef topLevelElement;
1112 #if TARGET_HOST_UNIX_X11
1113 # if defined(__FreeBSD__) || defined(__NetBSD__)
1123 /* Default values (for no joystick -- each conditional will reset the error flag) */
1125 joy->num_axes = joy->num_buttons = 0;
1126 joy->name [0] = '\0';
1128 #if TARGET_HOST_MACINTOSH
1130 * XXX FIXME: get joystick name in Mac
1133 err = ISpStartup ();
1137 #define ISP_CHECK_ERR(x) if ( x != noErr ) { joy->error = GL_TRUE; return; }
1139 joy->error = GL_TRUE;
1141 /* initialize the needs structure */
1142 ISpNeed temp_isp_needs[isp_num_needs] =
1144 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1145 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1146 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1147 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1148 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1149 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1150 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1151 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1152 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1154 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1155 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1156 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1157 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1158 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1159 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1160 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1161 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 memcpy ( joy->isp_needs, temp_isp_needs, sizeof(temp_isp_needs) );
1191 /* next two calls allow keyboard and mouse to emulate other input
1192 * devices (gamepads, joysticks, etc)
1195 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1199 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1203 err = ISpElement_NewVirtualFromNeeds ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 0 );
1206 err = ISpInit ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 'freeglut', nil, 0, 128, 0 );
1209 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1210 joy->num_axes = joy->isp_num_axis;
1212 for ( i = 0; i < joy->num_axes; i++ )
1214 joy->dead_band [ i ] = 0;
1215 joy->saturate [ i ] = 1;
1216 joy->center [ i ] = kISpAxisMiddle;
1217 joy->max [ i ] = kISpAxisMaximum;
1218 joy->min [ i ] = kISpAxisMinimum;
1221 joy->error = GL_FALSE;
1224 joy->num_buttons = joy->num_axes = 0;
1227 #if TARGET_HOST_MAC_OSX
1228 if (joy->id >= numDevices) {
1229 fgWarning ( "%s", "device index out of range in fgJoystickOpen()");
1233 /* create device interface */
1234 rv = IOCreatePlugInInterfaceForService ( ioDevices[joy->id],
1235 kIOHIDDeviceUserClientTypeID,
1236 kIOCFPlugInInterfaceID,
1239 if (rv != kIOReturnSuccess) {
1240 fgWarning ( "%s", "error creating plugin for io device");
1244 pluginResult = (*plugin)->QueryInterface ( plugin,
1245 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), &(LPVOID) joy->hidDev );
1247 if ( pluginResult != S_OK )
1248 fgWarning ( "%s", "QI-ing IO plugin to HID Device interface failed");
1250 (*plugin)->Release(plugin); /* don't leak a ref */
1251 if (joy->hidDev == NULL) return;
1253 /* store the interface in this instance */
1254 rv = (*(joy->hidDev))->open(joy->hidDev, 0);
1255 if (rv != kIOReturnSuccess) {
1256 fgWarning ( "%s", "error opening device interface");
1260 props = getCFProperties(ioDevices[joy->id]);
1262 /* recursively enumerate all the bits */
1263 CFTypeRef topLevelElement =
1264 CFDictionaryGetValue ( props, CFSTR ( kIOHIDElementKey ) );
1265 enumerateElements ( topLevelElement );
1267 CFRelease ( props );
1270 #if TARGET_HOST_WIN32
1271 joy->js.dwFlags = JOY_RETURNALL;
1272 joy->js.dwSize = sizeof( joy->js );
1274 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1277 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1280 if ( joy->jsCaps.wNumAxes == 0 )
1283 joy->error = GL_TRUE;
1287 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1288 * at least for USB. Try to get the real name from the registry.
1290 if ( ! fghJoystickGetOEMProductName ( joy, joy->name, sizeof(joy->name) ) )
1292 fgWarning ( "%s", "JS: Failed to read joystick name from registry" );
1293 strncpy ( joy->name, joy->jsCaps.szPname, sizeof(joy->name) );
1296 /* Windows joystick drivers may provide any combination of
1297 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1299 if ( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1301 joy->num_axes = _JS_MAX_AXES;
1302 joy->min [ 7 ] = -1.0; joy->max [ 7 ] = 1.0; /* POV Y */
1303 joy->min [ 6 ] = -1.0; joy->max [ 6 ] = 1.0; /* POV X */
1308 joy->min[ 5 ] = (float) joy->jsCaps.wVmin;
1309 joy->max[ 5 ] = (float) joy->jsCaps.wVmax;
1310 joy->min[ 4 ] = (float) joy->jsCaps.wUmin;
1311 joy->max[ 4 ] = (float) joy->jsCaps.wUmax;
1312 joy->min[ 3 ] = (float) joy->jsCaps.wRmin;
1313 joy->max[ 3 ] = (float) joy->jsCaps.wRmax;
1314 joy->min[ 2 ] = (float) joy->jsCaps.wZmin;
1315 joy->max[ 2 ] = (float) joy->jsCaps.wZmax;
1316 joy->min[ 1 ] = (float) joy->jsCaps.wYmin;
1317 joy->max[ 1 ] = (float) joy->jsCaps.wYmax;
1318 joy->min[ 0 ] = (float) joy->jsCaps.wXmin;
1319 joy->max[ 0 ] = (float) joy->jsCaps.wXmax;
1323 * Guess all the rest judging on the axes extremals
1325 for( i = 0; i < joy->num_axes; i++ )
1327 joy->center [ i ] = (joy->max[i] + joy->min[i]) * 0.5f;
1328 joy->dead_band[ i ] = 0.0f;
1329 joy->saturate [ i ] = 1.0f;
1333 #if TARGET_HOST_UNIX_X11
1334 #if defined(__FreeBSD__) || defined(__NetBSD__)
1335 for ( i = 0; i < _JS_MAX_AXES; i++ )
1336 joy->os->cache_axes [ i ] = 0.0f;
1338 joy->os->cache_buttons = 0;
1340 joy->os->fd = open ( joy->os->fname, O_RDONLY | O_NONBLOCK);
1342 if ( joy->os->fd < 0 && errno == EACCES)
1343 fgWarning ( "%s exists but is not readable by you\n", joy->os->fname );
1345 joy->error = ( joy->os->fd < 0 );
1351 joy->num_buttons = 0;
1352 if ( joy->os->is_analog )
1355 char joyfname [ 1024 ];
1356 int noargs, in_no_axes;
1358 float axes [ _JS_MAX_AXES ];
1359 int buttons [ _JS_MAX_AXES ];
1362 joy->num_buttons = 32;
1364 fghJoystickRawRead ( joy, buttons, axes );
1365 joy->error = axes[0] < -1000000000.0f;
1369 sprintf( joyfname, "%s/.joy%drc", getenv ( "HOME" ), joy->id );
1371 joyfile = fopen ( joyfname, "r" );
1372 joy->error = ( joyfile == NULL );
1376 noargs = fscanf ( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1377 &joy->min [ 0 ], &joy->center [ 0 ], &joy->max [ 0 ],
1378 &joy->min [ 1 ], &joy->center [ 1 ], &joy->max [ 1 ] );
1379 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1384 for ( i = 0; i < _JS_MAX_AXES; i++ )
1386 joy->dead_band [ i ] = 0.0f;
1387 joy->saturate [ i ] = 1.0f;
1390 return; /* End of analog code */
1394 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1395 &joy->num_buttons ) )
1397 close ( joy->os->fd );
1398 joy->error = GL_TRUE;
1402 cp = strrchr(joy->os->fname, '/');
1404 if ( fghJoystickFindUSBdev ( &cp[1], joy->name, sizeof(joy->name) ) == 0 )
1405 strcpy ( joy->name, &cp[1] );
1408 if ( joy->num_axes > _JS_MAX_AXES )
1409 joy->num_axes = _JS_MAX_AXES;
1411 for ( i = 0; i < _JS_MAX_AXES; i++ )
1413 /* We really should get this from the HID, but that data seems
1414 * to be quite unreliable for analog-to-USB converters. Punt for
1417 if ( joy->os->axes_usage [ i ] == HUG_HAT_SWITCH )
1419 joy->max [ i ] = 1.0f;
1420 joy->center [ i ] = 0.0f;
1421 joy->min [ i ] = -1.0f;
1425 joy->max [ i ] = 255.0f;
1426 joy->center [ i ] = 127.0f;
1427 joy->min [ i ] = 0.0f;
1430 joy->dead_band [ i ] = 0.0f;
1431 joy->saturate [ i ] = 1.0f;
1436 #if defined(__linux__)
1438 * Default for older Linux systems.
1441 joy->num_buttons = 32;
1444 for( i = 0; i < _JS_MAX_AXES; i++ )
1445 joy->tmp_axes[ i ] = 0.0f;
1447 joy->tmp_buttons = 0;
1450 joy->fd = open( joy->fname, O_RDONLY );
1452 joy->error = (joy->fd < 0);
1458 * Set the correct number of axes for the linux driver
1461 /* Melchior Franz's fixes for big-endian Linuxes since writing
1462 * to the upper byte of an uninitialized word doesn't work.
1465 ioctl ( joy->fd, JSIOCGAXES , &u );
1467 ioctl ( joy->fd, JSIOCGBUTTONS, &u );
1468 joy->num_buttons = u;
1469 ioctl ( joy->fd, JSIOCGNAME ( sizeof(joy->name) ), joy->name );
1470 fcntl ( joy->fd, F_SETFL , O_NONBLOCK );
1474 * The Linux driver seems to return 512 for all axes
1475 * when no stick is present - but there is a chance
1476 * that could happen by accident - so it's gotta happen
1477 * on both axes for at least 100 attempts.
1479 * PWO: shouldn't be that done somehow wiser on the kernel level?
1486 fghJoystickRawRead( joy, NULL, joy->center );
1488 } while( !joy->error &&
1490 joy->center[ 0 ] == 512.0f &&
1491 joy->center[ 1 ] == 512.0f );
1493 if ( counter >= 100 )
1494 joy->error = GL_TRUE;
1497 for ( i = 0; i < _JS_MAX_AXES; i++ )
1500 joy->max [ i ] = 32767.0f;
1501 joy->center[ i ] = 0.0f;
1502 joy->min [ i ] = -32767.0f;
1504 joy->max[ i ] = joy->center[ i ] * 2.0f;
1505 joy->min[ i ] = 0.0f;
1507 joy->dead_band[ i ] = 0.0f;
1508 joy->saturate [ i ] = 1.0f;
1515 * This function replaces the constructor method in the JS library.
1517 void fgJoystickInit( int ident )
1519 if ( ident >= MAX_NUM_JOYSTICKS )
1520 fgError( "Too large a joystick number" );
1522 if( fgJoystick[ident] )
1523 fgError( "illegal attempt to initialize joystick device" );
1525 fgJoystick[ident] = ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1528 fgJoystick[ident]->num_axes = fgJoystick[ident]->num_buttons = 0;
1529 fgJoystick[ident]->error = GL_TRUE;
1531 #if TARGET_HOST_MACINTOSH
1532 fgJoystick[ident]->id = ident;
1533 sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident ); /* FIXME */
1534 fgJoystick[ident]->error = GL_FALSE;
1537 #if TARGET_HOST_MAC_OSX
1538 fgJoystick[ident]->id = ident;
1539 fgJoystick[ident]->error = GL_FALSE;
1540 fgJoystick[ident]->num_axes = 0;
1541 fgJoystick[ident]->num_buttons = 0;
1543 if (numDevices < 0) {
1544 /* do first-time init (since we can't over-ride jsInit, hmm */
1547 mach_port_t masterPort;
1548 IOReturn rv = IOMasterPort ( bootstrap_port, &masterPort );
1549 if ( rv != kIOReturnSuccess ) {
1550 fgWarning ( "%s", "error getting master Mach port");
1553 fghJoystickFindDevices ( masterPort );
1556 if ( ident >= numDevices ) {
1557 fgJoystick[ident]->error = GL_TRUE;
1561 /* get the name now too */
1562 CFDictionaryRef properties = getCFProperties(ioDevices[ident]);
1563 CFTypeRef ref = CFDictionaryGetValue (properties, CFSTR(kIOHIDProductKey));
1565 ref = CFDictionaryGetValue (properties, CFSTR("USB Product Name"));
1567 if (!ref || !CFStringGetCString ((CFStringRef) ref, name, 128, CFStringGetSystemEncoding ())) {
1568 fgWarning ( "%s", "error getting device name");
1573 #if TARGET_HOST_WIN32
1577 fgJoystick[ident]->js_id = JOYSTICKID1;
1578 fgJoystick[ident]->error = GL_FALSE;
1581 fgJoystick[ident]->js_id = JOYSTICKID2;
1582 fgJoystick[ident]->error = GL_FALSE;
1585 fgJoystick[ident]->num_axes = 0;
1586 fgJoystick[ident]->error = GL_TRUE;
1591 #if TARGET_HOST_UNIX_X11
1592 # if defined(__FreeBSD__) || defined(__NetBSD__)
1593 fgJoystick[ident]->id = ident;
1594 fgJoystick[ident]->error = GL_FALSE;
1596 fgJoystick[ident]->os = calloc (1, sizeof (struct os_specific_s));
1597 memset ( fgJoystick[ident]->os, 0, sizeof(struct os_specific_s) );
1598 if (ident < USB_IDENT_OFFSET)
1599 fgJoystick[ident]->os->is_analog = 1;
1600 if (fgJoystick[ident]->os->is_analog)
1601 sprintf ( fgJoystick[ident]->os->fname, "%s%d", AJSDEV, ident );
1603 sprintf ( fgJoystick[ident]->os->fname, "%s%d", UHIDDEV, ident - USB_IDENT_OFFSET );
1604 # elif defined(__linux__)
1605 fgJoystick[ident]->id = ident;
1606 fgJoystick[ident]->error = GL_FALSE;
1608 sprintf ( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1610 if ( access ( fgJoystick[ident]->fname, F_OK ) != 0 )
1611 sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident );
1615 fghJoystickOpen ( fgJoystick[ident] );
1621 void fgJoystickClose( void )
1624 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1626 if ( fgJoystick[ident] )
1629 #if TARGET_HOST_MACINTOSH
1635 #if TARGET_HOST_MAC_OSX
1636 (*(fgJoystick[ident]->hidDev))->close(fgJoystick[ident]->hidDev);
1639 #if TARGET_HOST_WIN32
1640 /* Do nothing special */
1643 #if TARGET_HOST_UNIX_X11
1644 #if defined(__FreeBSD__) || defined(__NetBSD__)
1645 if ( fgJoystick[ident]->os )
1647 if ( ! fgJoystick[ident]->error )
1648 close ( fgJoystick[ident]->os->fd );
1650 if ( fgJoystick[ident]->os->hids )
1651 free (fgJoystick[ident]->os->hids);
1652 if ( fgJoystick[ident]->os->hid_data_buf )
1653 free (fgJoystick[ident]->os->hid_data_buf);
1655 free (fgJoystick[ident]->os);
1659 if( ! fgJoystick[ident]->error )
1660 close( fgJoystick[ident]->fd );
1663 free( fgJoystick[ident] );
1664 fgJoystick[ident] = NULL; /* show joystick has been deinitialized */
1670 * Polls the joystick and executes the joystick callback hooked to the
1671 * window specified in the function's parameter:
1673 void fgJoystickPollWindow( SFG_Window* window )
1675 float axes[ _JS_MAX_AXES ];
1679 freeglut_return_if_fail( window );
1680 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1682 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1684 if ( fgJoystick[ident] )
1686 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1688 if ( !fgJoystick[ident]->error )
1689 INVOKE_WCB( *window, Joystick,
1691 (int) (axes[ 0 ] * 1000.0f ),
1692 (int) (axes[ 1 ] * 1000.0f ),
1693 (int) (axes[ 2 ] * 1000.0f ) )
1700 * PWO: These jsJoystick class methods have not been implemented.
1701 * We might consider adding such functions to freeglut-2.0.
1703 int getNumAxes ( int ident )
1704 { return fgJoystick[ident]->num_axes; }
1705 int notWorking ( int ident )
1706 { return fgJoystick[ident]->error; }
1708 float getDeadBand ( int ident, int axis )
1709 { return fgJoystick[ident]->dead_band [ axis ]; }
1710 void setDeadBand ( int ident, int axis, float db )
1711 { fgJoystick[ident]->dead_band [ axis ] = db; }
1713 float getSaturation ( int ident, int axis )
1714 { return fgJoystick[ident]->saturate [ axis ]; }
1715 void setSaturation ( int ident, int axis, float st )
1716 { fgJoystick[ident]->saturate [ axis ] = st; }
1718 void setMinRange ( int ident, float *axes )
1719 { memcpy ( fgJoystick[ident]->min , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1720 void setMaxRange ( int ident, float *axes )
1721 { memcpy ( fgJoystick[ident]->max , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1722 void setCenter ( int ident, float *axes )
1723 { memcpy ( fgJoystick[ident]->center, axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1725 void getMinRange ( int ident, float *axes )
1726 { memcpy ( axes, fgJoystick[ident]->min , fgJoystick[ident]->num_axes * sizeof(float) ); }
1727 void getMaxRange ( int ident, float *axes )
1728 { memcpy ( axes, fgJoystick[ident]->max , fgJoystick[ident]->num_axes * sizeof(float) ); }
1729 void getCenter ( int ident, float *axes )
1730 { memcpy ( axes, fgJoystick[ident]->center, fgJoystick[ident]->num_axes * sizeof(float) ); }
1732 /*** END OF FILE ***/