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