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