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 <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 below hack is done until freeglut's autoconf is updated. */
77 # define HAVE_USB_JS 1
79 # include <sys/ioctl.h>
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))
100 # if defined(__linux__)
101 # include <sys/ioctl.h>
102 # include <linux/joystick.h>
104 /* check the joystick driver version */
105 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
108 # else /* Not BSD or Linux */
112 * We'll put these values in and that should
113 * allow the code to at least compile when there is
114 * no support. The JS open routine should error out
115 * and shut off all the code downstream anyway and if
116 * the application doesn't use a joystick we'll be fine.
126 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
134 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
136 #if defined(__NetBSD__) || defined(__FreeBSD__)
139 # if defined(__NetBSD__)
140 /* XXX The below hack is done until freeglut's autoconf is updated. */
141 # define HAVE_USBHID_H 1
142 # ifdef HAVE_USBHID_H
147 # elif defined(__FreeBSD__)
148 # if __FreeBSD_version < 500000
149 # include <libusbhid.h>
151 /* XXX The below hack is done until freeglut's autoconf is updated. */
152 # define HAVE_USBHID_H 1
156 # include <dev/usb/usb.h>
157 # include <dev/usb/usbhid.h>
159 /* Compatibility with older usb.h revisions */
160 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
161 # define USB_MAX_DEVNAMES MAXDEVNAMES
165 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
166 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
167 struct os_specific_s {
171 /* The following structure members are specific to analog joysticks */
174 /* The following structure members are specific to USB joysticks */
175 struct hid_item *hids;
179 int axes_usage [ _JS_MAX_AXES ];
181 /* We keep button and axes state ourselves, as they might not be updated
182 * on every read of a USB device
185 float cache_axes [ _JS_MAX_AXES ];
188 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
189 # define USB_IDENT_OFFSET 2
191 # define USBDEV "/dev/usb"
192 # define UHIDDEV "/dev/uhid"
193 # define AJSDEV "/dev/joy"
197 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
198 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
199 * return the uhidN device name. We warn the user of this situation once.
201 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
203 struct usb_device_info di;
207 for (a = 1; a < USB_MAX_DEVICES; a++) {
209 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
211 for (i = 0; i < USB_MAX_DEVNAMES; i++)
212 if (di.udi_devnames[i][0] &&
213 strcmp(di.udi_devnames[i], dev) == 0) {
214 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
215 strcpy(cp, di.udi_vendor);
217 strcat(cp, di.udi_product);
218 strncpy(out, cp, outlen - 1);
227 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
232 static int protection_warned = 0;
234 for (i = 0; i < 16; i++) {
235 sprintf(buf, "%s%d", USBDEV, i);
236 f = open(buf, O_RDONLY);
238 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
242 } else if (errno == EACCES) {
243 if (!protection_warned) {
244 fgWarning ( "Can't open %s for read!", buf );
245 protection_warned = 1;
252 static int fghJoystickInitializeHID(struct os_specific_s *os,
253 int *num_axes, int *num_buttons)
255 int size, is_joystick;
256 # ifdef HAVE_USBHID_H
263 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
265 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
271 # ifdef HAVE_USBHID_H
272 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
274 /*** XXX {report_id} may not be the right variable? ***/
275 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
279 size = hid_report_size( rd, hid_input, report_id );
281 size = hid_report_size( rd, 0, hid_input );
283 os->hid_data_buf = calloc( 1, size );
287 # ifdef HAVE_USBHID_H
288 d = hid_start_parse( rd, 1 << hid_input, report_id );
290 d = hid_start_parse( rd, 1 << hid_input );
292 while( hid_get_item( d, &h ) )
294 int usage, page, interesting_hid;
296 page = HID_PAGE( h.usage );
297 usage = HID_USAGE( h.usage );
299 /* This test is somewhat too simplistic, but this is how MicroSoft
300 * does, so I guess it works for all joysticks/game pads. */
301 is_joystick = is_joystick ||
302 ( h.kind == hid_collection &&
303 page == HUP_GENERIC_DESKTOP &&
304 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
306 if( h.kind != hid_input )
312 interesting_hid = TRUE;
313 if( page == HUP_GENERIC_DESKTOP )
324 if( *num_axes < _JS_MAX_AXES )
326 os->axes_usage[ *num_axes ] = usage;
331 /* Allocate two axes for a hat */
332 if( *num_axes + 1 < _JS_MAX_AXES )
334 os->axes_usage[ *num_axes ] = usage;
336 os->axes_usage[ *num_axes ] = usage;
341 interesting_hid = FALSE;
345 else if( page == HUP_BUTTON )
347 interesting_hid = ( usage > 0 ) &&
348 ( usage <= _JS_MAX_BUTTONS );
350 if( interesting_hid && usage - 1 > *num_buttons )
351 *num_buttons = usage - 1;
354 if( interesting_hid )
357 os->hids = calloc( 1, sizeof ( struct hid_item ) );
363 return os->hids != NULL;
369 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
370 * See "js.h" lines 80-178.
372 typedef struct tagSFG_Joystick SFG_Joystick;
373 struct tagSFG_Joystick
375 #if TARGET_HOST_MACINTOSH
376 #define ISP_NUM_AXIS 9
377 #define ISP_NUM_NEEDS 41
378 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
379 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
382 #if TARGET_HOST_MAC_OSX
383 IOHIDDeviceInterface ** hidDev;
384 IOHIDElementCookie buttonCookies[41];
385 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
386 long minReport[_JS_MAX_AXES],
387 maxReport[_JS_MAX_AXES];
390 #if TARGET_HOST_WIN32
397 #if TARGET_HOST_UNIX_X11
398 # if defined(__FreeBSD__) || defined(__NetBSD__)
399 struct os_specific_s *os;
405 float tmp_axes [ _JS_MAX_AXES ];
407 struct JS_DATA_TYPE js;
420 float dead_band[ _JS_MAX_AXES ];
421 float saturate [ _JS_MAX_AXES ];
422 float center [ _JS_MAX_AXES ];
423 float max [ _JS_MAX_AXES ];
424 float min [ _JS_MAX_AXES ];
428 * Functions associated with the "jsJoystick" class in PLIB
430 #if TARGET_HOST_MAC_OSX
431 #define K_NUM_DEVICES 32
433 io_object_t ioDevices[K_NUM_DEVICES];
435 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
436 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
438 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
439 /* callback for CFArrayApply */
440 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
441 static void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
443 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
444 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
445 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
450 * The static joystick structure pointer
452 #define MAX_NUM_JOYSTICKS 2
453 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
457 * Read the raw joystick data
459 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
461 #if TARGET_HOST_WIN32
467 #if defined(__FreeBSD__) || defined(__NetBSD__)
478 for( i = 0; i < joy->num_axes; i++ )
484 #if TARGET_HOST_MACINTOSH
489 for ( i = 0; i < joy->num_buttons; i++ )
492 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
495 *buttons |= state << i;
501 for ( i = 0; i < joy->num_axes; i++ )
504 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
507 axes [i] = (float) state;
512 #if TARGET_HOST_MAC_OSX
513 if ( buttons != NULL )
517 for ( i = 0; i < joy->num_buttons; i++ )
519 IOHIDEventStruct hidEvent;
520 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
521 if ( hidEvent.value )
528 for ( i = 0; i < joy->num_axes; i++ )
530 IOHIDEventStruct hidEvent;
531 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
532 axes[i] = hidEvent.value;
537 #if TARGET_HOST_WIN32
538 status = joyGetPosEx( joy->js_id, &joy->js );
540 if ( status != JOYERR_NOERROR )
542 joy->error = GL_TRUE;
547 *buttons = joy->js.dwButtons;
552 * WARNING - Fall through case clauses!!
554 switch ( joy->num_axes )
557 /* Generate two POV axes from the POV hat angle.
558 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
559 * hundredths of a degree, or 0xFFFF when idle.
561 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
568 /* This is the contentious bit: how to convert angle to X/Y.
569 * wk: I know of no define for PI that we could use here:
570 * SG_PI would pull in sg, M_PI is undefined for MSVC
571 * But the accuracy of the value of PI is very unimportant at
574 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
575 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
577 /* Convert to coordinates on a square so that North-East
578 * is (1,1) not (.7,.7), etc.
579 * s and c cannot both be zero so we won't divide by zero.
581 if ( fabs ( s ) < fabs ( c ) )
583 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
584 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
588 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
589 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
593 case 6: axes[5] = (float) joy->js.dwVpos;
594 case 5: axes[4] = (float) joy->js.dwUpos;
595 case 4: axes[3] = (float) joy->js.dwRpos;
596 case 3: axes[2] = (float) joy->js.dwZpos;
597 case 2: axes[1] = (float) joy->js.dwYpos;
598 case 1: axes[0] = (float) joy->js.dwXpos;
603 #if TARGET_HOST_UNIX_X11
604 # if defined(__FreeBSD__) || defined(__NetBSD__)
605 if ( joy->os->is_analog )
607 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
608 if ( status != sizeof(joy->os->ajs) ) {
609 perror ( joy->os->fname );
610 joy->error = GL_TRUE;
613 if ( buttons != NULL )
614 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
618 axes[0] = (float) joy->os->ajs.x;
619 axes[1] = (float) joy->os->ajs.y;
626 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
630 for ( h = joy->os->hids; h; h = h->next )
632 int d = hid_get_data ( joy->os->hid_data_buf, h );
634 int page = HID_PAGE ( h->usage );
635 int usage = HID_USAGE ( h->usage );
637 if ( page == HUP_GENERIC_DESKTOP )
640 for ( i = 0; i < joy->num_axes; i++ )
641 if (joy->os->axes_usage[i] == usage)
643 if (usage == HUG_HAT_SWITCH)
647 joy->os->cache_axes[i] = (float)hatmap_x[d];
648 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
652 joy->os->cache_axes[i] = (float)d;
657 else if (page == HUP_BUTTON)
659 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
662 joy->os->cache_buttons |= (1 << usage - 1);
664 joy->os->cache_buttons &= ~(1 << usage - 1);
669 if ( len < 0 && errno != EAGAIN )
671 perror( joy->os->fname );
674 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
676 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
684 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
686 if ( status != sizeof( struct js_event ) )
688 if ( errno == EAGAIN )
690 /* Use the old values */
692 *buttons = joy->tmp_buttons;
694 memcpy( axes, joy->tmp_axes,
695 sizeof( float ) * joy->num_axes );
699 fgWarning ( "%s", joy->fname );
700 joy->error = GL_TRUE;
704 switch ( joy->js.type & ~JS_EVENT_INIT )
706 case JS_EVENT_BUTTON:
707 if( joy->js.value == 0 ) /* clear the flag */
708 joy->tmp_buttons &= ~( 1 << joy->js.number );
710 joy->tmp_buttons |= ( 1 << joy->js.number );
714 if ( joy->js.number < joy->num_axes )
716 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
719 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
724 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
726 /* use the old values */
728 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
730 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
736 *buttons = joy->tmp_buttons;
740 status = read( joy->fd, &joy->js, JS_RETURN );
742 if ( status != JS_RETURN )
744 fgWarning( "%s", joy->fname );
745 joy->error = GL_TRUE;
750 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
751 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
753 *buttons = joy->js.buttons;
758 axes[ 0 ] = (float) joy->js.x;
759 axes[ 1 ] = (float) joy->js.y;
766 * Correct the joystick axis data
768 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
770 if( value < joy->center[ axis ] )
772 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
775 if( xx < -joy->saturate[ axis ] )
778 if( xx > -joy->dead_band [ axis ] )
781 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
782 joy->dead_band[ axis ] );
784 return ( xx < -1.0f ) ? -1.0f : xx;
788 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
789 joy->center[ axis ] );
791 if( xx > joy->saturate[ axis ] )
794 if( xx < joy->dead_band[ axis ] )
797 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
798 joy->dead_band[ axis ] );
800 return ( xx > 1.0f ) ? 1.0f : xx;
805 * Read the corrected joystick data
807 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
809 float raw_axes[ _JS_MAX_AXES ];
818 for ( i=0; i<joy->num_axes; i++ )
822 fghJoystickRawRead( joy, buttons, raw_axes );
825 for( i=0; i<joy->num_axes; i++ )
826 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
830 * Happy happy happy joy joy joy (happy new year toudi :D)
834 #if TARGET_HOST_MAC_OSX
835 /** open the IOKit connection, enumerate all the HID devices, add their
836 interface references to the static array. We then use the array index
837 as the device number when we come to open() the joystick. */
838 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
840 CFMutableDictionaryRef hidMatch = NULL;
841 IOReturn rv = kIOReturnSuccess;
843 io_iterator_t hidIterator;
846 /* build a dictionary matching HID devices */
847 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
849 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
850 if (rv != kIOReturnSuccess || !hidIterator) {
851 fgWarning( "no joystick (HID) devices found" );
856 while ((ioDev = IOIteratorNext(hidIterator))) {
857 /* filter out keyboard and mouse devices */
858 CFDictionaryRef properties = getCFProperties(ioDev);
861 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
862 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
863 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
864 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
866 /* keep only joystick devices */
867 if ( ( page == kHIDPage_GenericDesktop ) && (
868 (usage == kHIDUsage_GD_Joystick)
869 || (usage == kHIDUsage_GD_GamePad)
870 || (usage == kHIDUsage_GD_MultiAxisController)
871 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
872 /* add it to the array */
873 ioDevices[numDevices++] = ioDev;
876 IOObjectRelease(hidIterator);
879 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
882 CFMutableDictionaryRef cfProperties;
885 /* comment copied from darwin/SDL_sysjoystick.c */
886 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
887 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
890 io_registry_entry_t parent1, parent2;
892 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
893 if (rv != kIOReturnSuccess) {
894 fgWarning ( "error getting device entry parent");
898 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
899 if (rv != kIOReturnSuccess) {
900 fgWarning ( "error getting device entry parent 2");
905 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
906 &cfProperties, kCFAllocatorDefault, kNilOptions);
907 if (rv != kIOReturnSuccess || !cfProperties) {
908 fgWarning ( "error getting device properties");
915 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
917 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
918 fgError ( "%s", "element enumerator passed non-dictionary value");
922 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
925 /** element enumerator function : pass NULL for top-level*/
926 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
928 assert(CFGetTypeID(element) == CFArrayGetTypeID());
930 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
931 CFArrayApplyFunction((CFArrayRef) element, range,
932 &fghJoystickElementEnumerator, joy );
935 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
937 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
938 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
940 long type, page, usage;
942 CFNumberGetValue((CFNumberRef)
943 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
944 kCFNumberLongType, &type);
947 case kIOHIDElementTypeInput_Misc:
948 case kIOHIDElementTypeInput_Axis:
949 case kIOHIDElementTypeInput_Button:
950 printf("got input element...");
951 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
952 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
954 if (page == kHIDPage_GenericDesktop) {
955 switch ( usage ) /* look at usage to determine function */
960 case kHIDUsage_GD_Rx:
961 case kHIDUsage_GD_Ry:
962 case kHIDUsage_GD_Rz:
963 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
965 fghJoystickAddAxisElement((CFDictionaryRef) element);
968 case kHIDUsage_GD_Hatswitch:
970 fghJoystickAddHatElement((CFDictionaryRef) element);
974 fgWarning ( "input type element has weird usage (%x)", usage);
977 } else if (page == kHIDPage_Button) {
979 fghJoystickAddButtonElement((CFDictionaryRef) element);
981 fgWarning ( "input type element has weird page (%x)", page);
984 case kIOHIDElementTypeCollection:
985 fghJoystickEnumerateElements (
986 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
995 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
997 long cookie, lmin, lmax;
998 int index = joy->num_axes++;
1000 CFNumberGetValue ((CFNumberRef)
1001 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1002 kCFNumberLongType, &cookie);
1004 axisCookies[index] = (IOHIDElementCookie) cookie;
1006 CFNumberGetValue ((CFNumberRef)
1007 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1008 kCFNumberLongType, &lmin);
1010 CFNumberGetValue ((CFNumberRef)
1011 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1012 kCFNumberLongType, &lmax);
1014 joy->min[index] = lmin;
1015 joy->max[index] = lmax;
1016 joy->dead_band[index] = 0.0;
1017 joy->saturate[index] = 1.0;
1018 joy->center[index] = (lmax + lmin) * 0.5;
1021 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1024 CFNumberGetValue ((CFNumberRef)
1025 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1026 kCFNumberLongType, &cookie);
1028 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1029 /* anything else for buttons? */
1032 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1034 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1035 /* do we map hats to axes or buttons? */
1039 #if TARGET_HOST_WIN32
1041 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1043 # if defined(_MSC_VER)
1044 # pragma comment (lib, "advapi32.lib")
1047 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1049 char buffer [ 256 ];
1051 char OEMKey [ 256 ];
1060 /* Open .. MediaResources\CurrentJoystickSettings */
1061 sprintf ( buffer, "%s\\%s\\%s",
1062 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1063 REGSTR_KEY_JOYCURR );
1065 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1067 if ( lr != ERROR_SUCCESS ) return 0;
1069 /* Get OEM Key name */
1070 dwcb = sizeof(OEMKey);
1072 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1073 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1075 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1076 RegCloseKey ( hKey );
1078 if ( lr != ERROR_SUCCESS ) return 0;
1080 /* Open OEM Key from ...MediaProperties */
1081 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1083 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1085 if ( lr != ERROR_SUCCESS ) return 0;
1090 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1092 RegCloseKey ( hKey );
1094 if ( lr != ERROR_SUCCESS ) return 0;
1101 static void fghJoystickOpen( SFG_Joystick* joy )
1104 #if TARGET_HOST_MACINTOSH
1107 #if TARGET_HOST_MAC_OSX
1110 IOCFPlugInInterface **plugin;
1112 HRESULT pluginResult;
1114 CFDictionaryRef props;
1115 CFTypeRef topLevelElement;
1117 #if TARGET_HOST_UNIX_X11
1118 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1129 * Default values (for no joystick -- each conditional will reset the
1133 joy->num_axes = joy->num_buttons = 0;
1134 joy->name[ 0 ] = '\0';
1136 #if TARGET_HOST_MACINTOSH
1137 /* XXX FIXME: get joystick name in Mac */
1139 err = ISpStartup( );
1143 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1145 joy->error = GL_TRUE;
1147 /* initialize the needs structure */
1148 ISpNeed temp_isp_needs[ isp_num_needs ] =
1150 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1151 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1152 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1153 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1154 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1155 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1156 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1157 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1158 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1160 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1161 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1190 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1191 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1194 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1197 /* next two calls allow keyboard and mouse to emulate other input
1198 * devices (gamepads, joysticks, etc)
1201 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1205 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1209 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1210 joy->isp_needs, joy->isp_elem,
1212 ISP_CHECK_ERR( err )
1214 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1215 'freeglut', nil, 0, 128, 0 );
1216 ISP_CHECK_ERR( err )
1218 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1219 joy->num_axes = joy->isp_num_axis;
1221 for( i = 0; i < joy->num_axes; i++ )
1223 joy->dead_band[ i ] = 0;
1224 joy->saturate [ i ] = 1;
1225 joy->center [ i ] = kISpAxisMiddle;
1226 joy->max [ i ] = kISpAxisMaximum;
1227 joy->min [ i ] = kISpAxisMinimum;
1230 joy->error = GL_FALSE;
1233 joy->num_buttons = joy->num_axes = 0;
1236 #if TARGET_HOST_MAC_OSX
1237 if( joy->id >= numDevices )
1239 fgWarning( "device index out of range in fgJoystickOpen()" );
1243 /* create device interface */
1244 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1245 kIOHIDDeviceUserClientTypeID,
1246 kIOCFPlugInInterfaceID,
1249 if( rv != kIOReturnSuccess )
1251 fgWarning( "error creating plugin for io device" );
1255 pluginResult = ( *plugin )->QueryInterface(
1257 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1258 &( LPVOID )joy->hidDev
1261 if( pluginResult != S_OK )
1262 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1264 ( *plugin )->Release( plugin ); /* don't leak a ref */
1265 if( joy->hidDev == NULL )
1268 /* store the interface in this instance */
1269 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1270 if( rv != kIOReturnSuccess )
1272 fgWarning( "error opening device interface");
1276 props = getCFProperties( ioDevices[ joy->id ] );
1278 /* recursively enumerate all the bits */
1279 CFTypeRef topLevelElement =
1280 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1281 enumerateElements( topLevelElement );
1286 #if TARGET_HOST_WIN32
1287 joy->js.dwFlags = JOY_RETURNALL;
1288 joy->js.dwSize = sizeof( joy->js );
1290 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1293 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1296 if( joy->jsCaps.wNumAxes == 0 )
1299 joy->error = GL_TRUE;
1303 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1304 * at least for USB. Try to get the real name from the registry.
1306 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1307 sizeof( joy->name ) ) )
1309 fgWarning( "JS: Failed to read joystick name from registry" );
1310 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1313 /* Windows joystick drivers may provide any combination of
1314 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1316 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1318 joy->num_axes = _JS_MAX_AXES;
1319 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1320 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1325 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1326 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1327 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1328 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1329 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1330 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1331 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1332 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1333 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1334 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1335 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1336 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1339 /* Guess all the rest judging on the axes extremals */
1340 for( i = 0; i < joy->num_axes; i++ )
1342 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1343 joy->dead_band[ i ] = 0.0f;
1344 joy->saturate [ i ] = 1.0f;
1348 #if TARGET_HOST_UNIX_X11
1349 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1350 for( i = 0; i < _JS_MAX_AXES; i++ )
1351 joy->os->cache_axes[ i ] = 0.0f;
1353 joy->os->cache_buttons = 0;
1355 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1357 if( joy->os->fd < 0 && errno == EACCES )
1358 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1360 joy->error =( joy->os->fd < 0 );
1366 joy->num_buttons = 0;
1367 if( joy->os->is_analog )
1370 char joyfname[ 1024 ];
1371 int noargs, in_no_axes;
1373 float axes [ _JS_MAX_AXES ];
1374 int buttons[ _JS_MAX_AXES ];
1377 joy->num_buttons = 32;
1379 fghJoystickRawRead( joy, buttons, axes );
1380 joy->error = axes[ 0 ] < -1000000000.0f;
1384 sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
1386 joyfile = fopen( joyfname, "r" );
1387 joy->error =( joyfile == NULL );
1391 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1392 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1393 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1394 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1399 for( i = 0; i < _JS_MAX_AXES; i++ )
1401 joy->dead_band[ i ] = 0.0f;
1402 joy->saturate [ i ] = 1.0f;
1405 return; /* End of analog code */
1409 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1410 &joy->num_buttons ) )
1412 close( joy->os->fd );
1413 joy->error = GL_TRUE;
1417 cp = strrchr( joy->os->fname, '/' );
1420 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1422 strcpy( joy->name, &cp[1] );
1425 if( joy->num_axes > _JS_MAX_AXES )
1426 joy->num_axes = _JS_MAX_AXES;
1428 for( i = 0; i < _JS_MAX_AXES; i++ )
1430 /* We really should get this from the HID, but that data seems
1431 * to be quite unreliable for analog-to-USB converters. Punt for
1434 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1436 joy->max [ i ] = 1.0f;
1437 joy->center[ i ] = 0.0f;
1438 joy->min [ i ] = -1.0f;
1442 joy->max [ i ] = 255.0f;
1443 joy->center[ i ] = 127.0f;
1444 joy->min [ i ] = 0.0f;
1447 joy->dead_band[ i ] = 0.0f;
1448 joy->saturate[ i ] = 1.0f;
1453 #if defined( __linux__ )
1454 /* Default for older Linux systems. */
1456 joy->num_buttons = 32;
1459 for( i = 0; i < _JS_MAX_AXES; i++ )
1460 joy->tmp_axes[ i ] = 0.0f;
1462 joy->tmp_buttons = 0;
1465 joy->fd = open( joy->fname, O_RDONLY );
1467 joy->error =( joy->fd < 0 );
1472 /* Set the correct number of axes for the linux driver */
1474 /* Melchior Franz's fixes for big-endian Linuxes since writing
1475 * to the upper byte of an uninitialized word doesn't work.
1478 ioctl( joy->fd, JSIOCGAXES, &u );
1480 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1481 joy->num_buttons = u;
1482 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1483 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1487 * The Linux driver seems to return 512 for all axes
1488 * when no stick is present - but there is a chance
1489 * that could happen by accident - so it's gotta happen
1490 * on both axes for at least 100 attempts.
1492 * PWO: shouldn't be that done somehow wiser on the kernel level?
1499 fghJoystickRawRead( joy, NULL, joy->center );
1501 } while( !joy->error &&
1503 joy->center[ 0 ] == 512.0f &&
1504 joy->center[ 1 ] == 512.0f );
1506 if ( counter >= 100 )
1507 joy->error = GL_TRUE;
1510 for( i = 0; i < _JS_MAX_AXES; i++ )
1513 joy->max [ i ] = 32767.0f;
1514 joy->center[ i ] = 0.0f;
1515 joy->min [ i ] = -32767.0f;
1517 joy->max[ i ] = joy->center[ i ] * 2.0f;
1518 joy->min[ i ] = 0.0f;
1520 joy->dead_band[ i ] = 0.0f;
1521 joy->saturate [ i ] = 1.0f;
1528 * This function replaces the constructor method in the JS library.
1530 static void fghJoystickInit( int ident )
1532 if( ident >= MAX_NUM_JOYSTICKS )
1533 fgError( "Too large a joystick number: %d", ident );
1535 if( fgJoystick[ ident ] )
1536 fgError( "illegal attempt to initialize joystick device again" );
1538 fgJoystick[ ident ] =
1539 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1542 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1543 fgJoystick[ ident ]->error = GL_TRUE;
1545 #if TARGET_HOST_MACINTOSH
1546 fgJoystick[ ident ]->id = ident;
1547 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
1548 fgJoystick[ ident ]->error = GL_FALSE;
1551 #if TARGET_HOST_MAC_OSX
1552 fgJoystick[ ident ]->id = ident;
1553 fgJoystick[ ident ]->error = GL_FALSE;
1554 fgJoystick[ ident ]->num_axes = 0;
1555 fgJoystick[ ident ]->num_buttons = 0;
1557 if( numDevices < 0 )
1559 /* do first-time init (since we can't over-ride jsInit, hmm */
1562 mach_port_t masterPort;
1563 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1564 if( rv != kIOReturnSuccess )
1566 fgWarning( "error getting master Mach port" );
1569 fghJoystickFindDevices( masterPort );
1572 if ( ident >= numDevices )
1574 fgJoystick[ ident ]->error = GL_TRUE;
1578 /* get the name now too */
1579 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1580 CFTypeRef ref = CFDictionaryGetValue( properties,
1581 CFSTR( kIOHIDProductKey ) );
1583 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1586 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1587 CFStringGetSystemEncoding( ) ) )
1589 fgWarning( "error getting device name" );
1594 #if TARGET_HOST_WIN32
1598 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1599 fgJoystick[ ident ]->error = GL_FALSE;
1602 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1603 fgJoystick[ ident ]->error = GL_FALSE;
1606 fgJoystick[ ident ]->num_axes = 0;
1607 fgJoystick[ ident ]->error = GL_TRUE;
1612 #if TARGET_HOST_UNIX_X11
1613 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1614 fgJoystick[ ident ]->id = ident;
1615 fgJoystick[ ident ]->error = GL_FALSE;
1617 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1618 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1619 if( ident < USB_IDENT_OFFSET )
1620 fgJoystick[ ident ]->os->is_analog = 1;
1621 if( fgJoystick[ ident ]->os->is_analog )
1622 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
1624 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
1625 ident - USB_IDENT_OFFSET );
1626 # elif defined( __linux__ )
1627 fgJoystick[ ident ]->id = ident;
1628 fgJoystick[ ident ]->error = GL_FALSE;
1630 sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1632 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1633 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
1637 fghJoystickOpen( fgJoystick[ ident ] );
1641 * Try initializing all the joysticks (well, both of them)
1643 void fgInitialiseJoysticks ( void )
1645 /* Initialization courtesy of OpenGLUT -- do we want it? */
1646 if( !fgState.JoysticksInitialised )
1649 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1650 fghJoystickInit( ident );
1652 fgState.JoysticksInitialised = GL_TRUE;
1659 void fgJoystickClose( void )
1662 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1664 if( fgJoystick[ ident ] )
1667 #if TARGET_HOST_MACINTOSH
1673 #if TARGET_HOST_MAC_OSX
1674 ( *( fgJoystick[ ident ]->hidDev ) )->
1675 close( fgJoystick[ ident ]->hidDev );
1678 #if TARGET_HOST_WIN32
1679 /* Do nothing special */
1682 #if TARGET_HOST_UNIX_X11
1683 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1684 if( fgJoystick[ident]->os )
1686 if( ! fgJoystick[ ident ]->error )
1687 close( fgJoystick[ ident ]->os->fd );
1689 if( fgJoystick[ ident ]->os->hids )
1690 free (fgJoystick[ ident ]->os->hids);
1691 if( fgJoystick[ ident ]->os->hid_data_buf )
1692 free( fgJoystick[ ident ]->os->hid_data_buf );
1694 free( fgJoystick[ident]->os );
1698 if( ! fgJoystick[ident]->error )
1699 close( fgJoystick[ ident ]->fd );
1702 free( fgJoystick[ ident ] );
1703 fgJoystick[ ident ] = NULL;
1704 /* show joystick has been deinitialized */
1710 * Polls the joystick and executes the joystick callback hooked to the
1711 * window specified in the function's parameter:
1713 void fgJoystickPollWindow( SFG_Window* window )
1715 float axes[ _JS_MAX_AXES ];
1719 freeglut_return_if_fail( window );
1720 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1722 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1724 if( fgJoystick[ident] )
1726 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1728 if( !fgJoystick[ident]->error )
1729 INVOKE_WCB( *window, Joystick,
1731 (int) ( axes[ 0 ] * 1000.0f ),
1732 (int) ( axes[ 1 ] * 1000.0f ),
1733 (int) ( axes[ 2 ] * 1000.0f ) )
1740 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1742 int fgJoystickDetect( void )
1746 fgInitialiseJoysticks ();
1751 if ( !fgState.JoysticksInitialised )
1754 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1755 if( fgJoystick[ident] && !fgJoystick[ident]->error )
1762 * Joystick information functions
1764 int glutJoystickGetNumAxes( int ident )
1766 return fgJoystick[ ident ]->num_axes;
1768 int glutJoystickGetNumButtons( int ident )
1770 return fgJoystick[ ident ]->num_buttons;
1772 int glutJoystickNotWorking( int ident )
1774 return fgJoystick[ ident ]->error;
1777 float glutJoystickGetDeadBand( int ident, int axis )
1779 return fgJoystick[ ident ]->dead_band [ axis ];
1781 void glutJoystickSetDeadBand( int ident, int axis, float db )
1783 fgJoystick[ ident ]->dead_band[ axis ] = db;
1786 float glutJoystickGetSaturation( int ident, int axis )
1788 return fgJoystick[ ident ]->saturate[ axis ];
1790 void glutJoystickSetSaturation( int ident, int axis, float st )
1792 fgJoystick[ ident ]->saturate [ axis ] = st;
1795 void glutJoystickSetMinRange( int ident, float *axes )
1797 memcpy( fgJoystick[ ident ]->min, axes,
1798 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1800 void glutJoystickSetMaxRange( int ident, float *axes )
1802 memcpy( fgJoystick[ ident ]->max, axes,
1803 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1805 void glutJoystickSetCenter( int ident, float *axes )
1807 memcpy( fgJoystick[ ident ]->center, axes,
1808 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1811 void glutJoystickGetMinRange( int ident, float *axes )
1813 memcpy( axes, fgJoystick[ ident ]->min,
1814 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1816 void glutJoystickGetMaxRange( int ident, float *axes )
1818 memcpy( axes, fgJoystick[ ident ]->max,
1819 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1821 void glutJoystickGetCenter( int ident, float *axes )
1823 memcpy( axes, fgJoystick[ ident ]->center,
1824 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1827 /*** END OF FILE ***/