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