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 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
929 "Joystick element type mismatch",
930 "fghJoystickEnumerateElements" );
932 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
933 CFArrayApplyFunction((CFArrayRef) element, range,
934 &fghJoystickElementEnumerator, joy );
937 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
939 CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
940 CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
942 long type, page, usage;
944 CFNumberGetValue((CFNumberRef)
945 CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
946 kCFNumberLongType, &type);
949 case kIOHIDElementTypeInput_Misc:
950 case kIOHIDElementTypeInput_Axis:
951 case kIOHIDElementTypeInput_Button:
952 printf("got input element...");
953 CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
954 CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
956 if (page == kHIDPage_GenericDesktop) {
957 switch ( usage ) /* look at usage to determine function */
962 case kHIDUsage_GD_Rx:
963 case kHIDUsage_GD_Ry:
964 case kHIDUsage_GD_Rz:
965 case kHIDUsage_GD_Slider: /* for throttle / trim controls */
967 fghJoystickAddAxisElement((CFDictionaryRef) element);
970 case kHIDUsage_GD_Hatswitch:
972 fghJoystickAddHatElement((CFDictionaryRef) element);
976 fgWarning ( "input type element has weird usage (%x)", usage);
979 } else if (page == kHIDPage_Button) {
981 fghJoystickAddButtonElement((CFDictionaryRef) element);
983 fgWarning ( "input type element has weird page (%x)", page);
986 case kIOHIDElementTypeCollection:
987 fghJoystickEnumerateElements (
988 CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
997 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
999 long cookie, lmin, lmax;
1000 int index = joy->num_axes++;
1002 CFNumberGetValue ((CFNumberRef)
1003 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1004 kCFNumberLongType, &cookie);
1006 axisCookies[index] = (IOHIDElementCookie) cookie;
1008 CFNumberGetValue ((CFNumberRef)
1009 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1010 kCFNumberLongType, &lmin);
1012 CFNumberGetValue ((CFNumberRef)
1013 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1014 kCFNumberLongType, &lmax);
1016 joy->min[index] = lmin;
1017 joy->max[index] = lmax;
1018 joy->dead_band[index] = 0.0;
1019 joy->saturate[index] = 1.0;
1020 joy->center[index] = (lmax + lmin) * 0.5;
1023 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1026 CFNumberGetValue ((CFNumberRef)
1027 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1028 kCFNumberLongType, &cookie);
1030 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1031 /* anything else for buttons? */
1034 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1036 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1037 /* do we map hats to axes or buttons? */
1041 #if TARGET_HOST_WIN32
1043 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1045 # if defined(_MSC_VER)
1046 # pragma comment (lib, "advapi32.lib")
1049 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1051 char buffer [ 256 ];
1053 char OEMKey [ 256 ];
1062 /* Open .. MediaResources\CurrentJoystickSettings */
1063 sprintf ( buffer, "%s\\%s\\%s",
1064 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1065 REGSTR_KEY_JOYCURR );
1067 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1069 if ( lr != ERROR_SUCCESS ) return 0;
1071 /* Get OEM Key name */
1072 dwcb = sizeof(OEMKey);
1074 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1075 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1077 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1078 RegCloseKey ( hKey );
1080 if ( lr != ERROR_SUCCESS ) return 0;
1082 /* Open OEM Key from ...MediaProperties */
1083 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1085 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1087 if ( lr != ERROR_SUCCESS ) return 0;
1092 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1094 RegCloseKey ( hKey );
1096 if ( lr != ERROR_SUCCESS ) return 0;
1103 static void fghJoystickOpen( SFG_Joystick* joy )
1106 #if TARGET_HOST_MACINTOSH
1109 #if TARGET_HOST_MAC_OSX
1112 IOCFPlugInInterface **plugin;
1114 HRESULT pluginResult;
1116 CFDictionaryRef props;
1117 CFTypeRef topLevelElement;
1119 #if TARGET_HOST_UNIX_X11
1120 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1131 * Default values (for no joystick -- each conditional will reset the
1135 joy->num_axes = joy->num_buttons = 0;
1136 joy->name[ 0 ] = '\0';
1138 #if TARGET_HOST_MACINTOSH
1139 /* XXX FIXME: get joystick name in Mac */
1141 err = ISpStartup( );
1145 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1147 joy->error = GL_TRUE;
1149 /* initialize the needs structure */
1150 ISpNeed temp_isp_needs[ isp_num_needs ] =
1152 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1153 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1154 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1155 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1156 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1157 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1158 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1159 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1160 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1162 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1190 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1191 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1192 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1193 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1196 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1199 /* next two calls allow keyboard and mouse to emulate other input
1200 * devices (gamepads, joysticks, etc)
1203 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1207 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1211 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1212 joy->isp_needs, joy->isp_elem,
1214 ISP_CHECK_ERR( err )
1216 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1217 'freeglut', nil, 0, 128, 0 );
1218 ISP_CHECK_ERR( err )
1220 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1221 joy->num_axes = joy->isp_num_axis;
1223 for( i = 0; i < joy->num_axes; i++ )
1225 joy->dead_band[ i ] = 0;
1226 joy->saturate [ i ] = 1;
1227 joy->center [ i ] = kISpAxisMiddle;
1228 joy->max [ i ] = kISpAxisMaximum;
1229 joy->min [ i ] = kISpAxisMinimum;
1232 joy->error = GL_FALSE;
1235 joy->num_buttons = joy->num_axes = 0;
1238 #if TARGET_HOST_MAC_OSX
1239 if( joy->id >= numDevices )
1241 fgWarning( "device index out of range in fgJoystickOpen()" );
1245 /* create device interface */
1246 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1247 kIOHIDDeviceUserClientTypeID,
1248 kIOCFPlugInInterfaceID,
1251 if( rv != kIOReturnSuccess )
1253 fgWarning( "error creating plugin for io device" );
1257 pluginResult = ( *plugin )->QueryInterface(
1259 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1260 &( LPVOID )joy->hidDev
1263 if( pluginResult != S_OK )
1264 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1266 ( *plugin )->Release( plugin ); /* don't leak a ref */
1267 if( joy->hidDev == NULL )
1270 /* store the interface in this instance */
1271 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1272 if( rv != kIOReturnSuccess )
1274 fgWarning( "error opening device interface");
1278 props = getCFProperties( ioDevices[ joy->id ] );
1280 /* recursively enumerate all the bits */
1281 CFTypeRef topLevelElement =
1282 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1283 enumerateElements( topLevelElement );
1288 #if TARGET_HOST_WIN32
1289 joy->js.dwFlags = JOY_RETURNALL;
1290 joy->js.dwSize = sizeof( joy->js );
1292 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1295 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1298 if( joy->jsCaps.wNumAxes == 0 )
1301 joy->error = GL_TRUE;
1305 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1306 * at least for USB. Try to get the real name from the registry.
1308 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1309 sizeof( joy->name ) ) )
1311 fgWarning( "JS: Failed to read joystick name from registry" );
1312 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1315 /* Windows joystick drivers may provide any combination of
1316 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1318 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1320 joy->num_axes = _JS_MAX_AXES;
1321 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1322 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1327 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1328 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1329 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1330 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1331 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1332 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1333 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1334 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1335 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1336 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1337 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1338 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1341 /* Guess all the rest judging on the axes extremals */
1342 for( i = 0; i < joy->num_axes; i++ )
1344 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1345 joy->dead_band[ i ] = 0.0f;
1346 joy->saturate [ i ] = 1.0f;
1350 #if TARGET_HOST_UNIX_X11
1351 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1352 for( i = 0; i < _JS_MAX_AXES; i++ )
1353 joy->os->cache_axes[ i ] = 0.0f;
1355 joy->os->cache_buttons = 0;
1357 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1359 if( joy->os->fd < 0 && errno == EACCES )
1360 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1362 joy->error =( joy->os->fd < 0 );
1368 joy->num_buttons = 0;
1369 if( joy->os->is_analog )
1372 char joyfname[ 1024 ];
1373 int noargs, in_no_axes;
1375 float axes [ _JS_MAX_AXES ];
1376 int buttons[ _JS_MAX_AXES ];
1379 joy->num_buttons = 32;
1381 fghJoystickRawRead( joy, buttons, axes );
1382 joy->error = axes[ 0 ] < -1000000000.0f;
1386 sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
1388 joyfile = fopen( joyfname, "r" );
1389 joy->error =( joyfile == NULL );
1393 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1394 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1395 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1396 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1401 for( i = 0; i < _JS_MAX_AXES; i++ )
1403 joy->dead_band[ i ] = 0.0f;
1404 joy->saturate [ i ] = 1.0f;
1407 return; /* End of analog code */
1411 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1412 &joy->num_buttons ) )
1414 close( joy->os->fd );
1415 joy->error = GL_TRUE;
1419 cp = strrchr( joy->os->fname, '/' );
1422 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1424 strcpy( joy->name, &cp[1] );
1427 if( joy->num_axes > _JS_MAX_AXES )
1428 joy->num_axes = _JS_MAX_AXES;
1430 for( i = 0; i < _JS_MAX_AXES; i++ )
1432 /* We really should get this from the HID, but that data seems
1433 * to be quite unreliable for analog-to-USB converters. Punt for
1436 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1438 joy->max [ i ] = 1.0f;
1439 joy->center[ i ] = 0.0f;
1440 joy->min [ i ] = -1.0f;
1444 joy->max [ i ] = 255.0f;
1445 joy->center[ i ] = 127.0f;
1446 joy->min [ i ] = 0.0f;
1449 joy->dead_band[ i ] = 0.0f;
1450 joy->saturate[ i ] = 1.0f;
1455 #if defined( __linux__ )
1456 /* Default for older Linux systems. */
1458 joy->num_buttons = 32;
1461 for( i = 0; i < _JS_MAX_AXES; i++ )
1462 joy->tmp_axes[ i ] = 0.0f;
1464 joy->tmp_buttons = 0;
1467 joy->fd = open( joy->fname, O_RDONLY );
1469 joy->error =( joy->fd < 0 );
1474 /* Set the correct number of axes for the linux driver */
1476 /* Melchior Franz's fixes for big-endian Linuxes since writing
1477 * to the upper byte of an uninitialized word doesn't work.
1480 ioctl( joy->fd, JSIOCGAXES, &u );
1482 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1483 joy->num_buttons = u;
1484 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1485 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1489 * The Linux driver seems to return 512 for all axes
1490 * when no stick is present - but there is a chance
1491 * that could happen by accident - so it's gotta happen
1492 * on both axes for at least 100 attempts.
1494 * PWO: shouldn't be that done somehow wiser on the kernel level?
1501 fghJoystickRawRead( joy, NULL, joy->center );
1503 } while( !joy->error &&
1505 joy->center[ 0 ] == 512.0f &&
1506 joy->center[ 1 ] == 512.0f );
1508 if ( counter >= 100 )
1509 joy->error = GL_TRUE;
1512 for( i = 0; i < _JS_MAX_AXES; i++ )
1515 joy->max [ i ] = 32767.0f;
1516 joy->center[ i ] = 0.0f;
1517 joy->min [ i ] = -32767.0f;
1519 joy->max[ i ] = joy->center[ i ] * 2.0f;
1520 joy->min[ i ] = 0.0f;
1522 joy->dead_band[ i ] = 0.0f;
1523 joy->saturate [ i ] = 1.0f;
1530 * This function replaces the constructor method in the JS library.
1532 static void fghJoystickInit( int ident )
1534 if( ident >= MAX_NUM_JOYSTICKS )
1535 fgError( "Too large a joystick number: %d", ident );
1537 if( fgJoystick[ ident ] )
1538 fgError( "illegal attempt to initialize joystick device again" );
1540 fgJoystick[ ident ] =
1541 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1544 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1545 fgJoystick[ ident ]->error = GL_TRUE;
1547 #if TARGET_HOST_MACINTOSH
1548 fgJoystick[ ident ]->id = ident;
1549 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
1550 fgJoystick[ ident ]->error = GL_FALSE;
1553 #if TARGET_HOST_MAC_OSX
1554 fgJoystick[ ident ]->id = ident;
1555 fgJoystick[ ident ]->error = GL_FALSE;
1556 fgJoystick[ ident ]->num_axes = 0;
1557 fgJoystick[ ident ]->num_buttons = 0;
1559 if( numDevices < 0 )
1561 /* do first-time init (since we can't over-ride jsInit, hmm */
1564 mach_port_t masterPort;
1565 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1566 if( rv != kIOReturnSuccess )
1568 fgWarning( "error getting master Mach port" );
1571 fghJoystickFindDevices( masterPort );
1574 if ( ident >= numDevices )
1576 fgJoystick[ ident ]->error = GL_TRUE;
1580 /* get the name now too */
1581 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1582 CFTypeRef ref = CFDictionaryGetValue( properties,
1583 CFSTR( kIOHIDProductKey ) );
1585 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1588 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1589 CFStringGetSystemEncoding( ) ) )
1591 fgWarning( "error getting device name" );
1596 #if TARGET_HOST_WIN32
1600 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1601 fgJoystick[ ident ]->error = GL_FALSE;
1604 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1605 fgJoystick[ ident ]->error = GL_FALSE;
1608 fgJoystick[ ident ]->num_axes = 0;
1609 fgJoystick[ ident ]->error = GL_TRUE;
1614 #if TARGET_HOST_UNIX_X11
1615 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1616 fgJoystick[ ident ]->id = ident;
1617 fgJoystick[ ident ]->error = GL_FALSE;
1619 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1620 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1621 if( ident < USB_IDENT_OFFSET )
1622 fgJoystick[ ident ]->os->is_analog = 1;
1623 if( fgJoystick[ ident ]->os->is_analog )
1624 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
1626 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
1627 ident - USB_IDENT_OFFSET );
1628 # elif defined( __linux__ )
1629 fgJoystick[ ident ]->id = ident;
1630 fgJoystick[ ident ]->error = GL_FALSE;
1632 sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1634 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1635 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
1639 fghJoystickOpen( fgJoystick[ ident ] );
1643 * Try initializing all the joysticks (well, both of them)
1645 void fgInitialiseJoysticks ( void )
1647 /* Initialization courtesy of OpenGLUT -- do we want it? */
1648 if( !fgState.JoysticksInitialised )
1651 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1652 fghJoystickInit( ident );
1654 fgState.JoysticksInitialised = GL_TRUE;
1661 void fgJoystickClose( void )
1664 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1666 if( fgJoystick[ ident ] )
1669 #if TARGET_HOST_MACINTOSH
1675 #if TARGET_HOST_MAC_OSX
1676 ( *( fgJoystick[ ident ]->hidDev ) )->
1677 close( fgJoystick[ ident ]->hidDev );
1680 #if TARGET_HOST_WIN32
1681 /* Do nothing special */
1684 #if TARGET_HOST_UNIX_X11
1685 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1686 if( fgJoystick[ident]->os )
1688 if( ! fgJoystick[ ident ]->error )
1689 close( fgJoystick[ ident ]->os->fd );
1691 if( fgJoystick[ ident ]->os->hids )
1692 free (fgJoystick[ ident ]->os->hids);
1693 if( fgJoystick[ ident ]->os->hid_data_buf )
1694 free( fgJoystick[ ident ]->os->hid_data_buf );
1696 free( fgJoystick[ident]->os );
1700 if( ! fgJoystick[ident]->error )
1701 close( fgJoystick[ ident ]->fd );
1704 free( fgJoystick[ ident ] );
1705 fgJoystick[ ident ] = NULL;
1706 /* show joystick has been deinitialized */
1712 * Polls the joystick and executes the joystick callback hooked to the
1713 * window specified in the function's parameter:
1715 void fgJoystickPollWindow( SFG_Window* window )
1717 float axes[ _JS_MAX_AXES ];
1721 freeglut_return_if_fail( window );
1722 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1724 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1726 if( fgJoystick[ident] )
1728 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1730 if( !fgJoystick[ident]->error )
1731 INVOKE_WCB( *window, Joystick,
1733 (int) ( axes[ 0 ] * 1000.0f ),
1734 (int) ( axes[ 1 ] * 1000.0f ),
1735 (int) ( axes[ 2 ] * 1000.0f ) )
1742 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1744 int fgJoystickDetect( void )
1748 fgInitialiseJoysticks ();
1753 if ( !fgState.JoysticksInitialised )
1756 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1757 if( fgJoystick[ident] && !fgJoystick[ident]->error )
1764 * Joystick information functions
1766 int glutJoystickGetNumAxes( int ident )
1768 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
1769 return fgJoystick[ ident ]->num_axes;
1771 int glutJoystickGetNumButtons( int ident )
1773 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
1774 return fgJoystick[ ident ]->num_buttons;
1776 int glutJoystickNotWorking( int ident )
1778 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
1779 return fgJoystick[ ident ]->error;
1782 float glutJoystickGetDeadBand( int ident, int axis )
1784 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1785 return fgJoystick[ ident ]->dead_band [ axis ];
1787 void glutJoystickSetDeadBand( int ident, int axis, float db )
1789 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1790 fgJoystick[ ident ]->dead_band[ axis ] = db;
1793 float glutJoystickGetSaturation( int ident, int axis )
1795 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1796 return fgJoystick[ ident ]->saturate[ axis ];
1798 void glutJoystickSetSaturation( int ident, int axis, float st )
1800 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1801 fgJoystick[ ident ]->saturate [ axis ] = st;
1804 void glutJoystickSetMinRange( int ident, float *axes )
1806 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1807 memcpy( fgJoystick[ ident ]->min, axes,
1808 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1810 void glutJoystickSetMaxRange( int ident, float *axes )
1812 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1813 memcpy( fgJoystick[ ident ]->max, axes,
1814 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1816 void glutJoystickSetCenter( int ident, float *axes )
1818 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1819 memcpy( fgJoystick[ ident ]->center, axes,
1820 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1823 void glutJoystickGetMinRange( int ident, float *axes )
1825 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1826 memcpy( axes, fgJoystick[ ident ]->min,
1827 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1829 void glutJoystickGetMaxRange( int ident, float *axes )
1831 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1832 memcpy( axes, fgJoystick[ ident ]->max,
1833 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1835 void glutJoystickGetCenter( int ident, float *axes )
1837 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1838 memcpy( axes, fgJoystick[ ident ]->center,
1839 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1842 /*** END OF FILE ***/