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