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
1041 # pragma comment (lib, "advapi32.lib")
1043 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1045 char buffer [ 256 ];
1047 char OEMKey [ 256 ];
1056 /* Open .. MediaResources\CurrentJoystickSettings */
1057 sprintf ( buffer, "%s\\%s\\%s",
1058 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1059 REGSTR_KEY_JOYCURR );
1061 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1063 if ( lr != ERROR_SUCCESS ) return 0;
1065 /* Get OEM Key name */
1066 dwcb = sizeof(OEMKey);
1068 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1069 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1071 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1072 RegCloseKey ( hKey );
1074 if ( lr != ERROR_SUCCESS ) return 0;
1076 /* Open OEM Key from ...MediaProperties */
1077 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1079 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1081 if ( lr != ERROR_SUCCESS ) return 0;
1086 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1088 RegCloseKey ( hKey );
1090 if ( lr != ERROR_SUCCESS ) return 0;
1097 static void fghJoystickOpen( SFG_Joystick* joy )
1100 #if TARGET_HOST_MACINTOSH
1103 #if TARGET_HOST_MAC_OSX
1106 IOCFPlugInInterface **plugin;
1108 HRESULT pluginResult;
1110 CFDictionaryRef props;
1111 CFTypeRef topLevelElement;
1113 #if TARGET_HOST_UNIX_X11
1114 # if defined(__FreeBSD__) || defined(__NetBSD__)
1124 /* Default values (for no joystick -- each conditional will reset the error flag) */
1126 joy->num_axes = joy->num_buttons = 0;
1127 joy->name [0] = '\0';
1129 #if TARGET_HOST_MACINTOSH
1131 * XXX FIXME: get joystick name in Mac
1134 err = ISpStartup ();
1138 #define ISP_CHECK_ERR(x) if ( x != noErr ) { joy->error = GL_TRUE; return; }
1140 joy->error = GL_TRUE;
1142 /* initialize the needs structure */
1143 ISpNeed temp_isp_needs[isp_num_needs] =
1145 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1146 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1147 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1148 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1149 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1150 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1151 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1152 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1153 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1155 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1156 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1157 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1158 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1159 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1160 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1161 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189 memcpy ( joy->isp_needs, temp_isp_needs, sizeof(temp_isp_needs) );
1192 /* next two calls allow keyboard and mouse to emulate other input
1193 * devices (gamepads, joysticks, etc)
1196 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1200 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1204 err = ISpElement_NewVirtualFromNeeds ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 0 );
1207 err = ISpInit ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 'freeglut', nil, 0, 128, 0 );
1210 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1211 joy->num_axes = joy->isp_num_axis;
1213 for ( i = 0; i < joy->num_axes; i++ )
1215 joy->dead_band [ i ] = 0;
1216 joy->saturate [ i ] = 1;
1217 joy->center [ i ] = kISpAxisMiddle;
1218 joy->max [ i ] = kISpAxisMaximum;
1219 joy->min [ i ] = kISpAxisMinimum;
1222 joy->error = GL_FALSE;
1225 joy->num_buttons = joy->num_axes = 0;
1228 #if TARGET_HOST_MAC_OSX
1229 if (joy->id >= numDevices) {
1230 fgWarning ( "%s", "device index out of range in fgJoystickOpen()");
1234 /* create device interface */
1235 rv = IOCreatePlugInInterfaceForService ( ioDevices[joy->id],
1236 kIOHIDDeviceUserClientTypeID,
1237 kIOCFPlugInInterfaceID,
1240 if (rv != kIOReturnSuccess) {
1241 fgWarning ( "%s", "error creating plugin for io device");
1245 pluginResult = (*plugin)->QueryInterface ( plugin,
1246 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), &(LPVOID) joy->hidDev );
1248 if ( pluginResult != S_OK )
1249 fgWarning ( "%s", "QI-ing IO plugin to HID Device interface failed");
1251 (*plugin)->Release(plugin); /* don't leak a ref */
1252 if (joy->hidDev == NULL) return;
1254 /* store the interface in this instance */
1255 rv = (*(joy->hidDev))->open(joy->hidDev, 0);
1256 if (rv != kIOReturnSuccess) {
1257 fgWarning ( "%s", "error opening device interface");
1261 props = getCFProperties(ioDevices[joy->id]);
1263 /* recursively enumerate all the bits */
1264 CFTypeRef topLevelElement =
1265 CFDictionaryGetValue ( props, CFSTR ( kIOHIDElementKey ) );
1266 enumerateElements ( topLevelElement );
1268 CFRelease ( props );
1271 #if TARGET_HOST_WIN32
1272 joy->js.dwFlags = JOY_RETURNALL;
1273 joy->js.dwSize = sizeof( joy->js );
1275 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1278 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1281 if ( joy->jsCaps.wNumAxes == 0 )
1284 joy->error = GL_TRUE;
1288 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1289 * at least for USB. Try to get the real name from the registry.
1291 if ( ! fghJoystickGetOEMProductName ( joy, joy->name, sizeof(joy->name) ) )
1293 fgWarning ( "%s", "JS: Failed to read joystick name from registry" );
1294 strncpy ( joy->name, joy->jsCaps.szPname, sizeof(joy->name) );
1297 /* Windows joystick drivers may provide any combination of
1298 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1300 if ( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1302 joy->num_axes = _JS_MAX_AXES;
1303 joy->min [ 7 ] = -1.0; joy->max [ 7 ] = 1.0; /* POV Y */
1304 joy->min [ 6 ] = -1.0; joy->max [ 6 ] = 1.0; /* POV X */
1309 joy->min[ 5 ] = (float) joy->jsCaps.wVmin;
1310 joy->max[ 5 ] = (float) joy->jsCaps.wVmax;
1311 joy->min[ 4 ] = (float) joy->jsCaps.wUmin;
1312 joy->max[ 4 ] = (float) joy->jsCaps.wUmax;
1313 joy->min[ 3 ] = (float) joy->jsCaps.wRmin;
1314 joy->max[ 3 ] = (float) joy->jsCaps.wRmax;
1315 joy->min[ 2 ] = (float) joy->jsCaps.wZmin;
1316 joy->max[ 2 ] = (float) joy->jsCaps.wZmax;
1317 joy->min[ 1 ] = (float) joy->jsCaps.wYmin;
1318 joy->max[ 1 ] = (float) joy->jsCaps.wYmax;
1319 joy->min[ 0 ] = (float) joy->jsCaps.wXmin;
1320 joy->max[ 0 ] = (float) joy->jsCaps.wXmax;
1324 * Guess all the rest judging on the axes extremals
1326 for( i = 0; i < joy->num_axes; i++ )
1328 joy->center [ i ] = (joy->max[i] + joy->min[i]) * 0.5f;
1329 joy->dead_band[ i ] = 0.0f;
1330 joy->saturate [ i ] = 1.0f;
1334 #if TARGET_HOST_UNIX_X11
1335 #if defined(__FreeBSD__) || defined(__NetBSD__)
1336 for ( i = 0; i < _JS_MAX_AXES; i++ )
1337 joy->os->cache_axes [ i ] = 0.0f;
1339 joy->os->cache_buttons = 0;
1341 joy->os->fd = open ( joy->os->fname, O_RDONLY | O_NONBLOCK);
1343 if ( joy->os->fd < 0 && errno == EACCES)
1344 fgWarning ( "%s exists but is not readable by you\n", joy->os->fname );
1346 joy->error = ( joy->os->fd < 0 );
1352 joy->num_buttons = 0;
1353 if ( joy->os->is_analog )
1356 char joyfname [ 1024 ];
1357 int noargs, in_no_axes;
1359 float axes [ _JS_MAX_AXES ];
1360 int buttons [ _JS_MAX_AXES ];
1363 joy->num_buttons = 32;
1365 fghJoystickRawRead ( joy, buttons, axes );
1366 joy->error = axes[0] < -1000000000.0f;
1370 sprintf( joyfname, "%s/.joy%drc", getenv ( "HOME" ), joy->id );
1372 joyfile = fopen ( joyfname, "r" );
1373 joy->error = ( joyfile == NULL );
1377 noargs = fscanf ( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1378 &joy->min [ 0 ], &joy->center [ 0 ], &joy->max [ 0 ],
1379 &joy->min [ 1 ], &joy->center [ 1 ], &joy->max [ 1 ] );
1380 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1385 for ( i = 0; i < _JS_MAX_AXES; i++ )
1387 joy->dead_band [ i ] = 0.0f;
1388 joy->saturate [ i ] = 1.0f;
1391 return; /* End of analog code */
1395 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1396 &joy->num_buttons ) )
1398 close ( joy->os->fd );
1399 joy->error = GL_TRUE;
1403 cp = strrchr(joy->os->fname, '/');
1405 if ( fghJoystickFindUSBdev ( &cp[1], joy->name, sizeof(joy->name) ) == 0 )
1406 strcpy ( joy->name, &cp[1] );
1409 if ( joy->num_axes > _JS_MAX_AXES )
1410 joy->num_axes = _JS_MAX_AXES;
1412 for ( i = 0; i < _JS_MAX_AXES; i++ )
1414 /* We really should get this from the HID, but that data seems
1415 * to be quite unreliable for analog-to-USB converters. Punt for
1418 if ( joy->os->axes_usage [ i ] == HUG_HAT_SWITCH )
1420 joy->max [ i ] = 1.0f;
1421 joy->center [ i ] = 0.0f;
1422 joy->min [ i ] = -1.0f;
1426 joy->max [ i ] = 255.0f;
1427 joy->center [ i ] = 127.0f;
1428 joy->min [ i ] = 0.0f;
1431 joy->dead_band [ i ] = 0.0f;
1432 joy->saturate [ i ] = 1.0f;
1437 #if defined(__linux__)
1439 * Default for older Linux systems.
1442 joy->num_buttons = 32;
1445 for( i = 0; i < _JS_MAX_AXES; i++ )
1446 joy->tmp_axes[ i ] = 0.0f;
1448 joy->tmp_buttons = 0;
1451 joy->fd = open( joy->fname, O_RDONLY );
1453 joy->error = (joy->fd < 0);
1459 * Set the correct number of axes for the linux driver
1462 /* Melchior Franz's fixes for big-endian Linuxes since writing
1463 * to the upper byte of an uninitialized word doesn't work.
1466 ioctl ( joy->fd, JSIOCGAXES , &u );
1468 ioctl ( joy->fd, JSIOCGBUTTONS, &u );
1469 joy->num_buttons = u;
1470 ioctl ( joy->fd, JSIOCGNAME ( sizeof(joy->name) ), joy->name );
1471 fcntl ( joy->fd, F_SETFL , O_NONBLOCK );
1475 * The Linux driver seems to return 512 for all axes
1476 * when no stick is present - but there is a chance
1477 * that could happen by accident - so it's gotta happen
1478 * on both axes for at least 100 attempts.
1480 * PWO: shouldn't be that done somehow wiser on the kernel level?
1487 fghJoystickRawRead( joy, NULL, joy->center );
1489 } while( !joy->error &&
1491 joy->center[ 0 ] == 512.0f &&
1492 joy->center[ 1 ] == 512.0f );
1494 if ( counter >= 100 )
1495 joy->error = GL_TRUE;
1498 for ( i = 0; i < _JS_MAX_AXES; i++ )
1501 joy->max [ i ] = 32767.0f;
1502 joy->center[ i ] = 0.0f;
1503 joy->min [ i ] = -32767.0f;
1505 joy->max[ i ] = joy->center[ i ] * 2.0f;
1506 joy->min[ i ] = 0.0f;
1508 joy->dead_band[ i ] = 0.0f;
1509 joy->saturate [ i ] = 1.0f;
1516 * This function replaces the constructor method in the JS library.
1518 void fgJoystickInit( int ident )
1520 if ( ident >= MAX_NUM_JOYSTICKS )
1521 fgError( "Too large a joystick number" );
1523 if( fgJoystick[ident] )
1524 fgError( "illegal attempt to initialize joystick device" );
1526 fgJoystick[ident] = ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1529 fgJoystick[ident]->num_axes = fgJoystick[ident]->num_buttons = 0;
1530 fgJoystick[ident]->error = GL_TRUE;
1532 #if TARGET_HOST_MACINTOSH
1533 fgJoystick[ident]->id = ident;
1534 sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident ); /* FIXME */
1535 fgJoystick[ident]->error = GL_FALSE;
1538 #if TARGET_HOST_MAC_OSX
1539 fgJoystick[ident]->id = ident;
1540 fgJoystick[ident]->error = GL_FALSE;
1541 fgJoystick[ident]->num_axes = 0;
1542 fgJoystick[ident]->num_buttons = 0;
1544 if (numDevices < 0) {
1545 /* do first-time init (since we can't over-ride jsInit, hmm */
1548 mach_port_t masterPort;
1549 IOReturn rv = IOMasterPort ( bootstrap_port, &masterPort );
1550 if ( rv != kIOReturnSuccess ) {
1551 fgWarning ( "%s", "error getting master Mach port");
1554 fghJoystickFindDevices ( masterPort );
1557 if ( ident >= numDevices ) {
1558 fgJoystick[ident]->error = GL_TRUE;
1562 /* get the name now too */
1563 CFDictionaryRef properties = getCFProperties(ioDevices[ident]);
1564 CFTypeRef ref = CFDictionaryGetValue (properties, CFSTR(kIOHIDProductKey));
1566 ref = CFDictionaryGetValue (properties, CFSTR("USB Product Name"));
1568 if (!ref || !CFStringGetCString ((CFStringRef) ref, name, 128, CFStringGetSystemEncoding ())) {
1569 fgWarning ( "%s", "error getting device name");
1574 #if TARGET_HOST_WIN32
1578 fgJoystick[ident]->js_id = JOYSTICKID1;
1579 fgJoystick[ident]->error = GL_FALSE;
1582 fgJoystick[ident]->js_id = JOYSTICKID2;
1583 fgJoystick[ident]->error = GL_FALSE;
1586 fgJoystick[ident]->num_axes = 0;
1587 fgJoystick[ident]->error = GL_TRUE;
1592 #if TARGET_HOST_UNIX_X11
1593 # if defined(__FreeBSD__) || defined(__NetBSD__)
1594 fgJoystick[ident]->id = ident;
1595 fgJoystick[ident]->error = GL_FALSE;
1597 fgJoystick[ident]->os = calloc (1, sizeof (struct os_specific_s));
1598 memset ( fgJoystick[ident]->os, 0, sizeof(struct os_specific_s) );
1599 if (ident < USB_IDENT_OFFSET)
1600 fgJoystick[ident]->os->is_analog = 1;
1601 if (fgJoystick[ident]->os->is_analog)
1602 sprintf ( fgJoystick[ident]->os->fname, "%s%d", AJSDEV, ident );
1604 sprintf ( fgJoystick[ident]->os->fname, "%s%d", UHIDDEV, ident - USB_IDENT_OFFSET );
1605 # elif defined(__linux__)
1606 fgJoystick[ident]->id = ident;
1607 fgJoystick[ident]->error = GL_FALSE;
1609 sprintf ( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1611 if ( access ( fgJoystick[ident]->fname, F_OK ) != 0 )
1612 sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident );
1616 fghJoystickOpen ( fgJoystick[ident] );
1622 void fgJoystickClose( void )
1625 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1627 if ( fgJoystick[ident] )
1630 #if TARGET_HOST_MACINTOSH
1636 #if TARGET_HOST_MAC_OSX
1637 (*(fgJoystick[ident]->hidDev))->close(fgJoystick[ident]->hidDev);
1640 #if TARGET_HOST_WIN32
1641 /* Do nothing special */
1644 #if TARGET_HOST_UNIX_X11
1645 #if defined(__FreeBSD__) || defined(__NetBSD__)
1646 if ( fgJoystick[ident]->os )
1648 if ( ! fgJoystick[ident]->error )
1649 close ( fgJoystick[ident]->os->fd );
1651 if ( fgJoystick[ident]->os->hids )
1652 free (fgJoystick[ident]->os->hids);
1653 if ( fgJoystick[ident]->os->hid_data_buf )
1654 free (fgJoystick[ident]->os->hid_data_buf);
1656 free (fgJoystick[ident]->os);
1660 if( ! fgJoystick[ident]->error )
1661 close( fgJoystick[ident]->fd );
1664 free( fgJoystick[ident] );
1665 fgJoystick[ident] = NULL; /* show joystick has been deinitialized */
1671 * Polls the joystick and executes the joystick callback hooked to the
1672 * window specified in the function's parameter:
1674 void fgJoystickPollWindow( SFG_Window* window )
1676 float axes[ _JS_MAX_AXES ];
1680 freeglut_return_if_fail( window );
1681 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1683 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1685 if ( fgJoystick[ident] )
1687 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1689 if ( !fgJoystick[ident]->error )
1690 INVOKE_WCB( *window, Joystick,
1692 (int) (axes[ 0 ] * 1000.0f ),
1693 (int) (axes[ 1 ] * 1000.0f ),
1694 (int) (axes[ 2 ] * 1000.0f ) )
1701 * PWO: These jsJoystick class methods have not been implemented.
1702 * We might consider adding such functions to freeglut-2.0.
1704 int getNumAxes ( int ident )
1705 { return fgJoystick[ident]->num_axes; }
1706 int notWorking ( int ident )
1707 { return fgJoystick[ident]->error; }
1709 float getDeadBand ( int ident, int axis )
1710 { return fgJoystick[ident]->dead_band [ axis ]; }
1711 void setDeadBand ( int ident, int axis, float db )
1712 { fgJoystick[ident]->dead_band [ axis ] = db; }
1714 float getSaturation ( int ident, int axis )
1715 { return fgJoystick[ident]->saturate [ axis ]; }
1716 void setSaturation ( int ident, int axis, float st )
1717 { fgJoystick[ident]->saturate [ axis ] = st; }
1719 void setMinRange ( int ident, float *axes )
1720 { memcpy ( fgJoystick[ident]->min , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1721 void setMaxRange ( int ident, float *axes )
1722 { memcpy ( fgJoystick[ident]->max , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1723 void setCenter ( int ident, float *axes )
1724 { memcpy ( fgJoystick[ident]->center, axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1726 void getMinRange ( int ident, float *axes )
1727 { memcpy ( axes, fgJoystick[ident]->min , fgJoystick[ident]->num_axes * sizeof(float) ); }
1728 void getMaxRange ( int ident, float *axes )
1729 { memcpy ( axes, fgJoystick[ident]->max , fgJoystick[ident]->num_axes * sizeof(float) ); }
1730 void getCenter ( int ident, float *axes )
1731 { memcpy ( axes, fgJoystick[ident]->center, fgJoystick[ident]->num_axes * sizeof(float) ); }
1733 /*** END OF FILE ***/