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