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