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