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