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