John added a #pragma to the joystick code for WIN32 users.
[freeglut] / src / freeglut_joystick.c
1 /*
2  * freeglut_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 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
35 #    include <sys/param.h>
36 #endif
37
38 #ifdef HAVE_CONFIG_H
39 #    include "config.h"
40 #endif
41
42 #include "../include/GL/freeglut.h"
43 #include "freeglut_internal.h"
44
45 /*
46  * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
47  * interspersed
48  */
49 #define _JS_MAX_BUTTONS 32
50
51
52 #if TARGET_HOST_MACINTOSH
53 #    define _JS_MAX_AXES  9
54 #    include <InputSprocket.h>
55 #endif
56
57 #if TARGET_HOST_MAC_OSX
58 #    define _JS_MAX_AXES 16
59 #    include <mach/mach.h>
60 #    include <IOKit/IOkitLib.h>
61 #    include <IOKit/hid/IOHIDLib.h>
62 #endif
63
64 #if TARGET_HOST_WIN32
65 #    define _JS_MAX_AXES  8
66 #    include <windows.h>
67 #    include <mmsystem.h>
68 #    include <string.h>
69 #    include <regstr.h>
70
71 #endif
72
73 #if TARGET_HOST_UNIX_X11
74 #    define _JS_MAX_AXES 16
75 #    if defined(__FreeBSD__) || defined(__NetBSD__)
76 /* XXX The next line is an unjustified hack which needs to be changed by someone who
77  * XXX is familiar with *nix, BSD, and USB joysticks.
78  */
79 #        define HAVE_USB_JS    1
80
81 #        include <sys/ioctl.h>
82 #        include <machine/joystick.h>         /* For analog joysticks */
83
84 #        if __FreeBSD_version >= 500000
85 #            include <sys/joystick.h>
86 #        else
87 #            include <machine/joystick.h>
88 #        endif
89 #        define JS_DATA_TYPE joystick
90 #        define JS_RETURN (sizeof(struct JS_DATA_TYPE))
91 #    endif
92
93 #    include <unistd.h>
94 #    include <fcntl.h>
95 #    include <errno.h>
96
97 #    if defined(__linux__)
98 #        include <sys/ioctl.h>
99 #        include <linux/joystick.h>
100
101 /* check the joystick driver version */
102 #        if defined(JS_VERSION) && JS_VERSION >= 0x010000
103 #            define JS_NEW
104 #        endif
105 #    else  /* Not BSD or Linux */
106 #        ifndef JS_RETURN
107
108   /*
109    * We'll put these values in and that should
110    * allow the code to at least compile when there is
111    * no support. The JS open routine should error out
112    * and shut off all the code downstream anyway and if
113    * the application doesn't use a joystick we'll be fine.
114    */
115
116   struct JS_DATA_TYPE
117   {
118     int buttons;
119     int x;
120     int y;
121   };
122
123 #            define JS_RETURN (sizeof(struct JS_DATA_TYPE))
124 #        endif
125 #    endif
126 #endif
127
128 #define JS_TRUE  1
129 #define JS_FALSE 0
130
131 /*
132  * BSD defines from "jsBSD.cxx" around lines 42-270
133  */
134
135 #if defined(__NetBSD__) || defined(__FreeBSD__)
136
137 #    ifdef HAVE_USB_JS
138 #        if defined(__NetBSD__)
139 /*
140  * XXX Apparently another ugly hack which someone who knows BSD and USBHID needs to solve
141  */
142 #            define HAVE_USBHID_H 1
143 #            ifdef HAVE_USBHID_H
144 #                include <usbhid.h>
145 #            else
146 #                include <usb.h>
147 #            endif
148 #        elif defined(__FreeBSD__)
149 #            include <libusbhid.h>
150 #        endif
151 #        include <dev/usb/usb.h>
152 #        include <dev/usb/usbhid.h>
153
154 /* Compatibility with older usb.h revisions */
155 #        if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
156 #            define USB_MAX_DEVNAMES MAXDEVNAMES
157 #        endif
158 #    endif
159
160 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
161 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
162 struct os_specific_s {
163   char             fname [128 ];
164   int              fd;
165   int              is_analog;
166   /* The following structure members are specific to analog joysticks */
167   struct joystick  ajs;
168 #    ifdef HAVE_USB_JS
169   /* The following structure members are specific to USB joysticks */
170   struct hid_item *hids;
171   int              hid_dlen;
172   int              hid_offset;
173   char            *hid_data_buf;
174   int              axes_usage [ _JS_MAX_AXES ];
175 #    endif
176   /* We keep button and axes state ourselves, as they might not be updated
177    * on every read of a USB device
178    */
179   int              cache_buttons;
180   float            cache_axes [ _JS_MAX_AXES ];
181 };
182
183 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
184 #    define USB_IDENT_OFFSET    2
185
186 #    define USBDEV "/dev/usb"
187 #    define UHIDDEV "/dev/uhid"
188 #    define AJSDEV "/dev/joy"
189
190 #    ifdef HAVE_USB_JS
191 /*
192  * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
193  * the full name of a USB device. If /dev/usbN isn't readable, we punt and
194  * return the uhidN device name. We warn the user of this situation once.
195  */
196 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
197 {
198   struct usb_device_info di;
199   int i, a;
200   char *cp;
201
202   for (a = 1; a < USB_MAX_DEVICES; a++) {
203     di.udi_addr = a;
204     if (ioctl(f, USB_DEVICEINFO, &di) != 0)
205       return NULL;
206     for (i = 0; i < USB_MAX_DEVNAMES; i++)
207       if (di.udi_devnames[i][0] &&
208           strcmp(di.udi_devnames[i], dev) == 0) {
209         cp =  calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
210         strcpy(cp, di.udi_vendor);
211         strcat(cp, " ");
212         strcat(cp, di.udi_product);
213         strncpy(out, cp, outlen - 1);
214         out[outlen - 1] = 0;
215         free( cp );
216         return out;
217       }
218   }
219   return NULL;
220 }
221
222 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
223 {
224   int i, f;
225   char buf[50];
226   char *cp;
227   static int protection_warned = 0;
228
229   for (i = 0; i < 16; i++) {
230     sprintf(buf, "%s%d", USBDEV, i);
231     f = open(buf, O_RDONLY);
232     if (f >= 0) {
233       cp = fghJoystickWalkUSBdev(f, name, out, outlen);
234       close(f);
235       if (cp)
236         return 1;
237     } else if (errno == EACCES) {
238       if (!protection_warned) {
239         fprintf(stderr, "Can't open %s for read!\n",
240           buf);
241         protection_warned = 1;
242       }
243     }
244   }
245   return 0;
246 }
247
248 static int fghJoystickInitializeHID(struct os_specific_s *os,
249        int *num_axes, int *num_buttons)
250 {
251     int size, is_joystick;
252 #   ifdef HAVE_USBHID_H
253         int report_id = 0;
254 #   endif
255     struct hid_data *d;
256     struct hid_item h;
257     report_desc_t rd;
258
259     if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
260     {
261         fprintf( stderr, "error: %s: %s", os->fname, strerror( errno ) );
262         return FALSE;
263     }
264
265     os->hids = NULL;
266
267 #   ifdef HAVE_USBHID_H
268         if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
269         {
270             /*** XXX {report_id} may not be the right variable? ***/
271             fprintf( stderr, "error: %s%d: %s",
272                      UHIDDEV, report_id, strerror( errno ) );
273             return FALSE;
274         }
275
276         size = hid_report_size( rd, hid_input, report_id );
277 #   else
278         size = hid_report_size( rd, 0, hid_input );
279 #   endif
280     os->hid_data_buf = calloc( 1, size );
281     os->hid_dlen = size;
282
283     is_joystick = 0;
284 #   ifdef HAVE_USBHID_H
285         d = hid_start_parse( rd, 1 << hid_input, report_id );
286 #   else
287         d = hid_start_parse( rd, 1 << hid_input );
288 #   endif
289         while( hid_get_item( d, &h ) )
290         {
291             int usage, page, interesting_hid;
292
293             page = HID_PAGE( h.usage );
294             usage = HID_USAGE( h.usage );
295
296             /* This test is somewhat too simplistic, but this is how MicroSoft
297              * does, so I guess it works for all joysticks/game pads. */
298             is_joystick = is_joystick ||
299                 ( h.kind == hid_collection &&
300                   page == HUP_GENERIC_DESKTOP &&
301                   ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
302
303             if( h.kind != hid_input )
304                 continue;
305
306             if( !is_joystick )
307                 continue;
308
309             interesting_hid = TRUE;
310             if( page == HUP_GENERIC_DESKTOP )
311             {
312                 switch( usage )
313                 {
314                 case HUG_X:
315                 case HUG_RX:
316                 case HUG_Y:
317                 case HUG_RY:
318                 case HUG_Z:
319                 case HUG_RZ:
320                 case HUG_SLIDER:
321                     if( *num_axes < _JS_MAX_AXES )
322                     {
323                         os->axes_usage[ *num_axes ] = usage;
324                         ( *num_axes )++;
325                     }
326                     break;
327                 case HUG_HAT_SWITCH:
328                     /* Allocate two axes for a hat */
329                     if( *num_axes + 1 < _JS_MAX_AXES )
330                     {
331                         os->axes_usage[ *num_axes ] = usage;
332                         (*num_axes)++;
333                         os->axes_usage[ *num_axes ] = usage;
334                         (*num_axes)++;
335                     }
336                     break;
337                 default:
338                     interesting_hid = FALSE;
339                     break;
340                 }
341             }
342             else if( page == HUP_BUTTON )
343             {
344                 interesting_hid = ( usage > 0 ) &&
345                     ( usage <= _JS_MAX_BUTTONS );
346
347                 if( interesting_hid && usage - 1 > *num_buttons )
348                     *num_buttons = usage - 1;
349             }
350
351             if( interesting_hid )
352             {
353                 h.next = os->hids;
354                 os->hids = calloc( 1, sizeof ( struct hid_item ) );
355                 *os->hids = h;
356             }
357         }
358         hid_end_parse( d );
359
360         return os->hids != NULL;
361 }
362 #    endif
363 #endif
364
365 /*
366  * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
367  * See "js.h" lines 80-178.
368  */
369 typedef struct tagSFG_Joystick SFG_Joystick;
370 struct tagSFG_Joystick
371 {
372 #if TARGET_HOST_MACINTOSH
373 #define  ISP_NUM_AXIS    9
374 #define  ISP_NUM_NEEDS  41
375     ISpElementReference isp_elem  [ ISP_NUM_NEEDS ];
376     ISpNeed             isp_needs [ ISP_NUM_NEEDS ];
377 #endif
378
379 #if TARGET_HOST_MAC_OSX
380     IOHIDDeviceInterface ** hidDev;
381     IOHIDElementCookie buttonCookies[41];
382     IOHIDElementCookie axisCookies[_JS_MAX_AXES];
383     long minReport[_JS_MAX_AXES],
384          maxReport[_JS_MAX_AXES];
385 #endif
386
387 #if TARGET_HOST_WIN32
388     JOYCAPS     jsCaps;
389     JOYINFOEX   js;
390     UINT        js_id;
391 #endif
392
393
394 #if TARGET_HOST_UNIX_X11
395 #   if defined(__FreeBSD__) || defined(__NetBSD__)
396        struct os_specific_s *os;
397 #   endif
398
399 #   ifdef JS_NEW
400        struct js_event     js         ;
401        int          tmp_buttons;
402        float        tmp_axes [ _JS_MAX_AXES ];
403 #   else
404        struct JS_DATA_TYPE js;
405 #   endif
406
407     char         fname [ 128 ];
408     int          fd;
409 #endif
410
411     int          id;
412     GLboolean    error;
413     char         name [ 128 ];
414     int          num_axes;
415     int          num_buttons;
416
417     float dead_band[ _JS_MAX_AXES ];
418     float saturate [ _JS_MAX_AXES ];
419     float center   [ _JS_MAX_AXES ];
420     float max      [ _JS_MAX_AXES ];
421     float min      [ _JS_MAX_AXES ];
422 };
423
424 /*
425  * Functions associated with the "jsJoystick" class in PLIB
426  */
427 #if TARGET_HOST_MAC_OSX
428 #define K_NUM_DEVICES   32
429 int numDevices;
430 io_object_t ioDevices[K_NUM_DEVICES];
431
432 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
433 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
434
435 void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
436 /* callback for CFArrayApply */
437 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
438 void fghJoystickParseElement ( SFG_Joystick* joy, CFDictionaryRef element );
439
440 void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
441 void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
442 void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
443 #endif
444
445
446 /*
447  * The static joystick structure pointer
448  */
449 #define MAX_NUM_JOYSTICKS  2
450 static int fgNumberOfJoysticks = 0;
451 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
452
453
454 /*
455  * Read the raw joystick data
456  */
457 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
458 {
459 #if TARGET_HOST_WIN32
460     MMRESULT status;
461 #else
462     int status;
463 #endif
464
465 #if defined(__FreeBSD__) || defined(__NetBSD__)
466     int len;
467 #endif
468
469     int i;
470
471     /* Defaults */
472     if( buttons )
473         *buttons = 0;
474
475     if( axes )
476         for( i = 0; i < joy->num_axes; i++ )
477             axes[ i ] = 1500.0f;
478
479     if( joy->error )
480         return;
481
482 #if TARGET_HOST_MACINTOSH
483     if ( buttons )
484     {
485         *buttons = 0;
486
487         for ( i = 0; i < joy->num_buttons; i++ )
488         {
489             UInt32 state;
490             int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
491             ISP_CHECK_ERR(err)
492
493             *buttons |= state << i;
494         }
495     }
496
497     if ( axes )
498     {
499         for ( i = 0; i < joy->num_axes; i++ )
500         {
501             UInt32 state;
502             int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
503             ISP_CHECK_ERR(err)
504
505             axes [i] = (float) state;
506         }
507     }
508 #endif
509
510 #if TARGET_HOST_MAC_OSX
511     if ( buttons != NULL )
512     {
513         *buttons = 0;
514
515         for ( i = 0; i < joy->num_buttons; i++ )
516         {
517             IOHIDEventStruct hidEvent;
518             (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
519             if ( hidEvent.value )
520                 *buttons |= 1 << i; 
521         }
522     }
523
524     if ( axes != NULL )
525     {
526         for ( i = 0; i < joy->num_axes; i++ )
527         {
528             IOHIDEventStruct hidEvent;
529             (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
530             axes[i] = hidEvent.value;
531         }
532     }
533 #endif
534
535 #if TARGET_HOST_WIN32
536     status = joyGetPosEx( joy->js_id, &joy->js );
537
538     if ( status != JOYERR_NOERROR )
539     {
540         joy->error = GL_TRUE;
541         return;
542     }
543
544     if ( buttons )
545         *buttons = joy->js.dwButtons;
546
547     if ( axes )
548     {
549         /*
550          * WARNING - Fall through case clauses!!
551          */
552         switch ( joy->num_axes )
553         {
554         case 8:
555             /* Generate two POV axes from the POV hat angle.
556              * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
557              *   hundredths of a degree, or 0xFFFF when idle.
558              */
559             if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
560             {
561               axes [ 6 ] = 0.0;
562               axes [ 7 ] = 0.0;
563             }
564             else
565             {
566               /* This is the contentious bit: how to convert angle to X/Y.
567                *    wk: I know of no define for PI that we could use here:
568                *    SG_PI would pull in sg, M_PI is undefined for MSVC
569                * But the accuracy of the value of PI is very unimportant at
570                * this point.
571                */
572               float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
573               float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
574
575               /* Convert to coordinates on a square so that North-East
576                * is (1,1) not (.7,.7), etc.
577                * s and c cannot both be zero so we won't divide by zero.
578                */
579               if ( fabs ( s ) < fabs ( c ) )
580               {
581                 axes [ 6 ] = ( c < 0.0 ) ? -s/c  : s/c ;
582                 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
583               }
584               else
585               {
586                 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
587                 axes [ 7 ] = ( s < 0.0 ) ? -c/s  : c/s ;
588               }
589             }
590
591         case 6: axes[5] = (float) joy->js.dwVpos;
592         case 5: axes[4] = (float) joy->js.dwUpos;
593         case 4: axes[3] = (float) joy->js.dwRpos;
594         case 3: axes[2] = (float) joy->js.dwZpos;
595         case 2: axes[1] = (float) joy->js.dwYpos;
596         case 1: axes[0] = (float) joy->js.dwXpos;
597         }
598     }
599 #endif
600
601 #if TARGET_HOST_UNIX_X11
602 #    if defined(__FreeBSD__) || defined(__NetBSD__)
603     if ( joy->os->is_analog )
604     {
605         int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
606         if ( status != sizeof(joy->os->ajs) ) {
607             perror ( joy->os->fname );
608             joy->error = GL_TRUE;
609             return;
610         }
611         if ( buttons != NULL )
612             *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
613
614         if ( axes != NULL )
615         {
616             axes[0] = (float) joy->os->ajs.x;
617             axes[1] = (float) joy->os->ajs.y;
618         }
619
620         return;
621     }
622
623 #        ifdef HAVE_USB_JS
624     while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
625     {
626         struct hid_item *h;
627
628         for  ( h = joy->os->hids; h; h = h->next )
629         {
630             int d = hid_get_data ( joy->os->hid_data_buf, h );
631
632             int page = HID_PAGE ( h->usage );
633             int usage = HID_USAGE ( h->usage );
634
635             if ( page == HUP_GENERIC_DESKTOP )
636             {
637                 int i;
638                 for ( i = 0; i < joy->num_axes; i++ )
639                     if (joy->os->axes_usage[i] == usage)
640                     {
641                         if (usage == HUG_HAT_SWITCH)
642                         {
643                             if (d < 0 || d > 8)
644                                 d = 0;  /* safety */
645                             joy->os->cache_axes[i] = (float)hatmap_x[d];
646                             joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
647                         }
648                         else
649                         {
650                             joy->os->cache_axes[i] = (float)d;
651                         }
652                         break;
653                     }
654             }
655             else if (page == HUP_BUTTON)
656             {
657                if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
658                {
659                    if (d)
660                        joy->os->cache_buttons |= (1 << usage - 1);
661                    else
662                        joy->os->cache_buttons &= ~(1 << usage - 1);
663                }
664             }
665         }
666     }
667     if ( len < 0 && errno != EAGAIN )
668     {
669         perror( joy->os->fname );
670         joy->error = 1;
671     }
672     if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
673     if ( axes    != NULL )
674         memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
675 #        endif
676 #    endif
677
678 #    ifdef JS_NEW
679
680     while ( 1 )
681     {
682         status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
683
684         if ( status != sizeof( struct js_event ) )
685         {
686             if ( errno == EAGAIN )
687             {
688                 /* Use the old values */
689                 if ( buttons )
690                     *buttons = joy->tmp_buttons;
691                 if ( axes )
692                     memcpy( axes, joy->tmp_axes,
693                             sizeof( float ) * joy->num_axes );
694                 return;
695             }
696
697             fgWarning ( "%s", joy->fname );
698             joy->error = GL_TRUE;
699             return;
700         }
701
702         switch ( joy->js.type & ~JS_EVENT_INIT )
703         {
704         case JS_EVENT_BUTTON:
705             if( joy->js.value == 0 ) /* clear the flag */
706                 joy->tmp_buttons &= ~( 1 << joy->js.number );
707             else
708                 joy->tmp_buttons |= ( 1 << joy->js.number );
709             break;
710
711         case JS_EVENT_AXIS:
712             if ( joy->js.number < joy->num_axes )
713             {
714                 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
715
716                 if( axes )
717                     memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
718             }
719             break;
720
721         default:
722             fgWarning ( "%s", "PLIB_JS: Unrecognised /dev/js return!?!" );
723
724             /* use the old values */
725
726             if ( buttons != NULL ) *buttons = joy->tmp_buttons;
727             if ( axes    != NULL )
728                 memcpy ( axes, joy->tmp_axes, sizeof(float) * num_axes );
729
730             return;
731         }
732
733         if( buttons )
734             *buttons = joy->tmp_buttons;
735     }
736 #    else
737
738     status = read( joy->fd, &joy->js, JS_RETURN );
739
740     if ( status != JS_RETURN )
741     {
742         fgWarning( "%s", joy->fname );
743         joy->error = GL_TRUE;
744         return;
745     }
746
747     if ( buttons )
748 #        if defined( __FreeBSD__ ) || defined( __NetBSD__ )
749         *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 );  /* XXX Should not be here -- BSD is handled earlier */
750 #        else
751         *buttons = joy->js.buttons;
752 #        endif
753
754     if ( axes )
755     {
756         axes[ 0 ] = (float) joy->js.x;
757         axes[ 1 ] = (float) joy->js.y;
758     }
759 #    endif
760 #endif
761 }
762
763 /*
764  * Correct the joystick axis data
765  */
766 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
767 {
768     if( value < joy->center[ axis ] )
769     {
770         float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
771                                                        joy->min[ axis ] );
772
773         if( xx < -joy->saturate[ axis ] )
774             return -1.0f;
775
776         if( xx > -joy->dead_band [ axis ] )
777             return 0.0f;
778
779         xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
780                                                  joy->dead_band[ axis ] );
781
782         return ( xx < -1.0f ) ? -1.0f : xx;
783     }
784     else
785     {
786         float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
787                                                         joy->center[ axis ] );
788
789         if( xx > joy->saturate[ axis ] )
790             return 1.0f;
791
792         if( xx < joy->dead_band[ axis ] )
793             return 0.0f;
794
795         xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
796                                                  joy->dead_band[ axis ] );
797
798         return ( xx > 1.0f ) ? 1.0f : xx;
799     }
800 }
801
802 /*
803  * Read the corrected joystick data
804  */
805 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
806 {
807     float raw_axes[ _JS_MAX_AXES ];
808     int  i;
809
810     if( joy->error )
811     {
812         if( buttons )
813             *buttons = 0;
814
815         if( axes )
816             for ( i=0; i<joy->num_axes; i++ )
817                 axes[ i ] = 0.0f;
818     }
819
820     fghJoystickRawRead( joy, buttons, raw_axes );
821
822     if( axes )
823         for( i=0; i<joy->num_axes; i++ )
824             axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
825 }
826
827 /*
828  * Happy happy happy joy joy joy (happy new year toudi :D)
829  */
830
831
832 #if TARGET_HOST_MAC_OSX
833 /** open the IOKit connection, enumerate all the HID devices, add their
834 interface references to the static array. We then use the array index
835 as the device number when we come to open() the joystick. */
836 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
837 {
838     CFMutableDictionaryRef hidMatch = NULL;
839     IOReturn rv = kIOReturnSuccess;
840
841     io_iterator_t hidIterator;
842     io_object_t ioDev;
843
844     /* build a dictionary matching HID devices */
845     hidMatch = IOServiceMatching(kIOHIDDeviceKey);
846
847     rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
848     if (rv != kIOReturnSuccess || !hidIterator) {
849       fgWarning ( "%s", "no joystick (HID) devices found");
850       return;
851     }
852
853     /* iterate */
854     while ((ioDev = IOIteratorNext(hidIterator))) {
855         /* filter out keyboard and mouse devices */
856         CFDictionaryRef properties = getCFProperties(ioDev);
857         long usage, page;
858
859         CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
860         CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
861         CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
862         CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
863
864         /* keep only joystick devices */
865         if ( ( page == kHIDPage_GenericDesktop ) && (
866                             (usage == kHIDUsage_GD_Joystick)
867                          || (usage == kHIDUsage_GD_GamePad)
868                          || (usage == kHIDUsage_GD_MultiAxisController)
869                          || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
870             /* add it to the array */
871             ioDevices[numDevices++] = ioDev;
872     }
873
874     IOObjectRelease(hidIterator);
875 }
876
877 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
878 {
879     IOReturn rv;
880     CFMutableDictionaryRef cfProperties;
881
882 #if 0  
883     /* comment copied from darwin/SDL_sysjoystick.c */
884     /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
885      * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
886      */
887
888     io_registry_entry_t parent1, parent2;
889
890     rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
891     if (rv != kIOReturnSuccess) {
892         fgWarning ( "%s", "error getting device entry parent");
893         return NULL;
894     }
895
896     rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
897     if (rv != kIOReturnSuccess) {
898         fgWarning ( "%s", "error getting device entry parent 2");
899         return NULL;
900     }
901 #endif
902
903     rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/, 
904         &cfProperties, kCFAllocatorDefault, kNilOptions);
905     if (rv != kIOReturnSuccess || !cfProperties) {
906         fgWarning ( "%s", "error getting device properties");
907         return NULL;
908     }
909
910     return cfProperties;
911 }
912
913 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
914 {
915       if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
916             fgError ( "%s", "element enumerator passed non-dictionary value");
917             return;
918     }
919
920       static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );        
921 }
922
923 /** element enumerator function : pass NULL for top-level*/
924 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
925 {
926       assert(CFGetTypeID(element) == CFArrayGetTypeID());
927
928       CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
929       CFArrayApplyFunction((CFArrayRef) element, range, 
930             &fghJoystickElementEnumerator, joy );
931 }
932
933 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
934 {
935     CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
936     CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
937
938     long type, page, usage;
939
940     CFNumberGetValue((CFNumberRef)
941         CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)), 
942         kCFNumberLongType, &type);
943
944     switch ( typ e) {
945     case kIOHIDElementTypeInput_Misc:
946     case kIOHIDElementTypeInput_Axis:
947     case kIOHIDElementTypeInput_Button:
948         printf("got input element...");
949         CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
950         CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
951
952         if (page == kHIDPage_GenericDesktop) {    
953             switch ( usage ) /* look at usage to determine function */
954             {
955             case kHIDUsage_GD_X:
956             case kHIDUsage_GD_Y:
957             case kHIDUsage_GD_Z:
958             case kHIDUsage_GD_Rx:
959             case kHIDUsage_GD_Ry:
960             case kHIDUsage_GD_Rz:
961             case kHIDUsage_GD_Slider: /* for throttle / trim controls */
962                 printf(" axis\n");
963                 fghJoystickAddAxisElement((CFDictionaryRef) element);
964                 break;
965
966             case kHIDUsage_GD_Hatswitch:
967                 printf(" hat\n");
968                 fghJoystickAddHatElement((CFDictionaryRef) element);
969                 break;
970
971             default:
972                 printf("input type element has weird usage (%x)\n", usage);
973                 break;
974             }
975         } else if (page == kHIDPage_Button) {
976             printf(" button\n");
977             fghJoystickAddButtonElement((CFDictionaryRef) element);
978         } else
979             printf("input type element has weird page (%x)\n", page);
980         break;
981
982     case kIOHIDElementTypeCollection:
983         fghJoystickEnumerateElements (
984             CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
985         );
986         break;
987
988     default:
989         break;
990     }  
991 }
992
993 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
994 {
995     long cookie, lmin, lmax;
996     int index = joy->num_axes++;
997
998     CFNumberGetValue ((CFNumberRef)
999         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ), 
1000         kCFNumberLongType, &cookie);
1001
1002     axisCookies[index] = (IOHIDElementCookie) cookie;
1003
1004     CFNumberGetValue ((CFNumberRef)
1005         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ), 
1006         kCFNumberLongType, &lmin);
1007
1008     CFNumberGetValue ((CFNumberRef)
1009         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ), 
1010         kCFNumberLongType, &lmax);
1011
1012     joy->min[index] = lmin;
1013     joy->max[index] = lmax;
1014     joy->dead_band[index] = 0.0;
1015     joy->saturate[index] = 1.0;
1016     joy->center[index] = (lmax + lmin) * 0.5;
1017 }
1018
1019 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1020 {
1021     long cookie;
1022     CFNumberGetValue ((CFNumberRef)
1023             CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ), 
1024             kCFNumberLongType, &cookie);
1025
1026     joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1027     /* anything else for buttons? */
1028 }
1029
1030 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1031 {
1032     /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1033     /* do we map hats to axes or buttons? */
1034 }
1035 #endif
1036
1037 #if TARGET_HOST_WIN32
1038 /* Inspired by
1039    http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1040  */
1041 #    pragma comment (lib, "advapi32.lib")
1042
1043 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1044 {
1045     char buffer [ 256 ];
1046
1047     char OEMKey [ 256 ];
1048
1049     HKEY  hKey;
1050     DWORD dwcb;
1051     LONG  lr;
1052
1053     if ( joy->error )
1054         return 0;
1055
1056     /* Open .. MediaResources\CurrentJoystickSettings */
1057     sprintf ( buffer, "%s\\%s\\%s",
1058               REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1059               REGSTR_KEY_JOYCURR );
1060
1061     lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1062
1063     if ( lr != ERROR_SUCCESS ) return 0;
1064
1065     /* Get OEM Key name */
1066     dwcb = sizeof(OEMKey);
1067
1068     /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1069     sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1070
1071     lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1072     RegCloseKey ( hKey );
1073
1074     if ( lr != ERROR_SUCCESS ) return 0;
1075
1076     /* Open OEM Key from ...MediaProperties */
1077     sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1078
1079     lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1080
1081     if ( lr != ERROR_SUCCESS ) return 0;
1082
1083     /* Get OEM Name */
1084     dwcb = buf_sz;
1085
1086     lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1087                              &dwcb );
1088     RegCloseKey ( hKey );
1089
1090     if ( lr != ERROR_SUCCESS ) return 0;
1091
1092     return 1;
1093 }
1094 #endif
1095
1096
1097 static void fghJoystickOpen( SFG_Joystick* joy )
1098 {
1099     int i;
1100 #if TARGET_HOST_MACINTOSH
1101     OSStatus err;
1102 #endif
1103 #if TARGET_HOST_MAC_OSX
1104         IOReturn rv;
1105         SInt32 score;
1106         IOCFPlugInInterface **plugin;
1107
1108         HRESULT pluginResult;
1109
1110         CFDictionaryRef props;
1111     CFTypeRef topLevelElement;
1112 #endif
1113 #if TARGET_HOST_UNIX_X11
1114 #    if defined(__FreeBSD__) || defined(__NetBSD__)
1115        char *cp;
1116 #    endif
1117 #    ifdef JS_NEW
1118        unsigned char u;
1119 #    else
1120        int counter;
1121 #    endif
1122 #endif
1123
1124     /* Default values (for no joystick -- each conditional will reset the error flag) */
1125     joy->error = TRUE;
1126     joy->num_axes = joy->num_buttons = 0;
1127     joy->name [0] = '\0';
1128
1129 #if TARGET_HOST_MACINTOSH
1130     /*
1131      * XXX FIXME: get joystick name in Mac
1132      */
1133
1134     err = ISpStartup ();
1135
1136     if ( err == noErr )
1137     {
1138 #define ISP_CHECK_ERR(x) if ( x != noErr ) { joy->error = GL_TRUE; return; }
1139
1140         joy->error = GL_TRUE;
1141
1142         /* initialize the needs structure */
1143         ISpNeed temp_isp_needs[isp_num_needs] =
1144         {
1145           { "\pX-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1146           { "\pY-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1147           { "\pZ-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1148           { "\pR-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1149           { "\pAxis   4",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1150           { "\pAxis   5",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1151           { "\pAxis   6",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1152           { "\pAxis   7",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1153           { "\pAxis   8",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1154
1155           { "\pButton 0",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1156           { "\pButton 1",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1157           { "\pButton 2",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1158           { "\pButton 3",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1159           { "\pButton 4",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1160           { "\pButton 5",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1161           { "\pButton 6",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162           { "\pButton 7",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163           { "\pButton 8",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164           { "\pButton 9",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165           { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166           { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167           { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168           { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169           { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170           { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171           { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172           { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173           { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174           { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175           { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176           { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177           { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178           { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179           { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180           { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181           { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182           { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183           { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184           { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185           { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186           { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187         };
1188
1189         memcpy ( joy->isp_needs, temp_isp_needs, sizeof(temp_isp_needs) );
1190
1191
1192         /* next two calls allow keyboard and mouse to emulate other input
1193          * devices (gamepads, joysticks, etc)
1194          */
1195         /*
1196           err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1197           ISP_CHECK_ERR(err)
1198
1199
1200           err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1201           ISP_CHECK_ERR(err)
1202         */
1203
1204         err = ISpElement_NewVirtualFromNeeds ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 0 );
1205         ISP_CHECK_ERR(err)
1206
1207         err = ISpInit ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 'freeglut', nil, 0, 128, 0 );
1208         ISP_CHECK_ERR(err)
1209
1210         joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1211         joy->num_axes    = joy->isp_num_axis;
1212
1213         for ( i = 0; i < joy->num_axes; i++ )
1214         {
1215           joy->dead_band [ i ] = 0;
1216           joy->saturate  [ i ] = 1;
1217           joy->center    [ i ] = kISpAxisMiddle;
1218           joy->max       [ i ] = kISpAxisMaximum;
1219           joy->min       [ i ] = kISpAxisMinimum;
1220         }
1221
1222         joy->error = GL_FALSE;
1223     }
1224     else
1225         joy->num_buttons = joy->num_axes = 0;
1226 #endif
1227
1228 #if TARGET_HOST_MAC_OSX
1229     if (joy->id >= numDevices) {
1230         fgWarning ( "%s", "device index out of range in fgJoystickOpen()");
1231         return;
1232     }
1233
1234     /* create device interface */
1235     rv = IOCreatePlugInInterfaceForService ( ioDevices[joy->id], 
1236                                              kIOHIDDeviceUserClientTypeID,
1237                                              kIOCFPlugInInterfaceID,
1238                                              &plugin, &score);
1239
1240     if (rv != kIOReturnSuccess) {
1241         fgWarning ( "%s", "error creating plugin for io device");
1242         return;
1243     }
1244
1245     pluginResult = (*plugin)->QueryInterface ( plugin, 
1246                     CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), &(LPVOID) joy->hidDev );
1247
1248     if ( pluginResult != S_OK )
1249         fgWarning ( "%s", "QI-ing IO plugin to HID Device interface failed");
1250
1251     (*plugin)->Release(plugin); /* don't leak a ref */
1252     if (joy->hidDev == NULL) return;
1253
1254     /* store the interface in this instance */
1255     rv = (*(joy->hidDev))->open(joy->hidDev, 0);
1256     if (rv != kIOReturnSuccess) {
1257         fgWarning ( "%s", "error opening device interface");
1258         return;
1259     }
1260
1261     props = getCFProperties(ioDevices[joy->id]);
1262
1263     /* recursively enumerate all the bits */
1264     CFTypeRef topLevelElement = 
1265                       CFDictionaryGetValue ( props, CFSTR ( kIOHIDElementKey ) );
1266     enumerateElements ( topLevelElement );
1267
1268     CFRelease ( props );
1269 #endif
1270
1271 #if TARGET_HOST_WIN32
1272     joy->js.dwFlags = JOY_RETURNALL;
1273     joy->js.dwSize  = sizeof( joy->js );
1274
1275     memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1276
1277     joy->error =
1278         ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1279           JOYERR_NOERROR );
1280
1281     if ( joy->jsCaps.wNumAxes == 0 )
1282     {
1283         joy->num_axes = 0;
1284         joy->error = GL_TRUE;
1285     }
1286     else
1287     {
1288         /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1289          * at least for USB.  Try to get the real name from the registry.
1290          */
1291         if ( ! fghJoystickGetOEMProductName ( joy, joy->name, sizeof(joy->name) ) )
1292         {
1293             fgWarning ( "%s", "JS: Failed to read joystick name from registry" );
1294             strncpy ( joy->name, joy->jsCaps.szPname, sizeof(joy->name) );
1295         }
1296
1297         /* Windows joystick drivers may provide any combination of
1298          * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1299          */
1300         if ( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1301         {
1302             joy->num_axes = _JS_MAX_AXES;
1303             joy->min [ 7 ] = -1.0; joy->max [ 7 ] = 1.0;  /* POV Y */
1304             joy->min [ 6 ] = -1.0; joy->max [ 6 ] = 1.0;  /* POV X */
1305         }
1306         else
1307             joy->num_axes = 6;
1308
1309         joy->min[ 5 ] = (float) joy->jsCaps.wVmin;
1310         joy->max[ 5 ] = (float) joy->jsCaps.wVmax;
1311         joy->min[ 4 ] = (float) joy->jsCaps.wUmin;
1312         joy->max[ 4 ] = (float) joy->jsCaps.wUmax;
1313         joy->min[ 3 ] = (float) joy->jsCaps.wRmin;
1314         joy->max[ 3 ] = (float) joy->jsCaps.wRmax;
1315         joy->min[ 2 ] = (float) joy->jsCaps.wZmin;
1316         joy->max[ 2 ] = (float) joy->jsCaps.wZmax;
1317         joy->min[ 1 ] = (float) joy->jsCaps.wYmin;
1318         joy->max[ 1 ] = (float) joy->jsCaps.wYmax;
1319         joy->min[ 0 ] = (float) joy->jsCaps.wXmin;
1320         joy->max[ 0 ] = (float) joy->jsCaps.wXmax;
1321     }
1322
1323     /*
1324      * Guess all the rest judging on the axes extremals
1325      */
1326     for( i = 0; i < joy->num_axes; i++ )
1327     {
1328         joy->center   [ i ] = (joy->max[i] + joy->min[i]) * 0.5f;
1329         joy->dead_band[ i ] = 0.0f;
1330         joy->saturate [ i ] = 1.0f;
1331     }
1332 #endif
1333
1334 #if TARGET_HOST_UNIX_X11
1335 #if defined(__FreeBSD__) || defined(__NetBSD__)
1336     for ( i = 0; i < _JS_MAX_AXES; i++ )
1337         joy->os->cache_axes [ i ] = 0.0f;
1338
1339     joy->os->cache_buttons = 0;
1340
1341     joy->os->fd = open ( joy->os->fname, O_RDONLY | O_NONBLOCK);
1342
1343     if ( joy->os->fd < 0 && errno == EACCES)
1344       fgWarning ( "%s exists but is not readable by you\n", joy->os->fname );
1345
1346     joy->error = ( joy->os->fd < 0 );
1347
1348     if ( joy->error )
1349       return;
1350
1351     joy->num_axes = 0;
1352     joy->num_buttons = 0;
1353     if ( joy->os->is_analog )
1354     {
1355         FILE *joyfile;
1356         char joyfname [ 1024 ];
1357         int noargs, in_no_axes;
1358
1359         float axes  [ _JS_MAX_AXES ];
1360         int buttons [ _JS_MAX_AXES ];
1361
1362         joy->num_axes    =  2;
1363         joy->num_buttons = 32;
1364
1365         fghJoystickRawRead ( joy, buttons, axes );
1366         joy->error = axes[0] < -1000000000.0f;
1367         if ( joy->error )
1368           return;
1369
1370         sprintf( joyfname, "%s/.joy%drc", getenv ( "HOME" ), joy->id );
1371
1372         joyfile = fopen ( joyfname, "r" );
1373         joy->error = ( joyfile == NULL );
1374         if ( joy->error )
1375           return;
1376
1377         noargs = fscanf ( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1378                           &joy->min [ 0 ], &joy->center [ 0 ], &joy->max [ 0 ],
1379                           &joy->min [ 1 ], &joy->center [ 1 ], &joy->max [ 1 ] );
1380         joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1381         fclose ( joyfile );
1382         if ( joy->error )
1383           return;
1384
1385         for ( i = 0; i < _JS_MAX_AXES; i++ )
1386         {
1387             joy->dead_band [ i ] = 0.0f;
1388             joy->saturate  [ i ] = 1.0f;
1389         }
1390
1391         return;    /* End of analog code */
1392     }
1393
1394 #    ifdef HAVE_USB_JS
1395     if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1396                                     &joy->num_buttons ) )
1397     {
1398         close ( joy->os->fd );
1399         joy->error = GL_TRUE;
1400         return;
1401     }
1402
1403     cp = strrchr(joy->os->fname, '/');
1404     if ( cp ) {
1405         if ( fghJoystickFindUSBdev ( &cp[1], joy->name, sizeof(joy->name) ) == 0 )
1406             strcpy ( joy->name, &cp[1] );
1407     }
1408
1409     if ( joy->num_axes > _JS_MAX_AXES )
1410         joy->num_axes = _JS_MAX_AXES;
1411
1412     for ( i = 0; i < _JS_MAX_AXES; i++ )
1413     {
1414         /* We really should get this from the HID, but that data seems
1415          * to be quite unreliable for analog-to-USB converters. Punt for
1416          * now.
1417          */
1418         if ( joy->os->axes_usage [ i ] == HUG_HAT_SWITCH )
1419         {
1420             joy->max       [ i ] = 1.0f;
1421             joy->center    [ i ] = 0.0f;
1422             joy->min       [ i ] = -1.0f;
1423         }
1424         else
1425         {
1426             joy->max       [ i ] = 255.0f;
1427             joy->center    [ i ] = 127.0f;
1428             joy->min       [ i ] = 0.0f;
1429         }
1430
1431         joy->dead_band [ i ] = 0.0f;
1432         joy->saturate  [ i ] = 1.0f;
1433     }
1434 #    endif
1435 #endif
1436
1437 #if defined(__linux__)
1438     /*
1439      * Default for older Linux systems.
1440      */
1441     joy->num_axes    =  2;
1442     joy->num_buttons = 32;
1443
1444 #    ifdef JS_NEW
1445     for( i = 0; i < _JS_MAX_AXES; i++ )
1446         joy->tmp_axes[ i ] = 0.0f;
1447
1448     joy->tmp_buttons = 0;
1449 #    endif
1450
1451     joy->fd = open( joy->fname, O_RDONLY );
1452
1453     joy->error = (joy->fd < 0);
1454
1455     if ( joy->error )
1456         return;
1457
1458     /*
1459      * Set the correct number of axes for the linux driver
1460      */
1461 #    ifdef JS_NEW
1462     /* Melchior Franz's fixes for big-endian Linuxes since writing 
1463      *  to the upper byte of an uninitialized word doesn't work. 
1464      *  9 April 2003 
1465      */
1466     ioctl ( joy->fd, JSIOCGAXES   , &u ); 
1467     joy->num_axes = u;
1468     ioctl ( joy->fd, JSIOCGBUTTONS, &u );
1469     joy->num_buttons = u;
1470     ioctl ( joy->fd, JSIOCGNAME ( sizeof(joy->name) ), joy->name );
1471     fcntl ( joy->fd, F_SETFL      , O_NONBLOCK   );
1472 #    endif
1473
1474     /*
1475      * The Linux driver seems to return 512 for all axes
1476      * when no stick is present - but there is a chance
1477      * that could happen by accident - so it's gotta happen
1478      * on both axes for at least 100 attempts.
1479      *
1480      * PWO: shouldn't be that done somehow wiser on the kernel level?
1481      */
1482 #    ifndef JS_NEW
1483     counter = 0;
1484
1485     do
1486     { 
1487         fghJoystickRawRead( joy, NULL, joy->center );
1488         counter++;
1489     } while( !joy->error &&
1490              counter < 100 &&
1491              joy->center[ 0 ] == 512.0f &&
1492              joy->center[ 1 ] == 512.0f );
1493
1494     if ( counter >= 100 )
1495         joy->error = GL_TRUE;
1496 #    endif
1497
1498     for ( i = 0; i < _JS_MAX_AXES; i++ )
1499     {
1500 #    ifdef JS_NEW
1501         joy->max   [ i ] =  32767.0f;
1502         joy->center[ i ] =      0.0f;
1503         joy->min   [ i ] = -32767.0f;
1504 #    else
1505         joy->max[ i ] = joy->center[ i ] * 2.0f;
1506         joy->min[ i ] = 0.0f;
1507 #    endif
1508         joy->dead_band[ i ] = 0.0f;
1509         joy->saturate [ i ] = 1.0f;
1510     }
1511 #endif
1512 #endif
1513 }
1514
1515 /*
1516  * This function replaces the constructor method in the JS library.
1517  */
1518 void fgJoystickInit( int ident )
1519 {
1520     if ( ident >= MAX_NUM_JOYSTICKS )
1521       fgError( "Too large a joystick number" );
1522
1523     if( fgJoystick[ident] )
1524         fgError( "illegal attempt to initialize joystick device" );
1525
1526     fgJoystick[ident] = ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1527
1528     /* Set defaults */
1529     fgJoystick[ident]->num_axes = fgJoystick[ident]->num_buttons = 0;
1530     fgJoystick[ident]->error = GL_TRUE;
1531
1532 #if TARGET_HOST_MACINTOSH
1533     fgJoystick[ident]->id = ident;
1534     sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident ); /* FIXME */
1535     fgJoystick[ident]->error = GL_FALSE;
1536 #endif
1537
1538 #if TARGET_HOST_MAC_OSX
1539     fgJoystick[ident]->id = ident;
1540     fgJoystick[ident]->error = GL_FALSE;
1541     fgJoystick[ident]->num_axes = 0;
1542     fgJoystick[ident]->num_buttons = 0;
1543
1544     if (numDevices < 0) {
1545         /* do first-time init (since we can't over-ride jsInit, hmm */
1546         numDevices = 0;
1547
1548         mach_port_t masterPort;
1549         IOReturn rv = IOMasterPort ( bootstrap_port, &masterPort );
1550         if ( rv != kIOReturnSuccess ) {
1551             fgWarning ( "%s", "error getting master Mach port");
1552             return;
1553         }
1554         fghJoystickFindDevices ( masterPort );
1555     }
1556
1557     if ( ident >= numDevices ) {
1558         fgJoystick[ident]->error = GL_TRUE;
1559         return;
1560     }
1561
1562     /* get the name now too */
1563     CFDictionaryRef properties = getCFProperties(ioDevices[ident]);
1564     CFTypeRef ref = CFDictionaryGetValue (properties, CFSTR(kIOHIDProductKey));
1565     if (!ref)
1566       ref = CFDictionaryGetValue (properties, CFSTR("USB Product Name"));
1567
1568     if (!ref || !CFStringGetCString ((CFStringRef) ref, name, 128, CFStringGetSystemEncoding ())) {
1569       fgWarning ( "%s", "error getting device name");
1570       name[0] = '\0';
1571     }
1572 #endif
1573
1574 #if TARGET_HOST_WIN32
1575     switch( ident )
1576     {
1577     case 0:
1578         fgJoystick[ident]->js_id = JOYSTICKID1;
1579         fgJoystick[ident]->error = GL_FALSE;
1580         break;
1581     case 1:
1582         fgJoystick[ident]->js_id = JOYSTICKID2;
1583         fgJoystick[ident]->error = GL_FALSE;
1584         break;
1585     default:
1586         fgJoystick[ident]->num_axes = 0;
1587         fgJoystick[ident]->error = GL_TRUE;
1588         return;
1589     }
1590 #endif
1591
1592 #if TARGET_HOST_UNIX_X11
1593 #    if defined(__FreeBSD__) || defined(__NetBSD__)
1594     fgJoystick[ident]->id = ident;
1595     fgJoystick[ident]->error = GL_FALSE;
1596
1597     fgJoystick[ident]->os = calloc (1, sizeof (struct os_specific_s));
1598     memset ( fgJoystick[ident]->os, 0, sizeof(struct os_specific_s) );
1599     if (ident < USB_IDENT_OFFSET)
1600         fgJoystick[ident]->os->is_analog = 1;
1601     if (fgJoystick[ident]->os->is_analog)
1602         sprintf ( fgJoystick[ident]->os->fname, "%s%d", AJSDEV, ident );
1603     else
1604         sprintf ( fgJoystick[ident]->os->fname, "%s%d", UHIDDEV, ident - USB_IDENT_OFFSET );
1605 #    elif defined(__linux__)
1606     fgJoystick[ident]->id = ident;
1607     fgJoystick[ident]->error = GL_FALSE;
1608
1609     sprintf ( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1610
1611     if ( access ( fgJoystick[ident]->fname, F_OK ) != 0 )
1612         sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident );
1613 #    endif
1614 #endif
1615
1616     fghJoystickOpen ( fgJoystick[ident] );
1617 }
1618
1619 /*
1620  *
1621  */
1622 void fgJoystickClose( void )
1623 {
1624     int ident ;
1625     for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1626     {
1627         if ( fgJoystick[ident] )
1628         {
1629
1630 #if TARGET_HOST_MACINTOSH
1631             ISpSuspend  ();
1632             ISpStop     ();
1633             ISpShutdown ();
1634 #endif
1635
1636 #if TARGET_HOST_MAC_OSX
1637             (*(fgJoystick[ident]->hidDev))->close(fgJoystick[ident]->hidDev);
1638 #endif
1639
1640 #if TARGET_HOST_WIN32
1641             /* Do nothing special */
1642 #endif
1643
1644 #if TARGET_HOST_UNIX_X11
1645 #if defined(__FreeBSD__) || defined(__NetBSD__)
1646             if ( fgJoystick[ident]->os )
1647             {
1648                 if ( ! fgJoystick[ident]->error )
1649                     close ( fgJoystick[ident]->os->fd );
1650 #ifdef HAVE_USB_JS
1651                 if ( fgJoystick[ident]->os->hids )
1652                     free (fgJoystick[ident]->os->hids);
1653                 if ( fgJoystick[ident]->os->hid_data_buf )
1654                     free (fgJoystick[ident]->os->hid_data_buf);
1655 #endif
1656                 free (fgJoystick[ident]->os);
1657             }
1658 #endif
1659
1660             if( ! fgJoystick[ident]->error )
1661                 close( fgJoystick[ident]->fd );
1662 #endif
1663
1664             free( fgJoystick[ident] );
1665             fgJoystick[ident] = NULL;  /* show joystick has been deinitialized */
1666         }
1667     }
1668 }
1669
1670 /*
1671  * Polls the joystick and executes the joystick callback hooked to the
1672  * window specified in the function's parameter:
1673  */
1674 void fgJoystickPollWindow( SFG_Window* window )
1675 {
1676     float axes[ _JS_MAX_AXES ];
1677     int buttons;
1678     int ident;
1679
1680     freeglut_return_if_fail( window );
1681     freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1682
1683     for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1684     {
1685         if ( fgJoystick[ident] )
1686         {
1687             fghJoystickRead( fgJoystick[ident], &buttons, axes );
1688
1689             if ( !fgJoystick[ident]->error )
1690                 INVOKE_WCB( *window, Joystick,
1691                             ( buttons,
1692                               (int) (axes[ 0 ] * 1000.0f ),
1693                               (int) (axes[ 1 ] * 1000.0f ),
1694                               (int) (axes[ 2 ] * 1000.0f ) )
1695                           );
1696         }
1697     }
1698 }
1699
1700 /*
1701  * PWO: These jsJoystick class methods have not been implemented.
1702  *      We might consider adding such functions to freeglut-2.0.
1703  */
1704 int  getNumAxes ( int ident )
1705     { return fgJoystick[ident]->num_axes; }
1706 int  notWorking ( int ident )
1707     { return fgJoystick[ident]->error; }
1708
1709 float getDeadBand ( int ident, int axis )
1710     { return fgJoystick[ident]->dead_band [ axis ]; }
1711 void  setDeadBand ( int ident, int axis, float db )
1712     { fgJoystick[ident]->dead_band [ axis ] = db; }
1713
1714 float getSaturation ( int ident, int axis )
1715     { return fgJoystick[ident]->saturate [ axis ]; }
1716 void  setSaturation ( int ident, int axis, float st )
1717     { fgJoystick[ident]->saturate [ axis ] = st; }
1718
1719 void setMinRange ( int ident, float *axes )
1720     { memcpy ( fgJoystick[ident]->min   , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1721 void setMaxRange ( int ident, float *axes )
1722     { memcpy ( fgJoystick[ident]->max   , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1723 void setCenter   ( int ident, float *axes )
1724     { memcpy ( fgJoystick[ident]->center, axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1725
1726 void getMinRange ( int ident, float *axes )
1727     { memcpy ( axes, fgJoystick[ident]->min   , fgJoystick[ident]->num_axes * sizeof(float) ); }
1728 void getMaxRange ( int ident, float *axes )
1729     { memcpy ( axes, fgJoystick[ident]->max   , fgJoystick[ident]->num_axes * sizeof(float) ); }
1730 void getCenter   ( int ident, float *axes )
1731     { memcpy ( axes, fgJoystick[ident]->center, fgJoystick[ident]->num_axes * sizeof(float) ); }
1732
1733 /*** END OF FILE ***/