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