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