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 "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
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  */
fghJoystickWalkUSBdev(int f,char * dev,char * out,int outlen)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 
fghJoystickFindUSBdev(char * name,char * out,int outlen)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
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 
fghJoystickInitializeHID(struct os_specific_s * os,int * num_axes,int * num_buttons)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
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
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  */
fghJoystickRawRead(SFG_Joystick * joy,int * buttons,float * axes)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
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
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  */
fghJoystickFudgeAxis(SFG_Joystick * joy,float value,int axis)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  */
fghJoystickRead(SFG_Joystick * joy,int * buttons,float * axes)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. */
fghJoystickFindDevices(SFG_Joystick * joy,mach_port_t masterPort)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
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 ***/
1802