Putting in changes recommended by Evan Felix to fix the X11 build per e-mail dated...
[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         int i = 0;\r
1041        unsigned char u;\r
1042 #else\r
1043 #  if defined( __linux__ ) || TARGET_HOST_SOLARIS\r
1044         int i = 0;\r
1045     int counter = 0;\r
1046 #  endif\r
1047 #endif\r
1048 \r
1049 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )\r
1050     for( i = 0; i < _JS_MAX_AXES; i++ )\r
1051         joy->pJoystick.os->cache_axes[ i ] = 0.0f;\r
1052 \r
1053     joy->pJoystick.os->cache_buttons = 0;\r
1054 \r
1055     joy->pJoystick.os->fd = open( joy->pJoystick.os->fname, O_RDONLY | O_NONBLOCK);\r
1056 \r
1057 #ifdef HAVE_ERRNO_H\r
1058     if( joy->pJoystick.os->fd < 0 && errno == EACCES )\r
1059         fgWarning ( "%s exists but is not readable by you", joy->pJoystick.os->fname );\r
1060 #endif\r
1061 \r
1062     joy->error =( joy->pJoystick.os->fd < 0 );\r
1063 \r
1064     if( joy->error )\r
1065         return;\r
1066 \r
1067     joy->num_axes = 0;\r
1068     joy->num_buttons = 0;\r
1069     if( joy->pJoystick.os->is_analog )\r
1070     {\r
1071         FILE *joyfile;\r
1072         char joyfname[ 1024 ];\r
1073         int noargs, in_no_axes;\r
1074 \r
1075         float axes [ _JS_MAX_AXES ];\r
1076         int buttons[ _JS_MAX_AXES ];\r
1077 \r
1078         joy->num_axes    =  2;\r
1079         joy->num_buttons = 32;\r
1080 \r
1081         fghJoystickRawRead( joy, buttons, axes );\r
1082         joy->error = axes[ 0 ] < -1000000000.0f;\r
1083         if( joy->error )\r
1084             return;\r
1085 \r
1086         snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );\r
1087 \r
1088         joyfile = fopen( joyfname, "r" );\r
1089         joy->error =( joyfile == NULL );\r
1090         if( joy->error )\r
1091             return;\r
1092 \r
1093         noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,\r
1094                          &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],\r
1095                          &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );\r
1096         joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;\r
1097         fclose( joyfile );\r
1098         if( joy->error )\r
1099             return;\r
1100 \r
1101         for( i = 0; i < _JS_MAX_AXES; i++ )\r
1102         {\r
1103             joy->dead_band[ i ] = 0.0f;\r
1104             joy->saturate [ i ] = 1.0f;\r
1105         }\r
1106 \r
1107         return;    /* End of analog code */\r
1108     }\r
1109 \r
1110 #    ifdef HAVE_USB_JS\r
1111     if( ! fghJoystickInitializeHID( joy->pJoystick.os, &joy->num_axes,\r
1112                                     &joy->num_buttons ) )\r
1113     {\r
1114         close( joy->pJoystick.os->fd );\r
1115         joy->error = GL_TRUE;\r
1116         return;\r
1117     }\r
1118 \r
1119     cp = strrchr( joy->pJoystick.os->fname, '/' );\r
1120     if( cp )\r
1121     {\r
1122         if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==\r
1123             0 )\r
1124             strcpy( joy->name, &cp[1] );\r
1125     }\r
1126 \r
1127     if( joy->num_axes > _JS_MAX_AXES )\r
1128         joy->num_axes = _JS_MAX_AXES;\r
1129 \r
1130     for( i = 0; i < _JS_MAX_AXES; i++ )\r
1131     {\r
1132         /* We really should get this from the HID, but that data seems\r
1133          * to be quite unreliable for analog-to-USB converters. Punt for\r
1134          * now.\r
1135          */\r
1136         if( joy->pJoystick.os->axes_usage[ i ] == HUG_HAT_SWITCH )\r
1137         {\r
1138             joy->max   [ i ] = 1.0f;\r
1139             joy->center[ i ] = 0.0f;\r
1140             joy->min   [ i ] = -1.0f;\r
1141         }\r
1142         else\r
1143         {\r
1144             joy->max   [ i ] = 255.0f;\r
1145             joy->center[ i ] = 127.0f;\r
1146             joy->min   [ i ] = 0.0f;\r
1147         }\r
1148 \r
1149         joy->dead_band[ i ] = 0.0f;\r
1150         joy->saturate[ i ] = 1.0f;\r
1151     }\r
1152 #    endif\r
1153 #endif\r
1154 \r
1155 #if defined( __linux__ ) || TARGET_HOST_SOLARIS\r
1156     /* Default for older Linux systems. */\r
1157     joy->num_axes    =  2;\r
1158     joy->num_buttons = 32;\r
1159 \r
1160 #    ifdef JS_NEW\r
1161     for( i = 0; i < _JS_MAX_AXES; i++ )\r
1162         joy->pJoystick.tmp_axes[ i ] = 0.0f;\r
1163 \r
1164     joy->pJoystick.tmp_buttons = 0;\r
1165 #    endif\r
1166 \r
1167     joy->pJoystick.fd = open( joy->pJoystick.fname, O_RDONLY );\r
1168 \r
1169     joy->error =( joy->pJoystick.fd < 0 );\r
1170 \r
1171     if( joy->error )\r
1172         return;\r
1173 \r
1174     /* Set the correct number of axes for the linux driver */\r
1175 #    ifdef JS_NEW\r
1176     /* Melchior Franz's fixes for big-endian Linuxes since writing\r
1177      *  to the upper byte of an uninitialized word doesn't work.\r
1178      *  9 April 2003\r
1179      */\r
1180     ioctl( joy->pJoystick.fd, JSIOCGAXES, &u );\r
1181     joy->num_axes = u;\r
1182     ioctl( joy->pJoystick.fd, JSIOCGBUTTONS, &u );\r
1183     joy->num_buttons = u;\r
1184     ioctl( joy->pJoystick.fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );\r
1185     fcntl( joy->pJoystick.fd, F_SETFL, O_NONBLOCK );\r
1186 #    endif\r
1187 \r
1188     /*\r
1189      * The Linux driver seems to return 512 for all axes\r
1190      * when no stick is present - but there is a chance\r
1191      * that could happen by accident - so it's gotta happen\r
1192      * on both axes for at least 100 attempts.\r
1193      *\r
1194      * PWO: shouldn't be that done somehow wiser on the kernel level?\r
1195      */\r
1196 #    ifndef JS_NEW\r
1197     counter = 0;\r
1198 \r
1199     do\r
1200     {\r
1201         fghJoystickRawRead( joy, NULL, joy->center );\r
1202         counter++;\r
1203     } while( !joy->error &&\r
1204              counter < 100 &&\r
1205              joy->center[ 0 ] == 512.0f &&\r
1206              joy->center[ 1 ] == 512.0f );\r
1207 \r
1208     if ( counter >= 100 )\r
1209         joy->error = GL_TRUE;\r
1210 #    endif\r
1211 \r
1212     for( i = 0; i < _JS_MAX_AXES; i++ )\r
1213     {\r
1214 #    ifdef JS_NEW\r
1215         joy->max   [ i ] =  32767.0f;\r
1216         joy->center[ i ] =      0.0f;\r
1217         joy->min   [ i ] = -32767.0f;\r
1218 #    else\r
1219         joy->max[ i ] = joy->center[ i ] * 2.0f;\r
1220         joy->min[ i ] = 0.0f;\r
1221 #    endif\r
1222         joy->dead_band[ i ] = 0.0f;\r
1223         joy->saturate [ i ] = 1.0f;\r
1224     }\r
1225 #endif\r
1226 }\r
1227 \r
1228 \r
1229 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )\r
1230 {\r
1231 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )\r
1232     fgJoystick[ ident ]->id = ident;\r
1233     fgJoystick[ ident ]->error = GL_FALSE;\r
1234 \r
1235     fgJoystick[ ident ]->pJoystick.os = calloc( 1, sizeof( struct os_specific_s ) );\r
1236     memset( fgJoystick[ ident ]->pJoystick.os, 0, sizeof( struct os_specific_s ) );\r
1237     if( ident < USB_IDENT_OFFSET )\r
1238         fgJoystick[ ident ]->pJoystick.os->is_analog = 1;\r
1239     if( fgJoystick[ ident ]->pJoystick.os->is_analog )\r
1240         snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", AJSDEV, ident );\r
1241     else\r
1242         snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", UHIDDEV,\r
1243                  ident - USB_IDENT_OFFSET );\r
1244 #elif defined( __linux__ )\r
1245     fgJoystick[ ident ]->id = ident;\r
1246     fgJoystick[ ident ]->error = GL_FALSE;\r
1247 \r
1248     snprintf( fgJoystick[ident]->pJoystick.fname, sizeof(fgJoystick[ident]->pJoystick.fname), "/dev/input/js%d", ident );\r
1249 \r
1250     if( access( fgJoystick[ ident ]->pJoystick.fname, F_OK ) != 0 )\r
1251         snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d", ident );\r
1252 #endif\r
1253 }\r
1254 \r
1255 \r
1256 void fgPlatformJoystickClose ( int ident )\r
1257 {\r
1258 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )\r
1259     if( fgJoystick[ident]->pJoystick.os )\r
1260     {\r
1261         if( ! fgJoystick[ ident ]->error )\r
1262             close( fgJoystick[ ident ]->pJoystick.os->fd );\r
1263 #ifdef HAVE_USB_JS\r
1264         if( fgJoystick[ ident ]->pJoystick.os->hids )\r
1265             free (fgJoystick[ ident ]->pJoystick.os->hids);\r
1266         if( fgJoystick[ ident ]->pJoystick.os->hid_data_buf )\r
1267             free( fgJoystick[ ident ]->pJoystick.os->hid_data_buf );\r
1268 #endif\r
1269         free( fgJoystick[ident]->pJoystick.os );\r
1270         }\r
1271 #endif\r
1272 \r
1273     if( ! fgJoystick[ident]->error )\r
1274          close( fgJoystick[ ident ]->pJoystick.fd );\r
1275 }\r
1276 #endif\r
1277 \r
1278 \r
1279 \r
1280 \r
1281 \r
1282 \r
1283 static void fghJoystickOpen( SFG_Joystick* joy )\r
1284 {\r
1285     /*\r
1286      * Default values (for no joystick -- each conditional will reset the\r
1287      * error flag)\r
1288      */\r
1289     joy->error = TRUE;\r
1290     joy->num_axes = joy->num_buttons = 0;\r
1291     joy->name[ 0 ] = '\0';\r
1292 \r
1293         fgPlatformJoystickOpen ( joy );\r
1294 \r
1295 }\r
1296 \r
1297 /*\r
1298  * This function replaces the constructor method in the JS library.\r
1299  */\r
1300 static void fghJoystickInit( int ident )\r
1301 {\r
1302     if( ident >= MAX_NUM_JOYSTICKS )\r
1303       fgError( "Too large a joystick number: %d", ident );\r
1304 \r
1305     if( fgJoystick[ ident ] )\r
1306         fgError( "illegal attempt to initialize joystick device again" );\r
1307 \r
1308     fgJoystick[ ident ] =\r
1309         ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );\r
1310 \r
1311     /* Set defaults */\r
1312     fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;\r
1313     fgJoystick[ ident ]->error = GL_TRUE;\r
1314 \r
1315         fgPlatformJoystickInit( fgJoystick, ident );\r
1316 \r
1317     fghJoystickOpen( fgJoystick[ ident  ] );\r
1318 }\r
1319 \r
1320 /*\r
1321  * Try initializing all the joysticks (well, both of them)\r
1322  */\r
1323 void fgInitialiseJoysticks ( void )\r
1324 {\r
1325     if( !fgState.JoysticksInitialised )\r
1326     {\r
1327         int ident ;\r
1328         for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )\r
1329             fghJoystickInit( ident );\r
1330 \r
1331         fgState.JoysticksInitialised = GL_TRUE;\r
1332     }\r
1333 }\r
1334 \r
1335 \r
1336 void fgJoystickClose( void )\r
1337 {\r
1338     int ident ;\r
1339     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )\r
1340     {\r
1341         if( fgJoystick[ ident ] )\r
1342         {\r
1343                         fgPlatformJoystickClose ( ident );\r
1344 \r
1345             free( fgJoystick[ ident ] );\r
1346             fgJoystick[ ident ] = NULL;\r
1347             /* show joystick has been deinitialized */\r
1348         }\r
1349     }\r
1350 }\r
1351 \r
1352 /*\r
1353  * Polls the joystick and executes the joystick callback hooked to the\r
1354  * window specified in the function's parameter:\r
1355  */\r
1356 void fgJoystickPollWindow( SFG_Window* window )\r
1357 {\r
1358     float axes[ _JS_MAX_AXES ];\r
1359     int buttons;\r
1360     int ident;\r
1361 \r
1362     freeglut_return_if_fail( window );\r
1363     freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );\r
1364 \r
1365     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )\r
1366     {\r
1367         if( fgJoystick[ident] )\r
1368         {\r
1369             fghJoystickRead( fgJoystick[ident], &buttons, axes );\r
1370 \r
1371             if( !fgJoystick[ident]->error )\r
1372                 INVOKE_WCB( *window, Joystick,\r
1373                             ( buttons,\r
1374                               (int) ( axes[ 0 ] * 1000.0f ),\r
1375                               (int) ( axes[ 1 ] * 1000.0f ),\r
1376                               (int) ( axes[ 2 ] * 1000.0f ) )\r
1377                 );\r
1378         }\r
1379     }\r
1380 }\r
1381 \r
1382 /*\r
1383  * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)\r
1384  */\r
1385 int fgJoystickDetect( void )\r
1386 {\r
1387     int ident;\r
1388 \r
1389     fgInitialiseJoysticks ();\r
1390 \r
1391     if ( !fgState.JoysticksInitialised )\r
1392         return 0;\r
1393 \r
1394     for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )\r
1395         if( fgJoystick[ident] && !fgJoystick[ident]->error )\r
1396             return 1;\r
1397 \r
1398     return 0;\r
1399 }\r
1400 \r
1401 /*\r
1402  * Joystick information functions\r
1403  */\r
1404 int  glutJoystickGetNumAxes( int ident )\r
1405 {\r
1406     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );\r
1407     return fgJoystick[ ident ]->num_axes;\r
1408 }\r
1409 int  glutJoystickGetNumButtons( int ident )\r
1410 {\r
1411     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );\r
1412     return fgJoystick[ ident ]->num_buttons;\r
1413 }\r
1414 int  glutJoystickNotWorking( int ident )\r
1415 {\r
1416     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );\r
1417     return fgJoystick[ ident ]->error;\r
1418 }\r
1419 \r
1420 float glutJoystickGetDeadBand( int ident, int axis )\r
1421 {\r
1422     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );\r
1423     return fgJoystick[ ident ]->dead_band [ axis ];\r
1424 }\r
1425 void  glutJoystickSetDeadBand( int ident, int axis, float db )\r
1426 {\r
1427     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );\r
1428     fgJoystick[ ident ]->dead_band[ axis ] = db;\r
1429 }\r
1430 \r
1431 float glutJoystickGetSaturation( int ident, int axis )\r
1432 {\r
1433     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );\r
1434     return fgJoystick[ ident ]->saturate[ axis ];\r
1435 }\r
1436 void  glutJoystickSetSaturation( int ident, int axis, float st )\r
1437 {\r
1438     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );\r
1439     fgJoystick[ ident ]->saturate [ axis ] = st;\r
1440 }\r
1441 \r
1442 void glutJoystickSetMinRange( int ident, float *axes )\r
1443 {\r
1444     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );\r
1445     memcpy( fgJoystick[ ident ]->min, axes,\r
1446             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1447 }\r
1448 void glutJoystickSetMaxRange( int ident, float *axes )\r
1449 {\r
1450     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );\r
1451     memcpy( fgJoystick[ ident ]->max, axes,\r
1452             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1453 }\r
1454 void glutJoystickSetCenter( int ident, float *axes )\r
1455 {\r
1456     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );\r
1457     memcpy( fgJoystick[ ident ]->center, axes,\r
1458             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1459 }\r
1460 \r
1461 void glutJoystickGetMinRange( int ident, float *axes )\r
1462 {\r
1463     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );\r
1464     memcpy( axes, fgJoystick[ ident ]->min,\r
1465             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1466 }\r
1467 void glutJoystickGetMaxRange( int ident, float *axes )\r
1468 {\r
1469     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );\r
1470     memcpy( axes, fgJoystick[ ident ]->max,\r
1471             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1472 }\r
1473 void glutJoystickGetCenter( int ident, float *axes )\r
1474 {\r
1475     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );\r
1476     memcpy( axes, fgJoystick[ ident ]->center,\r
1477             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1478 }\r
1479 \r
1480 /*** END OF FILE ***/\r