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