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