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