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