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