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 "fg_internal.h"
36 #ifdef HAVE_SYS_PARAM_H
37 # include <sys/param.h>
43 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
45 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
48 # if defined(__NetBSD__)
49 /* XXX The below hack is done until freeglut's autoconf is updated. */
50 # define HAVE_USBHID_H 1
56 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
60 # include <libusbhid.h>
63 # include <legacy/dev/usb/usb.h>
64 # include <dev/usb/usbhid.h>
66 /* Compatibility with older usb.h revisions */
67 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
68 # define USB_MAX_DEVNAMES MAXDEVNAMES
72 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
73 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
74 struct os_specific_s {
78 /* The following structure members are specific to analog joysticks */
81 /* The following structure members are specific to USB joysticks */
82 struct hid_item *hids;
86 int axes_usage [ _JS_MAX_AXES ];
88 /* We keep button and axes state ourselves, as they might not be updated
89 * on every read of a USB device
92 float cache_axes [ _JS_MAX_AXES ];
95 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
96 # define USB_IDENT_OFFSET 2
98 # define USBDEV "/dev/usb"
99 # define UHIDDEV "/dev/uhid"
100 # define AJSDEV "/dev/joy"
104 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
105 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
106 * return the uhidN device name. We warn the user of this situation once.
108 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
110 struct usb_device_info di;
114 for (a = 1; a < USB_MAX_DEVICES; a++) {
116 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
118 for (i = 0; i < USB_MAX_DEVNAMES; i++)
119 if (di.udi_devnames[i][0] &&
120 strcmp(di.udi_devnames[i], dev) == 0) {
121 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
122 strcpy(cp, di.udi_vendor);
124 strcat(cp, di.udi_product);
125 strncpy(out, cp, outlen - 1);
134 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
139 static int protection_warned = 0;
141 for (i = 0; i < 16; i++) {
142 snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
143 f = open(buf, O_RDONLY);
145 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
150 else if (errno == EACCES) {
151 if (!protection_warned) {
152 fgWarning ( "Can't open %s for read!", buf );
153 protection_warned = 1;
160 static int fghJoystickInitializeHID(struct os_specific_s *os,
161 int *num_axes, int *num_buttons)
163 int size, is_joystick;
164 # ifdef HAVE_USBHID_H
171 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
173 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
179 # ifdef HAVE_USBHID_H
180 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
182 /*** XXX {report_id} may not be the right variable? ***/
183 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
187 size = hid_report_size( rd, hid_input, report_id );
189 size = hid_report_size( rd, 0, hid_input );
191 os->hid_data_buf = calloc( 1, size );
195 # ifdef HAVE_USBHID_H
196 d = hid_start_parse( rd, 1 << hid_input, report_id );
198 d = hid_start_parse( rd, 1 << hid_input );
200 while( hid_get_item( d, &h ) )
202 int usage, page, interesting_hid;
204 page = HID_PAGE( h.usage );
205 usage = HID_USAGE( h.usage );
207 /* This test is somewhat too simplistic, but this is how MicroSoft
208 * does, so I guess it works for all joysticks/game pads. */
209 is_joystick = is_joystick ||
210 ( h.kind == hid_collection &&
211 page == HUP_GENERIC_DESKTOP &&
212 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
214 if( h.kind != hid_input )
220 interesting_hid = TRUE;
221 if( page == HUP_GENERIC_DESKTOP )
232 if( *num_axes < _JS_MAX_AXES )
234 os->axes_usage[ *num_axes ] = usage;
239 /* Allocate two axes for a hat */
240 if( *num_axes + 1 < _JS_MAX_AXES )
242 os->axes_usage[ *num_axes ] = usage;
244 os->axes_usage[ *num_axes ] = usage;
249 interesting_hid = FALSE;
253 else if( page == HUP_BUTTON )
255 interesting_hid = ( usage > 0 ) &&
256 ( usage <= _JS_MAX_BUTTONS );
258 if( interesting_hid && usage - 1 > *num_buttons )
259 *num_buttons = usage - 1;
262 if( interesting_hid )
265 os->hids = calloc( 1, sizeof ( struct hid_item ) );
271 return os->hids != NULL;
277 * Functions associated with the "jsJoystick" class in PLIB
279 #if TARGET_HOST_MAC_OSX
280 #define K_NUM_DEVICES 32
282 io_object_t ioDevices[K_NUM_DEVICES];
284 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
285 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
287 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
288 /* callback for CFArrayApply */
289 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
291 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
292 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
293 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
297 /* External function declarations (mostly platform-specific) */
298 extern void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes );
299 extern void fgPlatformJoystickOpen( SFG_Joystick* joy );
300 extern void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident );
301 extern void fgPlatformJoystickClose ( int ident );
304 * The static joystick structure pointer
306 #define MAX_NUM_JOYSTICKS 2
307 SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
310 * Read the raw joystick data
312 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
321 for( i = 0; i < joy->num_axes; i++ )
327 fgPlatformJoystickRawRead ( joy, buttons, axes );
331 * Correct the joystick axis data
333 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
335 if( value < joy->center[ axis ] )
337 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
340 if( xx < -joy->saturate[ axis ] )
343 if( xx > -joy->dead_band [ axis ] )
346 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
347 joy->dead_band[ axis ] );
349 return ( xx < -1.0f ) ? -1.0f : xx;
353 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
354 joy->center[ axis ] );
356 if( xx > joy->saturate[ axis ] )
359 if( xx < joy->dead_band[ axis ] )
362 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
363 joy->dead_band[ axis ] );
365 return ( xx > 1.0f ) ? 1.0f : xx;
370 * Read the corrected joystick data
372 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
374 float raw_axes[ _JS_MAX_AXES ];
383 for ( i=0; i<joy->num_axes; i++ )
387 fghJoystickRawRead( joy, buttons, raw_axes );
390 for( i=0; i<joy->num_axes; i++ )
391 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
395 * Happy happy happy joy joy joy (happy new year toudi :D)
399 #if TARGET_HOST_MAC_OSX
400 /** open the IOKit connection, enumerate all the HID devices, add their
401 interface references to the static array. We then use the array index
402 as the device number when we come to open() the joystick. */
403 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
405 CFMutableDictionaryRef hidMatch = NULL;
406 IOReturn rv = kIOReturnSuccess;
408 io_iterator_t hidIterator;
411 /* build a dictionary matching HID devices */
412 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
414 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
415 if (rv != kIOReturnSuccess || !hidIterator) {
416 fgWarning( "no joystick (HID) devices found" );
421 while ((ioDev = IOIteratorNext(hidIterator))) {
422 /* filter out keyboard and mouse devices */
423 CFDictionaryRef properties = getCFProperties(ioDev);
426 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
427 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
428 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
429 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
431 /* keep only joystick devices */
432 if ( ( page == kHIDPage_GenericDesktop ) && (
433 (usage == kHIDUsage_GD_Joystick)
434 || (usage == kHIDUsage_GD_GamePad)
435 || (usage == kHIDUsage_GD_MultiAxisController)
436 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
437 /* add it to the array */
438 ioDevices[numDevices++] = ioDev;
441 IOObjectRelease(hidIterator);
444 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
447 CFMutableDictionaryRef cfProperties;
450 /* comment copied from darwin/SDL_sysjoystick.c */
451 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
452 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
455 io_registry_entry_t parent1, parent2;
457 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
458 if (rv != kIOReturnSuccess) {
459 fgWarning ( "error getting device entry parent");
463 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
464 if (rv != kIOReturnSuccess) {
465 fgWarning ( "error getting device entry parent 2");
470 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
471 &cfProperties, kCFAllocatorDefault, kNilOptions);
472 if (rv != kIOReturnSuccess || !cfProperties) {
473 fgWarning ( "error getting device properties");
480 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
482 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
483 fgError ( "%s", "element enumerator passed non-dictionary value");
487 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
490 /** element enumerator function : pass NULL for top-level*/
491 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
493 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
494 "Joystick element type mismatch",
495 "fghJoystickEnumerateElements" );
497 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
498 CFArrayApplyFunction((CFArrayRef) element, range,
499 &fghJoystickElementEnumerator, joy );
502 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
504 long cookie, lmin, lmax;
505 int index = joy->num_axes++;
507 CFNumberGetValue ((CFNumberRef)
508 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
509 kCFNumberLongType, &cookie);
511 joy->pJoystick.axisCookies[index] = (IOHIDElementCookie) cookie;
513 CFNumberGetValue ((CFNumberRef)
514 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
515 kCFNumberLongType, &lmin);
517 CFNumberGetValue ((CFNumberRef)
518 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
519 kCFNumberLongType, &lmax);
521 joy->min[index] = lmin;
522 joy->max[index] = lmax;
523 joy->dead_band[index] = 0.0;
524 joy->saturate[index] = 1.0;
525 joy->center[index] = (lmax + lmin) * 0.5;
528 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
531 CFNumberGetValue ((CFNumberRef)
532 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
533 kCFNumberLongType, &cookie);
535 joy->pJoystick.buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
536 /* anything else for buttons? */
539 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
541 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
542 /* do we map hats to axes or buttons? */
547 * Platform-Specific Code
550 #if TARGET_HOST_MACINTOSH
551 void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
559 for ( i = 0; i < joy->num_buttons; i++ )
562 int err = ISpElement_GetSimpleState ( joy->pJoystick.isp_elem [ i + ISP_NUM_AXIS ], &state);
565 *buttons |= state << i;
571 for ( i = 0; i < joy->num_axes; i++ )
574 int err = ISpElement_GetSimpleState ( joy->pJoystick.isp_elem [ i ], &state );
577 axes [i] = (float) state;
583 void fgPlatformJoystickOpen( SFG_Joystick* joy )
588 /* XXX FIXME: get joystick name in Mac */
594 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
596 joy->error = GL_TRUE;
598 /* initialize the needs structure */
599 ISpNeed temp_isp_needs[ ISP_NUM_NEEDS ] =
601 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
602 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
603 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
604 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
605 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
606 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
607 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
608 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
609 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
611 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
612 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
613 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
614 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
615 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
616 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
617 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
618 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
619 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
620 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
621 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
622 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
623 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
624 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
625 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
626 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
627 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
628 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
629 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
630 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
631 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
632 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
633 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
634 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
635 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
636 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
637 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
638 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
639 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
640 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
641 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
642 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
645 memcpy( joy->pJoystick.isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
648 /* next two calls allow keyboard and mouse to emulate other input
649 * devices (gamepads, joysticks, etc)
652 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
656 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
660 err = ISpElement_NewVirtualFromNeeds( ISP_NUM_NEEDS,
661 joy->pJoystick.isp_needs, joy->pJoystick.isp_elem,
665 err = ISpInit( ISP_NUM_NEEDS, joy->pJoystick.isp_needs, joy->pJoystick.isp_elem,
666 'freeglut', nil, 0, 128, 0 );
669 joy->num_buttons = ISP_NUM_NEEDS - ISP_NUM_AXIS;
670 joy->num_axes = ISP_NUM_AXIS;
672 for( i = 0; i < joy->num_axes; i++ )
674 joy->dead_band[ i ] = 0;
675 joy->saturate [ i ] = 1;
676 joy->center [ i ] = kISpAxisMiddle;
677 joy->max [ i ] = kISpAxisMaximum;
678 joy->min [ i ] = kISpAxisMinimum;
681 joy->error = GL_FALSE;
684 joy->num_buttons = joy->num_axes = 0;
688 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
690 fgJoystick[ ident ]->id = ident;
691 snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d", ident ); /* FIXME */
692 fgJoystick[ ident ]->error = GL_FALSE;
696 void fgPlatformJoystickClose ( int ident )
704 #if TARGET_HOST_MAC_OSX
705 void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
709 if ( buttons != NULL )
713 for ( i = 0; i < joy->num_buttons; i++ )
715 IOHIDEventStruct hidEvent;
716 (*(joy->pJoystick.hidDev))->getElementValue ( joy->pJoystick.hidDev, joy->pJoystick.buttonCookies[i], &hidEvent );
717 if ( hidEvent.value )
724 for ( i = 0; i < joy->num_axes; i++ )
726 IOHIDEventStruct hidEvent;
727 (*(joy->pJoystick.hidDev))->getElementValue ( joy->pJoystick.hidDev, joy->pJoystick.axisCookies[i], &hidEvent );
728 axes[i] = hidEvent.value;
734 void fgPlatformJoystickOpen( SFG_Joystick* joy )
738 IOCFPlugInInterface **plugin;
740 HRESULT pluginResult;
742 CFDictionaryRef props;
743 CFTypeRef topLevelElement;
745 if( joy->id >= numDevices )
747 fgWarning( "device index out of range in fgJoystickOpen()" );
751 /* create device interface */
752 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
753 kIOHIDDeviceUserClientTypeID,
754 kIOCFPlugInInterfaceID,
757 if( rv != kIOReturnSuccess )
759 fgWarning( "error creating plugin for io device" );
763 pluginResult = ( *plugin )->QueryInterface(
765 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
766 &( LPVOID )joy->pJoystick.hidDev
769 if( pluginResult != S_OK )
770 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
772 ( *plugin )->Release( plugin ); /* don't leak a ref */
773 if( joy->pJoystick.hidDev == NULL )
776 /* store the interface in this instance */
777 rv = ( *( joy->pJoystick.hidDev ) )->open( joy->pJoystick.hidDev, 0 );
778 if( rv != kIOReturnSuccess )
780 fgWarning( "error opening device interface");
784 props = getCFProperties( ioDevices[ joy->id ] );
786 /* recursively enumerate all the bits */
787 CFTypeRef topLevelElement =
788 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
789 enumerateElements( topLevelElement );
795 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
797 fgJoystick[ ident ]->id = ident;
798 fgJoystick[ ident ]->error = GL_FALSE;
799 fgJoystick[ ident ]->num_axes = 0;
800 fgJoystick[ ident ]->num_buttons = 0;
804 /* do first-time init (since we can't over-ride jsInit, hmm */
807 mach_port_t masterPort;
808 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
809 if( rv != kIOReturnSuccess )
811 fgWarning( "error getting master Mach port" );
814 fghJoystickFindDevices( masterPort );
817 if ( ident >= numDevices )
819 fgJoystick[ ident ]->error = GL_TRUE;
823 /* get the name now too */
824 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
825 CFTypeRef ref = CFDictionaryGetValue( properties,
826 CFSTR( kIOHIDProductKey ) );
828 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
831 !CFStringGetCString( ( CFStringRef )ref, name, 128,
832 CFStringGetSystemEncoding( ) ) )
834 fgWarning( "error getting device name" );
840 void fgPlatformJoystickClose ( int ident )
842 ( *( fgJoystick[ ident ]->pJoystick.hidDev ) )->
843 close( fgJoystick[ ident ]->pJoystick.hidDev );
850 static void fghJoystickOpen( SFG_Joystick* joy )
853 * Default values (for no joystick -- each conditional will reset the
857 joy->num_axes = joy->num_buttons = 0;
858 joy->name[ 0 ] = '\0';
860 fgPlatformJoystickOpen ( joy );
865 * This function replaces the constructor method in the JS library.
867 static void fghJoystickInit( int ident )
869 if( ident >= MAX_NUM_JOYSTICKS )
870 fgError( "Too large a joystick number: %d", ident );
872 if( fgJoystick[ ident ] )
873 fgError( "illegal attempt to initialize joystick device again" );
875 fgJoystick[ ident ] =
876 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
879 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
880 fgJoystick[ ident ]->error = GL_TRUE;
882 fgPlatformJoystickInit( fgJoystick, ident );
884 fghJoystickOpen( fgJoystick[ ident ] );
888 * Try initializing all the joysticks (well, both of them)
890 void fgInitialiseJoysticks ( void )
892 if( !fgState.JoysticksInitialised )
895 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
896 fghJoystickInit( ident );
898 fgState.JoysticksInitialised = GL_TRUE;
903 void fgJoystickClose( void )
906 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
908 if( fgJoystick[ ident ] )
910 fgPlatformJoystickClose ( ident );
912 free( fgJoystick[ ident ] );
913 fgJoystick[ ident ] = NULL;
914 /* show joystick has been deinitialized */
920 * Polls the joystick and executes the joystick callback hooked to the
921 * window specified in the function's parameter:
923 void fgJoystickPollWindow( SFG_Window* window )
925 float axes[ _JS_MAX_AXES ];
929 freeglut_return_if_fail( window );
930 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
932 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
934 if( fgJoystick[ident] )
936 fghJoystickRead( fgJoystick[ident], &buttons, axes );
938 if( !fgJoystick[ident]->error )
939 INVOKE_WCB( *window, Joystick,
941 (int) ( axes[ 0 ] * 1000.0f ),
942 (int) ( axes[ 1 ] * 1000.0f ),
943 (int) ( axes[ 2 ] * 1000.0f ) )
950 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
952 int fgJoystickDetect( void )
956 fgInitialiseJoysticks ();
958 if ( !fgState.JoysticksInitialised )
961 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
962 if( fgJoystick[ident] && !fgJoystick[ident]->error )
969 * Joystick information, setup and execution functions
973 * Forces the joystick callback to be executed
975 void FGAPIENTRY glutForceJoystickFunc( void )
977 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutForceJoystickFunc" );
978 #if !defined(_WIN32_WCE)
979 freeglut_return_if_fail( fgStructure.CurrentWindow != NULL );
980 freeglut_return_if_fail( FETCH_WCB( *( fgStructure.CurrentWindow ), Joystick ) );
981 fgJoystickPollWindow( fgStructure.CurrentWindow );
982 #endif /* !defined(_WIN32_WCE) */
984 int glutJoystickGetNumAxes( int ident )
986 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
987 return fgJoystick[ ident ]->num_axes;
989 int glutJoystickGetNumButtons( int ident )
991 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
992 return fgJoystick[ ident ]->num_buttons;
994 int glutJoystickNotWorking( int ident )
996 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
997 return fgJoystick[ ident ]->error;
1000 float glutJoystickGetDeadBand( int ident, int axis )
1002 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1003 return fgJoystick[ ident ]->dead_band [ axis ];
1005 void glutJoystickSetDeadBand( int ident, int axis, float db )
1007 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1008 fgJoystick[ ident ]->dead_band[ axis ] = db;
1011 float glutJoystickGetSaturation( int ident, int axis )
1013 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1014 return fgJoystick[ ident ]->saturate[ axis ];
1016 void glutJoystickSetSaturation( int ident, int axis, float st )
1018 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1019 fgJoystick[ ident ]->saturate [ axis ] = st;
1022 void glutJoystickSetMinRange( int ident, float *axes )
1024 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1025 memcpy( fgJoystick[ ident ]->min, axes,
1026 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1028 void glutJoystickSetMaxRange( int ident, float *axes )
1030 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1031 memcpy( fgJoystick[ ident ]->max, axes,
1032 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1034 void glutJoystickSetCenter( int ident, float *axes )
1036 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1037 memcpy( fgJoystick[ ident ]->center, axes,
1038 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1041 void glutJoystickGetMinRange( int ident, float *axes )
1043 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1044 memcpy( axes, fgJoystick[ ident ]->min,
1045 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1047 void glutJoystickGetMaxRange( int ident, float *axes )
1049 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1050 memcpy( axes, fgJoystick[ ident ]->max,
1051 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1053 void glutJoystickGetCenter( int ident, float *axes )
1055 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1056 memcpy( axes, fgJoystick[ ident ]->center,
1057 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1060 /*** END OF FILE ***/