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