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