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(__NetBSD__)
83 /* XXX The below hack is done until freeglut's autoconf is updated. */
84 # define HAVE_USB_JS 1
86 # if defined(__FreeBSD__)
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__)
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__)
149 # if __FreeBSD_version < 500000
150 # include <libusbhid.h>
152 /* XXX The below hack is done until freeglut's autoconf is updated. */
153 # define HAVE_USBHID_H 1
157 # include <dev/usb/usb.h>
158 # include <dev/usb/usbhid.h>
160 /* Compatibility with older usb.h revisions */
161 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
162 # define USB_MAX_DEVNAMES MAXDEVNAMES
166 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
167 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
168 struct os_specific_s {
172 /* The following structure members are specific to analog joysticks */
175 /* The following structure members are specific to USB joysticks */
176 struct hid_item *hids;
180 int axes_usage [ _JS_MAX_AXES ];
182 /* We keep button and axes state ourselves, as they might not be updated
183 * on every read of a USB device
186 float cache_axes [ _JS_MAX_AXES ];
189 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
190 # define USB_IDENT_OFFSET 2
192 # define USBDEV "/dev/usb"
193 # define UHIDDEV "/dev/uhid"
194 # define AJSDEV "/dev/joy"
198 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
199 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
200 * return the uhidN device name. We warn the user of this situation once.
202 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
204 struct usb_device_info di;
208 for (a = 1; a < USB_MAX_DEVICES; a++) {
210 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
212 for (i = 0; i < USB_MAX_DEVNAMES; i++)
213 if (di.udi_devnames[i][0] &&
214 strcmp(di.udi_devnames[i], dev) == 0) {
215 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
216 strcpy(cp, di.udi_vendor);
218 strcat(cp, di.udi_product);
219 strncpy(out, cp, outlen - 1);
228 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
233 static int protection_warned = 0;
235 for (i = 0; i < 16; i++) {
236 snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
237 f = open(buf, O_RDONLY);
239 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
245 else if (errno == EACCES) {
246 if (!protection_warned) {
247 fgWarning ( "Can't open %s for read!", buf );
248 protection_warned = 1;
256 static int fghJoystickInitializeHID(struct os_specific_s *os,
257 int *num_axes, int *num_buttons)
259 int size, is_joystick;
260 # ifdef HAVE_USBHID_H
267 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
270 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
272 fgWarning ( "error: %s", os->fname );
279 # ifdef HAVE_USBHID_H
280 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
282 /*** XXX {report_id} may not be the right variable? ***/
284 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
286 fgWarning ( "error: %s%d", UHIDDEV, report_id );
291 size = hid_report_size( rd, hid_input, report_id );
293 size = hid_report_size( rd, 0, hid_input );
295 os->hid_data_buf = calloc( 1, size );
299 # ifdef HAVE_USBHID_H
300 d = hid_start_parse( rd, 1 << hid_input, report_id );
302 d = hid_start_parse( rd, 1 << hid_input );
304 while( hid_get_item( d, &h ) )
306 int usage, page, interesting_hid;
308 page = HID_PAGE( h.usage );
309 usage = HID_USAGE( h.usage );
311 /* This test is somewhat too simplistic, but this is how MicroSoft
312 * does, so I guess it works for all joysticks/game pads. */
313 is_joystick = is_joystick ||
314 ( h.kind == hid_collection &&
315 page == HUP_GENERIC_DESKTOP &&
316 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
318 if( h.kind != hid_input )
324 interesting_hid = TRUE;
325 if( page == HUP_GENERIC_DESKTOP )
336 if( *num_axes < _JS_MAX_AXES )
338 os->axes_usage[ *num_axes ] = usage;
343 /* Allocate two axes for a hat */
344 if( *num_axes + 1 < _JS_MAX_AXES )
346 os->axes_usage[ *num_axes ] = usage;
348 os->axes_usage[ *num_axes ] = usage;
353 interesting_hid = FALSE;
357 else if( page == HUP_BUTTON )
359 interesting_hid = ( usage > 0 ) &&
360 ( usage <= _JS_MAX_BUTTONS );
362 if( interesting_hid && usage - 1 > *num_buttons )
363 *num_buttons = usage - 1;
366 if( interesting_hid )
369 os->hids = calloc( 1, sizeof ( struct hid_item ) );
375 return os->hids != NULL;
381 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
382 * See "js.h" lines 80-178.
384 typedef struct tagSFG_Joystick SFG_Joystick;
385 struct tagSFG_Joystick
387 #if TARGET_HOST_MACINTOSH
388 #define ISP_NUM_AXIS 9
389 #define ISP_NUM_NEEDS 41
390 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
391 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
394 #if TARGET_HOST_MAC_OSX
395 IOHIDDeviceInterface ** hidDev;
396 IOHIDElementCookie buttonCookies[41];
397 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
398 long minReport[_JS_MAX_AXES],
399 maxReport[_JS_MAX_AXES];
402 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
409 #if TARGET_HOST_POSIX_X11
410 # if defined(__FreeBSD__) || defined(__NetBSD__)
411 struct os_specific_s *os;
417 float tmp_axes [ _JS_MAX_AXES ];
419 struct JS_DATA_TYPE js;
432 float dead_band[ _JS_MAX_AXES ];
433 float saturate [ _JS_MAX_AXES ];
434 float center [ _JS_MAX_AXES ];
435 float max [ _JS_MAX_AXES ];
436 float min [ _JS_MAX_AXES ];
440 * Functions associated with the "jsJoystick" class in PLIB
442 #if TARGET_HOST_MAC_OSX
443 #define K_NUM_DEVICES 32
445 io_object_t ioDevices[K_NUM_DEVICES];
447 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
448 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
450 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
451 /* callback for CFArrayApply */
452 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
454 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
455 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
456 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
461 * The static joystick structure pointer
463 #define MAX_NUM_JOYSTICKS 2
464 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
468 * Read the raw joystick data
470 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
472 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
478 #if defined(__FreeBSD__) || defined(__NetBSD__)
489 for( i = 0; i < joy->num_axes; i++ )
495 #if TARGET_HOST_MACINTOSH
500 for ( i = 0; i < joy->num_buttons; i++ )
503 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
506 *buttons |= state << i;
512 for ( i = 0; i < joy->num_axes; i++ )
515 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
518 axes [i] = (float) state;
523 #if TARGET_HOST_MAC_OSX
524 if ( buttons != NULL )
528 for ( i = 0; i < joy->num_buttons; i++ )
530 IOHIDEventStruct hidEvent;
531 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
532 if ( hidEvent.value )
539 for ( i = 0; i < joy->num_axes; i++ )
541 IOHIDEventStruct hidEvent;
542 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
543 axes[i] = hidEvent.value;
548 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
549 status = joyGetPosEx( joy->js_id, &joy->js );
551 if ( status != JOYERR_NOERROR )
553 joy->error = GL_TRUE;
558 *buttons = joy->js.dwButtons;
563 * WARNING - Fall through case clauses!!
565 switch ( joy->num_axes )
568 /* Generate two POV axes from the POV hat angle.
569 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
570 * hundredths of a degree, or 0xFFFF when idle.
572 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
579 /* This is the contentious bit: how to convert angle to X/Y.
580 * wk: I know of no define for PI that we could use here:
581 * SG_PI would pull in sg, M_PI is undefined for MSVC
582 * But the accuracy of the value of PI is very unimportant at
585 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
586 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
588 /* Convert to coordinates on a square so that North-East
589 * is (1,1) not (.7,.7), etc.
590 * s and c cannot both be zero so we won't divide by zero.
592 if ( fabs ( s ) < fabs ( c ) )
594 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
595 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
599 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
600 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
604 case 6: axes[5] = (float) joy->js.dwVpos;
605 case 5: axes[4] = (float) joy->js.dwUpos;
606 case 4: axes[3] = (float) joy->js.dwRpos;
607 case 3: axes[2] = (float) joy->js.dwZpos;
608 case 2: axes[1] = (float) joy->js.dwYpos;
609 case 1: axes[0] = (float) joy->js.dwXpos;
614 #if TARGET_HOST_POSIX_X11
615 # if defined(__FreeBSD__) || defined(__NetBSD__)
616 if ( joy->os->is_analog )
618 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
619 if ( status != sizeof(joy->os->ajs) ) {
620 perror ( joy->os->fname );
621 joy->error = GL_TRUE;
624 if ( buttons != NULL )
625 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
629 axes[0] = (float) joy->os->ajs.x;
630 axes[1] = (float) joy->os->ajs.y;
637 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
641 for ( h = joy->os->hids; h; h = h->next )
643 int d = hid_get_data ( joy->os->hid_data_buf, h );
645 int page = HID_PAGE ( h->usage );
646 int usage = HID_USAGE ( h->usage );
648 if ( page == HUP_GENERIC_DESKTOP )
651 for ( i = 0; i < joy->num_axes; i++ )
652 if (joy->os->axes_usage[i] == usage)
654 if (usage == HUG_HAT_SWITCH)
658 joy->os->cache_axes[i] = (float)hatmap_x[d];
659 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
663 joy->os->cache_axes[i] = (float)d;
668 else if (page == HUP_BUTTON)
670 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
673 joy->os->cache_buttons |= (1 << ( usage - 1 ));
675 joy->os->cache_buttons &= ~(1 << ( usage - 1 ));
681 if ( len < 0 && errno != EAGAIN )
686 perror( joy->os->fname );
689 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
691 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
699 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
701 if ( status != sizeof( struct js_event ) )
704 if ( errno == EAGAIN )
706 /* Use the old values */
708 *buttons = joy->tmp_buttons;
710 memcpy( axes, joy->tmp_axes,
711 sizeof( float ) * joy->num_axes );
716 fgWarning ( "%s", joy->fname );
717 joy->error = GL_TRUE;
721 switch ( joy->js.type & ~JS_EVENT_INIT )
723 case JS_EVENT_BUTTON:
724 if( joy->js.value == 0 ) /* clear the flag */
725 joy->tmp_buttons &= ~( 1 << joy->js.number );
727 joy->tmp_buttons |= ( 1 << joy->js.number );
731 if ( joy->js.number < joy->num_axes )
733 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
736 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
741 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
743 /* use the old values */
745 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
747 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
753 *buttons = joy->tmp_buttons;
757 status = read( joy->fd, &joy->js, JS_RETURN );
759 if ( status != JS_RETURN )
761 fgWarning( "%s", joy->fname );
762 joy->error = GL_TRUE;
767 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
768 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
770 *buttons = joy->js.buttons;
775 axes[ 0 ] = (float) joy->js.x;
776 axes[ 1 ] = (float) joy->js.y;
783 * Correct the joystick axis data
785 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
787 if( value < joy->center[ axis ] )
789 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
792 if( xx < -joy->saturate[ axis ] )
795 if( xx > -joy->dead_band [ axis ] )
798 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
799 joy->dead_band[ axis ] );
801 return ( xx < -1.0f ) ? -1.0f : xx;
805 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
806 joy->center[ axis ] );
808 if( xx > joy->saturate[ axis ] )
811 if( xx < joy->dead_band[ axis ] )
814 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
815 joy->dead_band[ axis ] );
817 return ( xx > 1.0f ) ? 1.0f : xx;
822 * Read the corrected joystick data
824 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
826 float raw_axes[ _JS_MAX_AXES ];
835 for ( i=0; i<joy->num_axes; i++ )
839 fghJoystickRawRead( joy, buttons, raw_axes );
842 for( i=0; i<joy->num_axes; i++ )
843 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
847 * Happy happy happy joy joy joy (happy new year toudi :D)
851 #if TARGET_HOST_MAC_OSX
852 /** open the IOKit connection, enumerate all the HID devices, add their
853 interface references to the static array. We then use the array index
854 as the device number when we come to open() the joystick. */
855 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
857 CFMutableDictionaryRef hidMatch = NULL;
858 IOReturn rv = kIOReturnSuccess;
860 io_iterator_t hidIterator;
863 /* build a dictionary matching HID devices */
864 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
866 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
867 if (rv != kIOReturnSuccess || !hidIterator) {
868 fgWarning( "no joystick (HID) devices found" );
873 while ((ioDev = IOIteratorNext(hidIterator))) {
874 /* filter out keyboard and mouse devices */
875 CFDictionaryRef properties = getCFProperties(ioDev);
878 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
879 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
880 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
881 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
883 /* keep only joystick devices */
884 if ( ( page == kHIDPage_GenericDesktop ) && (
885 (usage == kHIDUsage_GD_Joystick)
886 || (usage == kHIDUsage_GD_GamePad)
887 || (usage == kHIDUsage_GD_MultiAxisController)
888 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
889 /* add it to the array */
890 ioDevices[numDevices++] = ioDev;
893 IOObjectRelease(hidIterator);
896 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
899 CFMutableDictionaryRef cfProperties;
902 /* comment copied from darwin/SDL_sysjoystick.c */
903 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
904 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
907 io_registry_entry_t parent1, parent2;
909 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
910 if (rv != kIOReturnSuccess) {
911 fgWarning ( "error getting device entry parent");
915 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
916 if (rv != kIOReturnSuccess) {
917 fgWarning ( "error getting device entry parent 2");
922 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
923 &cfProperties, kCFAllocatorDefault, kNilOptions);
924 if (rv != kIOReturnSuccess || !cfProperties) {
925 fgWarning ( "error getting device properties");
932 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
934 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
935 fgError ( "%s", "element enumerator passed non-dictionary value");
939 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
942 /** element enumerator function : pass NULL for top-level*/
943 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
945 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
946 "Joystick element type mismatch",
947 "fghJoystickEnumerateElements" );
949 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
950 CFArrayApplyFunction((CFArrayRef) element, range,
951 &fghJoystickElementEnumerator, joy );
954 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
956 long cookie, lmin, lmax;
957 int index = joy->num_axes++;
959 CFNumberGetValue ((CFNumberRef)
960 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
961 kCFNumberLongType, &cookie);
963 axisCookies[index] = (IOHIDElementCookie) cookie;
965 CFNumberGetValue ((CFNumberRef)
966 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
967 kCFNumberLongType, &lmin);
969 CFNumberGetValue ((CFNumberRef)
970 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
971 kCFNumberLongType, &lmax);
973 joy->min[index] = lmin;
974 joy->max[index] = lmax;
975 joy->dead_band[index] = 0.0;
976 joy->saturate[index] = 1.0;
977 joy->center[index] = (lmax + lmin) * 0.5;
980 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
983 CFNumberGetValue ((CFNumberRef)
984 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
985 kCFNumberLongType, &cookie);
987 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
988 /* anything else for buttons? */
991 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
993 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
994 /* do we map hats to axes or buttons? */
998 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
1000 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1002 # if FREEGLUT_LIB_PRAGMAS
1003 # pragma comment (lib, "advapi32.lib")
1006 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1008 char buffer [ 256 ];
1010 char OEMKey [ 256 ];
1019 /* Open .. MediaResources\CurrentJoystickSettings */
1020 _snprintf ( buffer, sizeof(buffer), "%s\\%s\\%s",
1021 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1022 REGSTR_KEY_JOYCURR );
1024 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1026 if ( lr != ERROR_SUCCESS ) return 0;
1028 /* Get OEM Key name */
1029 dwcb = sizeof(OEMKey);
1031 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1032 _snprintf ( buffer, sizeof(buffer), "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1034 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1035 RegCloseKey ( hKey );
1037 if ( lr != ERROR_SUCCESS ) return 0;
1039 /* Open OEM Key from ...MediaProperties */
1040 _snprintf ( buffer, sizeof(buffer), "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1042 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1044 if ( lr != ERROR_SUCCESS ) return 0;
1049 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1051 RegCloseKey ( hKey );
1053 if ( lr != ERROR_SUCCESS ) return 0;
1060 static void fghJoystickOpen( SFG_Joystick* joy )
1063 #if TARGET_HOST_MACINTOSH
1066 #if TARGET_HOST_MAC_OSX
1069 IOCFPlugInInterface **plugin;
1071 HRESULT pluginResult;
1073 CFDictionaryRef props;
1074 CFTypeRef topLevelElement;
1076 #if TARGET_HOST_POSIX_X11
1077 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1083 # if defined( __linux__ ) || TARGET_HOST_SOLARIS
1089 /* Silence gcc, the correct #ifdefs would be too fragile... */
1093 * Default values (for no joystick -- each conditional will reset the
1097 joy->num_axes = joy->num_buttons = 0;
1098 joy->name[ 0 ] = '\0';
1100 #if TARGET_HOST_MACINTOSH
1101 /* XXX FIXME: get joystick name in Mac */
1103 err = ISpStartup( );
1107 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1109 joy->error = GL_TRUE;
1111 /* initialize the needs structure */
1112 ISpNeed temp_isp_needs[ isp_num_needs ] =
1114 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1115 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1116 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1117 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1118 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1119 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1120 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1121 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1122 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
1124 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1125 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1126 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1127 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1128 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1129 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1130 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1131 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1132 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1133 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1134 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1135 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1136 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1137 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1138 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1139 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1140 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1141 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1142 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1143 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1144 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1145 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1146 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1147 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1148 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1149 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1150 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1151 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1152 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1153 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1154 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1155 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1158 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1161 /* next two calls allow keyboard and mouse to emulate other input
1162 * devices (gamepads, joysticks, etc)
1165 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1169 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1173 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1174 joy->isp_needs, joy->isp_elem,
1176 ISP_CHECK_ERR( err )
1178 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1179 'freeglut', nil, 0, 128, 0 );
1180 ISP_CHECK_ERR( err )
1182 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1183 joy->num_axes = joy->isp_num_axis;
1185 for( i = 0; i < joy->num_axes; i++ )
1187 joy->dead_band[ i ] = 0;
1188 joy->saturate [ i ] = 1;
1189 joy->center [ i ] = kISpAxisMiddle;
1190 joy->max [ i ] = kISpAxisMaximum;
1191 joy->min [ i ] = kISpAxisMinimum;
1194 joy->error = GL_FALSE;
1197 joy->num_buttons = joy->num_axes = 0;
1200 #if TARGET_HOST_MAC_OSX
1201 if( joy->id >= numDevices )
1203 fgWarning( "device index out of range in fgJoystickOpen()" );
1207 /* create device interface */
1208 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1209 kIOHIDDeviceUserClientTypeID,
1210 kIOCFPlugInInterfaceID,
1213 if( rv != kIOReturnSuccess )
1215 fgWarning( "error creating plugin for io device" );
1219 pluginResult = ( *plugin )->QueryInterface(
1221 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1222 &( LPVOID )joy->hidDev
1225 if( pluginResult != S_OK )
1226 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1228 ( *plugin )->Release( plugin ); /* don't leak a ref */
1229 if( joy->hidDev == NULL )
1232 /* store the interface in this instance */
1233 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1234 if( rv != kIOReturnSuccess )
1236 fgWarning( "error opening device interface");
1240 props = getCFProperties( ioDevices[ joy->id ] );
1242 /* recursively enumerate all the bits */
1243 CFTypeRef topLevelElement =
1244 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1245 enumerateElements( topLevelElement );
1250 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
1251 joy->js.dwFlags = JOY_RETURNALL;
1252 joy->js.dwSize = sizeof( joy->js );
1254 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1257 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1260 if( joy->jsCaps.wNumAxes == 0 )
1263 joy->error = GL_TRUE;
1267 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1268 * at least for USB. Try to get the real name from the registry.
1270 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1271 sizeof( joy->name ) ) )
1273 fgWarning( "JS: Failed to read joystick name from registry" );
1274 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1277 /* Windows joystick drivers may provide any combination of
1278 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1280 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1282 joy->num_axes = _JS_MAX_AXES;
1283 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
1284 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
1289 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1290 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1291 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1292 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1293 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1294 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1295 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1296 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1297 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1298 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1299 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1300 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1303 /* Guess all the rest judging on the axes extremals */
1304 for( i = 0; i < joy->num_axes; i++ )
1306 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1307 joy->dead_band[ i ] = 0.0f;
1308 joy->saturate [ i ] = 1.0f;
1312 #if TARGET_HOST_POSIX_X11
1313 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1314 for( i = 0; i < _JS_MAX_AXES; i++ )
1315 joy->os->cache_axes[ i ] = 0.0f;
1317 joy->os->cache_buttons = 0;
1319 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1322 if( joy->os->fd < 0 && errno == EACCES )
1323 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1326 joy->error =( joy->os->fd < 0 );
1332 joy->num_buttons = 0;
1333 if( joy->os->is_analog )
1336 char joyfname[ 1024 ];
1337 int noargs, in_no_axes;
1339 float axes [ _JS_MAX_AXES ];
1340 int buttons[ _JS_MAX_AXES ];
1343 joy->num_buttons = 32;
1345 fghJoystickRawRead( joy, buttons, axes );
1346 joy->error = axes[ 0 ] < -1000000000.0f;
1350 snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
1352 joyfile = fopen( joyfname, "r" );
1353 joy->error =( joyfile == NULL );
1357 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1358 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1359 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1360 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1365 for( i = 0; i < _JS_MAX_AXES; i++ )
1367 joy->dead_band[ i ] = 0.0f;
1368 joy->saturate [ i ] = 1.0f;
1371 return; /* End of analog code */
1375 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1376 &joy->num_buttons ) )
1378 close( joy->os->fd );
1379 joy->error = GL_TRUE;
1383 cp = strrchr( joy->os->fname, '/' );
1386 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1388 strcpy( joy->name, &cp[1] );
1391 if( joy->num_axes > _JS_MAX_AXES )
1392 joy->num_axes = _JS_MAX_AXES;
1394 for( i = 0; i < _JS_MAX_AXES; i++ )
1396 /* We really should get this from the HID, but that data seems
1397 * to be quite unreliable for analog-to-USB converters. Punt for
1400 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1402 joy->max [ i ] = 1.0f;
1403 joy->center[ i ] = 0.0f;
1404 joy->min [ i ] = -1.0f;
1408 joy->max [ i ] = 255.0f;
1409 joy->center[ i ] = 127.0f;
1410 joy->min [ i ] = 0.0f;
1413 joy->dead_band[ i ] = 0.0f;
1414 joy->saturate[ i ] = 1.0f;
1419 #if defined( __linux__ ) || TARGET_HOST_SOLARIS
1420 /* Default for older Linux systems. */
1422 joy->num_buttons = 32;
1425 for( i = 0; i < _JS_MAX_AXES; i++ )
1426 joy->tmp_axes[ i ] = 0.0f;
1428 joy->tmp_buttons = 0;
1431 joy->fd = open( joy->fname, O_RDONLY );
1433 joy->error =( joy->fd < 0 );
1438 /* Set the correct number of axes for the linux driver */
1440 /* Melchior Franz's fixes for big-endian Linuxes since writing
1441 * to the upper byte of an uninitialized word doesn't work.
1444 ioctl( joy->fd, JSIOCGAXES, &u );
1446 ioctl( joy->fd, JSIOCGBUTTONS, &u );
1447 joy->num_buttons = u;
1448 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1449 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1453 * The Linux driver seems to return 512 for all axes
1454 * when no stick is present - but there is a chance
1455 * that could happen by accident - so it's gotta happen
1456 * on both axes for at least 100 attempts.
1458 * PWO: shouldn't be that done somehow wiser on the kernel level?
1465 fghJoystickRawRead( joy, NULL, joy->center );
1467 } while( !joy->error &&
1469 joy->center[ 0 ] == 512.0f &&
1470 joy->center[ 1 ] == 512.0f );
1472 if ( counter >= 100 )
1473 joy->error = GL_TRUE;
1476 for( i = 0; i < _JS_MAX_AXES; i++ )
1479 joy->max [ i ] = 32767.0f;
1480 joy->center[ i ] = 0.0f;
1481 joy->min [ i ] = -32767.0f;
1483 joy->max[ i ] = joy->center[ i ] * 2.0f;
1484 joy->min[ i ] = 0.0f;
1486 joy->dead_band[ i ] = 0.0f;
1487 joy->saturate [ i ] = 1.0f;
1494 * This function replaces the constructor method in the JS library.
1496 static void fghJoystickInit( int ident )
1498 if( ident >= MAX_NUM_JOYSTICKS )
1499 fgError( "Too large a joystick number: %d", ident );
1501 if( fgJoystick[ ident ] )
1502 fgError( "illegal attempt to initialize joystick device again" );
1504 fgJoystick[ ident ] =
1505 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1508 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1509 fgJoystick[ ident ]->error = GL_TRUE;
1511 #if TARGET_HOST_MACINTOSH
1512 fgJoystick[ ident ]->id = ident;
1513 snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident ); /* FIXME */
1514 fgJoystick[ ident ]->error = GL_FALSE;
1517 #if TARGET_HOST_MAC_OSX
1518 fgJoystick[ ident ]->id = ident;
1519 fgJoystick[ ident ]->error = GL_FALSE;
1520 fgJoystick[ ident ]->num_axes = 0;
1521 fgJoystick[ ident ]->num_buttons = 0;
1523 if( numDevices < 0 )
1525 /* do first-time init (since we can't over-ride jsInit, hmm */
1528 mach_port_t masterPort;
1529 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1530 if( rv != kIOReturnSuccess )
1532 fgWarning( "error getting master Mach port" );
1535 fghJoystickFindDevices( masterPort );
1538 if ( ident >= numDevices )
1540 fgJoystick[ ident ]->error = GL_TRUE;
1544 /* get the name now too */
1545 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1546 CFTypeRef ref = CFDictionaryGetValue( properties,
1547 CFSTR( kIOHIDProductKey ) );
1549 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1552 !CFStringGetCString( ( CFStringRef )ref, name, 128,
1553 CFStringGetSystemEncoding( ) ) )
1555 fgWarning( "error getting device name" );
1560 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
1564 fgJoystick[ ident ]->js_id = JOYSTICKID1;
1565 fgJoystick[ ident ]->error = GL_FALSE;
1568 fgJoystick[ ident ]->js_id = JOYSTICKID2;
1569 fgJoystick[ ident ]->error = GL_FALSE;
1572 fgJoystick[ ident ]->num_axes = 0;
1573 fgJoystick[ ident ]->error = GL_TRUE;
1578 #if TARGET_HOST_POSIX_X11
1579 # if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1580 fgJoystick[ ident ]->id = ident;
1581 fgJoystick[ ident ]->error = GL_FALSE;
1583 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1584 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1585 if( ident < USB_IDENT_OFFSET )
1586 fgJoystick[ ident ]->os->is_analog = 1;
1587 if( fgJoystick[ ident ]->os->is_analog )
1588 snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", AJSDEV, ident );
1590 snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", UHIDDEV,
1591 ident - USB_IDENT_OFFSET );
1592 # elif defined( __linux__ )
1593 fgJoystick[ ident ]->id = ident;
1594 fgJoystick[ ident ]->error = GL_FALSE;
1596 snprintf( fgJoystick[ident]->fname, sizeof(fgJoystick[ident]->fname), "/dev/input/js%d", ident );
1598 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1599 snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident );
1603 fghJoystickOpen( fgJoystick[ ident ] );
1607 * Try initializing all the joysticks (well, both of them)
1609 void fgInitialiseJoysticks ( void )
1611 if( !fgState.JoysticksInitialised )
1614 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1615 fghJoystickInit( ident );
1617 fgState.JoysticksInitialised = GL_TRUE;
1624 void fgJoystickClose( void )
1627 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1629 if( fgJoystick[ ident ] )
1632 #if TARGET_HOST_MACINTOSH
1638 #if TARGET_HOST_MAC_OSX
1639 ( *( fgJoystick[ ident ]->hidDev ) )->
1640 close( fgJoystick[ ident ]->hidDev );
1643 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
1644 /* Do nothing special */
1647 #if TARGET_HOST_POSIX_X11
1648 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1649 if( fgJoystick[ident]->os )
1651 if( ! fgJoystick[ ident ]->error )
1652 close( fgJoystick[ ident ]->os->fd );
1654 if( fgJoystick[ ident ]->os->hids )
1655 free (fgJoystick[ ident ]->os->hids);
1656 if( fgJoystick[ ident ]->os->hid_data_buf )
1657 free( fgJoystick[ ident ]->os->hid_data_buf );
1659 free( fgJoystick[ident]->os );
1663 if( ! fgJoystick[ident]->error )
1664 close( fgJoystick[ ident ]->fd );
1667 free( fgJoystick[ ident ] );
1668 fgJoystick[ ident ] = NULL;
1669 /* show joystick has been deinitialized */
1675 * Polls the joystick and executes the joystick callback hooked to the
1676 * window specified in the function's parameter:
1678 void fgJoystickPollWindow( SFG_Window* window )
1680 float axes[ _JS_MAX_AXES ];
1684 freeglut_return_if_fail( window );
1685 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1687 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1689 if( fgJoystick[ident] )
1691 fghJoystickRead( fgJoystick[ident], &buttons, axes );
1693 if( !fgJoystick[ident]->error )
1694 INVOKE_WCB( *window, Joystick,
1696 (int) ( axes[ 0 ] * 1000.0f ),
1697 (int) ( axes[ 1 ] * 1000.0f ),
1698 (int) ( axes[ 2 ] * 1000.0f ) )
1705 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1707 int fgJoystickDetect( void )
1711 fgInitialiseJoysticks ();
1713 if ( !fgState.JoysticksInitialised )
1716 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1717 if( fgJoystick[ident] && !fgJoystick[ident]->error )
1724 * Joystick information functions
1726 int glutJoystickGetNumAxes( int ident )
1728 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
1729 return fgJoystick[ ident ]->num_axes;
1731 int glutJoystickGetNumButtons( int ident )
1733 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
1734 return fgJoystick[ ident ]->num_buttons;
1736 int glutJoystickNotWorking( int ident )
1738 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
1739 return fgJoystick[ ident ]->error;
1742 float glutJoystickGetDeadBand( int ident, int axis )
1744 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1745 return fgJoystick[ ident ]->dead_band [ axis ];
1747 void glutJoystickSetDeadBand( int ident, int axis, float db )
1749 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1750 fgJoystick[ ident ]->dead_band[ axis ] = db;
1753 float glutJoystickGetSaturation( int ident, int axis )
1755 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1756 return fgJoystick[ ident ]->saturate[ axis ];
1758 void glutJoystickSetSaturation( int ident, int axis, float st )
1760 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1761 fgJoystick[ ident ]->saturate [ axis ] = st;
1764 void glutJoystickSetMinRange( int ident, float *axes )
1766 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1767 memcpy( fgJoystick[ ident ]->min, axes,
1768 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1770 void glutJoystickSetMaxRange( int ident, float *axes )
1772 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1773 memcpy( fgJoystick[ ident ]->max, axes,
1774 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1776 void glutJoystickSetCenter( int ident, float *axes )
1778 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1779 memcpy( fgJoystick[ ident ]->center, axes,
1780 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1783 void glutJoystickGetMinRange( int ident, float *axes )
1785 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1786 memcpy( axes, fgJoystick[ ident ]->min,
1787 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1789 void glutJoystickGetMaxRange( int ident, float *axes )
1791 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1792 memcpy( axes, fgJoystick[ ident ]->max,
1793 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1795 void glutJoystickGetCenter( int ident, float *axes )
1797 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1798 memcpy( axes, fgJoystick[ ident ]->center,
1799 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1802 /*** END OF FILE ***/