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