bump version numbers before minor release
[freeglut] / src / fg_joystick.c
1 /*
2  * fg_joystick.c
3  *
4  * Joystick handling code
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Steve Baker, <sjbaker1@airmail.net>
8  *
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:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
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.
25  */
26
27 /*
28  * FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu>
29  *
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.
32  */
33
34 #include <GL/freeglut.h>
35 #include "fg_internal.h"
36 #ifdef HAVE_SYS_PARAM_H
37 #    include <sys/param.h>
38 #endif
39
40 #define JS_TRUE  1
41 #define JS_FALSE 0
42
43 /*
44  * Functions associated with the "jsJoystick" class in PLIB
45  */
46 #if TARGET_HOST_MAC_OSX
47 #define K_NUM_DEVICES   32
48 int numDevices;
49 io_object_t ioDevices[K_NUM_DEVICES];
50
51 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
52 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
53
54 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
55 /* callback for CFArrayApply */
56 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
57
58 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
59 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
60 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
61 #endif
62
63
64 /* External function declarations (mostly platform-specific) */
65 extern void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes );
66 extern void fgPlatformJoystickOpen( SFG_Joystick* joy );
67 extern void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident );
68 extern void fgPlatformJoystickClose ( int ident );
69
70 /*
71  * The static joystick structure pointer
72  */
73 #define MAX_NUM_JOYSTICKS  2
74 SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
75
76 /*
77  * Read the raw joystick data
78  */
79 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
80 {
81     int i;
82
83     /* Defaults */
84     if( buttons )
85         *buttons = 0;
86
87     if( axes )
88         for( i = 0; i < joy->num_axes; i++ )
89             axes[ i ] = 1500.0f;
90
91     if( joy->error )
92         return;
93
94     fgPlatformJoystickRawRead ( joy, buttons, axes );
95 }
96
97 /*
98  * Correct the joystick axis data
99  */
100 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
101 {
102     if( value < joy->center[ axis ] )
103     {
104         float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
105                                                        joy->min[ axis ] );
106
107         if( xx < -joy->saturate[ axis ] )
108             return -1.0f;
109
110         if( xx > -joy->dead_band [ axis ] )
111             return 0.0f;
112
113         xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
114                                                  joy->dead_band[ axis ] );
115
116         return ( xx < -1.0f ) ? -1.0f : xx;
117     }
118     else
119     {
120         float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
121                                                         joy->center[ axis ] );
122
123         if( xx > joy->saturate[ axis ] )
124             return 1.0f;
125
126         if( xx < joy->dead_band[ axis ] )
127             return 0.0f;
128
129         xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
130                                                  joy->dead_band[ axis ] );
131
132         return ( xx > 1.0f ) ? 1.0f : xx;
133     }
134 }
135
136 /*
137  * Read the corrected joystick data
138  */
139 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
140 {
141     float raw_axes[ _JS_MAX_AXES ];
142     int  i;
143
144     if( joy->error )
145     {
146         if( buttons )
147             *buttons = 0;
148
149         if( axes )
150             for ( i=0; i<joy->num_axes; i++ )
151                 axes[ i ] = 0.0f;
152     }
153
154     fghJoystickRawRead( joy, buttons, raw_axes );
155
156     if( axes )
157         for( i=0; i<joy->num_axes; i++ )
158             axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
159 }
160
161 /*
162  * Happy happy happy joy joy joy (happy new year toudi :D)
163  */
164
165
166 #if TARGET_HOST_MAC_OSX
167 /** open the IOKit connection, enumerate all the HID devices, add their
168 interface references to the static array. We then use the array index
169 as the device number when we come to open() the joystick. */
170 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
171 {
172     CFMutableDictionaryRef hidMatch = NULL;
173     IOReturn rv = kIOReturnSuccess;
174
175     io_iterator_t hidIterator;
176     io_object_t ioDev;
177
178     /* build a dictionary matching HID devices */
179     hidMatch = IOServiceMatching(kIOHIDDeviceKey);
180
181     rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
182     if (rv != kIOReturnSuccess || !hidIterator) {
183       fgWarning( "no joystick (HID) devices found" );
184       return;
185     }
186
187     /* iterate */
188     while ((ioDev = IOIteratorNext(hidIterator))) {
189         /* filter out keyboard and mouse devices */
190         CFDictionaryRef properties = getCFProperties(ioDev);
191         long usage, page;
192
193         CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
194         CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
195         CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
196         CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
197
198         /* keep only joystick devices */
199         if ( ( page == kHIDPage_GenericDesktop ) && (
200                             (usage == kHIDUsage_GD_Joystick)
201                          || (usage == kHIDUsage_GD_GamePad)
202                          || (usage == kHIDUsage_GD_MultiAxisController)
203                          || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
204             /* add it to the array */
205             ioDevices[numDevices++] = ioDev;
206     }
207
208     IOObjectRelease(hidIterator);
209 }
210
211 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
212 {
213     IOReturn rv;
214     CFMutableDictionaryRef cfProperties;
215
216 #if 0
217     /* comment copied from darwin/SDL_sysjoystick.c */
218     /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
219      * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
220      */
221
222     io_registry_entry_t parent1, parent2;
223
224     rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
225     if (rv != kIOReturnSuccess) {
226         fgWarning ( "error getting device entry parent");
227         return NULL;
228     }
229
230     rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
231     if (rv != kIOReturnSuccess) {
232         fgWarning ( "error getting device entry parent 2");
233         return NULL;
234     }
235 #endif
236
237     rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
238         &cfProperties, kCFAllocatorDefault, kNilOptions);
239     if (rv != kIOReturnSuccess || !cfProperties) {
240         fgWarning ( "error getting device properties");
241         return NULL;
242     }
243
244     return cfProperties;
245 }
246
247 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
248 {
249       if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
250             fgError ( "%s", "element enumerator passed non-dictionary value");
251             return;
252     }
253
254       static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
255 }
256
257 /** element enumerator function : pass NULL for top-level*/
258 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
259 {
260       FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
261                                     "Joystick element type mismatch",
262                                     "fghJoystickEnumerateElements" );
263
264       CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
265       CFArrayApplyFunction((CFArrayRef) element, range,
266             &fghJoystickElementEnumerator, joy );
267 }
268
269 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
270 {
271     long cookie, lmin, lmax;
272     int index = joy->num_axes++;
273
274     CFNumberGetValue ((CFNumberRef)
275         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
276         kCFNumberLongType, &cookie);
277
278     joy->pJoystick.axisCookies[index] = (IOHIDElementCookie) cookie;
279
280     CFNumberGetValue ((CFNumberRef)
281         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
282         kCFNumberLongType, &lmin);
283
284     CFNumberGetValue ((CFNumberRef)
285         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
286         kCFNumberLongType, &lmax);
287
288     joy->min[index] = lmin;
289     joy->max[index] = lmax;
290     joy->dead_band[index] = 0.0;
291     joy->saturate[index] = 1.0;
292     joy->center[index] = (lmax + lmin) * 0.5;
293 }
294
295 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
296 {
297     long cookie;
298     CFNumberGetValue ((CFNumberRef)
299             CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
300             kCFNumberLongType, &cookie);
301
302     joy->pJoystick.buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
303     /* anything else for buttons? */
304 }
305
306 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
307 {
308     /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
309     /* do we map hats to axes or buttons? */
310 }
311 #endif
312
313 /*
314  *  Platform-Specific Code
315  */
316
317 #if TARGET_HOST_MACINTOSH
318 void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
319 {
320     int i;
321
322     if ( buttons )
323     {
324         *buttons = 0;
325
326         for ( i = 0; i < joy->num_buttons; i++ )
327         {
328             UInt32 state;
329             int err = ISpElement_GetSimpleState ( joy->pJoystick.isp_elem [ i + ISP_NUM_AXIS ], &state);
330             ISP_CHECK_ERR(err)
331
332             *buttons |= state << i;
333         }
334     }
335
336     if ( axes )
337     {
338         for ( i = 0; i < joy->num_axes; i++ )
339         {
340             UInt32 state;
341             int err = ISpElement_GetSimpleState ( joy->pJoystick.isp_elem [ i ], &state );
342             ISP_CHECK_ERR(err)
343
344             axes [i] = (float) state;
345         }
346     }
347 }
348
349
350 void fgPlatformJoystickOpen( SFG_Joystick* joy )
351 {
352     int i = 0;
353     OSStatus err;
354
355     /* XXX FIXME: get joystick name in Mac */
356
357     err = ISpStartup( );
358
359     if( err == noErr )
360     {
361 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
362
363         joy->error = GL_TRUE;
364
365         /* initialize the needs structure */
366         ISpNeed temp_isp_needs[ ISP_NUM_NEEDS ] =
367         {
368           { "\pX-Axis",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
369           { "\pY-Axis",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
370           { "\pZ-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
371           { "\pR-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
372           { "\pAxis   4",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
373           { "\pAxis   5",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
374           { "\pAxis   6",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
375           { "\pAxis   7",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
376           { "\pAxis   8",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
377
378           { "\pButton 0",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
379           { "\pButton 1",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
380           { "\pButton 2",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
381           { "\pButton 3",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
382           { "\pButton 4",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
383           { "\pButton 5",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
384           { "\pButton 6",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
385           { "\pButton 7",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
386           { "\pButton 8",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
387           { "\pButton 9",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
388           { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
389           { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
390           { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
391           { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
392           { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
393           { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
394           { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
395           { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
396           { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
397           { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
398           { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
399           { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
400           { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
401           { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
402           { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
403           { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
404           { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
405           { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
406           { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
407           { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
408           { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
409           { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
410         };
411
412         memcpy( joy->pJoystick.isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
413
414
415         /* next two calls allow keyboard and mouse to emulate other input
416          * devices (gamepads, joysticks, etc)
417          */
418         /*
419           err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
420           ISP_CHECK_ERR(err)
421
422
423           err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
424           ISP_CHECK_ERR(err)
425         */
426
427         err = ISpElement_NewVirtualFromNeeds( ISP_NUM_NEEDS,
428                                               joy->pJoystick.isp_needs, joy->pJoystick.isp_elem,
429                                               0 );
430         ISP_CHECK_ERR( err )
431
432         err = ISpInit( ISP_NUM_NEEDS, joy->pJoystick.isp_needs, joy->pJoystick.isp_elem,
433                        'freeglut', nil, 0, 128, 0 );
434         ISP_CHECK_ERR( err )
435
436         joy->num_buttons = ISP_NUM_NEEDS - ISP_NUM_AXIS;
437         joy->num_axes    = ISP_NUM_AXIS;
438
439         for( i = 0; i < joy->num_axes; i++ )
440         {
441             joy->dead_band[ i ] = 0;
442             joy->saturate [ i ] = 1;
443             joy->center   [ i ] = kISpAxisMiddle;
444             joy->max      [ i ] = kISpAxisMaximum;
445             joy->min      [ i ] = kISpAxisMinimum;
446         }
447
448         joy->error = GL_FALSE;
449     }
450     else
451         joy->num_buttons = joy->num_axes = 0;
452 }
453
454
455 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
456 {
457     fgJoystick[ ident ]->id = ident;
458     snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d", ident ); /* FIXME */
459     fgJoystick[ ident ]->error = GL_FALSE;
460 }
461
462
463 void fgPlatformJoystickClose ( int ident )
464 {
465     ISpSuspend( );
466     ISpStop( );
467     ISpShutdown( );
468 }
469 #endif
470
471 #if TARGET_HOST_MAC_OSX
472 void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
473 {
474     int i;
475
476     if ( buttons != NULL )
477     {
478         *buttons = 0;
479
480         for ( i = 0; i < joy->num_buttons; i++ )
481         {
482             IOHIDEventStruct hidEvent;
483             (*(joy->pJoystick.hidDev))->getElementValue ( joy->pJoystick.hidDev, joy->pJoystick.buttonCookies[i], &hidEvent );
484             if ( hidEvent.value )
485                 *buttons |= 1 << i;
486         }
487     }
488
489     if ( axes != NULL )
490     {
491         for ( i = 0; i < joy->num_axes; i++ )
492         {
493             IOHIDEventStruct hidEvent;
494             (*(joy->pJoystick.hidDev))->getElementValue ( joy->pJoystick.hidDev, joy->pJoystick.axisCookies[i], &hidEvent );
495             axes[i] = hidEvent.value;
496         }
497     }
498 }
499
500
501 void fgPlatformJoystickOpen( SFG_Joystick* joy )
502 {
503     IOReturn rv;
504     SInt32 score;
505     IOCFPlugInInterface **plugin;
506
507     HRESULT pluginResult;
508
509     CFDictionaryRef props;
510     CFTypeRef topLevelElement;
511
512     if( joy->id >= numDevices )
513     {
514         fgWarning( "device index out of range in fgJoystickOpen()" );
515         return;
516     }
517
518     /* create device interface */
519     rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
520                                             kIOHIDDeviceUserClientTypeID,
521                                             kIOCFPlugInInterfaceID,
522                                             &plugin, &score );
523
524     if( rv != kIOReturnSuccess )
525     {
526         fgWarning( "error creating plugin for io device" );
527         return;
528     }
529
530     pluginResult = ( *plugin )->QueryInterface(
531         plugin,
532         CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
533         &( LPVOID )joy->pJoystick.hidDev
534     );
535
536     if( pluginResult != S_OK )
537         fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
538
539     ( *plugin )->Release( plugin ); /* don't leak a ref */
540     if( joy->pJoystick.hidDev == NULL )
541         return;
542
543     /* store the interface in this instance */
544     rv = ( *( joy->pJoystick.hidDev ) )->open( joy->pJoystick.hidDev, 0 );
545     if( rv != kIOReturnSuccess )
546     {
547         fgWarning( "error opening device interface");
548         return;
549     }
550
551     props = getCFProperties( ioDevices[ joy->id ] );
552
553     /* recursively enumerate all the bits */
554     CFTypeRef topLevelElement =
555         CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
556     enumerateElements( topLevelElement );
557
558     CFRelease( props );
559 }
560
561
562 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
563 {
564     fgJoystick[ ident ]->id = ident;
565     fgJoystick[ ident ]->error = GL_FALSE;
566     fgJoystick[ ident ]->num_axes = 0;
567     fgJoystick[ ident ]->num_buttons = 0;
568
569     if( numDevices < 0 )
570     {
571         /* do first-time init (since we can't over-ride jsInit, hmm */
572         numDevices = 0;
573
574         mach_port_t masterPort;
575         IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
576         if( rv != kIOReturnSuccess )
577         {
578             fgWarning( "error getting master Mach port" );
579             return;
580         }
581         fghJoystickFindDevices( masterPort );
582     }
583
584     if ( ident >= numDevices )
585     {
586         fgJoystick[ ident ]->error = GL_TRUE;
587         return;
588     }
589
590     /* get the name now too */
591     CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
592     CFTypeRef ref = CFDictionaryGetValue( properties,
593                                           CFSTR( kIOHIDProductKey ) );
594     if (!ref)
595         ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
596
597     if( !ref ||
598         !CFStringGetCString( ( CFStringRef )ref, name, 128,
599                              CFStringGetSystemEncoding( ) ) )
600     {
601         fgWarning( "error getting device name" );
602         name[ 0 ] = '\0';
603     }
604 }
605
606
607 void fgPlatformJoystickClose ( int ident )
608 {
609     ( *( fgJoystick[ ident ]->pJoystick.hidDev ) )->
610         close( fgJoystick[ ident ]->pJoystick.hidDev );
611 }
612 #endif
613
614
615
616
617 static void fghJoystickOpen( SFG_Joystick* joy )
618 {
619     /*
620      * Default values (for no joystick -- each conditional will reset the
621      * error flag)
622      */
623     joy->error = TRUE;
624     joy->num_axes = joy->num_buttons = 0;
625     joy->name[ 0 ] = '\0';
626
627     fgPlatformJoystickOpen ( joy );
628
629 }
630
631 /*
632  * This function replaces the constructor method in the JS library.
633  */
634 static void fghJoystickInit( int ident )
635 {
636     if( ident >= MAX_NUM_JOYSTICKS )
637       fgError( "Too large a joystick number: %d", ident );
638
639     if( fgJoystick[ ident ] )
640         fgError( "illegal attempt to initialize joystick device again" );
641
642     fgJoystick[ ident ] =
643         ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
644
645     /* Set defaults */
646     fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
647     fgJoystick[ ident ]->error = GL_TRUE;
648
649     fgPlatformJoystickInit( fgJoystick, ident );
650
651     fghJoystickOpen( fgJoystick[ ident  ] );
652 }
653
654 /*
655  * Try initializing all the joysticks (well, both of them)
656  */
657 void fgInitialiseJoysticks ( void )
658 {
659     if( !fgState.JoysticksInitialised )
660     {
661         int ident ;
662         for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
663             fghJoystickInit( ident );
664
665         fgState.JoysticksInitialised = GL_TRUE;
666     }
667 }
668
669
670 void fgJoystickClose( void )
671 {
672     int ident ;
673     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
674     {
675         if( fgJoystick[ ident ] )
676         {
677             fgPlatformJoystickClose ( ident );
678
679             free( fgJoystick[ ident ] );
680             fgJoystick[ ident ] = NULL;
681             /* show joystick has been deinitialized */
682         }
683     }
684 }
685
686 /*
687  * Polls the joystick and executes the joystick callback hooked to the
688  * window specified in the function's parameter:
689  */
690 void fgJoystickPollWindow( SFG_Window* window )
691 {
692     float axes[ _JS_MAX_AXES ];
693     int buttons;
694     int ident;
695
696     freeglut_return_if_fail( window );
697     freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
698
699     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
700     {
701         if( fgJoystick[ident] )
702         {
703             fghJoystickRead( fgJoystick[ident], &buttons, axes );
704
705             if( !fgJoystick[ident]->error )
706                 INVOKE_WCB( *window, Joystick,
707                             ( buttons,
708                               (int) ( axes[ 0 ] * 1000.0f ),
709                               (int) ( axes[ 1 ] * 1000.0f ),
710                               (int) ( axes[ 2 ] * 1000.0f ) )
711                 );
712         }
713     }
714 }
715
716 /*
717  * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
718  */
719 int fgJoystickDetect( void )
720 {
721     int ident;
722
723     fgInitialiseJoysticks ();
724
725     if ( !fgState.JoysticksInitialised )
726         return 0;
727
728     for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
729         if( fgJoystick[ident] && !fgJoystick[ident]->error )
730             return 1;
731
732     return 0;
733 }
734
735 /*
736  * Joystick information, setup and execution functions
737  */
738
739 /*
740  * Forces the joystick callback to be executed
741  */
742 void FGAPIENTRY glutForceJoystickFunc( void )
743 {
744     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutForceJoystickFunc" );
745 #if !defined(_WIN32_WCE)
746     freeglut_return_if_fail( fgStructure.CurrentWindow != NULL );
747     freeglut_return_if_fail( FETCH_WCB( *( fgStructure.CurrentWindow ), Joystick ) );
748     fgJoystickPollWindow( fgStructure.CurrentWindow );
749 #endif /* !defined(_WIN32_WCE) */
750 }
751 int  glutJoystickGetNumAxes( int ident )
752 {
753     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
754     return fgJoystick[ ident ]->num_axes;
755 }
756 int  glutJoystickGetNumButtons( int ident )
757 {
758     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
759     return fgJoystick[ ident ]->num_buttons;
760 }
761 int  glutJoystickNotWorking( int ident )
762 {
763     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
764     return fgJoystick[ ident ]->error;
765 }
766
767 float glutJoystickGetDeadBand( int ident, int axis )
768 {
769     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
770     return fgJoystick[ ident ]->dead_band [ axis ];
771 }
772 void  glutJoystickSetDeadBand( int ident, int axis, float db )
773 {
774     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
775     fgJoystick[ ident ]->dead_band[ axis ] = db;
776 }
777
778 float glutJoystickGetSaturation( int ident, int axis )
779 {
780     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
781     return fgJoystick[ ident ]->saturate[ axis ];
782 }
783 void  glutJoystickSetSaturation( int ident, int axis, float st )
784 {
785     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
786     fgJoystick[ ident ]->saturate [ axis ] = st;
787 }
788
789 void glutJoystickSetMinRange( int ident, float *axes )
790 {
791     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
792     memcpy( fgJoystick[ ident ]->min, axes,
793             fgJoystick[ ident ]->num_axes * sizeof( float ) );
794 }
795 void glutJoystickSetMaxRange( int ident, float *axes )
796 {
797     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
798     memcpy( fgJoystick[ ident ]->max, axes,
799             fgJoystick[ ident ]->num_axes * sizeof( float ) );
800 }
801 void glutJoystickSetCenter( int ident, float *axes )
802 {
803     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
804     memcpy( fgJoystick[ ident ]->center, axes,
805             fgJoystick[ ident ]->num_axes * sizeof( float ) );
806 }
807
808 void glutJoystickGetMinRange( int ident, float *axes )
809 {
810     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
811     memcpy( axes, fgJoystick[ ident ]->min,
812             fgJoystick[ ident ]->num_axes * sizeof( float ) );
813 }
814 void glutJoystickGetMaxRange( int ident, float *axes )
815 {
816     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
817     memcpy( axes, fgJoystick[ ident ]->max,
818             fgJoystick[ ident ]->num_axes * sizeof( float ) );
819 }
820 void glutJoystickGetCenter( int ident, float *axes )
821 {
822     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
823     memcpy( axes, fgJoystick[ ident ]->center,
824             fgJoystick[ ident ]->num_axes * sizeof( float ) );
825 }
826
827 /*** END OF FILE ***/