4 * Joystick handling code
6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7 * Written by Steve Baker, <sjbaker1@airmail.net>
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
23 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu>
30 * Redone by John Fay 2/4/04 with another look from the PLIB "js" library.
31 * Many thanks for Steve Baker for permission to pull from that library.
34 #include <GL/freeglut.h>
35 #include "freeglut_internal.h"
37 # include <sys/param.h>
41 * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
44 #define _JS_MAX_BUTTONS 32
47 #if TARGET_HOST_MACINTOSH
48 # define _JS_MAX_AXES 9
49 # include <InputSprocket.h>
52 #if TARGET_HOST_MAC_OSX
53 # define _JS_MAX_AXES 16
54 # include <mach/mach.h>
55 # include <IOKit/IOkitLib.h>
56 # include <IOKit/hid/IOHIDLib.h>
60 # define _JS_MAX_AXES 8
62 # include <mmsystem.h>
67 #if TARGET_HOST_UNIX_X11
68 # define _JS_MAX_AXES 16
70 # include <sys/ioctl.h>
76 # if defined(__FreeBSD__) || defined(__NetBSD__)
77 /* XXX The below hack is done until freeglut's autoconf is updated. */
78 # define HAVE_USB_JS 1
80 # if defined(__FreeBSD__) && __FreeBSD_version >= 500000
81 # include <sys/joystick.h>
84 * XXX NetBSD/amd64 systems may find that they have to steal the
85 * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
86 * XXX I cannot comment whether that works for the interface, but
87 * XXX it lets you compile...(^& I do not think that we can do away
88 * XXX with this header.
90 # include <machine/joystick.h> /* For analog joysticks */
92 # define JS_DATA_TYPE joystick
93 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
96 # if defined(__linux__)
97 # include <linux/joystick.h>
99 /* check the joystick driver version */
100 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
103 # else /* Not BSD or Linux */
107 * We'll put these values in and that should
108 * allow the code to at least compile when there is
109 * no support. The JS open routine should error out
110 * and shut off all the code downstream anyway and if
111 * the application doesn't use a joystick we'll be fine.
121 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
129 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
131 #if defined(__NetBSD__) || defined(__FreeBSD__)
134 # if defined(__NetBSD__)
135 /* XXX The below hack is done until freeglut's autoconf is updated. */
136 # define HAVE_USBHID_H 1
137 # ifdef HAVE_USBHID_H
142 # elif defined(__FreeBSD__)
143 # if __FreeBSD_version < 500000
144 # include <libusbhid.h>
146 /* XXX The below hack is done until freeglut's autoconf is updated. */
147 # define HAVE_USBHID_H 1
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 fgWarning ( "Can't open %s for read!", buf );
240 protection_warned = 1;
247 static int fghJoystickInitializeHID(struct os_specific_s *os,
248 int *num_axes, int *num_buttons)
250 int size, is_joystick;
251 # ifdef HAVE_USBHID_H
258 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
260 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
266 # ifdef HAVE_USBHID_H
267 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
269 /*** XXX {report_id} may not be the right variable? ***/
270 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
274 size = hid_report_size( rd, hid_input, report_id );
276 size = hid_report_size( rd, 0, hid_input );
278 os->hid_data_buf = calloc( 1, size );
282 # ifdef HAVE_USBHID_H
283 d = hid_start_parse( rd, 1 << hid_input, report_id );
285 d = hid_start_parse( rd, 1 << hid_input );
287 while( hid_get_item( d, &h ) )
289 int usage, page, interesting_hid;
291 page = HID_PAGE( h.usage );
292 usage = HID_USAGE( h.usage );
294 /* This test is somewhat too simplistic, but this is how MicroSoft
295 * does, so I guess it works for all joysticks/game pads. */
296 is_joystick = is_joystick ||
297 ( h.kind == hid_collection &&
298 page == HUP_GENERIC_DESKTOP &&
299 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
301 if( h.kind != hid_input )
307 interesting_hid = TRUE;
308 if( page == HUP_GENERIC_DESKTOP )
319 if( *num_axes < _JS_MAX_AXES )
321 os->axes_usage[ *num_axes ] = usage;
326 /* Allocate two axes for a hat */
327 if( *num_axes + 1 < _JS_MAX_AXES )
329 os->axes_usage[ *num_axes ] = usage;
331 os->axes_usage[ *num_axes ] = usage;
336 interesting_hid = FALSE;
340 else if( page == HUP_BUTTON )
342 interesting_hid = ( usage > 0 ) &&
343 ( usage <= _JS_MAX_BUTTONS );
345 if( interesting_hid && usage - 1 > *num_buttons )
346 *num_buttons = usage - 1;
349 if( interesting_hid )
352 os->hids = calloc( 1, sizeof ( struct hid_item ) );
358 return os->hids != NULL;
364 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
365 * See "js.h" lines 80-178.
367 typedef struct tagSFG_Joystick SFG_Joystick;
368 struct tagSFG_Joystick
370 #if TARGET_HOST_MACINTOSH
371 #define ISP_NUM_AXIS 9
372 #define ISP_NUM_NEEDS 41
373 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
374 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
377 #if TARGET_HOST_MAC_OSX
378 IOHIDDeviceInterface ** hidDev;
379 IOHIDElementCookie buttonCookies[41];
380 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
381 long minReport[_JS_MAX_AXES],
382 maxReport[_JS_MAX_AXES];
385 #if TARGET_HOST_WIN32
392 #if TARGET_HOST_UNIX_X11
393 # if defined(__FreeBSD__) || defined(__NetBSD__)
394 struct os_specific_s *os;
400 float tmp_axes [ _JS_MAX_AXES ];
402 struct JS_DATA_TYPE js;
415 float dead_band[ _JS_MAX_AXES ];
416 float saturate [ _JS_MAX_AXES ];
417 float center [ _JS_MAX_AXES ];
418 float max [ _JS_MAX_AXES ];
419 float min [ _JS_MAX_AXES ];
423 * Functions associated with the "jsJoystick" class in PLIB
425 #if TARGET_HOST_MAC_OSX
426 #define K_NUM_DEVICES 32
428 io_object_t ioDevices[K_NUM_DEVICES];
430 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
431 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
433 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
434 /* callback for CFArrayApply */
435 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
436 static void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
438 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
439 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
440 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
445 * The static joystick structure pointer
447 #define MAX_NUM_JOYSTICKS 2
448 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
452 * Read the raw joystick data
454 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
456 #if TARGET_HOST_WIN32
462 #if defined(__FreeBSD__) || defined(__NetBSD__)
473 for( i = 0; i < joy->num_axes; i++ )
479 #if TARGET_HOST_MACINTOSH
484 for ( i = 0; i < joy->num_buttons; i++ )
487 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
490 *buttons |= state << i;
496 for ( i = 0; i < joy->num_axes; i++ )
499 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
502 axes [i] = (float) state;
507 #if TARGET_HOST_MAC_OSX
508 if ( buttons != NULL )
512 for ( i = 0; i < joy->num_buttons; i++ )
514 IOHIDEventStruct hidEvent;
515 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
516 if ( hidEvent.value )
523 for ( i = 0; i < joy->num_axes; i++ )
525 IOHIDEventStruct hidEvent;
526 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
527 axes[i] = hidEvent.value;
532 #if TARGET_HOST_WIN32
533 status = joyGetPosEx( joy->js_id, &joy->js );
535 if ( status != JOYERR_NOERROR )
537 joy->error = GL_TRUE;
542 *buttons = joy->js.dwButtons;
547 * WARNING - Fall through case clauses!!
549 switch ( joy->num_axes )
552 /* Generate two POV axes from the POV hat angle.
553 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
554 * hundredths of a degree, or 0xFFFF when idle.
556 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
563 /* This is the contentious bit: how to convert angle to X/Y.
564 * wk: I know of no define for PI that we could use here:
565 * SG_PI would pull in sg, M_PI is undefined for MSVC
566 * But the accuracy of the value of PI is very unimportant at
569 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
570 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
572 /* Convert to coordinates on a square so that North-East
573 * is (1,1) not (.7,.7), etc.
574 * s and c cannot both be zero so we won't divide by zero.
576 if ( fabs ( s ) < fabs ( c ) )
578 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
579 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
583 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
584 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
588 case 6: axes[5] = (float) joy->js.dwVpos;
589 case 5: axes[4] = (float) joy->js.dwUpos;
590 case 4: axes[3] = (float) joy->js.dwRpos;
591 case 3: axes[2] = (float) joy->js.dwZpos;
592 case 2: axes[1] = (float) joy->js.dwYpos;
593 case 1: axes[0] = (float) joy->js.dwXpos;
598 #if TARGET_HOST_UNIX_X11
599 # if defined(__FreeBSD__) || defined(__NetBSD__)
600 if ( joy->os->is_analog )
602 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
603 if ( status != sizeof(joy->os->ajs) ) {
604 perror ( joy->os->fname );
605 joy->error = GL_TRUE;
608 if ( buttons != NULL )
609 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
613 axes[0] = (float) joy->os->ajs.x;
614 axes[1] = (float) joy->os->ajs.y;
621 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
625 for ( h = joy->os->hids; h; h = h->next )
627 int d = hid_get_data ( joy->os->hid_data_buf, h );
629 int page = HID_PAGE ( h->usage );
630 int usage = HID_USAGE ( h->usage );
632 if ( page == HUP_GENERIC_DESKTOP )
635 for ( i = 0; i < joy->num_axes; i++ )
636 if (joy->os->axes_usage[i] == usage)
638 if (usage == HUG_HAT_SWITCH)
642 joy->os->cache_axes[i] = (float)hatmap_x[d];
643 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
647 joy->os->cache_axes[i] = (float)d;
652 else if (page == HUP_BUTTON)
654 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
657 joy->os->cache_buttons |= (1 << usage - 1);
659 joy->os->cache_buttons &= ~(1 << usage - 1);
664 if ( len < 0 && errno != EAGAIN )
666 perror( joy->os->fname );
669 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
671 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
679 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
681 if ( status != sizeof( struct js_event ) )
683 if ( errno == EAGAIN )
685 /* Use the old values */
687 *buttons = joy->tmp_buttons;
689 memcpy( axes, joy->tmp_axes,
690 sizeof( float ) * joy->num_axes );
694 fgWarning ( "%s", joy->fname );
695 joy->error = GL_TRUE;
699 switch ( joy->js.type & ~JS_EVENT_INIT )
701 case JS_EVENT_BUTTON:
702 if( joy->js.value == 0 ) /* clear the flag */
703 joy->tmp_buttons &= ~( 1 << joy->js.number );
705 joy->tmp_buttons |= ( 1 << joy->js.number );
709 if ( joy->js.number < joy->num_axes )
711 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
714 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
719 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
721 /* use the old values */
723 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
725 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
731 *buttons = joy->tmp_buttons;
735 status = read( joy->fd, &joy->js, JS_RETURN );
737 if ( status != JS_RETURN )
739 fgWarning( "%s", joy->fname );
740 joy->error = GL_TRUE;
745 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
746 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
748 *buttons = joy->js.buttons;
753 axes[ 0 ] = (float) joy->js.x;
754 axes[ 1 ] = (float) joy->js.y;
761 * Correct the joystick axis data
763 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
765 if( value < joy->center[ axis ] )
767 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
770 if( xx < -joy->saturate[ axis ] )
773 if( xx > -joy->dead_band [ axis ] )
776 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
777 joy->dead_band[ axis ] );
779 return ( xx < -1.0f ) ? -1.0f : xx;
783 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
784 joy->center[ axis ] );
786 if( xx > joy->saturate[ axis ] )
789 if( xx < joy->dead_band[ axis ] )
792 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
793 joy->dead_band[ axis ] );
795 return ( xx > 1.0f ) ? 1.0f : xx;
800 * Read the corrected joystick data
802 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
804 float raw_axes[ _JS_MAX_AXES ];
813 for ( i=0; i<joy->num_axes; i++ )
817 fghJoystickRawRead( joy, buttons, raw_axes );
820 for( i=0; i<joy->num_axes; i++ )
821 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
825 * Happy happy happy joy joy joy (happy new year toudi :D)
829 #if TARGET_HOST_MAC_OSX
830 /** open the IOKit connection, enumerate all the HID devices, add their
831 interface references to the static array. We then use the array index
832 as the device number when we come to open() the joystick. */
833 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
835 CFMutableDictionaryRef hidMatch = NULL;
836 IOReturn rv = kIOReturnSuccess;
838 io_iterator_t hidIterator;
841 /* build a dictionary matching HID devices */
842 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
844 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
845 if (rv != kIOReturnSuccess || !hidIterator) {
846 fgWarning( "no joystick (HID) devices found" );
851 while ((ioDev = IOIteratorNext(hidIterator))) {
852 /* filter out keyboard and mouse devices */
853 CFDictionaryRef properties = getCFProperties(ioDev);
856 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
857 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
858 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
859 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
861 /* keep only joystick devices */
862 if ( ( page == kHIDPage_GenericDesktop ) && (
863 (usage == kHIDUsage_GD_Joystick)
864 || (usage == kHIDUsage_GD_GamePad)
865 || (usage == kHIDUsage_GD_MultiAxisController)
866 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
867 /* add it to the array */
868 ioDevices[numDevices++] = ioDev;
871 IOObjectRelease(hidIterator);
874 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
877 CFMutableDictionaryRef cfProperties;
880 /* comment copied from darwin/SDL_sysjoystick.c */
881 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
882 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
885 io_registry_entry_t parent1, parent2;
887 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
888 if (rv != kIOReturnSuccess) {
889 fgWarning ( "error getting device entry parent");
893 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
894 if (rv != kIOReturnSuccess) {
895 fgWarning ( "error getting device entry parent 2");
900 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
901 &cfProperties, kCFAllocatorDefault, kNilOptions);
902 if (rv != kIOReturnSuccess || !cfProperties) {
903 fgWarning ( "error getting device properties");
910 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
912 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
913 fgError ( "%s", "element enumerator passed non-dictionary value");
917 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
920 /** element enumerator function : pass NULL for top-level*/
921 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
923 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
924 "Joystick element type mismatch",
925 "fghJoystickEnumerateElements" );
927 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
928 CFArrayApplyFunction((CFArrayRef) element, range,
929 &fghJoystickElementEnumerator, joy );
932 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
934 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
935 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
937 long type, page, usage;
939 CFNumberGetValue((CFNumberRef)
940 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
941 kCFNumberLongType, &type);
944 case kIOHIDElementTypeInput_Misc:
945 case kIOHIDElementTypeInput_Axis:
946 case kIOHIDElementTypeInput_Button:
947 printf("got input element...");
948 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
949 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
951 if (page == kHIDPage_GenericDesktop) {
952 switch ( usage ) /* look at usage to determine function */
957 case kHIDUsage_GD_Rx:
958 case kHIDUsage_GD_Ry:
959 case kHIDUsage_GD_Rz:
960 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
962 fghJoystickAddAxisElement((CFDictionaryRef) element);
965 case kHIDUsage_GD_Hatswitch:
967 fghJoystickAddHatElement((CFDictionaryRef) element);
971 fgWarning ( "input type element has weird usage (%x)", usage);
974 } else if (page == kHIDPage_Button) {
976 fghJoystickAddButtonElement((CFDictionaryRef) element);
978 fgWarning ( "input type element has weird page (%x)", page);
981 case kIOHIDElementTypeCollection:
982 fghJoystickEnumerateElements (
983 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
992 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
994 long cookie, lmin, lmax;
995 int index = joy->num_axes++;
997 CFNumberGetValue ((CFNumberRef)
998 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
999 kCFNumberLongType, &cookie);
1001 axisCookies[index] = (IOHIDElementCookie) cookie;
1003 CFNumberGetValue ((CFNumberRef)
1004 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1005 kCFNumberLongType, &lmin);
1007 CFNumberGetValue ((CFNumberRef)
1008 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1009 kCFNumberLongType, &lmax);
1011 joy->min[index] = lmin;
1012 joy->max[index] = lmax;
1013 joy->dead_band[index] = 0.0;
1014 joy->saturate[index] = 1.0;
1015 joy->center[index] = (lmax + lmin) * 0.5;
1018 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1021 CFNumberGetValue ((CFNumberRef)
1022 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1023 kCFNumberLongType, &cookie);
1025 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1026 /* anything else for buttons? */
1029 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1031 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1032 /* do we map hats to axes or buttons? */
1036 #if TARGET_HOST_WIN32
1038 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1040 # if defined(_MSC_VER)
1041 # pragma comment (lib, "advapi32.lib")
1044 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1046 char buffer [ 256 ];
1048 char OEMKey [ 256 ];
1057 /* Open .. MediaResources\CurrentJoystickSettings */
1058 sprintf ( buffer, "%s\\%s\\%s",
1059 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1060 REGSTR_KEY_JOYCURR );
1062 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1064 if ( lr != ERROR_SUCCESS ) return 0;
1066 /* Get OEM Key name */
1067 dwcb = sizeof(OEMKey);
1069 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1070 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1072 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1073 RegCloseKey ( hKey );
1075 if ( lr != ERROR_SUCCESS ) return 0;
1077 /* Open OEM Key from ...MediaProperties */
1078 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1080 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1082 if ( lr != ERROR_SUCCESS ) return 0;
1087 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1089 RegCloseKey ( hKey );
1091 if ( lr != ERROR_SUCCESS ) return 0;
1098 static void fghJoystickOpen( SFG_Joystick* joy )
1101 #if TARGET_HOST_MACINTOSH
1104 #if TARGET_HOST_MAC_OSX
1107 IOCFPlugInInterface **plugin;
1109 HRESULT pluginResult;
1111 CFDictionaryRef props;
1112 CFTypeRef topLevelElement;
1114 #if TARGET_HOST_UNIX_X11
1115 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1126 * Default values (for no joystick -- each conditional will reset the
1130 joy->num_axes = joy->num_buttons = 0;
1131 joy->name[ 0 ] = '\0';
1133 #if TARGET_HOST_MACINTOSH
1134 /* XXX FIXME: get joystick name in Mac */
1136 err = ISpStartup( );
1140 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1142 joy->error = GL_TRUE;
1144 /* initialize the needs structure */
1145 ISpNeed temp_isp_needs[ isp_num_needs ] =
1147 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1148 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1149 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1150 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1151 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1152 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1153 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1154 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1155 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1157 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1158 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1159 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1160 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1161 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1191 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1194 /* next two calls allow keyboard and mouse to emulate other input
1195 * devices (gamepads, joysticks, etc)
1198 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1202 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1206 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1207 joy->isp_needs, joy->isp_elem,
1209 ISP_CHECK_ERR( err )
1211 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1212 'freeglut', nil, 0, 128, 0 );
1213 ISP_CHECK_ERR( err )
1215 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1216 joy->num_axes = joy->isp_num_axis;
1218 for( i = 0; i < joy->num_axes; i++ )
1220 joy->dead_band[ i ] = 0;
1221 joy->saturate [ i ] = 1;
1222 joy->center [ i ] = kISpAxisMiddle;
1223 joy->max [ i ] = kISpAxisMaximum;
1224 joy->min [ i ] = kISpAxisMinimum;
1227 joy->error = GL_FALSE;
1230 joy->num_buttons = joy->num_axes = 0;
1233 #if TARGET_HOST_MAC_OSX
1234 if( joy->id >= numDevices )
1236 fgWarning( "device index out of range in fgJoystickOpen()" );
1240 /* create device interface */
1241 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1242 kIOHIDDeviceUserClientTypeID,
1243 kIOCFPlugInInterfaceID,
1246 if( rv != kIOReturnSuccess )
1248 fgWarning( "error creating plugin for io device" );
1252 pluginResult = ( *plugin )->QueryInterface(
1254 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1255 &( LPVOID )joy->hidDev
1258 if( pluginResult != S_OK )
1259 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1261 ( *plugin )->Release( plugin ); /* don't leak a ref */
1262 if( joy->hidDev == NULL )
1265 /* store the interface in this instance */
1266 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1267 if( rv != kIOReturnSuccess )
1269 fgWarning( "error opening device interface");
1273 props = getCFProperties( ioDevices[ joy->id ] );
1275 /* recursively enumerate all the bits */
1276 CFTypeRef topLevelElement =
1277 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1278 enumerateElements( topLevelElement );
1283 #if TARGET_HOST_WIN32
1284 joy->js.dwFlags = JOY_RETURNALL;
1285 joy->js.dwSize = sizeof( joy->js );
1287 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1290 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1293 if( joy->jsCaps.wNumAxes == 0 )
1296 joy->error = GL_TRUE;
1300 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1301 * at least for USB. Try to get the real name from the registry.
1303 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1304 sizeof( joy->name ) ) )
1306 fgWarning( "JS: Failed to read joystick name from registry" );
1307 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1310 /* Windows joystick drivers may provide any combination of
1311 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1313 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1315 joy->num_axes = _JS_MAX_AXES;
1316 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1317 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1322 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1323 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1324 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1325 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1326 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1327 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1328 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1329 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1330 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1331 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1332 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1333 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1336 /* Guess all the rest judging on the axes extremals */
1337 for( i = 0; i < joy->num_axes; i++ )
1339 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1340 joy->dead_band[ i ] = 0.0f;
1341 joy->saturate [ i ] = 1.0f;
1345 #if TARGET_HOST_UNIX_X11
1346 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1347 for( i = 0; i < _JS_MAX_AXES; i++ )
1348 joy->os->cache_axes[ i ] = 0.0f;
1350 joy->os->cache_buttons = 0;
1352 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1354 if( joy->os->fd < 0 && errno == EACCES )
1355 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1357 joy->error =( joy->os->fd < 0 );
1363 joy->num_buttons = 0;
1364 if( joy->os->is_analog )
1367 char joyfname[ 1024 ];
1368 int noargs, in_no_axes;
1370 float axes [ _JS_MAX_AXES ];
1371 int buttons[ _JS_MAX_AXES ];
1374 joy->num_buttons = 32;
1376 fghJoystickRawRead( joy, buttons, axes );
1377 joy->error = axes[ 0 ] < -1000000000.0f;
1381 sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
1383 joyfile = fopen( joyfname, "r" );
1384 joy->error =( joyfile == NULL );
1388 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1389 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1390 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1391 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1396 for( i = 0; i < _JS_MAX_AXES; i++ )
1398 joy->dead_band[ i ] = 0.0f;
1399 joy->saturate [ i ] = 1.0f;
1402 return; /* End of analog code */
1406 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1407 &joy->num_buttons ) )
1409 close( joy->os->fd );
1410 joy->error = GL_TRUE;
1414 cp = strrchr( joy->os->fname, '/' );
1417 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1419 strcpy( joy->name, &cp[1] );
1422 if( joy->num_axes > _JS_MAX_AXES )
1423 joy->num_axes = _JS_MAX_AXES;
1425 for( i = 0; i < _JS_MAX_AXES; i++ )
1427 /* We really should get this from the HID, but that data seems
1428 * to be quite unreliable for analog-to-USB converters. Punt for
1431 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1433 joy->max [ i ] = 1.0f;
1434 joy->center[ i ] = 0.0f;
1435 joy->min [ i ] = -1.0f;
1439 joy->max [ i ] = 255.0f;
1440 joy->center[ i ] = 127.0f;
1441 joy->min [ i ] = 0.0f;
1444 joy->dead_band[ i ] = 0.0f;
1445 joy->saturate[ i ] = 1.0f;
1450 #if defined( __linux__ )
1451 /* Default for older Linux systems. */
1453 joy->num_buttons = 32;
1456 for( i = 0; i < _JS_MAX_AXES; i++ )
1457 joy->tmp_axes[ i ] = 0.0f;
1459 joy->tmp_buttons = 0;
1462 joy->fd = open( joy->fname, O_RDONLY );
1464 joy->error =( joy->fd < 0 );
1469 /* Set the correct number of axes for the linux driver */
1471 /* Melchior Franz's fixes for big-endian Linuxes since writing
1472 * to the upper byte of an uninitialized word doesn't work.
1475 ioctl( joy->fd, JSIOCGAXES, &u );
1477 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1478 joy->num_buttons = u;
1479 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1480 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1484 * The Linux driver seems to return 512 for all axes
1485 * when no stick is present - but there is a chance
1486 * that could happen by accident - so it's gotta happen
1487 * on both axes for at least 100 attempts.
1489 * PWO: shouldn't be that done somehow wiser on the kernel level?
1496 fghJoystickRawRead( joy, NULL, joy->center );
1498 } while( !joy->error &&
1500 joy->center[ 0 ] == 512.0f &&
1501 joy->center[ 1 ] == 512.0f );
1503 if ( counter >= 100 )
1504 joy->error = GL_TRUE;
1507 for( i = 0; i < _JS_MAX_AXES; i++ )
1510 joy->max [ i ] = 32767.0f;
1511 joy->center[ i ] = 0.0f;
1512 joy->min [ i ] = -32767.0f;
1514 joy->max[ i ] = joy->center[ i ] * 2.0f;
1515 joy->min[ i ] = 0.0f;
1517 joy->dead_band[ i ] = 0.0f;
1518 joy->saturate [ i ] = 1.0f;
1525 * This function replaces the constructor method in the JS library.
1527 static void fghJoystickInit( int ident )
1529 if( ident >= MAX_NUM_JOYSTICKS )
1530 fgError( "Too large a joystick number: %d", ident );
1532 if( fgJoystick[ ident ] )
1533 fgError( "illegal attempt to initialize joystick device again" );
1535 fgJoystick[ ident ] =
1536 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1539 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1540 fgJoystick[ ident ]->error = GL_TRUE;
1542 #if TARGET_HOST_MACINTOSH
1543 fgJoystick[ ident ]->id = ident;
1544 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
1545 fgJoystick[ ident ]->error = GL_FALSE;
1548 #if TARGET_HOST_MAC_OSX
1549 fgJoystick[ ident ]->id = ident;
1550 fgJoystick[ ident ]->error = GL_FALSE;
1551 fgJoystick[ ident ]->num_axes = 0;
1552 fgJoystick[ ident ]->num_buttons = 0;
1554 if( numDevices < 0 )
1556 /* do first-time init (since we can't over-ride jsInit, hmm */
1559 mach_port_t masterPort;
1560 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1561 if( rv != kIOReturnSuccess )
1563 fgWarning( "error getting master Mach port" );
1566 fghJoystickFindDevices( masterPort );
1569 if ( ident >= numDevices )
1571 fgJoystick[ ident ]->error = GL_TRUE;
1575 /* get the name now too */
1576 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1577 CFTypeRef ref = CFDictionaryGetValue( properties,
1578 CFSTR( kIOHIDProductKey ) );
1580 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1583 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1584 CFStringGetSystemEncoding( ) ) )
1586 fgWarning( "error getting device name" );
1591 #if TARGET_HOST_WIN32
1595 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1596 fgJoystick[ ident ]->error = GL_FALSE;
1599 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1600 fgJoystick[ ident ]->error = GL_FALSE;
1603 fgJoystick[ ident ]->num_axes = 0;
1604 fgJoystick[ ident ]->error = GL_TRUE;
1609 #if TARGET_HOST_UNIX_X11
1610 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1611 fgJoystick[ ident ]->id = ident;
1612 fgJoystick[ ident ]->error = GL_FALSE;
1614 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1615 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1616 if( ident < USB_IDENT_OFFSET )
1617 fgJoystick[ ident ]->os->is_analog = 1;
1618 if( fgJoystick[ ident ]->os->is_analog )
1619 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
1621 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
1622 ident - USB_IDENT_OFFSET );
1623 # elif defined( __linux__ )
1624 fgJoystick[ ident ]->id = ident;
1625 fgJoystick[ ident ]->error = GL_FALSE;
1627 sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1629 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1630 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
1634 fghJoystickOpen( fgJoystick[ ident ] );
1638 * Try initializing all the joysticks (well, both of them)
1640 void fgInitialiseJoysticks ( void )
1642 /* Initialization courtesy of OpenGLUT -- do we want it? */
1643 if( !fgState.JoysticksInitialised )
1646 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1647 fghJoystickInit( ident );
1649 fgState.JoysticksInitialised = GL_TRUE;
1656 void fgJoystickClose( void )
1659 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1661 if( fgJoystick[ ident ] )
1664 #if TARGET_HOST_MACINTOSH
1670 #if TARGET_HOST_MAC_OSX
1671 ( *( fgJoystick[ ident ]->hidDev ) )->
1672 close( fgJoystick[ ident ]->hidDev );
1675 #if TARGET_HOST_WIN32
1676 /* Do nothing special */
1679 #if TARGET_HOST_UNIX_X11
1680 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1681 if( fgJoystick[ident]->os )
1683 if( ! fgJoystick[ ident ]->error )
1684 close( fgJoystick[ ident ]->os->fd );
1686 if( fgJoystick[ ident ]->os->hids )
1687 free (fgJoystick[ ident ]->os->hids);
1688 if( fgJoystick[ ident ]->os->hid_data_buf )
1689 free( fgJoystick[ ident ]->os->hid_data_buf );
1691 free( fgJoystick[ident]->os );
1695 if( ! fgJoystick[ident]->error )
1696 close( fgJoystick[ ident ]->fd );
1699 free( fgJoystick[ ident ] );
1700 fgJoystick[ ident ] = NULL;
1701 /* show joystick has been deinitialized */
1707 * Polls the joystick and executes the joystick callback hooked to the
1708 * window specified in the function's parameter:
1710 void fgJoystickPollWindow( SFG_Window* window )
1712 float axes[ _JS_MAX_AXES ];
1716 freeglut_return_if_fail( window );
1717 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1719 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1721 if( fgJoystick[ident] )
1723 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1725 if( !fgJoystick[ident]->error )
1726 INVOKE_WCB( *window, Joystick,
1728 (int) ( axes[ 0 ] * 1000.0f ),
1729 (int) ( axes[ 1 ] * 1000.0f ),
1730 (int) ( axes[ 2 ] * 1000.0f ) )
1737 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1739 int fgJoystickDetect( void )
1743 fgInitialiseJoysticks ();
1748 if ( !fgState.JoysticksInitialised )
1751 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1752 if( fgJoystick[ident] && !fgJoystick[ident]->error )
1759 * Joystick information functions
1761 int glutJoystickGetNumAxes( int ident )
1763 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
1764 return fgJoystick[ ident ]->num_axes;
1766 int glutJoystickGetNumButtons( int ident )
1768 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
1769 return fgJoystick[ ident ]->num_buttons;
1771 int glutJoystickNotWorking( int ident )
1773 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
1774 return fgJoystick[ ident ]->error;
1777 float glutJoystickGetDeadBand( int ident, int axis )
1779 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1780 return fgJoystick[ ident ]->dead_band [ axis ];
1782 void glutJoystickSetDeadBand( int ident, int axis, float db )
1784 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1785 fgJoystick[ ident ]->dead_band[ axis ] = db;
1788 float glutJoystickGetSaturation( int ident, int axis )
1790 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1791 return fgJoystick[ ident ]->saturate[ axis ];
1793 void glutJoystickSetSaturation( int ident, int axis, float st )
1795 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1796 fgJoystick[ ident ]->saturate [ axis ] = st;
1799 void glutJoystickSetMinRange( int ident, float *axes )
1801 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1802 memcpy( fgJoystick[ ident ]->min, axes,
1803 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1805 void glutJoystickSetMaxRange( int ident, float *axes )
1807 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1808 memcpy( fgJoystick[ ident ]->max, axes,
1809 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1811 void glutJoystickSetCenter( int ident, float *axes )
1813 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1814 memcpy( fgJoystick[ ident ]->center, axes,
1815 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1818 void glutJoystickGetMinRange( int ident, float *axes )
1820 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1821 memcpy( axes, fgJoystick[ ident ]->min,
1822 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1824 void glutJoystickGetMaxRange( int ident, float *axes )
1826 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1827 memcpy( axes, fgJoystick[ ident ]->max,
1828 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1830 void glutJoystickGetCenter( int ident, float *axes )
1832 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1833 memcpy( axes, fgJoystick[ ident ]->center,
1834 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1837 /*** END OF FILE ***/