4 * Joystick handling code
6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7 * Written by Steve Baker, <sjbaker1@airmail.net>
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
23 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu>
30 * Redone by John Fay 2/4/04 with another look from the PLIB "js" library.
31 * Many thanks for Steve Baker for permission to pull from that library.
34 #include <GL/freeglut.h>
35 #include "freeglut_internal.h"
37 # include <sys/param.h>
41 * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
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>
63 # define _JS_MAX_AXES 8
65 # include <mmsystem.h>
70 #if TARGET_HOST_UNIX_X11
71 # define _JS_MAX_AXES 16
73 # include <sys/ioctl.h>
79 # if defined(__FreeBSD__) || defined(__NetBSD__)
80 /* XXX The below hack is done until freeglut's autoconf is updated. */
81 # define HAVE_USB_JS 1
83 # if defined(__FreeBSD__)
84 # include <sys/joystick.h>
87 * XXX NetBSD/amd64 systems may find that they have to steal the
88 * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
89 * XXX I cannot comment whether that works for the interface, but
90 * XXX it lets you compile...(^& I do not think that we can do away
91 * XXX with this header.
93 # include <machine/joystick.h> /* For analog joysticks */
95 # define JS_DATA_TYPE joystick
96 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
99 # if defined(__linux__)
100 # include <linux/joystick.h>
102 /* check the joystick driver version */
103 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
106 # else /* Not BSD or Linux */
110 * We'll put these values in and that should
111 * allow the code to at least compile when there is
112 * no support. The JS open routine should error out
113 * and shut off all the code downstream anyway and if
114 * the application doesn't use a joystick we'll be fine.
124 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
132 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
134 #if defined(__NetBSD__) || defined(__FreeBSD__)
137 # if defined(__NetBSD__)
138 /* XXX The below hack is done until freeglut's autoconf is updated. */
139 # define HAVE_USBHID_H 1
140 # ifdef HAVE_USBHID_H
145 # elif defined(__FreeBSD__)
146 # if __FreeBSD_version < 500000
147 # include <libusbhid.h>
149 /* XXX The below hack is done until freeglut's autoconf is updated. */
150 # define HAVE_USBHID_H 1
154 # include <dev/usb/usb.h>
155 # include <dev/usb/usbhid.h>
157 /* Compatibility with older usb.h revisions */
158 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
159 # define USB_MAX_DEVNAMES MAXDEVNAMES
163 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
164 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
165 struct os_specific_s {
169 /* The following structure members are specific to analog joysticks */
172 /* The following structure members are specific to USB joysticks */
173 struct hid_item *hids;
177 int axes_usage [ _JS_MAX_AXES ];
179 /* We keep button and axes state ourselves, as they might not be updated
180 * on every read of a USB device
183 float cache_axes [ _JS_MAX_AXES ];
186 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
187 # define USB_IDENT_OFFSET 2
189 # define USBDEV "/dev/usb"
190 # define UHIDDEV "/dev/uhid"
191 # define AJSDEV "/dev/joy"
195 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
196 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
197 * return the uhidN device name. We warn the user of this situation once.
199 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
201 struct usb_device_info di;
205 for (a = 1; a < USB_MAX_DEVICES; a++) {
207 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
209 for (i = 0; i < USB_MAX_DEVNAMES; i++)
210 if (di.udi_devnames[i][0] &&
211 strcmp(di.udi_devnames[i], dev) == 0) {
212 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
213 strcpy(cp, di.udi_vendor);
215 strcat(cp, di.udi_product);
216 strncpy(out, cp, outlen - 1);
225 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
230 static int protection_warned = 0;
232 for (i = 0; i < 16; i++) {
233 sprintf(buf, "%s%d", USBDEV, i);
234 f = open(buf, O_RDONLY);
236 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
240 } else if (errno == EACCES) {
241 if (!protection_warned) {
242 fgWarning ( "Can't open %s for read!", buf );
243 protection_warned = 1;
250 static int fghJoystickInitializeHID(struct os_specific_s *os,
251 int *num_axes, int *num_buttons)
253 int size, is_joystick;
254 # ifdef HAVE_USBHID_H
261 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
263 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
269 # ifdef HAVE_USBHID_H
270 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
272 /*** XXX {report_id} may not be the right variable? ***/
273 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
277 size = hid_report_size( rd, hid_input, report_id );
279 size = hid_report_size( rd, 0, hid_input );
281 os->hid_data_buf = calloc( 1, size );
285 # ifdef HAVE_USBHID_H
286 d = hid_start_parse( rd, 1 << hid_input, report_id );
288 d = hid_start_parse( rd, 1 << hid_input );
290 while( hid_get_item( d, &h ) )
292 int usage, page, interesting_hid;
294 page = HID_PAGE( h.usage );
295 usage = HID_USAGE( h.usage );
297 /* This test is somewhat too simplistic, but this is how MicroSoft
298 * does, so I guess it works for all joysticks/game pads. */
299 is_joystick = is_joystick ||
300 ( h.kind == hid_collection &&
301 page == HUP_GENERIC_DESKTOP &&
302 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
304 if( h.kind != hid_input )
310 interesting_hid = TRUE;
311 if( page == HUP_GENERIC_DESKTOP )
322 if( *num_axes < _JS_MAX_AXES )
324 os->axes_usage[ *num_axes ] = usage;
329 /* Allocate two axes for a hat */
330 if( *num_axes + 1 < _JS_MAX_AXES )
332 os->axes_usage[ *num_axes ] = usage;
334 os->axes_usage[ *num_axes ] = usage;
339 interesting_hid = FALSE;
343 else if( page == HUP_BUTTON )
345 interesting_hid = ( usage > 0 ) &&
346 ( usage <= _JS_MAX_BUTTONS );
348 if( interesting_hid && usage - 1 > *num_buttons )
349 *num_buttons = usage - 1;
352 if( interesting_hid )
355 os->hids = calloc( 1, sizeof ( struct hid_item ) );
361 return os->hids != NULL;
367 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
368 * See "js.h" lines 80-178.
370 typedef struct tagSFG_Joystick SFG_Joystick;
371 struct tagSFG_Joystick
373 #if TARGET_HOST_MACINTOSH
374 #define ISP_NUM_AXIS 9
375 #define ISP_NUM_NEEDS 41
376 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
377 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
380 #if TARGET_HOST_MAC_OSX
381 IOHIDDeviceInterface ** hidDev;
382 IOHIDElementCookie buttonCookies[41];
383 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
384 long minReport[_JS_MAX_AXES],
385 maxReport[_JS_MAX_AXES];
388 #if TARGET_HOST_WIN32
395 #if TARGET_HOST_UNIX_X11
396 # if defined(__FreeBSD__) || defined(__NetBSD__)
397 struct os_specific_s *os;
403 float tmp_axes [ _JS_MAX_AXES ];
405 struct JS_DATA_TYPE js;
418 float dead_band[ _JS_MAX_AXES ];
419 float saturate [ _JS_MAX_AXES ];
420 float center [ _JS_MAX_AXES ];
421 float max [ _JS_MAX_AXES ];
422 float min [ _JS_MAX_AXES ];
426 * Functions associated with the "jsJoystick" class in PLIB
428 #if TARGET_HOST_MAC_OSX
429 #define K_NUM_DEVICES 32
431 io_object_t ioDevices[K_NUM_DEVICES];
433 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
434 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
436 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
437 /* callback for CFArrayApply */
438 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
440 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
441 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
442 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
447 * The static joystick structure pointer
449 #define MAX_NUM_JOYSTICKS 2
450 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
454 * Read the raw joystick data
456 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
458 #if TARGET_HOST_WIN32
464 #if defined(__FreeBSD__) || defined(__NetBSD__)
475 for( i = 0; i < joy->num_axes; i++ )
481 #if TARGET_HOST_MACINTOSH
486 for ( i = 0; i < joy->num_buttons; i++ )
489 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
492 *buttons |= state << i;
498 for ( i = 0; i < joy->num_axes; i++ )
501 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
504 axes [i] = (float) state;
509 #if TARGET_HOST_MAC_OSX
510 if ( buttons != NULL )
514 for ( i = 0; i < joy->num_buttons; i++ )
516 IOHIDEventStruct hidEvent;
517 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
518 if ( hidEvent.value )
525 for ( i = 0; i < joy->num_axes; i++ )
527 IOHIDEventStruct hidEvent;
528 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
529 axes[i] = hidEvent.value;
534 #if TARGET_HOST_WIN32
535 status = joyGetPosEx( joy->js_id, &joy->js );
537 if ( status != JOYERR_NOERROR )
539 joy->error = GL_TRUE;
544 *buttons = joy->js.dwButtons;
549 * WARNING - Fall through case clauses!!
551 switch ( joy->num_axes )
554 /* Generate two POV axes from the POV hat angle.
555 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
556 * hundredths of a degree, or 0xFFFF when idle.
558 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
565 /* This is the contentious bit: how to convert angle to X/Y.
566 * wk: I know of no define for PI that we could use here:
567 * SG_PI would pull in sg, M_PI is undefined for MSVC
568 * But the accuracy of the value of PI is very unimportant at
571 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
572 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
574 /* Convert to coordinates on a square so that North-East
575 * is (1,1) not (.7,.7), etc.
576 * s and c cannot both be zero so we won't divide by zero.
578 if ( fabs ( s ) < fabs ( c ) )
580 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
581 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
585 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
586 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
590 case 6: axes[5] = (float) joy->js.dwVpos;
591 case 5: axes[4] = (float) joy->js.dwUpos;
592 case 4: axes[3] = (float) joy->js.dwRpos;
593 case 3: axes[2] = (float) joy->js.dwZpos;
594 case 2: axes[1] = (float) joy->js.dwYpos;
595 case 1: axes[0] = (float) joy->js.dwXpos;
600 #if TARGET_HOST_UNIX_X11
601 # if defined(__FreeBSD__) || defined(__NetBSD__)
602 if ( joy->os->is_analog )
604 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
605 if ( status != sizeof(joy->os->ajs) ) {
606 perror ( joy->os->fname );
607 joy->error = GL_TRUE;
610 if ( buttons != NULL )
611 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
615 axes[0] = (float) joy->os->ajs.x;
616 axes[1] = (float) joy->os->ajs.y;
623 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
627 for ( h = joy->os->hids; h; h = h->next )
629 int d = hid_get_data ( joy->os->hid_data_buf, h );
631 int page = HID_PAGE ( h->usage );
632 int usage = HID_USAGE ( h->usage );
634 if ( page == HUP_GENERIC_DESKTOP )
637 for ( i = 0; i < joy->num_axes; i++ )
638 if (joy->os->axes_usage[i] == usage)
640 if (usage == HUG_HAT_SWITCH)
644 joy->os->cache_axes[i] = (float)hatmap_x[d];
645 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
649 joy->os->cache_axes[i] = (float)d;
654 else if (page == HUP_BUTTON)
656 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
659 joy->os->cache_buttons |= (1 << ( usage - 1 ));
661 joy->os->cache_buttons &= ~(1 << ( usage - 1 ));
666 if ( len < 0 && errno != EAGAIN )
668 perror( joy->os->fname );
671 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
673 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
681 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
683 if ( status != sizeof( struct js_event ) )
685 if ( errno == EAGAIN )
687 /* Use the old values */
689 *buttons = joy->tmp_buttons;
691 memcpy( axes, joy->tmp_axes,
692 sizeof( float ) * joy->num_axes );
696 fgWarning ( "%s", joy->fname );
697 joy->error = GL_TRUE;
701 switch ( joy->js.type & ~JS_EVENT_INIT )
703 case JS_EVENT_BUTTON:
704 if( joy->js.value == 0 ) /* clear the flag */
705 joy->tmp_buttons &= ~( 1 << joy->js.number );
707 joy->tmp_buttons |= ( 1 << joy->js.number );
711 if ( joy->js.number < joy->num_axes )
713 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
716 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
721 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
723 /* use the old values */
725 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
727 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
733 *buttons = joy->tmp_buttons;
737 status = read( joy->fd, &joy->js, JS_RETURN );
739 if ( status != JS_RETURN )
741 fgWarning( "%s", joy->fname );
742 joy->error = GL_TRUE;
747 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
748 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
750 *buttons = joy->js.buttons;
755 axes[ 0 ] = (float) joy->js.x;
756 axes[ 1 ] = (float) joy->js.y;
763 * Correct the joystick axis data
765 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
767 if( value < joy->center[ axis ] )
769 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
772 if( xx < -joy->saturate[ axis ] )
775 if( xx > -joy->dead_band [ axis ] )
778 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
779 joy->dead_band[ axis ] );
781 return ( xx < -1.0f ) ? -1.0f : xx;
785 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
786 joy->center[ axis ] );
788 if( xx > joy->saturate[ axis ] )
791 if( xx < joy->dead_band[ axis ] )
794 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
795 joy->dead_band[ axis ] );
797 return ( xx > 1.0f ) ? 1.0f : xx;
802 * Read the corrected joystick data
804 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
806 float raw_axes[ _JS_MAX_AXES ];
815 for ( i=0; i<joy->num_axes; i++ )
819 fghJoystickRawRead( joy, buttons, raw_axes );
822 for( i=0; i<joy->num_axes; i++ )
823 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
827 * Happy happy happy joy joy joy (happy new year toudi :D)
831 #if TARGET_HOST_MAC_OSX
832 /** open the IOKit connection, enumerate all the HID devices, add their
833 interface references to the static array. We then use the array index
834 as the device number when we come to open() the joystick. */
835 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
837 CFMutableDictionaryRef hidMatch = NULL;
838 IOReturn rv = kIOReturnSuccess;
840 io_iterator_t hidIterator;
843 /* build a dictionary matching HID devices */
844 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
846 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
847 if (rv != kIOReturnSuccess || !hidIterator) {
848 fgWarning( "no joystick (HID) devices found" );
853 while ((ioDev = IOIteratorNext(hidIterator))) {
854 /* filter out keyboard and mouse devices */
855 CFDictionaryRef properties = getCFProperties(ioDev);
858 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
859 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
860 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
861 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
863 /* keep only joystick devices */
864 if ( ( page == kHIDPage_GenericDesktop ) && (
865 (usage == kHIDUsage_GD_Joystick)
866 || (usage == kHIDUsage_GD_GamePad)
867 || (usage == kHIDUsage_GD_MultiAxisController)
868 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
869 /* add it to the array */
870 ioDevices[numDevices++] = ioDev;
873 IOObjectRelease(hidIterator);
876 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
879 CFMutableDictionaryRef cfProperties;
882 /* comment copied from darwin/SDL_sysjoystick.c */
883 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
884 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
887 io_registry_entry_t parent1, parent2;
889 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
890 if (rv != kIOReturnSuccess) {
891 fgWarning ( "error getting device entry parent");
895 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
896 if (rv != kIOReturnSuccess) {
897 fgWarning ( "error getting device entry parent 2");
902 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
903 &cfProperties, kCFAllocatorDefault, kNilOptions);
904 if (rv != kIOReturnSuccess || !cfProperties) {
905 fgWarning ( "error getting device properties");
912 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
914 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
915 fgError ( "%s", "element enumerator passed non-dictionary value");
919 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
922 /** element enumerator function : pass NULL for top-level*/
923 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
925 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
926 "Joystick element type mismatch",
927 "fghJoystickEnumerateElements" );
929 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
930 CFArrayApplyFunction((CFArrayRef) element, range,
931 &fghJoystickElementEnumerator, joy );
934 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
936 long cookie, lmin, lmax;
937 int index = joy->num_axes++;
939 CFNumberGetValue ((CFNumberRef)
940 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
941 kCFNumberLongType, &cookie);
943 axisCookies[index] = (IOHIDElementCookie) cookie;
945 CFNumberGetValue ((CFNumberRef)
946 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
947 kCFNumberLongType, &lmin);
949 CFNumberGetValue ((CFNumberRef)
950 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
951 kCFNumberLongType, &lmax);
953 joy->min[index] = lmin;
954 joy->max[index] = lmax;
955 joy->dead_band[index] = 0.0;
956 joy->saturate[index] = 1.0;
957 joy->center[index] = (lmax + lmin) * 0.5;
960 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
963 CFNumberGetValue ((CFNumberRef)
964 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
965 kCFNumberLongType, &cookie);
967 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
968 /* anything else for buttons? */
971 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
973 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
974 /* do we map hats to axes or buttons? */
978 #if TARGET_HOST_WIN32
980 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
982 # if defined(_MSC_VER)
983 # pragma comment (lib, "advapi32.lib")
986 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
999 /* Open .. MediaResources\CurrentJoystickSettings */
1000 sprintf ( buffer, "%s\\%s\\%s",
1001 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1002 REGSTR_KEY_JOYCURR );
1004 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1006 if ( lr != ERROR_SUCCESS ) return 0;
1008 /* Get OEM Key name */
1009 dwcb = sizeof(OEMKey);
1011 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1012 sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1014 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1015 RegCloseKey ( hKey );
1017 if ( lr != ERROR_SUCCESS ) return 0;
1019 /* Open OEM Key from ...MediaProperties */
1020 sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1022 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1024 if ( lr != ERROR_SUCCESS ) return 0;
1029 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1031 RegCloseKey ( hKey );
1033 if ( lr != ERROR_SUCCESS ) return 0;
1040 static void fghJoystickOpen( SFG_Joystick* joy )
1043 #if TARGET_HOST_MACINTOSH
1046 #if TARGET_HOST_MAC_OSX
1049 IOCFPlugInInterface **plugin;
1051 HRESULT pluginResult;
1053 CFDictionaryRef props;
1054 CFTypeRef topLevelElement;
1056 #if TARGET_HOST_UNIX_X11
1057 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1063 # if defined( __linux__ )
1069 /* Silence gcc, the correct #ifdefs would be too fragile... */
1073 * Default values (for no joystick -- each conditional will reset the
1077 joy->num_axes = joy->num_buttons = 0;
1078 joy->name[ 0 ] = '\0';
1080 #if TARGET_HOST_MACINTOSH
1081 /* XXX FIXME: get joystick name in Mac */
1083 err = ISpStartup( );
1087 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1089 joy->error = GL_TRUE;
1091 /* initialize the needs structure */
1092 ISpNeed temp_isp_needs[ isp_num_needs ] =
1094 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1095 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1096 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1097 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1098 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1099 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1100 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1101 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1102 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1104 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1105 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1106 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1107 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1108 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1109 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1110 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1111 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1112 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1113 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1114 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1115 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1116 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1117 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1118 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1119 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1120 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1121 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1122 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1123 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1124 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1125 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1126 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1127 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1128 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1129 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1130 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1131 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1132 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1133 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1134 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1135 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1138 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1141 /* next two calls allow keyboard and mouse to emulate other input
1142 * devices (gamepads, joysticks, etc)
1145 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1149 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1153 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1154 joy->isp_needs, joy->isp_elem,
1156 ISP_CHECK_ERR( err )
1158 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1159 'freeglut', nil, 0, 128, 0 );
1160 ISP_CHECK_ERR( err )
1162 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1163 joy->num_axes = joy->isp_num_axis;
1165 for( i = 0; i < joy->num_axes; i++ )
1167 joy->dead_band[ i ] = 0;
1168 joy->saturate [ i ] = 1;
1169 joy->center [ i ] = kISpAxisMiddle;
1170 joy->max [ i ] = kISpAxisMaximum;
1171 joy->min [ i ] = kISpAxisMinimum;
1174 joy->error = GL_FALSE;
1177 joy->num_buttons = joy->num_axes = 0;
1180 #if TARGET_HOST_MAC_OSX
1181 if( joy->id >= numDevices )
1183 fgWarning( "device index out of range in fgJoystickOpen()" );
1187 /* create device interface */
1188 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1189 kIOHIDDeviceUserClientTypeID,
1190 kIOCFPlugInInterfaceID,
1193 if( rv != kIOReturnSuccess )
1195 fgWarning( "error creating plugin for io device" );
1199 pluginResult = ( *plugin )->QueryInterface(
1201 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1202 &( LPVOID )joy->hidDev
1205 if( pluginResult != S_OK )
1206 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1208 ( *plugin )->Release( plugin ); /* don't leak a ref */
1209 if( joy->hidDev == NULL )
1212 /* store the interface in this instance */
1213 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1214 if( rv != kIOReturnSuccess )
1216 fgWarning( "error opening device interface");
1220 props = getCFProperties( ioDevices[ joy->id ] );
1222 /* recursively enumerate all the bits */
1223 CFTypeRef topLevelElement =
1224 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1225 enumerateElements( topLevelElement );
1230 #if TARGET_HOST_WIN32
1231 joy->js.dwFlags = JOY_RETURNALL;
1232 joy->js.dwSize = sizeof( joy->js );
1234 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1237 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1240 if( joy->jsCaps.wNumAxes == 0 )
1243 joy->error = GL_TRUE;
1247 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1248 * at least for USB. Try to get the real name from the registry.
1250 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1251 sizeof( joy->name ) ) )
1253 fgWarning( "JS: Failed to read joystick name from registry" );
1254 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1257 /* Windows joystick drivers may provide any combination of
1258 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1260 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1262 joy->num_axes = _JS_MAX_AXES;
1263 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1264 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1269 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1270 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1271 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1272 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1273 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1274 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1275 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1276 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1277 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1278 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1279 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1280 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1283 /* Guess all the rest judging on the axes extremals */
1284 for( i = 0; i < joy->num_axes; i++ )
1286 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1287 joy->dead_band[ i ] = 0.0f;
1288 joy->saturate [ i ] = 1.0f;
1292 #if TARGET_HOST_UNIX_X11
1293 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1294 for( i = 0; i < _JS_MAX_AXES; i++ )
1295 joy->os->cache_axes[ i ] = 0.0f;
1297 joy->os->cache_buttons = 0;
1299 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1301 if( joy->os->fd < 0 && errno == EACCES )
1302 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1304 joy->error =( joy->os->fd < 0 );
1310 joy->num_buttons = 0;
1311 if( joy->os->is_analog )
1314 char joyfname[ 1024 ];
1315 int noargs, in_no_axes;
1317 float axes [ _JS_MAX_AXES ];
1318 int buttons[ _JS_MAX_AXES ];
1321 joy->num_buttons = 32;
1323 fghJoystickRawRead( joy, buttons, axes );
1324 joy->error = axes[ 0 ] < -1000000000.0f;
1328 sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
1330 joyfile = fopen( joyfname, "r" );
1331 joy->error =( joyfile == NULL );
1335 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1336 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1337 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1338 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1343 for( i = 0; i < _JS_MAX_AXES; i++ )
1345 joy->dead_band[ i ] = 0.0f;
1346 joy->saturate [ i ] = 1.0f;
1349 return; /* End of analog code */
1353 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1354 &joy->num_buttons ) )
1356 close( joy->os->fd );
1357 joy->error = GL_TRUE;
1361 cp = strrchr( joy->os->fname, '/' );
1364 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1366 strcpy( joy->name, &cp[1] );
1369 if( joy->num_axes > _JS_MAX_AXES )
1370 joy->num_axes = _JS_MAX_AXES;
1372 for( i = 0; i < _JS_MAX_AXES; i++ )
1374 /* We really should get this from the HID, but that data seems
1375 * to be quite unreliable for analog-to-USB converters. Punt for
1378 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1380 joy->max [ i ] = 1.0f;
1381 joy->center[ i ] = 0.0f;
1382 joy->min [ i ] = -1.0f;
1386 joy->max [ i ] = 255.0f;
1387 joy->center[ i ] = 127.0f;
1388 joy->min [ i ] = 0.0f;
1391 joy->dead_band[ i ] = 0.0f;
1392 joy->saturate[ i ] = 1.0f;
1397 #if defined( __linux__ )
1398 /* Default for older Linux systems. */
1400 joy->num_buttons = 32;
1403 for( i = 0; i < _JS_MAX_AXES; i++ )
1404 joy->tmp_axes[ i ] = 0.0f;
1406 joy->tmp_buttons = 0;
1409 joy->fd = open( joy->fname, O_RDONLY );
1411 joy->error =( joy->fd < 0 );
1416 /* Set the correct number of axes for the linux driver */
1418 /* Melchior Franz's fixes for big-endian Linuxes since writing
1419 * to the upper byte of an uninitialized word doesn't work.
1422 ioctl( joy->fd, JSIOCGAXES, &u );
1424 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1425 joy->num_buttons = u;
1426 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1427 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1431 * The Linux driver seems to return 512 for all axes
1432 * when no stick is present - but there is a chance
1433 * that could happen by accident - so it's gotta happen
1434 * on both axes for at least 100 attempts.
1436 * PWO: shouldn't be that done somehow wiser on the kernel level?
1443 fghJoystickRawRead( joy, NULL, joy->center );
1445 } while( !joy->error &&
1447 joy->center[ 0 ] == 512.0f &&
1448 joy->center[ 1 ] == 512.0f );
1450 if ( counter >= 100 )
1451 joy->error = GL_TRUE;
1454 for( i = 0; i < _JS_MAX_AXES; i++ )
1457 joy->max [ i ] = 32767.0f;
1458 joy->center[ i ] = 0.0f;
1459 joy->min [ i ] = -32767.0f;
1461 joy->max[ i ] = joy->center[ i ] * 2.0f;
1462 joy->min[ i ] = 0.0f;
1464 joy->dead_band[ i ] = 0.0f;
1465 joy->saturate [ i ] = 1.0f;
1472 * This function replaces the constructor method in the JS library.
1474 static void fghJoystickInit( int ident )
1476 if( ident >= MAX_NUM_JOYSTICKS )
1477 fgError( "Too large a joystick number: %d", ident );
1479 if( fgJoystick[ ident ] )
1480 fgError( "illegal attempt to initialize joystick device again" );
1482 fgJoystick[ ident ] =
1483 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1486 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1487 fgJoystick[ ident ]->error = GL_TRUE;
1489 #if TARGET_HOST_MACINTOSH
1490 fgJoystick[ ident ]->id = ident;
1491 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
1492 fgJoystick[ ident ]->error = GL_FALSE;
1495 #if TARGET_HOST_MAC_OSX
1496 fgJoystick[ ident ]->id = ident;
1497 fgJoystick[ ident ]->error = GL_FALSE;
1498 fgJoystick[ ident ]->num_axes = 0;
1499 fgJoystick[ ident ]->num_buttons = 0;
1501 if( numDevices < 0 )
1503 /* do first-time init (since we can't over-ride jsInit, hmm */
1506 mach_port_t masterPort;
1507 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1508 if( rv != kIOReturnSuccess )
1510 fgWarning( "error getting master Mach port" );
1513 fghJoystickFindDevices( masterPort );
1516 if ( ident >= numDevices )
1518 fgJoystick[ ident ]->error = GL_TRUE;
1522 /* get the name now too */
1523 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1524 CFTypeRef ref = CFDictionaryGetValue( properties,
1525 CFSTR( kIOHIDProductKey ) );
1527 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1530 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1531 CFStringGetSystemEncoding( ) ) )
1533 fgWarning( "error getting device name" );
1538 #if TARGET_HOST_WIN32
1542 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1543 fgJoystick[ ident ]->error = GL_FALSE;
1546 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1547 fgJoystick[ ident ]->error = GL_FALSE;
1550 fgJoystick[ ident ]->num_axes = 0;
1551 fgJoystick[ ident ]->error = GL_TRUE;
1556 #if TARGET_HOST_UNIX_X11
1557 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1558 fgJoystick[ ident ]->id = ident;
1559 fgJoystick[ ident ]->error = GL_FALSE;
1561 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1562 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1563 if( ident < USB_IDENT_OFFSET )
1564 fgJoystick[ ident ]->os->is_analog = 1;
1565 if( fgJoystick[ ident ]->os->is_analog )
1566 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
1568 sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
1569 ident - USB_IDENT_OFFSET );
1570 # elif defined( __linux__ )
1571 fgJoystick[ ident ]->id = ident;
1572 fgJoystick[ ident ]->error = GL_FALSE;
1574 sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1576 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1577 sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
1581 fghJoystickOpen( fgJoystick[ ident ] );
1585 * Try initializing all the joysticks (well, both of them)
1587 void fgInitialiseJoysticks ( void )
1589 if( !fgState.JoysticksInitialised )
1592 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1593 fghJoystickInit( ident );
1595 fgState.JoysticksInitialised = GL_TRUE;
1602 void fgJoystickClose( void )
1605 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1607 if( fgJoystick[ ident ] )
1610 #if TARGET_HOST_MACINTOSH
1616 #if TARGET_HOST_MAC_OSX
1617 ( *( fgJoystick[ ident ]->hidDev ) )->
1618 close( fgJoystick[ ident ]->hidDev );
1621 #if TARGET_HOST_WIN32
1622 /* Do nothing special */
1625 #if TARGET_HOST_UNIX_X11
1626 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1627 if( fgJoystick[ident]->os )
1629 if( ! fgJoystick[ ident ]->error )
1630 close( fgJoystick[ ident ]->os->fd );
1632 if( fgJoystick[ ident ]->os->hids )
1633 free (fgJoystick[ ident ]->os->hids);
1634 if( fgJoystick[ ident ]->os->hid_data_buf )
1635 free( fgJoystick[ ident ]->os->hid_data_buf );
1637 free( fgJoystick[ident]->os );
1641 if( ! fgJoystick[ident]->error )
1642 close( fgJoystick[ ident ]->fd );
1645 free( fgJoystick[ ident ] );
1646 fgJoystick[ ident ] = NULL;
1647 /* show joystick has been deinitialized */
1653 * Polls the joystick and executes the joystick callback hooked to the
1654 * window specified in the function's parameter:
1656 void fgJoystickPollWindow( SFG_Window* window )
1658 float axes[ _JS_MAX_AXES ];
1662 freeglut_return_if_fail( window );
1663 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1665 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1667 if( fgJoystick[ident] )
1669 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1671 if( !fgJoystick[ident]->error )
1672 INVOKE_WCB( *window, Joystick,
1674 (int) ( axes[ 0 ] * 1000.0f ),
1675 (int) ( axes[ 1 ] * 1000.0f ),
1676 (int) ( axes[ 2 ] * 1000.0f ) )
1683 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1685 int fgJoystickDetect( void )
1689 fgInitialiseJoysticks ();
1694 if ( !fgState.JoysticksInitialised )
1697 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1698 if( fgJoystick[ident] && !fgJoystick[ident]->error )
1705 * Joystick information functions
1707 int glutJoystickGetNumAxes( int ident )
1709 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
1710 return fgJoystick[ ident ]->num_axes;
1712 int glutJoystickGetNumButtons( int ident )
1714 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
1715 return fgJoystick[ ident ]->num_buttons;
1717 int glutJoystickNotWorking( int ident )
1719 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
1720 return fgJoystick[ ident ]->error;
1723 float glutJoystickGetDeadBand( int ident, int axis )
1725 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1726 return fgJoystick[ ident ]->dead_band [ axis ];
1728 void glutJoystickSetDeadBand( int ident, int axis, float db )
1730 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1731 fgJoystick[ ident ]->dead_band[ axis ] = db;
1734 float glutJoystickGetSaturation( int ident, int axis )
1736 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1737 return fgJoystick[ ident ]->saturate[ axis ];
1739 void glutJoystickSetSaturation( int ident, int axis, float st )
1741 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1742 fgJoystick[ ident ]->saturate [ axis ] = st;
1745 void glutJoystickSetMinRange( int ident, float *axes )
1747 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1748 memcpy( fgJoystick[ ident ]->min, axes,
1749 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1751 void glutJoystickSetMaxRange( int ident, float *axes )
1753 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1754 memcpy( fgJoystick[ ident ]->max, axes,
1755 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1757 void glutJoystickSetCenter( int ident, float *axes )
1759 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1760 memcpy( fgJoystick[ ident ]->center, axes,
1761 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1764 void glutJoystickGetMinRange( int ident, float *axes )
1766 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1767 memcpy( axes, fgJoystick[ ident ]->min,
1768 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1770 void glutJoystickGetMaxRange( int ident, float *axes )
1772 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1773 memcpy( axes, fgJoystick[ ident ]->max,
1774 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1776 void glutJoystickGetCenter( int ident, float *axes )
1778 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1779 memcpy( axes, fgJoystick[ ident ]->center,
1780 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1783 /*** END OF FILE ***/