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>
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
73 # include <sys/ioctl.h>
81 # if defined(__FreeBSD__) || defined(__NetBSD__)
82 /* XXX The below hack is done until freeglut's autoconf is updated. */
83 # define HAVE_USB_JS 1
85 # if defined(__FreeBSD__)
86 # include <sys/joystick.h>
89 * XXX NetBSD/amd64 systems may find that they have to steal the
90 * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
91 * XXX I cannot comment whether that works for the interface, but
92 * XXX it lets you compile...(^& I do not think that we can do away
93 * XXX with this header.
95 # include <machine/joystick.h> /* For analog joysticks */
97 # define JS_DATA_TYPE joystick
98 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
101 # if defined(__linux__)
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 snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
236 f = open(buf, O_RDONLY);
238 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
244 else if (errno == EACCES) {
245 if (!protection_warned) {
246 fgWarning ( "Can't open %s for read!", buf );
247 protection_warned = 1;
255 static int fghJoystickInitializeHID(struct os_specific_s *os,
256 int *num_axes, int *num_buttons)
258 int size, is_joystick;
259 # ifdef HAVE_USBHID_H
266 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
269 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
271 fgWarning ( "error: %s", os->fname );
278 # ifdef HAVE_USBHID_H
279 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
281 /*** XXX {report_id} may not be the right variable? ***/
283 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
285 fgWarning ( "error: %s%d", UHIDDEV, report_id );
290 size = hid_report_size( rd, hid_input, report_id );
292 size = hid_report_size( rd, 0, hid_input );
294 os->hid_data_buf = calloc( 1, size );
298 # ifdef HAVE_USBHID_H
299 d = hid_start_parse( rd, 1 << hid_input, report_id );
301 d = hid_start_parse( rd, 1 << hid_input );
303 while( hid_get_item( d, &h ) )
305 int usage, page, interesting_hid;
307 page = HID_PAGE( h.usage );
308 usage = HID_USAGE( h.usage );
310 /* This test is somewhat too simplistic, but this is how MicroSoft
311 * does, so I guess it works for all joysticks/game pads. */
312 is_joystick = is_joystick ||
313 ( h.kind == hid_collection &&
314 page == HUP_GENERIC_DESKTOP &&
315 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
317 if( h.kind != hid_input )
323 interesting_hid = TRUE;
324 if( page == HUP_GENERIC_DESKTOP )
335 if( *num_axes < _JS_MAX_AXES )
337 os->axes_usage[ *num_axes ] = usage;
342 /* Allocate two axes for a hat */
343 if( *num_axes + 1 < _JS_MAX_AXES )
345 os->axes_usage[ *num_axes ] = usage;
347 os->axes_usage[ *num_axes ] = usage;
352 interesting_hid = FALSE;
356 else if( page == HUP_BUTTON )
358 interesting_hid = ( usage > 0 ) &&
359 ( usage <= _JS_MAX_BUTTONS );
361 if( interesting_hid && usage - 1 > *num_buttons )
362 *num_buttons = usage - 1;
365 if( interesting_hid )
368 os->hids = calloc( 1, sizeof ( struct hid_item ) );
374 return os->hids != NULL;
380 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
381 * See "js.h" lines 80-178.
383 typedef struct tagSFG_Joystick SFG_Joystick;
384 struct tagSFG_Joystick
386 #if TARGET_HOST_MACINTOSH
387 #define ISP_NUM_AXIS 9
388 #define ISP_NUM_NEEDS 41
389 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
390 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
393 #if TARGET_HOST_MAC_OSX
394 IOHIDDeviceInterface ** hidDev;
395 IOHIDElementCookie buttonCookies[41];
396 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
397 long minReport[_JS_MAX_AXES],
398 maxReport[_JS_MAX_AXES];
401 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
408 #if TARGET_HOST_POSIX_X11
409 # if defined(__FreeBSD__) || defined(__NetBSD__)
410 struct os_specific_s *os;
416 float tmp_axes [ _JS_MAX_AXES ];
418 struct JS_DATA_TYPE js;
431 float dead_band[ _JS_MAX_AXES ];
432 float saturate [ _JS_MAX_AXES ];
433 float center [ _JS_MAX_AXES ];
434 float max [ _JS_MAX_AXES ];
435 float min [ _JS_MAX_AXES ];
439 * Functions associated with the "jsJoystick" class in PLIB
441 #if TARGET_HOST_MAC_OSX
442 #define K_NUM_DEVICES 32
444 io_object_t ioDevices[K_NUM_DEVICES];
446 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
447 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
449 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
450 /* callback for CFArrayApply */
451 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
453 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
454 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
455 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
460 * The static joystick structure pointer
462 #define MAX_NUM_JOYSTICKS 2
463 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
467 * Read the raw joystick data
469 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
471 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
477 #if defined(__FreeBSD__) || defined(__NetBSD__)
488 for( i = 0; i < joy->num_axes; i++ )
494 #if TARGET_HOST_MACINTOSH
499 for ( i = 0; i < joy->num_buttons; i++ )
502 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
505 *buttons |= state << i;
511 for ( i = 0; i < joy->num_axes; i++ )
514 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
517 axes [i] = (float) state;
522 #if TARGET_HOST_MAC_OSX
523 if ( buttons != NULL )
527 for ( i = 0; i < joy->num_buttons; i++ )
529 IOHIDEventStruct hidEvent;
530 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
531 if ( hidEvent.value )
538 for ( i = 0; i < joy->num_axes; i++ )
540 IOHIDEventStruct hidEvent;
541 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
542 axes[i] = hidEvent.value;
547 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
548 status = joyGetPosEx( joy->js_id, &joy->js );
550 if ( status != JOYERR_NOERROR )
552 joy->error = GL_TRUE;
557 *buttons = joy->js.dwButtons;
562 * WARNING - Fall through case clauses!!
564 switch ( joy->num_axes )
567 /* Generate two POV axes from the POV hat angle.
568 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
569 * hundredths of a degree, or 0xFFFF when idle.
571 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
578 /* This is the contentious bit: how to convert angle to X/Y.
579 * wk: I know of no define for PI that we could use here:
580 * SG_PI would pull in sg, M_PI is undefined for MSVC
581 * But the accuracy of the value of PI is very unimportant at
584 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
585 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
587 /* Convert to coordinates on a square so that North-East
588 * is (1,1) not (.7,.7), etc.
589 * s and c cannot both be zero so we won't divide by zero.
591 if ( fabs ( s ) < fabs ( c ) )
593 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
594 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
598 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
599 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
603 case 6: axes[5] = (float) joy->js.dwVpos;
604 case 5: axes[4] = (float) joy->js.dwUpos;
605 case 4: axes[3] = (float) joy->js.dwRpos;
606 case 3: axes[2] = (float) joy->js.dwZpos;
607 case 2: axes[1] = (float) joy->js.dwYpos;
608 case 1: axes[0] = (float) joy->js.dwXpos;
613 #if TARGET_HOST_POSIX_X11
614 # if defined(__FreeBSD__) || defined(__NetBSD__)
615 if ( joy->os->is_analog )
617 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
618 if ( status != sizeof(joy->os->ajs) ) {
619 perror ( joy->os->fname );
620 joy->error = GL_TRUE;
623 if ( buttons != NULL )
624 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
628 axes[0] = (float) joy->os->ajs.x;
629 axes[1] = (float) joy->os->ajs.y;
636 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
640 for ( h = joy->os->hids; h; h = h->next )
642 int d = hid_get_data ( joy->os->hid_data_buf, h );
644 int page = HID_PAGE ( h->usage );
645 int usage = HID_USAGE ( h->usage );
647 if ( page == HUP_GENERIC_DESKTOP )
650 for ( i = 0; i < joy->num_axes; i++ )
651 if (joy->os->axes_usage[i] == usage)
653 if (usage == HUG_HAT_SWITCH)
657 joy->os->cache_axes[i] = (float)hatmap_x[d];
658 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
662 joy->os->cache_axes[i] = (float)d;
667 else if (page == HUP_BUTTON)
669 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
672 joy->os->cache_buttons |= (1 << ( usage - 1 ));
674 joy->os->cache_buttons &= ~(1 << ( usage - 1 ));
680 if ( len < 0 && errno != EAGAIN )
685 perror( joy->os->fname );
688 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
690 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
698 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
700 if ( status != sizeof( struct js_event ) )
703 if ( errno == EAGAIN )
705 /* Use the old values */
707 *buttons = joy->tmp_buttons;
709 memcpy( axes, joy->tmp_axes,
710 sizeof( float ) * joy->num_axes );
715 fgWarning ( "%s", joy->fname );
716 joy->error = GL_TRUE;
720 switch ( joy->js.type & ~JS_EVENT_INIT )
722 case JS_EVENT_BUTTON:
723 if( joy->js.value == 0 ) /* clear the flag */
724 joy->tmp_buttons &= ~( 1 << joy->js.number );
726 joy->tmp_buttons |= ( 1 << joy->js.number );
730 if ( joy->js.number < joy->num_axes )
732 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
735 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
740 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
742 /* use the old values */
744 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
746 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
752 *buttons = joy->tmp_buttons;
756 status = read( joy->fd, &joy->js, JS_RETURN );
758 if ( status != JS_RETURN )
760 fgWarning( "%s", joy->fname );
761 joy->error = GL_TRUE;
766 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
767 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
769 *buttons = joy->js.buttons;
774 axes[ 0 ] = (float) joy->js.x;
775 axes[ 1 ] = (float) joy->js.y;
782 * Correct the joystick axis data
784 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
786 if( value < joy->center[ axis ] )
788 float xx = ( value - joy->center[ axis ] ) / ( 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;
804 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
805 joy->center[ axis ] );
807 if( xx > joy->saturate[ axis ] )
810 if( xx < joy->dead_band[ axis ] )
813 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
814 joy->dead_band[ axis ] );
816 return ( xx > 1.0f ) ? 1.0f : xx;
821 * Read the corrected joystick data
823 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
825 float raw_axes[ _JS_MAX_AXES ];
834 for ( i=0; i<joy->num_axes; i++ )
838 fghJoystickRawRead( joy, buttons, raw_axes );
841 for( i=0; i<joy->num_axes; i++ )
842 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
846 * Happy happy happy joy joy joy (happy new year toudi :D)
850 #if TARGET_HOST_MAC_OSX
851 /** open the IOKit connection, enumerate all the HID devices, add their
852 interface references to the static array. We then use the array index
853 as the device number when we come to open() the joystick. */
854 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
856 CFMutableDictionaryRef hidMatch = NULL;
857 IOReturn rv = kIOReturnSuccess;
859 io_iterator_t hidIterator;
862 /* build a dictionary matching HID devices */
863 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
865 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
866 if (rv != kIOReturnSuccess || !hidIterator) {
867 fgWarning( "no joystick (HID) devices found" );
872 while ((ioDev = IOIteratorNext(hidIterator))) {
873 /* filter out keyboard and mouse devices */
874 CFDictionaryRef properties = getCFProperties(ioDev);
877 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
878 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
879 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
880 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
882 /* keep only joystick devices */
883 if ( ( page == kHIDPage_GenericDesktop ) && (
884 (usage == kHIDUsage_GD_Joystick)
885 || (usage == kHIDUsage_GD_GamePad)
886 || (usage == kHIDUsage_GD_MultiAxisController)
887 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
888 /* add it to the array */
889 ioDevices[numDevices++] = ioDev;
892 IOObjectRelease(hidIterator);
895 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
898 CFMutableDictionaryRef cfProperties;
901 /* comment copied from darwin/SDL_sysjoystick.c */
902 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
903 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
906 io_registry_entry_t parent1, parent2;
908 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
909 if (rv != kIOReturnSuccess) {
910 fgWarning ( "error getting device entry parent");
914 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
915 if (rv != kIOReturnSuccess) {
916 fgWarning ( "error getting device entry parent 2");
921 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
922 &cfProperties, kCFAllocatorDefault, kNilOptions);
923 if (rv != kIOReturnSuccess || !cfProperties) {
924 fgWarning ( "error getting device properties");
931 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
933 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
934 fgError ( "%s", "element enumerator passed non-dictionary value");
938 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
941 /** element enumerator function : pass NULL for top-level*/
942 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
944 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
945 "Joystick element type mismatch",
946 "fghJoystickEnumerateElements" );
948 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
949 CFArrayApplyFunction((CFArrayRef) element, range,
950 &fghJoystickElementEnumerator, joy );
953 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
955 long cookie, lmin, lmax;
956 int index = joy->num_axes++;
958 CFNumberGetValue ((CFNumberRef)
959 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
960 kCFNumberLongType, &cookie);
962 axisCookies[index] = (IOHIDElementCookie) cookie;
964 CFNumberGetValue ((CFNumberRef)
965 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
966 kCFNumberLongType, &lmin);
968 CFNumberGetValue ((CFNumberRef)
969 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
970 kCFNumberLongType, &lmax);
972 joy->min[index] = lmin;
973 joy->max[index] = lmax;
974 joy->dead_band[index] = 0.0;
975 joy->saturate[index] = 1.0;
976 joy->center[index] = (lmax + lmin) * 0.5;
979 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
982 CFNumberGetValue ((CFNumberRef)
983 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
984 kCFNumberLongType, &cookie);
986 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
987 /* anything else for buttons? */
990 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
992 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
993 /* do we map hats to axes or buttons? */
997 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
999 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1001 # if FREEGLUT_LIB_PRAGMAS
1002 # pragma comment (lib, "advapi32.lib")
1005 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1007 char buffer [ 256 ];
1009 char OEMKey [ 256 ];
1018 /* Open .. MediaResources\CurrentJoystickSettings */
1019 _snprintf ( buffer, sizeof(buffer), "%s\\%s\\%s",
1020 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1021 REGSTR_KEY_JOYCURR );
1023 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1025 if ( lr != ERROR_SUCCESS ) return 0;
1027 /* Get OEM Key name */
1028 dwcb = sizeof(OEMKey);
1030 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1031 _snprintf ( buffer, sizeof(buffer), "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1033 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1034 RegCloseKey ( hKey );
1036 if ( lr != ERROR_SUCCESS ) return 0;
1038 /* Open OEM Key from ...MediaProperties */
1039 _snprintf ( buffer, sizeof(buffer), "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1041 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1043 if ( lr != ERROR_SUCCESS ) return 0;
1048 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1050 RegCloseKey ( hKey );
1052 if ( lr != ERROR_SUCCESS ) return 0;
1059 static void fghJoystickOpen( SFG_Joystick* joy )
1062 #if TARGET_HOST_MACINTOSH
1065 #if TARGET_HOST_MAC_OSX
1068 IOCFPlugInInterface **plugin;
1070 HRESULT pluginResult;
1072 CFDictionaryRef props;
1073 CFTypeRef topLevelElement;
1075 #if TARGET_HOST_POSIX_X11
1076 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1082 # if defined( __linux__ ) || TARGET_HOST_SOLARIS
1088 /* Silence gcc, the correct #ifdefs would be too fragile... */
1092 * Default values (for no joystick -- each conditional will reset the
1096 joy->num_axes = joy->num_buttons = 0;
1097 joy->name[ 0 ] = '\0';
1099 #if TARGET_HOST_MACINTOSH
1100 /* XXX FIXME: get joystick name in Mac */
1102 err = ISpStartup( );
1106 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1108 joy->error = GL_TRUE;
1110 /* initialize the needs structure */
1111 ISpNeed temp_isp_needs[ isp_num_needs ] =
1113 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1114 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1115 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1116 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1117 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1118 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1119 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1120 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1121 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1123 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1124 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1125 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1126 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1127 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1128 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1129 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1130 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1131 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1132 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1133 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1134 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1135 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1136 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1137 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1138 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1139 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1140 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1141 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1142 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1143 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1144 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1145 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1146 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1147 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1148 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1149 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1150 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1151 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1152 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1153 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1154 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1157 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1160 /* next two calls allow keyboard and mouse to emulate other input
1161 * devices (gamepads, joysticks, etc)
1164 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1168 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1172 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1173 joy->isp_needs, joy->isp_elem,
1175 ISP_CHECK_ERR( err )
1177 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1178 'freeglut', nil, 0, 128, 0 );
1179 ISP_CHECK_ERR( err )
1181 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1182 joy->num_axes = joy->isp_num_axis;
1184 for( i = 0; i < joy->num_axes; i++ )
1186 joy->dead_band[ i ] = 0;
1187 joy->saturate [ i ] = 1;
1188 joy->center [ i ] = kISpAxisMiddle;
1189 joy->max [ i ] = kISpAxisMaximum;
1190 joy->min [ i ] = kISpAxisMinimum;
1193 joy->error = GL_FALSE;
1196 joy->num_buttons = joy->num_axes = 0;
1199 #if TARGET_HOST_MAC_OSX
1200 if( joy->id >= numDevices )
1202 fgWarning( "device index out of range in fgJoystickOpen()" );
1206 /* create device interface */
1207 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1208 kIOHIDDeviceUserClientTypeID,
1209 kIOCFPlugInInterfaceID,
1212 if( rv != kIOReturnSuccess )
1214 fgWarning( "error creating plugin for io device" );
1218 pluginResult = ( *plugin )->QueryInterface(
1220 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1221 &( LPVOID )joy->hidDev
1224 if( pluginResult != S_OK )
1225 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1227 ( *plugin )->Release( plugin ); /* don't leak a ref */
1228 if( joy->hidDev == NULL )
1231 /* store the interface in this instance */
1232 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1233 if( rv != kIOReturnSuccess )
1235 fgWarning( "error opening device interface");
1239 props = getCFProperties( ioDevices[ joy->id ] );
1241 /* recursively enumerate all the bits */
1242 CFTypeRef topLevelElement =
1243 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1244 enumerateElements( topLevelElement );
1249 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
1250 joy->js.dwFlags = JOY_RETURNALL;
1251 joy->js.dwSize = sizeof( joy->js );
1253 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1256 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1259 if( joy->jsCaps.wNumAxes == 0 )
1262 joy->error = GL_TRUE;
1266 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1267 * at least for USB. Try to get the real name from the registry.
1269 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1270 sizeof( joy->name ) ) )
1272 fgWarning( "JS: Failed to read joystick name from registry" );
1273 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1276 /* Windows joystick drivers may provide any combination of
1277 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1279 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1281 joy->num_axes = _JS_MAX_AXES;
1282 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1283 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1288 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1289 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1290 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1291 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1292 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1293 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1294 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1295 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1296 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1297 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1298 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1299 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1302 /* Guess all the rest judging on the axes extremals */
1303 for( i = 0; i < joy->num_axes; i++ )
1305 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1306 joy->dead_band[ i ] = 0.0f;
1307 joy->saturate [ i ] = 1.0f;
1311 #if TARGET_HOST_POSIX_X11
1312 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1313 for( i = 0; i < _JS_MAX_AXES; i++ )
1314 joy->os->cache_axes[ i ] = 0.0f;
1316 joy->os->cache_buttons = 0;
1318 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1321 if( joy->os->fd < 0 && errno == EACCES )
1322 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1325 joy->error =( joy->os->fd < 0 );
1331 joy->num_buttons = 0;
1332 if( joy->os->is_analog )
1335 char joyfname[ 1024 ];
1336 int noargs, in_no_axes;
1338 float axes [ _JS_MAX_AXES ];
1339 int buttons[ _JS_MAX_AXES ];
1342 joy->num_buttons = 32;
1344 fghJoystickRawRead( joy, buttons, axes );
1345 joy->error = axes[ 0 ] < -1000000000.0f;
1349 snprintf( joyfname, sizeof(buffer), "%s/.joy%drc", getenv( "HOME" ), joy->id );
1351 joyfile = fopen( joyfname, "r" );
1352 joy->error =( joyfile == NULL );
1356 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1357 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1358 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1359 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1364 for( i = 0; i < _JS_MAX_AXES; i++ )
1366 joy->dead_band[ i ] = 0.0f;
1367 joy->saturate [ i ] = 1.0f;
1370 return; /* End of analog code */
1374 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1375 &joy->num_buttons ) )
1377 close( joy->os->fd );
1378 joy->error = GL_TRUE;
1382 cp = strrchr( joy->os->fname, '/' );
1385 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1387 strcpy( joy->name, &cp[1] );
1390 if( joy->num_axes > _JS_MAX_AXES )
1391 joy->num_axes = _JS_MAX_AXES;
1393 for( i = 0; i < _JS_MAX_AXES; i++ )
1395 /* We really should get this from the HID, but that data seems
1396 * to be quite unreliable for analog-to-USB converters. Punt for
1399 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1401 joy->max [ i ] = 1.0f;
1402 joy->center[ i ] = 0.0f;
1403 joy->min [ i ] = -1.0f;
1407 joy->max [ i ] = 255.0f;
1408 joy->center[ i ] = 127.0f;
1409 joy->min [ i ] = 0.0f;
1412 joy->dead_band[ i ] = 0.0f;
1413 joy->saturate[ i ] = 1.0f;
1418 #if defined( __linux__ ) || TARGET_HOST_SOLARIS
1419 /* Default for older Linux systems. */
1421 joy->num_buttons = 32;
1424 for( i = 0; i < _JS_MAX_AXES; i++ )
1425 joy->tmp_axes[ i ] = 0.0f;
1427 joy->tmp_buttons = 0;
1430 joy->fd = open( joy->fname, O_RDONLY );
1432 joy->error =( joy->fd < 0 );
1437 /* Set the correct number of axes for the linux driver */
1439 /* Melchior Franz's fixes for big-endian Linuxes since writing
1440 * to the upper byte of an uninitialized word doesn't work.
1443 ioctl( joy->fd, JSIOCGAXES, &u );
1445 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1446 joy->num_buttons = u;
1447 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1448 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1452 * The Linux driver seems to return 512 for all axes
1453 * when no stick is present - but there is a chance
1454 * that could happen by accident - so it's gotta happen
1455 * on both axes for at least 100 attempts.
1457 * PWO: shouldn't be that done somehow wiser on the kernel level?
1464 fghJoystickRawRead( joy, NULL, joy->center );
1466 } while( !joy->error &&
1468 joy->center[ 0 ] == 512.0f &&
1469 joy->center[ 1 ] == 512.0f );
1471 if ( counter >= 100 )
1472 joy->error = GL_TRUE;
1475 for( i = 0; i < _JS_MAX_AXES; i++ )
1478 joy->max [ i ] = 32767.0f;
1479 joy->center[ i ] = 0.0f;
1480 joy->min [ i ] = -32767.0f;
1482 joy->max[ i ] = joy->center[ i ] * 2.0f;
1483 joy->min[ i ] = 0.0f;
1485 joy->dead_band[ i ] = 0.0f;
1486 joy->saturate [ i ] = 1.0f;
1493 * This function replaces the constructor method in the JS library.
1495 static void fghJoystickInit( int ident )
1497 if( ident >= MAX_NUM_JOYSTICKS )
1498 fgError( "Too large a joystick number: %d", ident );
1500 if( fgJoystick[ ident ] )
1501 fgError( "illegal attempt to initialize joystick device again" );
1503 fgJoystick[ ident ] =
1504 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1507 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1508 fgJoystick[ ident ]->error = GL_TRUE;
1510 #if TARGET_HOST_MACINTOSH
1511 fgJoystick[ ident ]->id = ident;
1512 snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident ); /* FIXME */
1513 fgJoystick[ ident ]->error = GL_FALSE;
1516 #if TARGET_HOST_MAC_OSX
1517 fgJoystick[ ident ]->id = ident;
1518 fgJoystick[ ident ]->error = GL_FALSE;
1519 fgJoystick[ ident ]->num_axes = 0;
1520 fgJoystick[ ident ]->num_buttons = 0;
1522 if( numDevices < 0 )
1524 /* do first-time init (since we can't over-ride jsInit, hmm */
1527 mach_port_t masterPort;
1528 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1529 if( rv != kIOReturnSuccess )
1531 fgWarning( "error getting master Mach port" );
1534 fghJoystickFindDevices( masterPort );
1537 if ( ident >= numDevices )
1539 fgJoystick[ ident ]->error = GL_TRUE;
1543 /* get the name now too */
1544 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1545 CFTypeRef ref = CFDictionaryGetValue( properties,
1546 CFSTR( kIOHIDProductKey ) );
1548 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1551 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1552 CFStringGetSystemEncoding( ) ) )
1554 fgWarning( "error getting device name" );
1559 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
1563 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1564 fgJoystick[ ident ]->error = GL_FALSE;
1567 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1568 fgJoystick[ ident ]->error = GL_FALSE;
1571 fgJoystick[ ident ]->num_axes = 0;
1572 fgJoystick[ ident ]->error = GL_TRUE;
1577 #if TARGET_HOST_POSIX_X11
1578 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1579 fgJoystick[ ident ]->id = ident;
1580 fgJoystick[ ident ]->error = GL_FALSE;
1582 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1583 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1584 if( ident < USB_IDENT_OFFSET )
1585 fgJoystick[ ident ]->os->is_analog = 1;
1586 if( fgJoystick[ ident ]->os->is_analog )
1587 snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", AJSDEV, ident );
1589 snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", UHIDDEV,
1590 ident - USB_IDENT_OFFSET );
1591 # elif defined( __linux__ )
1592 fgJoystick[ ident ]->id = ident;
1593 fgJoystick[ ident ]->error = GL_FALSE;
1595 snprintf( fgJoystick[ident]->fname, sizeof(fgJoystick[ident]->fname), "/dev/input/js%d", ident );
1597 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1598 snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident );
1602 fghJoystickOpen( fgJoystick[ ident ] );
1606 * Try initializing all the joysticks (well, both of them)
1608 void fgInitialiseJoysticks ( void )
1610 if( !fgState.JoysticksInitialised )
1613 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1614 fghJoystickInit( ident );
1616 fgState.JoysticksInitialised = GL_TRUE;
1623 void fgJoystickClose( void )
1626 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1628 if( fgJoystick[ ident ] )
1631 #if TARGET_HOST_MACINTOSH
1637 #if TARGET_HOST_MAC_OSX
1638 ( *( fgJoystick[ ident ]->hidDev ) )->
1639 close( fgJoystick[ ident ]->hidDev );
1642 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
1643 /* Do nothing special */
1646 #if TARGET_HOST_POSIX_X11
1647 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1648 if( fgJoystick[ident]->os )
1650 if( ! fgJoystick[ ident ]->error )
1651 close( fgJoystick[ ident ]->os->fd );
1653 if( fgJoystick[ ident ]->os->hids )
1654 free (fgJoystick[ ident ]->os->hids);
1655 if( fgJoystick[ ident ]->os->hid_data_buf )
1656 free( fgJoystick[ ident ]->os->hid_data_buf );
1658 free( fgJoystick[ident]->os );
1662 if( ! fgJoystick[ident]->error )
1663 close( fgJoystick[ ident ]->fd );
1666 free( fgJoystick[ ident ] );
1667 fgJoystick[ ident ] = NULL;
1668 /* show joystick has been deinitialized */
1674 * Polls the joystick and executes the joystick callback hooked to the
1675 * window specified in the function's parameter:
1677 void fgJoystickPollWindow( SFG_Window* window )
1679 float axes[ _JS_MAX_AXES ];
1683 freeglut_return_if_fail( window );
1684 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1686 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1688 if( fgJoystick[ident] )
1690 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1692 if( !fgJoystick[ident]->error )
1693 INVOKE_WCB( *window, Joystick,
1695 (int) ( axes[ 0 ] * 1000.0f ),
1696 (int) ( axes[ 1 ] * 1000.0f ),
1697 (int) ( axes[ 2 ] * 1000.0f ) )
1704 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1706 int fgJoystickDetect( void )
1710 fgInitialiseJoysticks ();
1712 if ( !fgState.JoysticksInitialised )
1715 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1716 if( fgJoystick[ident] && !fgJoystick[ident]->error )
1723 * Joystick information functions
1725 int glutJoystickGetNumAxes( int ident )
1727 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
1728 return fgJoystick[ ident ]->num_axes;
1730 int glutJoystickGetNumButtons( int ident )
1732 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
1733 return fgJoystick[ ident ]->num_buttons;
1735 int glutJoystickNotWorking( int ident )
1737 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
1738 return fgJoystick[ ident ]->error;
1741 float glutJoystickGetDeadBand( int ident, int axis )
1743 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1744 return fgJoystick[ ident ]->dead_band [ axis ];
1746 void glutJoystickSetDeadBand( int ident, int axis, float db )
1748 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1749 fgJoystick[ ident ]->dead_band[ axis ] = db;
1752 float glutJoystickGetSaturation( int ident, int axis )
1754 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1755 return fgJoystick[ ident ]->saturate[ axis ];
1757 void glutJoystickSetSaturation( int ident, int axis, float st )
1759 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1760 fgJoystick[ ident ]->saturate [ axis ] = st;
1763 void glutJoystickSetMinRange( int ident, float *axes )
1765 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1766 memcpy( fgJoystick[ ident ]->min, axes,
1767 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1769 void glutJoystickSetMaxRange( int ident, float *axes )
1771 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1772 memcpy( fgJoystick[ ident ]->max, axes,
1773 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1775 void glutJoystickSetCenter( int ident, float *axes )
1777 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1778 memcpy( fgJoystick[ ident ]->center, axes,
1779 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1782 void glutJoystickGetMinRange( int ident, float *axes )
1784 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1785 memcpy( axes, fgJoystick[ ident ]->min,
1786 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1788 void glutJoystickGetMaxRange( int ident, float *axes )
1790 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1791 memcpy( axes, fgJoystick[ ident ]->max,
1792 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1794 void glutJoystickGetCenter( int ident, float *axes )
1796 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1797 memcpy( axes, fgJoystick[ ident ]->center,
1798 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1801 /*** END OF FILE ***/