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