Alas, the ChangeLog has not the usual ChangeLog format, so tell
[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       assert(CFGetTypeID(element) == CFArrayGetTypeID());
929
930       CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
931       CFArrayApplyFunction((CFArrayRef) element, range,
932             &fghJoystickElementEnumerator, joy );
933 }
934
935 static void fghJoystickParseElement ( SFG_Joystick *joy, CFDictionaryRef element )
936 {
937     CFTypeRef refPage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsagePageKey));
938     CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementUsageKey));
939
940     long type, page, usage;
941
942     CFNumberGetValue((CFNumberRef)
943         CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementTypeKey)),
944         kCFNumberLongType, &type);
945
946     switch ( typ e) {
947     case kIOHIDElementTypeInput_Misc:
948     case kIOHIDElementTypeInput_Axis:
949     case kIOHIDElementTypeInput_Button:
950         printf("got input element...");
951         CFNumberGetValue( (CFNumberRef) refUsage, kCFNumberLongType, &usage );
952         CFNumberGetValue( (CFNumberRef) refPage, kCFNumberLongType, &page );
953
954         if (page == kHIDPage_GenericDesktop) {
955             switch ( usage ) /* look at usage to determine function */
956             {
957             case kHIDUsage_GD_X:
958             case kHIDUsage_GD_Y:
959             case kHIDUsage_GD_Z:
960             case kHIDUsage_GD_Rx:
961             case kHIDUsage_GD_Ry:
962             case kHIDUsage_GD_Rz:
963             case kHIDUsage_GD_Slider: /* for throttle / trim controls */
964                 printf(" axis\n");
965                 fghJoystickAddAxisElement((CFDictionaryRef) element);
966                 break;
967
968             case kHIDUsage_GD_Hatswitch:
969                 printf(" hat\n");
970                 fghJoystickAddHatElement((CFDictionaryRef) element);
971                 break;
972
973             default:
974                 fgWarning ( "input type element has weird usage (%x)", usage);
975                 break;
976             }
977         } else if (page == kHIDPage_Button) {
978             printf(" button\n");
979             fghJoystickAddButtonElement((CFDictionaryRef) element);
980         } else
981             fgWarning ( "input type element has weird page (%x)", page);
982         break;
983
984     case kIOHIDElementTypeCollection:
985         fghJoystickEnumerateElements (
986             CFDictionaryGetValue ( element, CFSTR(kIOHIDElementKey) )
987         );
988         break;
989
990     default:
991         break;
992     }
993 }
994
995 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
996 {
997     long cookie, lmin, lmax;
998     int index = joy->num_axes++;
999
1000     CFNumberGetValue ((CFNumberRef)
1001         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
1002         kCFNumberLongType, &cookie);
1003
1004     axisCookies[index] = (IOHIDElementCookie) cookie;
1005
1006     CFNumberGetValue ((CFNumberRef)
1007         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
1008         kCFNumberLongType, &lmin);
1009
1010     CFNumberGetValue ((CFNumberRef)
1011         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
1012         kCFNumberLongType, &lmax);
1013
1014     joy->min[index] = lmin;
1015     joy->max[index] = lmax;
1016     joy->dead_band[index] = 0.0;
1017     joy->saturate[index] = 1.0;
1018     joy->center[index] = (lmax + lmin) * 0.5;
1019 }
1020
1021 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
1022 {
1023     long cookie;
1024     CFNumberGetValue ((CFNumberRef)
1025             CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
1026             kCFNumberLongType, &cookie);
1027
1028     joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
1029     /* anything else for buttons? */
1030 }
1031
1032 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
1033 {
1034     /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
1035     /* do we map hats to axes or buttons? */
1036 }
1037 #endif
1038
1039 #if TARGET_HOST_WIN32
1040 /* Inspired by
1041    http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
1042  */
1043 #    if defined(_MSC_VER)
1044 #        pragma comment (lib, "advapi32.lib")
1045 #    endif
1046
1047 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
1048 {
1049     char buffer [ 256 ];
1050
1051     char OEMKey [ 256 ];
1052
1053     HKEY  hKey;
1054     DWORD dwcb;
1055     LONG  lr;
1056
1057     if ( joy->error )
1058         return 0;
1059
1060     /* Open .. MediaResources\CurrentJoystickSettings */
1061     sprintf ( buffer, "%s\\%s\\%s",
1062               REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
1063               REGSTR_KEY_JOYCURR );
1064
1065     lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
1066
1067     if ( lr != ERROR_SUCCESS ) return 0;
1068
1069     /* Get OEM Key name */
1070     dwcb = sizeof(OEMKey);
1071
1072     /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
1073     sprintf ( buffer, "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
1074
1075     lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
1076     RegCloseKey ( hKey );
1077
1078     if ( lr != ERROR_SUCCESS ) return 0;
1079
1080     /* Open OEM Key from ...MediaProperties */
1081     sprintf ( buffer, "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
1082
1083     lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
1084
1085     if ( lr != ERROR_SUCCESS ) return 0;
1086
1087     /* Get OEM Name */
1088     dwcb = buf_sz;
1089
1090     lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
1091                              &dwcb );
1092     RegCloseKey ( hKey );
1093
1094     if ( lr != ERROR_SUCCESS ) return 0;
1095
1096     return 1;
1097 }
1098 #endif
1099
1100
1101 static void fghJoystickOpen( SFG_Joystick* joy )
1102 {
1103     int i;
1104 #if TARGET_HOST_MACINTOSH
1105     OSStatus err;
1106 #endif
1107 #if TARGET_HOST_MAC_OSX
1108         IOReturn rv;
1109         SInt32 score;
1110         IOCFPlugInInterface **plugin;
1111
1112         HRESULT pluginResult;
1113
1114         CFDictionaryRef props;
1115     CFTypeRef topLevelElement;
1116 #endif
1117 #if TARGET_HOST_UNIX_X11
1118 #    if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1119        char *cp;
1120 #    endif
1121 #    ifdef JS_NEW
1122        unsigned char u;
1123 #    else
1124        int counter;
1125 #    endif
1126 #endif
1127
1128     /*
1129      * Default values (for no joystick -- each conditional will reset the
1130      * error flag)
1131      */
1132     joy->error = TRUE;
1133     joy->num_axes = joy->num_buttons = 0;
1134     joy->name[ 0 ] = '\0';
1135
1136 #if TARGET_HOST_MACINTOSH
1137     /* XXX FIXME: get joystick name in Mac */
1138
1139     err = ISpStartup( );
1140
1141     if( err == noErr )
1142     {
1143 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
1144
1145         joy->error = GL_TRUE;
1146
1147         /* initialize the needs structure */
1148         ISpNeed temp_isp_needs[ isp_num_needs ] =
1149         {
1150           { "\pX-Axis",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1151           { "\pY-Axis",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1152           { "\pZ-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1153           { "\pR-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1154           { "\pAxis   4",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1155           { "\pAxis   5",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1156           { "\pAxis   6",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1157           { "\pAxis   7",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1158           { "\pAxis   8",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },
1159
1160           { "\pButton 0",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1161           { "\pButton 1",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1162           { "\pButton 2",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1163           { "\pButton 3",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1164           { "\pButton 4",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1165           { "\pButton 5",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1166           { "\pButton 6",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1167           { "\pButton 7",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1168           { "\pButton 8",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1169           { "\pButton 9",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1170           { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1171           { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1172           { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1173           { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1174           { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1175           { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1176           { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1177           { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1178           { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1179           { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1180           { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1181           { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1182           { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1183           { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1184           { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1185           { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1186           { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1187           { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1188           { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1189           { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1190           { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1191           { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
1192         };
1193
1194         memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
1195
1196
1197         /* next two calls allow keyboard and mouse to emulate other input
1198          * devices (gamepads, joysticks, etc)
1199          */
1200         /*
1201           err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
1202           ISP_CHECK_ERR(err)
1203
1204
1205           err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
1206           ISP_CHECK_ERR(err)
1207         */
1208
1209         err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
1210                                               joy->isp_needs, joy->isp_elem,
1211                                               0 );
1212         ISP_CHECK_ERR( err )
1213
1214         err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
1215                        'freeglut', nil, 0, 128, 0 );
1216         ISP_CHECK_ERR( err )
1217
1218         joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
1219         joy->num_axes    = joy->isp_num_axis;
1220
1221         for( i = 0; i < joy->num_axes; i++ )
1222         {
1223             joy->dead_band[ i ] = 0;
1224             joy->saturate [ i ] = 1;
1225             joy->center   [ i ] = kISpAxisMiddle;
1226             joy->max      [ i ] = kISpAxisMaximum;
1227             joy->min      [ i ] = kISpAxisMinimum;
1228         }
1229
1230         joy->error = GL_FALSE;
1231     }
1232     else
1233         joy->num_buttons = joy->num_axes = 0;
1234 #endif
1235
1236 #if TARGET_HOST_MAC_OSX
1237     if( joy->id >= numDevices )
1238     {
1239         fgWarning( "device index out of range in fgJoystickOpen()" );
1240         return;
1241     }
1242
1243     /* create device interface */
1244     rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
1245                                             kIOHIDDeviceUserClientTypeID,
1246                                             kIOCFPlugInInterfaceID,
1247                                             &plugin, &score );
1248
1249     if( rv != kIOReturnSuccess )
1250     {
1251         fgWarning( "error creating plugin for io device" );
1252         return;
1253     }
1254
1255     pluginResult = ( *plugin )->QueryInterface(
1256         plugin,
1257         CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
1258         &( LPVOID )joy->hidDev
1259     );
1260
1261     if( pluginResult != S_OK )
1262         fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
1263
1264     ( *plugin )->Release( plugin ); /* don't leak a ref */
1265     if( joy->hidDev == NULL )
1266         return;
1267
1268     /* store the interface in this instance */
1269     rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
1270     if( rv != kIOReturnSuccess )
1271     {
1272         fgWarning( "error opening device interface");
1273         return;
1274     }
1275
1276     props = getCFProperties( ioDevices[ joy->id ] );
1277
1278     /* recursively enumerate all the bits */
1279     CFTypeRef topLevelElement =
1280         CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
1281     enumerateElements( topLevelElement );
1282
1283     CFRelease( props );
1284 #endif
1285
1286 #if TARGET_HOST_WIN32
1287     joy->js.dwFlags = JOY_RETURNALL;
1288     joy->js.dwSize  = sizeof( joy->js );
1289
1290     memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
1291
1292     joy->error =
1293         ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
1294           JOYERR_NOERROR );
1295
1296     if( joy->jsCaps.wNumAxes == 0 )
1297     {
1298         joy->num_axes = 0;
1299         joy->error = GL_TRUE;
1300     }
1301     else
1302     {
1303         /* Device name from jsCaps is often "Microsoft PC-joystick driver",
1304          * at least for USB.  Try to get the real name from the registry.
1305          */
1306         if ( ! fghJoystickGetOEMProductName( joy, joy->name,
1307                                              sizeof( joy->name ) ) )
1308         {
1309             fgWarning( "JS: Failed to read joystick name from registry" );
1310             strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
1311         }
1312
1313         /* Windows joystick drivers may provide any combination of
1314          * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
1315          */
1316         if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
1317         {
1318             joy->num_axes = _JS_MAX_AXES;
1319             joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0;  /* POV Y */
1320             joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0;  /* POV X */
1321         }
1322         else
1323             joy->num_axes = 6;
1324
1325         joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
1326         joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
1327         joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
1328         joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
1329         joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
1330         joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
1331         joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
1332         joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
1333         joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
1334         joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
1335         joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
1336         joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
1337     }
1338
1339     /* Guess all the rest judging on the axes extremals */
1340     for( i = 0; i < joy->num_axes; i++ )
1341     {
1342         joy->center   [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
1343         joy->dead_band[ i ] = 0.0f;
1344         joy->saturate [ i ] = 1.0f;
1345     }
1346 #endif
1347
1348 #if TARGET_HOST_UNIX_X11
1349 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1350     for( i = 0; i < _JS_MAX_AXES; i++ )
1351         joy->os->cache_axes[ i ] = 0.0f;
1352
1353     joy->os->cache_buttons = 0;
1354
1355     joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
1356
1357     if( joy->os->fd < 0 && errno == EACCES )
1358         fgWarning ( "%s exists but is not readable by you", joy->os->fname );
1359
1360     joy->error =( joy->os->fd < 0 );
1361
1362     if( joy->error )
1363         return;
1364
1365     joy->num_axes = 0;
1366     joy->num_buttons = 0;
1367     if( joy->os->is_analog )
1368     {
1369         FILE *joyfile;
1370         char joyfname[ 1024 ];
1371         int noargs, in_no_axes;
1372
1373         float axes [ _JS_MAX_AXES ];
1374         int buttons[ _JS_MAX_AXES ];
1375
1376         joy->num_axes    =  2;
1377         joy->num_buttons = 32;
1378
1379         fghJoystickRawRead( joy, buttons, axes );
1380         joy->error = axes[ 0 ] < -1000000000.0f;
1381         if( joy->error )
1382             return;
1383
1384         sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
1385
1386         joyfile = fopen( joyfname, "r" );
1387         joy->error =( joyfile == NULL );
1388         if( joy->error )
1389             return;
1390
1391         noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
1392                          &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
1393                          &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
1394         joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
1395         fclose( joyfile );
1396         if( joy->error )
1397             return;
1398
1399         for( i = 0; i < _JS_MAX_AXES; i++ )
1400         {
1401             joy->dead_band[ i ] = 0.0f;
1402             joy->saturate [ i ] = 1.0f;
1403         }
1404
1405         return;    /* End of analog code */
1406     }
1407
1408 #    ifdef HAVE_USB_JS
1409     if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
1410                                     &joy->num_buttons ) )
1411     {
1412         close( joy->os->fd );
1413         joy->error = GL_TRUE;
1414         return;
1415     }
1416
1417     cp = strrchr( joy->os->fname, '/' );
1418     if( cp )
1419     {
1420         if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
1421             0 )
1422             strcpy( joy->name, &cp[1] );
1423     }
1424
1425     if( joy->num_axes > _JS_MAX_AXES )
1426         joy->num_axes = _JS_MAX_AXES;
1427
1428     for( i = 0; i < _JS_MAX_AXES; i++ )
1429     {
1430         /* We really should get this from the HID, but that data seems
1431          * to be quite unreliable for analog-to-USB converters. Punt for
1432          * now.
1433          */
1434         if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
1435         {
1436             joy->max   [ i ] = 1.0f;
1437             joy->center[ i ] = 0.0f;
1438             joy->min   [ i ] = -1.0f;
1439         }
1440         else
1441         {
1442             joy->max   [ i ] = 255.0f;
1443             joy->center[ i ] = 127.0f;
1444             joy->min   [ i ] = 0.0f;
1445         }
1446
1447         joy->dead_band[ i ] = 0.0f;
1448         joy->saturate[ i ] = 1.0f;
1449     }
1450 #    endif
1451 #endif
1452
1453 #if defined( __linux__ )
1454     /* Default for older Linux systems. */
1455     joy->num_axes    =  2;
1456     joy->num_buttons = 32;
1457
1458 #    ifdef JS_NEW
1459     for( i = 0; i < _JS_MAX_AXES; i++ )
1460         joy->tmp_axes[ i ] = 0.0f;
1461
1462     joy->tmp_buttons = 0;
1463 #    endif
1464
1465     joy->fd = open( joy->fname, O_RDONLY );
1466
1467     joy->error =( joy->fd < 0 );
1468
1469     if( joy->error )
1470         return;
1471
1472     /* Set the correct number of axes for the linux driver */
1473 #    ifdef JS_NEW
1474     /* Melchior Franz's fixes for big-endian Linuxes since writing
1475      *  to the upper byte of an uninitialized word doesn't work.
1476      *  9 April 2003
1477      */
1478     ioctl( joy->fd, JSIOCGAXES, &u );
1479     joy->num_axes = u;
1480     ioctl( joy->fd, JSIOCGBUTTONS, &u );
1481     joy->num_buttons = u;
1482     ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
1483     fcntl( joy->fd, F_SETFL, O_NONBLOCK );
1484 #    endif
1485
1486     /*
1487      * The Linux driver seems to return 512 for all axes
1488      * when no stick is present - but there is a chance
1489      * that could happen by accident - so it's gotta happen
1490      * on both axes for at least 100 attempts.
1491      *
1492      * PWO: shouldn't be that done somehow wiser on the kernel level?
1493      */
1494 #    ifndef JS_NEW
1495     counter = 0;
1496
1497     do
1498     {
1499         fghJoystickRawRead( joy, NULL, joy->center );
1500         counter++;
1501     } while( !joy->error &&
1502              counter < 100 &&
1503              joy->center[ 0 ] == 512.0f &&
1504              joy->center[ 1 ] == 512.0f );
1505
1506     if ( counter >= 100 )
1507         joy->error = GL_TRUE;
1508 #    endif
1509
1510     for( i = 0; i < _JS_MAX_AXES; i++ )
1511     {
1512 #    ifdef JS_NEW
1513         joy->max   [ i ] =  32767.0f;
1514         joy->center[ i ] =      0.0f;
1515         joy->min   [ i ] = -32767.0f;
1516 #    else
1517         joy->max[ i ] = joy->center[ i ] * 2.0f;
1518         joy->min[ i ] = 0.0f;
1519 #    endif
1520         joy->dead_band[ i ] = 0.0f;
1521         joy->saturate [ i ] = 1.0f;
1522     }
1523 #endif
1524 #endif
1525 }
1526
1527 /*
1528  * This function replaces the constructor method in the JS library.
1529  */
1530 static void fghJoystickInit( int ident )
1531 {
1532     if( ident >= MAX_NUM_JOYSTICKS )
1533       fgError( "Too large a joystick number: %d", ident );
1534
1535     if( fgJoystick[ ident ] )
1536         fgError( "illegal attempt to initialize joystick device again" );
1537
1538     fgJoystick[ ident ] =
1539         ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
1540
1541     /* Set defaults */
1542     fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
1543     fgJoystick[ ident ]->error = GL_TRUE;
1544
1545 #if TARGET_HOST_MACINTOSH
1546     fgJoystick[ ident ]->id = ident;
1547     sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident ); /* FIXME */
1548     fgJoystick[ ident ]->error = GL_FALSE;
1549 #endif
1550
1551 #if TARGET_HOST_MAC_OSX
1552     fgJoystick[ ident ]->id = ident;
1553     fgJoystick[ ident ]->error = GL_FALSE;
1554     fgJoystick[ ident ]->num_axes = 0;
1555     fgJoystick[ ident ]->num_buttons = 0;
1556
1557     if( numDevices < 0 )
1558     {
1559         /* do first-time init (since we can't over-ride jsInit, hmm */
1560         numDevices = 0;
1561
1562         mach_port_t masterPort;
1563         IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
1564         if( rv != kIOReturnSuccess )
1565         {
1566             fgWarning( "error getting master Mach port" );
1567             return;
1568         }
1569         fghJoystickFindDevices( masterPort );
1570     }
1571
1572     if ( ident >= numDevices )
1573     {
1574         fgJoystick[ ident ]->error = GL_TRUE;
1575         return;
1576     }
1577
1578     /* get the name now too */
1579     CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
1580     CFTypeRef ref = CFDictionaryGetValue( properties,
1581                                           CFSTR( kIOHIDProductKey ) );
1582     if (!ref)
1583         ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
1584
1585     if( !ref ||
1586         !CFStringGetCString( ( CFStringRef )ref, name, 128,
1587                              CFStringGetSystemEncoding( ) ) )
1588     {
1589         fgWarning( "error getting device name" );
1590         name[ 0 ] = '\0';
1591     }
1592 #endif
1593
1594 #if TARGET_HOST_WIN32
1595     switch( ident )
1596     {
1597     case 0:
1598         fgJoystick[ ident ]->js_id = JOYSTICKID1;
1599         fgJoystick[ ident ]->error = GL_FALSE;
1600         break;
1601     case 1:
1602         fgJoystick[ ident ]->js_id = JOYSTICKID2;
1603         fgJoystick[ ident ]->error = GL_FALSE;
1604         break;
1605     default:
1606         fgJoystick[ ident ]->num_axes = 0;
1607         fgJoystick[ ident ]->error = GL_TRUE;
1608         return;
1609     }
1610 #endif
1611
1612 #if TARGET_HOST_UNIX_X11
1613 #    if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1614     fgJoystick[ ident ]->id = ident;
1615     fgJoystick[ ident ]->error = GL_FALSE;
1616
1617     fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
1618     memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
1619     if( ident < USB_IDENT_OFFSET )
1620         fgJoystick[ ident ]->os->is_analog = 1;
1621     if( fgJoystick[ ident ]->os->is_analog )
1622         sprintf( fgJoystick[ ident ]->os->fname, "%s%d", AJSDEV, ident );
1623     else
1624         sprintf( fgJoystick[ ident ]->os->fname, "%s%d", UHIDDEV,
1625                  ident - USB_IDENT_OFFSET );
1626 #    elif defined( __linux__ )
1627     fgJoystick[ ident ]->id = ident;
1628     fgJoystick[ ident ]->error = GL_FALSE;
1629
1630     sprintf( fgJoystick[ident]->fname, "/dev/input/js%d", ident );
1631
1632     if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
1633         sprintf( fgJoystick[ ident ]->fname, "/dev/js%d", ident );
1634 #    endif
1635 #endif
1636
1637     fghJoystickOpen( fgJoystick[ ident  ] );
1638 }
1639
1640 /*
1641  * Try initializing all the joysticks (well, both of them)
1642  */
1643 void fgInitialiseJoysticks ( void )
1644 {
1645   /* Initialization courtesy of OpenGLUT -- do we want it? */
1646   if( !fgState.JoysticksInitialised )
1647   {
1648     int ident ;
1649     for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1650       fghJoystickInit( ident );
1651
1652     fgState.JoysticksInitialised = GL_TRUE;
1653   }
1654 }
1655
1656 /*
1657  *
1658  */
1659 void fgJoystickClose( void )
1660 {
1661     int ident ;
1662     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1663     {
1664         if( fgJoystick[ ident ] )
1665         {
1666
1667 #if TARGET_HOST_MACINTOSH
1668             ISpSuspend( );
1669             ISpStop( );
1670             ISpShutdown( );
1671 #endif
1672
1673 #if TARGET_HOST_MAC_OSX
1674             ( *( fgJoystick[ ident ]->hidDev ) )->
1675                 close( fgJoystick[ ident ]->hidDev );
1676 #endif
1677
1678 #if TARGET_HOST_WIN32
1679             /* Do nothing special */
1680 #endif
1681
1682 #if TARGET_HOST_UNIX_X11
1683 #if defined( __FreeBSD__ ) || defined( __NetBSD__ )
1684             if( fgJoystick[ident]->os )
1685             {
1686                 if( ! fgJoystick[ ident ]->error )
1687                     close( fgJoystick[ ident ]->os->fd );
1688 #ifdef HAVE_USB_JS
1689                 if( fgJoystick[ ident ]->os->hids )
1690                     free (fgJoystick[ ident ]->os->hids);
1691                 if( fgJoystick[ ident ]->os->hid_data_buf )
1692                     free( fgJoystick[ ident ]->os->hid_data_buf );
1693 #endif
1694                 free( fgJoystick[ident]->os );
1695             }
1696 #endif
1697
1698             if( ! fgJoystick[ident]->error )
1699                 close( fgJoystick[ ident ]->fd );
1700 #endif
1701
1702             free( fgJoystick[ ident ] );
1703             fgJoystick[ ident ] = NULL;
1704             /* show joystick has been deinitialized */
1705         }
1706     }
1707 }
1708
1709 /*
1710  * Polls the joystick and executes the joystick callback hooked to the
1711  * window specified in the function's parameter:
1712  */
1713 void fgJoystickPollWindow( SFG_Window* window )
1714 {
1715     float axes[ _JS_MAX_AXES ];
1716     int buttons;
1717     int ident;
1718
1719     freeglut_return_if_fail( window );
1720     freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
1721
1722     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
1723     {
1724         if( fgJoystick[ident] )
1725         {
1726             fghJoystickRead( fgJoystick[ident], &buttons, axes );
1727
1728             if( !fgJoystick[ident]->error )
1729                 INVOKE_WCB( *window, Joystick,
1730                             ( buttons,
1731                               (int) ( axes[ 0 ] * 1000.0f ),
1732                               (int) ( axes[ 1 ] * 1000.0f ),
1733                               (int) ( axes[ 2 ] * 1000.0f ) )
1734                 );
1735         }
1736     }
1737 }
1738
1739 /*
1740  * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
1741  */
1742 int fgJoystickDetect( void )
1743 {
1744   int ident;
1745
1746   fgInitialiseJoysticks ();
1747
1748   if ( !fgJoystick )
1749     return 0;
1750
1751   if ( !fgState.JoysticksInitialised )
1752     return 0;
1753
1754   for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
1755     if( fgJoystick[ident] && !fgJoystick[ident]->error )
1756       return 1;
1757
1758   return 0;
1759 }
1760
1761 /*
1762  * Joystick information functions
1763  */
1764 int  glutJoystickGetNumAxes( int ident )
1765 {
1766     return fgJoystick[ ident ]->num_axes;
1767 }
1768 int  glutJoystickGetNumButtons( int ident )
1769 {
1770     return fgJoystick[ ident ]->num_buttons;
1771 }
1772 int  glutJoystickNotWorking( int ident )
1773 {
1774     return fgJoystick[ ident ]->error;
1775 }
1776
1777 float glutJoystickGetDeadBand( int ident, int axis )
1778 {
1779     return fgJoystick[ ident ]->dead_band [ axis ];
1780 }
1781 void  glutJoystickSetDeadBand( int ident, int axis, float db )
1782 {
1783     fgJoystick[ ident ]->dead_band[ axis ] = db;
1784 }
1785
1786 float glutJoystickGetSaturation( int ident, int axis )
1787 {
1788     return fgJoystick[ ident ]->saturate[ axis ];
1789 }
1790 void  glutJoystickSetSaturation( int ident, int axis, float st )
1791 {
1792     fgJoystick[ ident ]->saturate [ axis ] = st;
1793 }
1794
1795 void glutJoystickSetMinRange( int ident, float *axes )
1796 {
1797     memcpy( fgJoystick[ ident ]->min, axes,
1798             fgJoystick[ ident ]->num_axes * sizeof( float ) );
1799 }
1800 void glutJoystickSetMaxRange( int ident, float *axes )
1801 {
1802     memcpy( fgJoystick[ ident ]->max, axes,
1803             fgJoystick[ ident ]->num_axes * sizeof( float ) );
1804 }
1805 void glutJoystickSetCenter( int ident, float *axes )
1806 {
1807     memcpy( fgJoystick[ ident ]->center, axes,
1808             fgJoystick[ ident ]->num_axes * sizeof( float ) );
1809 }
1810
1811 void glutJoystickGetMinRange( int ident, float *axes )
1812 {
1813     memcpy( axes, fgJoystick[ ident ]->min,
1814             fgJoystick[ ident ]->num_axes * sizeof( float ) );
1815 }
1816 void glutJoystickGetMaxRange( int ident, float *axes )
1817 {
1818     memcpy( axes, fgJoystick[ ident ]->max,
1819             fgJoystick[ ident ]->num_axes * sizeof( float ) );
1820 }
1821 void glutJoystickGetCenter( int ident, float *axes )
1822 {
1823     memcpy( axes, fgJoystick[ ident ]->center,
1824             fgJoystick[ ident ]->num_axes * sizeof( float ) );
1825 }
1826
1827 /*** END OF FILE ***/