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