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