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"
36 #ifdef HAVE_SYS_PARAM_H
37 # include <sys/param.h>
41 * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
45 /* XXX It might be better to poll the operating system for the numbers of buttons and
46 * XXX axes and then dynamically allocate the arrays.
48 #define _JS_MAX_BUTTONS 32
50 #if TARGET_HOST_MACINTOSH
51 # define _JS_MAX_AXES 9
52 # include <InputSprocket.h>
55 #if TARGET_HOST_MAC_OSX
56 # define _JS_MAX_AXES 16
57 # include <mach/mach.h>
58 # include <IOKit/IOkitLib.h>
59 # include <IOKit/hid/IOHIDLib.h>
62 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
63 # define _JS_MAX_AXES 8
65 # include <mmsystem.h>
70 #if TARGET_HOST_POSIX_X11
71 # define _JS_MAX_AXES 16
72 # ifdef HAVE_SYS_IOCTL_H
73 # include <sys/ioctl.h>
82 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
83 /* XXX The below hack is done until freeglut's autoconf is updated. */
84 # define HAVE_USB_JS 1
86 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
87 # include <sys/joystick.h>
90 * XXX NetBSD/amd64 systems may find that they have to steal the
91 * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
92 * XXX I cannot comment whether that works for the interface, but
93 * XXX it lets you compile...(^& I do not think that we can do away
94 * XXX with this header.
96 # include <machine/joystick.h> /* For analog joysticks */
98 # define JS_DATA_TYPE joystick
99 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
102 # if defined(__linux__)
103 # include <linux/joystick.h>
105 /* check the joystick driver version */
106 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
109 # else /* Not BSD or Linux */
113 * We'll put these values in and that should
114 * allow the code to at least compile when there is
115 * no support. The JS open routine should error out
116 * and shut off all the code downstream anyway and if
117 * the application doesn't use a joystick we'll be fine.
127 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
135 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
137 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
140 # if defined(__NetBSD__)
141 /* XXX The below hack is done until freeglut's autoconf is updated. */
142 # define HAVE_USBHID_H 1
143 # ifdef HAVE_USBHID_H
148 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
149 # ifdef HAVE_USBHID_H
152 # include <libusbhid.h>
155 # include <legacy/dev/usb/usb.h>
156 # include <dev/usb/usbhid.h>
158 /* Compatibility with older usb.h revisions */
159 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
160 # define USB_MAX_DEVNAMES MAXDEVNAMES
164 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
165 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
166 struct os_specific_s {
170 /* The following structure members are specific to analog joysticks */
173 /* The following structure members are specific to USB joysticks */
174 struct hid_item *hids;
178 int axes_usage [ _JS_MAX_AXES ];
180 /* We keep button and axes state ourselves, as they might not be updated
181 * on every read of a USB device
184 float cache_axes [ _JS_MAX_AXES ];
187 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
188 # define USB_IDENT_OFFSET 2
190 # define USBDEV "/dev/usb"
191 # define UHIDDEV "/dev/uhid"
192 # define AJSDEV "/dev/joy"
196 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
197 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
198 * return the uhidN device name. We warn the user of this situation once.
200 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
202 struct usb_device_info di;
206 for (a = 1; a < USB_MAX_DEVICES; a++) {
208 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
210 for (i = 0; i < USB_MAX_DEVNAMES; i++)
211 if (di.udi_devnames[i][0] &&
212 strcmp(di.udi_devnames[i], dev) == 0) {
213 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
214 strcpy(cp, di.udi_vendor);
216 strcat(cp, di.udi_product);
217 strncpy(out, cp, outlen - 1);
226 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
231 static int protection_warned = 0;
233 for (i = 0; i < 16; i++) {
234 snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
235 f = open(buf, O_RDONLY);
237 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
243 else if (errno == EACCES) {
244 if (!protection_warned) {
245 fgWarning ( "Can't open %s for read!", buf );
246 protection_warned = 1;
254 static int fghJoystickInitializeHID(struct os_specific_s *os,
255 int *num_axes, int *num_buttons)
257 int size, is_joystick;
258 # ifdef HAVE_USBHID_H
265 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
268 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
270 fgWarning ( "error: %s", os->fname );
277 # ifdef HAVE_USBHID_H
278 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
280 /*** XXX {report_id} may not be the right variable? ***/
282 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
284 fgWarning ( "error: %s%d", UHIDDEV, report_id );
289 size = hid_report_size( rd, hid_input, report_id );
291 size = hid_report_size( rd, 0, hid_input );
293 os->hid_data_buf = calloc( 1, size );
297 # ifdef HAVE_USBHID_H
298 d = hid_start_parse( rd, 1 << hid_input, report_id );
300 d = hid_start_parse( rd, 1 << hid_input );
302 while( hid_get_item( d, &h ) )
304 int usage, page, interesting_hid;
306 page = HID_PAGE( h.usage );
307 usage = HID_USAGE( h.usage );
309 /* This test is somewhat too simplistic, but this is how MicroSoft
310 * does, so I guess it works for all joysticks/game pads. */
311 is_joystick = is_joystick ||
312 ( h.kind == hid_collection &&
313 page == HUP_GENERIC_DESKTOP &&
314 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
316 if( h.kind != hid_input )
322 interesting_hid = TRUE;
323 if( page == HUP_GENERIC_DESKTOP )
334 if( *num_axes < _JS_MAX_AXES )
336 os->axes_usage[ *num_axes ] = usage;
341 /* Allocate two axes for a hat */
342 if( *num_axes + 1 < _JS_MAX_AXES )
344 os->axes_usage[ *num_axes ] = usage;
346 os->axes_usage[ *num_axes ] = usage;
351 interesting_hid = FALSE;
355 else if( page == HUP_BUTTON )
357 interesting_hid = ( usage > 0 ) &&
358 ( usage <= _JS_MAX_BUTTONS );
360 if( interesting_hid && usage - 1 > *num_buttons )
361 *num_buttons = usage - 1;
364 if( interesting_hid )
367 os->hids = calloc( 1, sizeof ( struct hid_item ) );
373 return os->hids != NULL;
379 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
380 * See "js.h" lines 80-178.
382 typedef struct tagSFG_Joystick SFG_Joystick;
383 struct tagSFG_Joystick
385 #if TARGET_HOST_MACINTOSH
386 #define ISP_NUM_AXIS 9
387 #define ISP_NUM_NEEDS 41
388 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
389 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
392 #if TARGET_HOST_MAC_OSX
393 IOHIDDeviceInterface ** hidDev;
394 IOHIDElementCookie buttonCookies[41];
395 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
396 long minReport[_JS_MAX_AXES],
397 maxReport[_JS_MAX_AXES];
400 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
407 #if TARGET_HOST_POSIX_X11
408 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
409 struct os_specific_s *os;
415 float tmp_axes [ _JS_MAX_AXES ];
417 struct JS_DATA_TYPE js;
430 float dead_band[ _JS_MAX_AXES ];
431 float saturate [ _JS_MAX_AXES ];
432 float center [ _JS_MAX_AXES ];
433 float max [ _JS_MAX_AXES ];
434 float min [ _JS_MAX_AXES ];
438 * Functions associated with the "jsJoystick" class in PLIB
440 #if TARGET_HOST_MAC_OSX
441 #define K_NUM_DEVICES 32
443 io_object_t ioDevices[K_NUM_DEVICES];
445 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
446 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
448 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
449 /* callback for CFArrayApply */
450 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
452 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
453 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
454 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
459 * The static joystick structure pointer
461 #define MAX_NUM_JOYSTICKS 2
462 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
466 * Read the raw joystick data
468 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
470 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
476 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
487 for( i = 0; i < joy->num_axes; i++ )
493 #if TARGET_HOST_MACINTOSH
498 for ( i = 0; i < joy->num_buttons; i++ )
501 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
504 *buttons |= state << i;
510 for ( i = 0; i < joy->num_axes; i++ )
513 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
516 axes [i] = (float) state;
521 #if TARGET_HOST_MAC_OSX
522 if ( buttons != NULL )
526 for ( i = 0; i < joy->num_buttons; i++ )
528 IOHIDEventStruct hidEvent;
529 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
530 if ( hidEvent.value )
537 for ( i = 0; i < joy->num_axes; i++ )
539 IOHIDEventStruct hidEvent;
540 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
541 axes[i] = hidEvent.value;
546 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
547 status = joyGetPosEx( joy->js_id, &joy->js );
549 if ( status != JOYERR_NOERROR )
551 joy->error = GL_TRUE;
556 *buttons = joy->js.dwButtons;
561 * WARNING - Fall through case clauses!!
563 switch ( joy->num_axes )
566 /* Generate two POV axes from the POV hat angle.
567 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
568 * hundredths of a degree, or 0xFFFF when idle.
570 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
577 /* This is the contentious bit: how to convert angle to X/Y.
578 * wk: I know of no define for PI that we could use here:
579 * SG_PI would pull in sg, M_PI is undefined for MSVC
580 * But the accuracy of the value of PI is very unimportant at
583 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
584 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
586 /* Convert to coordinates on a square so that North-East
587 * is (1,1) not (.7,.7), etc.
588 * s and c cannot both be zero so we won't divide by zero.
590 if ( fabs ( s ) < fabs ( c ) )
592 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
593 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
597 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
598 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
602 case 6: axes[5] = (float) joy->js.dwVpos;
603 case 5: axes[4] = (float) joy->js.dwUpos;
604 case 4: axes[3] = (float) joy->js.dwRpos;
605 case 3: axes[2] = (float) joy->js.dwZpos;
606 case 2: axes[1] = (float) joy->js.dwYpos;
607 case 1: axes[0] = (float) joy->js.dwXpos;
612 #if TARGET_HOST_POSIX_X11
613 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
614 if ( joy->os->is_analog )
616 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
617 if ( status != sizeof(joy->os->ajs) ) {
618 perror ( joy->os->fname );
619 joy->error = GL_TRUE;
622 if ( buttons != NULL )
623 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
627 axes[0] = (float) joy->os->ajs.x;
628 axes[1] = (float) joy->os->ajs.y;
635 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
639 for ( h = joy->os->hids; h; h = h->next )
641 int d = hid_get_data ( joy->os->hid_data_buf, h );
643 int page = HID_PAGE ( h->usage );
644 int usage = HID_USAGE ( h->usage );
646 if ( page == HUP_GENERIC_DESKTOP )
649 for ( i = 0; i < joy->num_axes; i++ )
650 if (joy->os->axes_usage[i] == usage)
652 if (usage == HUG_HAT_SWITCH)
656 joy->os->cache_axes[i] = (float)hatmap_x[d];
657 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
661 joy->os->cache_axes[i] = (float)d;
666 else if (page == HUP_BUTTON)
668 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
671 joy->os->cache_buttons |= (1 << ( usage - 1 ));
673 joy->os->cache_buttons &= ~(1 << ( usage - 1 ));
679 if ( len < 0 && errno != EAGAIN )
684 perror( joy->os->fname );
687 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
689 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
697 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
699 if ( status != sizeof( struct js_event ) )
702 if ( errno == EAGAIN )
704 /* Use the old values */
706 *buttons = joy->tmp_buttons;
708 memcpy( axes, joy->tmp_axes,
709 sizeof( float ) * joy->num_axes );
714 fgWarning ( "%s", joy->fname );
715 joy->error = GL_TRUE;
719 switch ( joy->js.type & ~JS_EVENT_INIT )
721 case JS_EVENT_BUTTON:
722 if( joy->js.value == 0 ) /* clear the flag */
723 joy->tmp_buttons &= ~( 1 << joy->js.number );
725 joy->tmp_buttons |= ( 1 << joy->js.number );
729 if ( joy->js.number < joy->num_axes )
731 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
734 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
739 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
741 /* use the old values */
743 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
745 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
751 *buttons = joy->tmp_buttons;
755 status = read( joy->fd, &joy->js, JS_RETURN );
757 if ( status != JS_RETURN )
759 fgWarning( "%s", joy->fname );
760 joy->error = GL_TRUE;
765 # if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
766 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
768 *buttons = joy->js.buttons;
773 axes[ 0 ] = (float) joy->js.x;
774 axes[ 1 ] = (float) joy->js.y;
781 * Correct the joystick axis data
783 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
785 if( value < joy->center[ axis ] )
787 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
790 if( xx < -joy->saturate[ axis ] )
793 if( xx > -joy->dead_band [ axis ] )
796 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
797 joy->dead_band[ axis ] );
799 return ( xx < -1.0f ) ? -1.0f : xx;
803 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
804 joy->center[ axis ] );
806 if( xx > joy->saturate[ axis ] )
809 if( xx < joy->dead_band[ axis ] )
812 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
813 joy->dead_band[ axis ] );
815 return ( xx > 1.0f ) ? 1.0f : xx;
820 * Read the corrected joystick data
822 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
824 float raw_axes[ _JS_MAX_AXES ];
833 for ( i=0; i<joy->num_axes; i++ )
837 fghJoystickRawRead( joy, buttons, raw_axes );
840 for( i=0; i<joy->num_axes; i++ )
841 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
845 * Happy happy happy joy joy joy (happy new year toudi :D)
849 #if TARGET_HOST_MAC_OSX
850 /** open the IOKit connection, enumerate all the HID devices, add their
851 interface references to the static array. We then use the array index
852 as the device number when we come to open() the joystick. */
853 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
855 CFMutableDictionaryRef hidMatch = NULL;
856 IOReturn rv = kIOReturnSuccess;
858 io_iterator_t hidIterator;
861 /* build a dictionary matching HID devices */
862 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
864 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
865 if (rv != kIOReturnSuccess || !hidIterator) {
866 fgWarning( "no joystick (HID) devices found" );
871 while ((ioDev = IOIteratorNext(hidIterator))) {
872 /* filter out keyboard and mouse devices */
873 CFDictionaryRef properties = getCFProperties(ioDev);
876 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
877 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
878 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
879 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
881 /* keep only joystick devices */
882 if ( ( page == kHIDPage_GenericDesktop ) && (
883 (usage == kHIDUsage_GD_Joystick)
884 || (usage == kHIDUsage_GD_GamePad)
885 || (usage == kHIDUsage_GD_MultiAxisController)
886 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
887 /* add it to the array */
888 ioDevices[numDevices++] = ioDev;
891 IOObjectRelease(hidIterator);
894 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
897 CFMutableDictionaryRef cfProperties;
900 /* comment copied from darwin/SDL_sysjoystick.c */
901 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
902 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
905 io_registry_entry_t parent1, parent2;
907 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
908 if (rv != kIOReturnSuccess) {
909 fgWarning ( "error getting device entry parent");
913 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
914 if (rv != kIOReturnSuccess) {
915 fgWarning ( "error getting device entry parent 2");
920 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
921 &cfProperties, kCFAllocatorDefault, kNilOptions);
922 if (rv != kIOReturnSuccess || !cfProperties) {
923 fgWarning ( "error getting device properties");
930 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
932 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
933 fgError ( "%s", "element enumerator passed non-dictionary value");
937 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
940 /** element enumerator function : pass NULL for top-level*/
941 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
943 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
944 "Joystick element type mismatch",
945 "fghJoystickEnumerateElements" );
947 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
948 CFArrayApplyFunction((CFArrayRef) element, range,
949 &fghJoystickElementEnumerator, joy );
952 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
954 long cookie, lmin, lmax;
955 int index = joy->num_axes++;
957 CFNumberGetValue ((CFNumberRef)
958 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
959 kCFNumberLongType, &cookie);
961 axisCookies[index] = (IOHIDElementCookie) cookie;
963 CFNumberGetValue ((CFNumberRef)
964 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
965 kCFNumberLongType, &lmin);
967 CFNumberGetValue ((CFNumberRef)
968 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
969 kCFNumberLongType, &lmax);
971 joy->min[index] = lmin;
972 joy->max[index] = lmax;
973 joy->dead_band[index] = 0.0;
974 joy->saturate[index] = 1.0;
975 joy->center[index] = (lmax + lmin) * 0.5;
978 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
981 CFNumberGetValue ((CFNumberRef)
982 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
983 kCFNumberLongType, &cookie);
985 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
986 /* anything else for buttons? */
989 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
991 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
992 /* do we map hats to axes or buttons? */
996 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
998 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1000 # if FREEGLUT_LIB_PRAGMAS
1001 # pragma comment (lib, "advapi32.lib")
1004 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1006 char buffer [ 256 ];
1008 char OEMKey [ 256 ];
1017 /* Open .. MediaResources\CurrentJoystickSettings */
1018 _snprintf ( buffer, sizeof(buffer), "%s\\%s\\%s",
1019 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1020 REGSTR_KEY_JOYCURR );
1022 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1024 if ( lr != ERROR_SUCCESS ) return 0;
1026 /* Get OEM Key name */
1027 dwcb = sizeof(OEMKey);
1029 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1030 _snprintf ( buffer, sizeof(buffer), "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1032 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1033 RegCloseKey ( hKey );
1035 if ( lr != ERROR_SUCCESS ) return 0;
1037 /* Open OEM Key from ...MediaProperties */
1038 _snprintf ( buffer, sizeof(buffer), "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1040 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1042 if ( lr != ERROR_SUCCESS ) return 0;
1047 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1049 RegCloseKey ( hKey );
1051 if ( lr != ERROR_SUCCESS ) return 0;
1058 static void fghJoystickOpen( SFG_Joystick* joy )
1061 #if TARGET_HOST_MACINTOSH
1064 #if TARGET_HOST_MAC_OSX
1067 IOCFPlugInInterface **plugin;
1069 HRESULT pluginResult;
1071 CFDictionaryRef props;
1072 CFTypeRef topLevelElement;
1074 #if TARGET_HOST_POSIX_X11
1075 # if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
1081 # if defined( __linux__ ) || TARGET_HOST_SOLARIS
1087 /* Silence gcc, the correct #ifdefs would be too fragile... */
1091 * Default values (for no joystick -- each conditional will reset the
1095 joy->num_axes = joy->num_buttons = 0;
1096 joy->name[ 0 ] = '\0';
1098 #if TARGET_HOST_MACINTOSH
1099 /* XXX FIXME: get joystick name in Mac */
1101 err = ISpStartup( );
1105 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1107 joy->error = GL_TRUE;
1109 /* initialize the needs structure */
1110 ISpNeed temp_isp_needs[ isp_num_needs ] =
1112 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1113 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1114 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1115 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1116 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1117 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1118 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1119 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1120 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1122 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1123 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1124 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1125 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1126 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1127 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1128 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1129 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1130 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1131 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1132 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1133 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1134 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1135 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1136 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1137 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1138 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1139 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1140 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1141 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1142 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1143 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1144 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1145 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1146 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1147 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1148 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1149 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1150 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1151 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1152 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1153 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1156 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1159 /* next two calls allow keyboard and mouse to emulate other input
1160 * devices (gamepads, joysticks, etc)
1163 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1167 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1171 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1172 joy->isp_needs, joy->isp_elem,
1174 ISP_CHECK_ERR( err )
1176 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1177 'freeglut', nil, 0, 128, 0 );
1178 ISP_CHECK_ERR( err )
1180 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1181 joy->num_axes = joy->isp_num_axis;
1183 for( i = 0; i < joy->num_axes; i++ )
1185 joy->dead_band[ i ] = 0;
1186 joy->saturate [ i ] = 1;
1187 joy->center [ i ] = kISpAxisMiddle;
1188 joy->max [ i ] = kISpAxisMaximum;
1189 joy->min [ i ] = kISpAxisMinimum;
1192 joy->error = GL_FALSE;
1195 joy->num_buttons = joy->num_axes = 0;
1198 #if TARGET_HOST_MAC_OSX
1199 if( joy->id >= numDevices )
1201 fgWarning( "device index out of range in fgJoystickOpen()" );
1205 /* create device interface */
1206 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1207 kIOHIDDeviceUserClientTypeID,
1208 kIOCFPlugInInterfaceID,
1211 if( rv != kIOReturnSuccess )
1213 fgWarning( "error creating plugin for io device" );
1217 pluginResult = ( *plugin )->QueryInterface(
1219 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1220 &( LPVOID )joy->hidDev
1223 if( pluginResult != S_OK )
1224 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1226 ( *plugin )->Release( plugin ); /* don't leak a ref */
1227 if( joy->hidDev == NULL )
1230 /* store the interface in this instance */
1231 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1232 if( rv != kIOReturnSuccess )
1234 fgWarning( "error opening device interface");
1238 props = getCFProperties( ioDevices[ joy->id ] );
1240 /* recursively enumerate all the bits */
1241 CFTypeRef topLevelElement =
1242 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1243 enumerateElements( topLevelElement );
1248 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
1249 joy->js.dwFlags = JOY_RETURNALL;
1250 joy->js.dwSize = sizeof( joy->js );
1252 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1255 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1258 if( joy->jsCaps.wNumAxes == 0 )
1261 joy->error = GL_TRUE;
1265 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1266 * at least for USB. Try to get the real name from the registry.
1268 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1269 sizeof( joy->name ) ) )
1271 fgWarning( "JS: Failed to read joystick name from registry" );
1272 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1275 /* Windows joystick drivers may provide any combination of
1276 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1278 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1280 joy->num_axes = _JS_MAX_AXES;
1281 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1282 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1287 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1288 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1289 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1290 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1291 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1292 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1293 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1294 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1295 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1296 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1297 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1298 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1301 /* Guess all the rest judging on the axes extremals */
1302 for( i = 0; i < joy->num_axes; i++ )
1304 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1305 joy->dead_band[ i ] = 0.0f;
1306 joy->saturate [ i ] = 1.0f;
1310 #if TARGET_HOST_POSIX_X11
1311 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
1312 for( i = 0; i < _JS_MAX_AXES; i++ )
1313 joy->os->cache_axes[ i ] = 0.0f;
1315 joy->os->cache_buttons = 0;
1317 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1320 if( joy->os->fd < 0 && errno == EACCES )
1321 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1324 joy->error =( joy->os->fd < 0 );
1330 joy->num_buttons = 0;
1331 if( joy->os->is_analog )
1334 char joyfname[ 1024 ];
1335 int noargs, in_no_axes;
1337 float axes [ _JS_MAX_AXES ];
1338 int buttons[ _JS_MAX_AXES ];
1341 joy->num_buttons = 32;
1343 fghJoystickRawRead( joy, buttons, axes );
1344 joy->error = axes[ 0 ] < -1000000000.0f;
1348 snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
1350 joyfile = fopen( joyfname, "r" );
1351 joy->error =( joyfile == NULL );
1355 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1356 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1357 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1358 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1363 for( i = 0; i < _JS_MAX_AXES; i++ )
1365 joy->dead_band[ i ] = 0.0f;
1366 joy->saturate [ i ] = 1.0f;
1369 return; /* End of analog code */
1373 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1374 &joy->num_buttons ) )
1376 close( joy->os->fd );
1377 joy->error = GL_TRUE;
1381 cp = strrchr( joy->os->fname, '/' );
1384 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1386 strcpy( joy->name, &cp[1] );
1389 if( joy->num_axes > _JS_MAX_AXES )
1390 joy->num_axes = _JS_MAX_AXES;
1392 for( i = 0; i < _JS_MAX_AXES; i++ )
1394 /* We really should get this from the HID, but that data seems
1395 * to be quite unreliable for analog-to-USB converters. Punt for
1398 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1400 joy->max [ i ] = 1.0f;
1401 joy->center[ i ] = 0.0f;
1402 joy->min [ i ] = -1.0f;
1406 joy->max [ i ] = 255.0f;
1407 joy->center[ i ] = 127.0f;
1408 joy->min [ i ] = 0.0f;
1411 joy->dead_band[ i ] = 0.0f;
1412 joy->saturate[ i ] = 1.0f;
1417 #if defined( __linux__ ) || TARGET_HOST_SOLARIS
1418 /* Default for older Linux systems. */
1420 joy->num_buttons = 32;
1423 for( i = 0; i < _JS_MAX_AXES; i++ )
1424 joy->tmp_axes[ i ] = 0.0f;
1426 joy->tmp_buttons = 0;
1429 joy->fd = open( joy->fname, O_RDONLY );
1431 joy->error =( joy->fd < 0 );
1436 /* Set the correct number of axes for the linux driver */
1438 /* Melchior Franz's fixes for big-endian Linuxes since writing
1439 * to the upper byte of an uninitialized word doesn't work.
1442 ioctl( joy->fd, JSIOCGAXES, &u );
1444 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1445 joy->num_buttons = u;
1446 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1447 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1451 * The Linux driver seems to return 512 for all axes
1452 * when no stick is present - but there is a chance
1453 * that could happen by accident - so it's gotta happen
1454 * on both axes for at least 100 attempts.
1456 * PWO: shouldn't be that done somehow wiser on the kernel level?
1463 fghJoystickRawRead( joy, NULL, joy->center );
1465 } while( !joy->error &&
1467 joy->center[ 0 ] == 512.0f &&
1468 joy->center[ 1 ] == 512.0f );
1470 if ( counter >= 100 )
1471 joy->error = GL_TRUE;
1474 for( i = 0; i < _JS_MAX_AXES; i++ )
1477 joy->max [ i ] = 32767.0f;
1478 joy->center[ i ] = 0.0f;
1479 joy->min [ i ] = -32767.0f;
1481 joy->max[ i ] = joy->center[ i ] * 2.0f;
1482 joy->min[ i ] = 0.0f;
1484 joy->dead_band[ i ] = 0.0f;
1485 joy->saturate [ i ] = 1.0f;
1492 * This function replaces the constructor method in the JS library.
1494 static void fghJoystickInit( int ident )
1496 if( ident >= MAX_NUM_JOYSTICKS )
1497 fgError( "Too large a joystick number: %d", ident );
1499 if( fgJoystick[ ident ] )
1500 fgError( "illegal attempt to initialize joystick device again" );
1502 fgJoystick[ ident ] =
1503 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1506 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1507 fgJoystick[ ident ]->error = GL_TRUE;
1509 #if TARGET_HOST_MACINTOSH
1510 fgJoystick[ ident ]->id = ident;
1511 snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident ); /* FIXME */
1512 fgJoystick[ ident ]->error = GL_FALSE;
1515 #if TARGET_HOST_MAC_OSX
1516 fgJoystick[ ident ]->id = ident;
1517 fgJoystick[ ident ]->error = GL_FALSE;
1518 fgJoystick[ ident ]->num_axes = 0;
1519 fgJoystick[ ident ]->num_buttons = 0;
1521 if( numDevices < 0 )
1523 /* do first-time init (since we can't over-ride jsInit, hmm */
1526 mach_port_t masterPort;
1527 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1528 if( rv != kIOReturnSuccess )
1530 fgWarning( "error getting master Mach port" );
1533 fghJoystickFindDevices( masterPort );
1536 if ( ident >= numDevices )
1538 fgJoystick[ ident ]->error = GL_TRUE;
1542 /* get the name now too */
1543 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1544 CFTypeRef ref = CFDictionaryGetValue( properties,
1545 CFSTR( kIOHIDProductKey ) );
1547 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1550 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1551 CFStringGetSystemEncoding( ) ) )
1553 fgWarning( "error getting device name" );
1558 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
1562 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1563 fgJoystick[ ident ]->error = GL_FALSE;
1566 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1567 fgJoystick[ ident ]->error = GL_FALSE;
1570 fgJoystick[ ident ]->num_axes = 0;
1571 fgJoystick[ ident ]->error = GL_TRUE;
1576 #if TARGET_HOST_POSIX_X11
1577 # if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
1578 fgJoystick[ ident ]->id = ident;
1579 fgJoystick[ ident ]->error = GL_FALSE;
1581 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1582 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1583 if( ident < USB_IDENT_OFFSET )
1584 fgJoystick[ ident ]->os->is_analog = 1;
1585 if( fgJoystick[ ident ]->os->is_analog )
1586 snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", AJSDEV, ident );
1588 snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", UHIDDEV,
1589 ident - USB_IDENT_OFFSET );
1590 # elif defined( __linux__ )
1591 fgJoystick[ ident ]->id = ident;
1592 fgJoystick[ ident ]->error = GL_FALSE;
1594 snprintf( fgJoystick[ident]->fname, sizeof(fgJoystick[ident]->fname), "/dev/input/js%d", ident );
1596 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1597 snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident );
1601 fghJoystickOpen( fgJoystick[ ident ] );
1605 * Try initializing all the joysticks (well, both of them)
1607 void fgInitialiseJoysticks ( void )
1609 if( !fgState.JoysticksInitialised )
1612 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1613 fghJoystickInit( ident );
1615 fgState.JoysticksInitialised = GL_TRUE;
1622 void fgJoystickClose( void )
1625 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1627 if( fgJoystick[ ident ] )
1630 #if TARGET_HOST_MACINTOSH
1636 #if TARGET_HOST_MAC_OSX
1637 ( *( fgJoystick[ ident ]->hidDev ) )->
1638 close( fgJoystick[ ident ]->hidDev );
1641 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
1642 /* Do nothing special */
1645 #if TARGET_HOST_POSIX_X11
1646 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
1647 if( fgJoystick[ident]->os )
1649 if( ! fgJoystick[ ident ]->error )
1650 close( fgJoystick[ ident ]->os->fd );
1652 if( fgJoystick[ ident ]->os->hids )
1653 free (fgJoystick[ ident ]->os->hids);
1654 if( fgJoystick[ ident ]->os->hid_data_buf )
1655 free( fgJoystick[ ident ]->os->hid_data_buf );
1657 free( fgJoystick[ident]->os );
1661 if( ! fgJoystick[ident]->error )
1662 close( fgJoystick[ ident ]->fd );
1665 free( fgJoystick[ ident ] );
1666 fgJoystick[ ident ] = NULL;
1667 /* show joystick has been deinitialized */
1673 * Polls the joystick and executes the joystick callback hooked to the
1674 * window specified in the function's parameter:
1676 void fgJoystickPollWindow( SFG_Window* window )
1678 float axes[ _JS_MAX_AXES ];
1682 freeglut_return_if_fail( window );
1683 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1685 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1687 if( fgJoystick[ident] )
1689 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1691 if( !fgJoystick[ident]->error )
1692 INVOKE_WCB( *window, Joystick,
1694 (int) ( axes[ 0 ] * 1000.0f ),
1695 (int) ( axes[ 1 ] * 1000.0f ),
1696 (int) ( axes[ 2 ] * 1000.0f ) )
1703 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1705 int fgJoystickDetect( void )
1709 fgInitialiseJoysticks ();
1711 if ( !fgState.JoysticksInitialised )
1714 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1715 if( fgJoystick[ident] && !fgJoystick[ident]->error )
1722 * Joystick information functions
1724 int glutJoystickGetNumAxes( int ident )
1726 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
1727 return fgJoystick[ ident ]->num_axes;
1729 int glutJoystickGetNumButtons( int ident )
1731 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
1732 return fgJoystick[ ident ]->num_buttons;
1734 int glutJoystickNotWorking( int ident )
1736 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
1737 return fgJoystick[ ident ]->error;
1740 float glutJoystickGetDeadBand( int ident, int axis )
1742 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1743 return fgJoystick[ ident ]->dead_band [ axis ];
1745 void glutJoystickSetDeadBand( int ident, int axis, float db )
1747 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1748 fgJoystick[ ident ]->dead_band[ axis ] = db;
1751 float glutJoystickGetSaturation( int ident, int axis )
1753 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1754 return fgJoystick[ ident ]->saturate[ axis ];
1756 void glutJoystickSetSaturation( int ident, int axis, float st )
1758 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1759 fgJoystick[ ident ]->saturate [ axis ] = st;
1762 void glutJoystickSetMinRange( int ident, float *axes )
1764 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1765 memcpy( fgJoystick[ ident ]->min, axes,
1766 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1768 void glutJoystickSetMaxRange( int ident, float *axes )
1770 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1771 memcpy( fgJoystick[ ident ]->max, axes,
1772 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1774 void glutJoystickSetCenter( int ident, float *axes )
1776 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1777 memcpy( fgJoystick[ ident ]->center, axes,
1778 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1781 void glutJoystickGetMinRange( int ident, float *axes )
1783 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1784 memcpy( axes, fgJoystick[ ident ]->min,
1785 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1787 void glutJoystickGetMaxRange( int ident, float *axes )
1789 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1790 memcpy( axes, fgJoystick[ ident ]->max,
1791 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1793 void glutJoystickGetCenter( int ident, float *axes )
1795 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1796 memcpy( axes, fgJoystick[ ident ]->center,
1797 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1800 /*** END OF FILE ***/