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