Big import of updated joystick code from PLIB, with permission from
[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
1042 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1043 {
1044     char buffer [ 256 ];
1045
1046     char OEMKey [ 256 ];
1047
1048     HKEY  hKey;
1049     DWORD dwcb;
1050     LONG  lr;
1051
1052     if ( joy->error )
1053         return 0;
1054
1055     /* Open .. MediaResources\CurrentJoystickSettings */
1056     sprintf ( buffer, "%s\\%s\\%s",
1057               REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1058               REGSTR_KEY_JOYCURR );
1059
1060     lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1061
1062     if ( lr != ERROR_SUCCESS ) return 0;
1063
1064     /* Get OEM Key name */
1065     dwcb = sizeof(OEMKey);
1066
1067     /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1068     sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1069
1070     lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1071     RegCloseKey ( hKey );
1072
1073     if ( lr != ERROR_SUCCESS ) return 0;
1074
1075     /* Open OEM Key from ...MediaProperties */
1076     sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1077
1078     lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1079
1080     if ( lr != ERROR_SUCCESS ) return 0;
1081
1082     /* Get OEM Name */
1083     dwcb = buf_sz;
1084
1085     lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1086                              &dwcb );
1087     RegCloseKey ( hKey );
1088
1089     if ( lr != ERROR_SUCCESS ) return 0;
1090
1091     return 1;
1092 }
1093 #endif
1094
1095
1096 static void fghJoystickOpen( SFG_Joystick* joy )
1097 {
1098     int i;
1099 #if TARGET_HOST_MACINTOSH
1100     OSStatus err;
1101 #endif
1102 #if TARGET_HOST_MAC_OSX
1103         IOReturn rv;
1104         SInt32 score;
1105         IOCFPlugInInterface **plugin;
1106
1107         HRESULT pluginResult;
1108
1109         CFDictionaryRef props;
1110     CFTypeRef topLevelElement;
1111 #endif
1112 #if TARGET_HOST_UNIX_X11
1113 #    if defined(__FreeBSD__) || defined(__NetBSD__)
1114        char *cp;
1115 #    endif
1116 #    ifdef JS_NEW
1117        unsigned char u;
1118 #    else
1119        int counter;
1120 #    endif
1121 #endif
1122
1123     /* Default values (for no joystick -- each conditional will reset the error flag) */
1124     joy->error = TRUE;
1125     joy->num_axes = joy->num_buttons = 0;
1126     joy->name [0] = '\0';
1127
1128 #if TARGET_HOST_MACINTOSH
1129     /*
1130      * XXX FIXME: get joystick name in Mac
1131      */
1132
1133     err = ISpStartup ();
1134
1135     if ( err == noErr )
1136     {
1137 #define ISP_CHECK_ERR(x) if ( x != noErr ) { joy->error = GL_TRUE; return; }
1138
1139         joy->error = GL_TRUE;
1140
1141         /* initialize the needs structure */
1142         ISpNeed temp_isp_needs[isp_num_needs] =
1143         {
1144           { "\pX-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1145           { "\pY-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1146           { "\pZ-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1147           { "\pR-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1148           { "\pAxis   4",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1149           { "\pAxis   5",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1150           { "\pAxis   6",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1151           { "\pAxis   7",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1152           { "\pAxis   8",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1153
1154           { "\pButton 0",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1155           { "\pButton 1",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1156           { "\pButton 2",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1157           { "\pButton 3",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1158           { "\pButton 4",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1159           { "\pButton 5",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1160           { "\pButton 6",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1161           { "\pButton 7",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162           { "\pButton 8",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163           { "\pButton 9",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164           { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165           { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166           { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167           { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168           { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169           { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170           { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171           { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172           { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173           { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174           { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175           { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176           { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177           { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178           { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179           { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180           { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181           { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182           { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183           { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184           { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185           { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186         };
1187
1188         memcpy ( joy->isp_needs, temp_isp_needs, sizeof(temp_isp_needs) );
1189
1190
1191         /* next two calls allow keyboard and mouse to emulate other input
1192          * devices (gamepads, joysticks, etc)
1193          */
1194         /*
1195           err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1196           ISP_CHECK_ERR(err)
1197
1198
1199           err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1200           ISP_CHECK_ERR(err)
1201         */
1202
1203         err = ISpElement_NewVirtualFromNeeds ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 0 );
1204         ISP_CHECK_ERR(err)
1205
1206         err = ISpInit ( joy->isp_num_needs, joy->isp_needs, joy->isp_elem, 'freeglut', nil, 0, 128, 0 );
1207         ISP_CHECK_ERR(err)
1208
1209         joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1210         joy->num_axes    = joy->isp_num_axis;
1211
1212         for ( i = 0; i < joy->num_axes; i++ )
1213         {
1214           joy->dead_band [ i ] = 0;
1215           joy->saturate  [ i ] = 1;
1216           joy->center    [ i ] = kISpAxisMiddle;
1217           joy->max       [ i ] = kISpAxisMaximum;
1218           joy->min       [ i ] = kISpAxisMinimum;
1219         }
1220
1221         joy->error = GL_FALSE;
1222     }
1223     else
1224         joy->num_buttons = joy->num_axes = 0;
1225 #endif
1226
1227 #if TARGET_HOST_MAC_OSX
1228     if (joy->id >= numDevices) {
1229         fgWarning ( "%s", "device index out of range in fgJoystickOpen()");
1230         return;
1231     }
1232
1233     /* create device interface */
1234     rv = IOCreatePlugInInterfaceForService ( ioDevices[joy->id], 
1235                                              kIOHIDDeviceUserClientTypeID,
1236                                              kIOCFPlugInInterfaceID,
1237                                              &plugin, &score);
1238
1239     if (rv != kIOReturnSuccess) {
1240         fgWarning ( "%s", "error creating plugin for io device");
1241         return;
1242     }
1243
1244     pluginResult = (*plugin)->QueryInterface ( plugin, 
1245                     CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), &(LPVOID) joy->hidDev );
1246
1247     if ( pluginResult != S_OK )
1248         fgWarning ( "%s", "QI-ing IO plugin to HID Device interface failed");
1249
1250     (*plugin)->Release(plugin); /* don't leak a ref */
1251     if (joy->hidDev == NULL) return;
1252
1253     /* store the interface in this instance */
1254     rv = (*(joy->hidDev))->open(joy->hidDev, 0);
1255     if (rv != kIOReturnSuccess) {
1256         fgWarning ( "%s", "error opening device interface");
1257         return;
1258     }
1259
1260     props = getCFProperties(ioDevices[joy->id]);
1261
1262     /* recursively enumerate all the bits */
1263     CFTypeRef topLevelElement = 
1264                       CFDictionaryGetValue ( props, CFSTR ( kIOHIDElementKey ) );
1265     enumerateElements ( topLevelElement );
1266
1267     CFRelease ( props );
1268 #endif
1269
1270 #if TARGET_HOST_WIN32
1271     joy->js.dwFlags = JOY_RETURNALL;
1272     joy->js.dwSize  = sizeof( joy->js );
1273
1274     memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1275
1276     joy->error =
1277         ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1278           JOYERR_NOERROR );
1279
1280     if ( joy->jsCaps.wNumAxes == 0 )
1281     {
1282         joy->num_axes = 0;
1283         joy->error = GL_TRUE;
1284     }
1285     else
1286     {
1287         /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1288          * at least for USB.  Try to get the real name from the registry.
1289          */
1290         if ( ! fghJoystickGetOEMProductName ( joy, joy->name, sizeof(joy->name) ) )
1291         {
1292             fgWarning ( "%s", "JS: Failed to read joystick name from registry" );
1293             strncpy ( joy->name, joy->jsCaps.szPname, sizeof(joy->name) );
1294         }
1295
1296         /* Windows joystick drivers may provide any combination of
1297          * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1298          */
1299         if ( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1300         {
1301             joy->num_axes = _JS_MAX_AXES;
1302             joy->min [ 7 ] = -1.0; joy->max [ 7 ] = 1.0;  /* POV Y */
1303             joy->min [ 6 ] = -1.0; joy->max [ 6 ] = 1.0;  /* POV X */
1304         }
1305         else
1306             joy->num_axes = 6;
1307
1308         joy->min[ 5 ] = (float) joy->jsCaps.wVmin;
1309         joy->max[ 5 ] = (float) joy->jsCaps.wVmax;
1310         joy->min[ 4 ] = (float) joy->jsCaps.wUmin;
1311         joy->max[ 4 ] = (float) joy->jsCaps.wUmax;
1312         joy->min[ 3 ] = (float) joy->jsCaps.wRmin;
1313         joy->max[ 3 ] = (float) joy->jsCaps.wRmax;
1314         joy->min[ 2 ] = (float) joy->jsCaps.wZmin;
1315         joy->max[ 2 ] = (float) joy->jsCaps.wZmax;
1316         joy->min[ 1 ] = (float) joy->jsCaps.wYmin;
1317         joy->max[ 1 ] = (float) joy->jsCaps.wYmax;
1318         joy->min[ 0 ] = (float) joy->jsCaps.wXmin;
1319         joy->max[ 0 ] = (float) joy->jsCaps.wXmax;
1320     }
1321
1322     /*
1323      * Guess all the rest judging on the axes extremals
1324      */
1325     for( i = 0; i < joy->num_axes; i++ )
1326     {
1327         joy->center   [ i ] = (joy->max[i] + joy->min[i]) * 0.5f;
1328         joy->dead_band[ i ] = 0.0f;
1329         joy->saturate [ i ] = 1.0f;
1330     }
1331 #endif
1332
1333 #if TARGET_HOST_UNIX_X11
1334 #if defined(__FreeBSD__) || defined(__NetBSD__)
1335     for ( i = 0; i < _JS_MAX_AXES; i++ )
1336         joy->os->cache_axes [ i ] = 0.0f;
1337
1338     joy->os->cache_buttons = 0;
1339
1340     joy->os->fd = open ( joy->os->fname, O_RDONLY | O_NONBLOCK);
1341
1342     if ( joy->os->fd < 0 && errno == EACCES)
1343       fgWarning ( "%s exists but is not readable by you\n", joy->os->fname );
1344
1345     joy->error = ( joy->os->fd < 0 );
1346
1347     if ( joy->error )
1348       return;
1349
1350     joy->num_axes = 0;
1351     joy->num_buttons = 0;
1352     if ( joy->os->is_analog )
1353     {
1354         FILE *joyfile;
1355         char joyfname [ 1024 ];
1356         int noargs, in_no_axes;
1357
1358         float axes  [ _JS_MAX_AXES ];
1359         int buttons [ _JS_MAX_AXES ];
1360
1361         joy->num_axes    =  2;
1362         joy->num_buttons = 32;
1363
1364         fghJoystickRawRead ( joy, buttons, axes );
1365         joy->error = axes[0] < -1000000000.0f;
1366         if ( joy->error )
1367           return;
1368
1369         sprintf( joyfname, "%s/.joy%drc", getenv ( "HOME" ), joy->id );
1370
1371         joyfile = fopen ( joyfname, "r" );
1372         joy->error = ( joyfile == NULL );
1373         if ( joy->error )
1374           return;
1375
1376         noargs = fscanf ( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1377                           &joy->min [ 0 ], &joy->center [ 0 ], &joy->max [ 0 ],
1378                           &joy->min [ 1 ], &joy->center [ 1 ], &joy->max [ 1 ] );
1379         joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1380         fclose ( joyfile );
1381         if ( joy->error )
1382           return;
1383
1384         for ( i = 0; i < _JS_MAX_AXES; i++ )
1385         {
1386             joy->dead_band [ i ] = 0.0f;
1387             joy->saturate  [ i ] = 1.0f;
1388         }
1389
1390         return;    /* End of analog code */
1391     }
1392
1393 #    ifdef HAVE_USB_JS
1394     if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1395                                     &joy->num_buttons ) )
1396     {
1397         close ( joy->os->fd );
1398         joy->error = GL_TRUE;
1399         return;
1400     }
1401
1402     cp = strrchr(joy->os->fname, '/');
1403     if ( cp ) {
1404         if ( fghJoystickFindUSBdev ( &cp[1], joy->name, sizeof(joy->name) ) == 0 )
1405             strcpy ( joy->name, &cp[1] );
1406     }
1407
1408     if ( joy->num_axes > _JS_MAX_AXES )
1409         joy->num_axes = _JS_MAX_AXES;
1410
1411     for ( i = 0; i < _JS_MAX_AXES; i++ )
1412     {
1413         /* We really should get this from the HID, but that data seems
1414          * to be quite unreliable for analog-to-USB converters. Punt for
1415          * now.
1416          */
1417         if ( joy->os->axes_usage [ i ] == HUG_HAT_SWITCH )
1418         {
1419             joy->max       [ i ] = 1.0f;
1420             joy->center    [ i ] = 0.0f;
1421             joy->min       [ i ] = -1.0f;
1422         }
1423         else
1424         {
1425             joy->max       [ i ] = 255.0f;
1426             joy->center    [ i ] = 127.0f;
1427             joy->min       [ i ] = 0.0f;
1428         }
1429
1430         joy->dead_band [ i ] = 0.0f;
1431         joy->saturate  [ i ] = 1.0f;
1432     }
1433 #    endif
1434 #endif
1435
1436 #if defined(__linux__)
1437     /*
1438      * Default for older Linux systems.
1439      */
1440     joy->num_axes    =  2;
1441     joy->num_buttons = 32;
1442
1443 #    ifdef JS_NEW
1444     for( i = 0; i < _JS_MAX_AXES; i++ )
1445         joy->tmp_axes[ i ] = 0.0f;
1446
1447     joy->tmp_buttons = 0;
1448 #    endif
1449
1450     joy->fd = open( joy->fname, O_RDONLY );
1451
1452     joy->error = (joy->fd < 0);
1453
1454     if ( joy->error )
1455         return;
1456
1457     /*
1458      * Set the correct number of axes for the linux driver
1459      */
1460 #    ifdef JS_NEW
1461     /* Melchior Franz's fixes for big-endian Linuxes since writing 
1462      *  to the upper byte of an uninitialized word doesn't work. 
1463      *  9 April 2003 
1464      */
1465     ioctl ( joy->fd, JSIOCGAXES   , &u ); 
1466     joy->num_axes = u;
1467     ioctl ( joy->fd, JSIOCGBUTTONS, &u );
1468     joy->num_buttons = u;
1469     ioctl ( joy->fd, JSIOCGNAME ( sizeof(joy->name) ), joy->name );
1470     fcntl ( joy->fd, F_SETFL      , O_NONBLOCK   );
1471 #    endif
1472
1473     /*
1474      * The Linux driver seems to return 512 for all axes
1475      * when no stick is present - but there is a chance
1476      * that could happen by accident - so it's gotta happen
1477      * on both axes for at least 100 attempts.
1478      *
1479      * PWO: shouldn't be that done somehow wiser on the kernel level?
1480      */
1481 #    ifndef JS_NEW
1482     counter = 0;
1483
1484     do
1485     { 
1486         fghJoystickRawRead( joy, NULL, joy->center );
1487         counter++;
1488     } while( !joy->error &&
1489              counter < 100 &&
1490              joy->center[ 0 ] == 512.0f &&
1491              joy->center[ 1 ] == 512.0f );
1492
1493     if ( counter >= 100 )
1494         joy->error = GL_TRUE;
1495 #    endif
1496
1497     for ( i = 0; i < _JS_MAX_AXES; i++ )
1498     {
1499 #    ifdef JS_NEW
1500         joy->max   [ i ] =  32767.0f;
1501         joy->center[ i ] =      0.0f;
1502         joy->min   [ i ] = -32767.0f;
1503 #    else
1504         joy->max[ i ] = joy->center[ i ] * 2.0f;
1505         joy->min[ i ] = 0.0f;
1506 #    endif
1507         joy->dead_band[ i ] = 0.0f;
1508         joy->saturate [ i ] = 1.0f;
1509     }
1510 #endif
1511 #endif
1512 }
1513
1514 /*
1515  * This function replaces the constructor method in the JS library.
1516  */
1517 void fgJoystickInit( int ident )
1518 {
1519     if ( ident >= MAX_NUM_JOYSTICKS )
1520       fgError( "Too large a joystick number" );
1521
1522     if( fgJoystick[ident] )
1523         fgError( "illegal attempt to initialize joystick device" );
1524
1525     fgJoystick[ident] = ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1526
1527     /* Set defaults */
1528     fgJoystick[ident]->num_axes = fgJoystick[ident]->num_buttons = 0;
1529     fgJoystick[ident]->error = GL_TRUE;
1530
1531 #if TARGET_HOST_MACINTOSH
1532     fgJoystick[ident]->id = ident;
1533     sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident ); /* FIXME */
1534     fgJoystick[ident]->error = GL_FALSE;
1535 #endif
1536
1537 #if TARGET_HOST_MAC_OSX
1538     fgJoystick[ident]->id = ident;
1539     fgJoystick[ident]->error = GL_FALSE;
1540     fgJoystick[ident]->num_axes = 0;
1541     fgJoystick[ident]->num_buttons = 0;
1542
1543     if (numDevices < 0) {
1544         /* do first-time init (since we can't over-ride jsInit, hmm */
1545         numDevices = 0;
1546
1547         mach_port_t masterPort;
1548         IOReturn rv = IOMasterPort ( bootstrap_port, &masterPort );
1549         if ( rv != kIOReturnSuccess ) {
1550             fgWarning ( "%s", "error getting master Mach port");
1551             return;
1552         }
1553         fghJoystickFindDevices ( masterPort );
1554     }
1555
1556     if ( ident >= numDevices ) {
1557         fgJoystick[ident]->error = GL_TRUE;
1558         return;
1559     }
1560
1561     /* get the name now too */
1562     CFDictionaryRef properties = getCFProperties(ioDevices[ident]);
1563     CFTypeRef ref = CFDictionaryGetValue (properties, CFSTR(kIOHIDProductKey));
1564     if (!ref)
1565       ref = CFDictionaryGetValue (properties, CFSTR("USB Product Name"));
1566
1567     if (!ref || !CFStringGetCString ((CFStringRef) ref, name, 128, CFStringGetSystemEncoding ())) {
1568       fgWarning ( "%s", "error getting device name");
1569       name[0] = '\0';
1570     }
1571 #endif
1572
1573 #if TARGET_HOST_WIN32
1574     switch( ident )
1575     {
1576     case 0:
1577         fgJoystick[ident]->js_id = JOYSTICKID1;
1578         fgJoystick[ident]->error = GL_FALSE;
1579         break;
1580     case 1:
1581         fgJoystick[ident]->js_id = JOYSTICKID2;
1582         fgJoystick[ident]->error = GL_FALSE;
1583         break;
1584     default:
1585         fgJoystick[ident]->num_axes = 0;
1586         fgJoystick[ident]->error = GL_TRUE;
1587         return;
1588     }
1589 #endif
1590
1591 #if TARGET_HOST_UNIX_X11
1592 #    if defined(__FreeBSD__) || defined(__NetBSD__)
1593     fgJoystick[ident]->id = ident;
1594     fgJoystick[ident]->error = GL_FALSE;
1595
1596     fgJoystick[ident]->os = calloc (1, sizeof (struct os_specific_s));
1597     memset ( fgJoystick[ident]->os, 0, sizeof(struct os_specific_s) );
1598     if (ident < USB_IDENT_OFFSET)
1599         fgJoystick[ident]->os->is_analog = 1;
1600     if (fgJoystick[ident]->os->is_analog)
1601         sprintf ( fgJoystick[ident]->os->fname, "%s%d", AJSDEV, ident );
1602     else
1603         sprintf ( fgJoystick[ident]->os->fname, "%s%d", UHIDDEV, ident - USB_IDENT_OFFSET );
1604 #    elif defined(__linux__)
1605     fgJoystick[ident]->id = ident;
1606     fgJoystick[ident]->error = GL_FALSE;
1607
1608     sprintf ( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1609
1610     if ( access ( fgJoystick[ident]->fname, F_OK ) != 0 )
1611         sprintf ( fgJoystick[ident]->fname, "/dev/js%d", ident );
1612 #    endif
1613 #endif
1614
1615     fghJoystickOpen ( fgJoystick[ident] );
1616 }
1617
1618 /*
1619  *
1620  */
1621 void fgJoystickClose( void )
1622 {
1623     int ident ;
1624     for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1625     {
1626         if ( fgJoystick[ident] )
1627         {
1628
1629 #if TARGET_HOST_MACINTOSH
1630             ISpSuspend  ();
1631             ISpStop     ();
1632             ISpShutdown ();
1633 #endif
1634
1635 #if TARGET_HOST_MAC_OSX
1636             (*(fgJoystick[ident]->hidDev))->close(fgJoystick[ident]->hidDev);
1637 #endif
1638
1639 #if TARGET_HOST_WIN32
1640             /* Do nothing special */
1641 #endif
1642
1643 #if TARGET_HOST_UNIX_X11
1644 #if defined(__FreeBSD__) || defined(__NetBSD__)
1645             if ( fgJoystick[ident]->os )
1646             {
1647                 if ( ! fgJoystick[ident]->error )
1648                     close ( fgJoystick[ident]->os->fd );
1649 #ifdef HAVE_USB_JS
1650                 if ( fgJoystick[ident]->os->hids )
1651                     free (fgJoystick[ident]->os->hids);
1652                 if ( fgJoystick[ident]->os->hid_data_buf )
1653                     free (fgJoystick[ident]->os->hid_data_buf);
1654 #endif
1655                 free (fgJoystick[ident]->os);
1656             }
1657 #endif
1658
1659             if( ! fgJoystick[ident]->error )
1660                 close( fgJoystick[ident]->fd );
1661 #endif
1662
1663             free( fgJoystick[ident] );
1664             fgJoystick[ident] = NULL;  /* show joystick has been deinitialized */
1665         }
1666     }
1667 }
1668
1669 /*
1670  * Polls the joystick and executes the joystick callback hooked to the
1671  * window specified in the function's parameter:
1672  */
1673 void fgJoystickPollWindow( SFG_Window* window )
1674 {
1675     float axes[ _JS_MAX_AXES ];
1676     int buttons;
1677     int ident;
1678
1679     freeglut_return_if_fail( window );
1680     freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1681
1682     for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1683     {
1684         if ( fgJoystick[ident] )
1685         {
1686             fghJoystickRead( fgJoystick[ident], &buttons, axes );
1687
1688             if ( !fgJoystick[ident]->error )
1689                 INVOKE_WCB( *window, Joystick,
1690                             ( buttons,
1691                               (int) (axes[ 0 ] * 1000.0f ),
1692                               (int) (axes[ 1 ] * 1000.0f ),
1693                               (int) (axes[ 2 ] * 1000.0f ) )
1694                           );
1695         }
1696     }
1697 }
1698
1699 /*
1700  * PWO: These jsJoystick class methods have not been implemented.
1701  *      We might consider adding such functions to freeglut-2.0.
1702  */
1703 int  getNumAxes ( int ident )
1704     { return fgJoystick[ident]->num_axes; }
1705 int  notWorking ( int ident )
1706     { return fgJoystick[ident]->error; }
1707
1708 float getDeadBand ( int ident, int axis )
1709     { return fgJoystick[ident]->dead_band [ axis ]; }
1710 void  setDeadBand ( int ident, int axis, float db )
1711     { fgJoystick[ident]->dead_band [ axis ] = db; }
1712
1713 float getSaturation ( int ident, int axis )
1714     { return fgJoystick[ident]->saturate [ axis ]; }
1715 void  setSaturation ( int ident, int axis, float st )
1716     { fgJoystick[ident]->saturate [ axis ] = st; }
1717
1718 void setMinRange ( int ident, float *axes )
1719     { memcpy ( fgJoystick[ident]->min   , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1720 void setMaxRange ( int ident, float *axes )
1721     { memcpy ( fgJoystick[ident]->max   , axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1722 void setCenter   ( int ident, float *axes )
1723     { memcpy ( fgJoystick[ident]->center, axes, fgJoystick[ident]->num_axes * sizeof(float) ); }
1724
1725 void getMinRange ( int ident, float *axes )
1726     { memcpy ( axes, fgJoystick[ident]->min   , fgJoystick[ident]->num_axes * sizeof(float) ); }
1727 void getMaxRange ( int ident, float *axes )
1728     { memcpy ( axes, fgJoystick[ident]->max   , fgJoystick[ident]->num_axes * sizeof(float) ); }
1729 void getCenter   ( int ident, float *axes )
1730     { memcpy ( axes, fgJoystick[ident]->center, fgJoystick[ident]->num_axes * sizeof(float) ); }
1731
1732 /*** END OF FILE ***/