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