Splitting the X11-specific "freeglut_joystick.c" code into its own file
[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 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 \r
858 \r
859 \r
860 static void fghJoystickOpen( SFG_Joystick* joy )\r
861 {\r
862     /*\r
863      * Default values (for no joystick -- each conditional will reset the\r
864      * error flag)\r
865      */\r
866     joy->error = TRUE;\r
867     joy->num_axes = joy->num_buttons = 0;\r
868     joy->name[ 0 ] = '\0';\r
869 \r
870         fgPlatformJoystickOpen ( joy );\r
871 \r
872 }\r
873 \r
874 /*\r
875  * This function replaces the constructor method in the JS library.\r
876  */\r
877 static void fghJoystickInit( int ident )\r
878 {\r
879     if( ident >= MAX_NUM_JOYSTICKS )\r
880       fgError( "Too large a joystick number: %d", ident );\r
881 \r
882     if( fgJoystick[ ident ] )\r
883         fgError( "illegal attempt to initialize joystick device again" );\r
884 \r
885     fgJoystick[ ident ] =\r
886         ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );\r
887 \r
888     /* Set defaults */\r
889     fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;\r
890     fgJoystick[ ident ]->error = GL_TRUE;\r
891 \r
892         fgPlatformJoystickInit( fgJoystick, ident );\r
893 \r
894     fghJoystickOpen( fgJoystick[ ident  ] );\r
895 }\r
896 \r
897 /*\r
898  * Try initializing all the joysticks (well, both of them)\r
899  */\r
900 void fgInitialiseJoysticks ( void )\r
901 {\r
902     if( !fgState.JoysticksInitialised )\r
903     {\r
904         int ident ;\r
905         for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )\r
906             fghJoystickInit( ident );\r
907 \r
908         fgState.JoysticksInitialised = GL_TRUE;\r
909     }\r
910 }\r
911 \r
912 \r
913 void fgJoystickClose( void )\r
914 {\r
915     int ident ;\r
916     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )\r
917     {\r
918         if( fgJoystick[ ident ] )\r
919         {\r
920                         fgPlatformJoystickClose ( ident );\r
921 \r
922             free( fgJoystick[ ident ] );\r
923             fgJoystick[ ident ] = NULL;\r
924             /* show joystick has been deinitialized */\r
925         }\r
926     }\r
927 }\r
928 \r
929 /*\r
930  * Polls the joystick and executes the joystick callback hooked to the\r
931  * window specified in the function's parameter:\r
932  */\r
933 void fgJoystickPollWindow( SFG_Window* window )\r
934 {\r
935     float axes[ _JS_MAX_AXES ];\r
936     int buttons;\r
937     int ident;\r
938 \r
939     freeglut_return_if_fail( window );\r
940     freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );\r
941 \r
942     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )\r
943     {\r
944         if( fgJoystick[ident] )\r
945         {\r
946             fghJoystickRead( fgJoystick[ident], &buttons, axes );\r
947 \r
948             if( !fgJoystick[ident]->error )\r
949                 INVOKE_WCB( *window, Joystick,\r
950                             ( buttons,\r
951                               (int) ( axes[ 0 ] * 1000.0f ),\r
952                               (int) ( axes[ 1 ] * 1000.0f ),\r
953                               (int) ( axes[ 2 ] * 1000.0f ) )\r
954                 );\r
955         }\r
956     }\r
957 }\r
958 \r
959 /*\r
960  * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)\r
961  */\r
962 int fgJoystickDetect( void )\r
963 {\r
964     int ident;\r
965 \r
966     fgInitialiseJoysticks ();\r
967 \r
968     if ( !fgState.JoysticksInitialised )\r
969         return 0;\r
970 \r
971     for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )\r
972         if( fgJoystick[ident] && !fgJoystick[ident]->error )\r
973             return 1;\r
974 \r
975     return 0;\r
976 }\r
977 \r
978 /*\r
979  * Joystick information functions\r
980  */\r
981 int  glutJoystickGetNumAxes( int ident )\r
982 {\r
983     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );\r
984     return fgJoystick[ ident ]->num_axes;\r
985 }\r
986 int  glutJoystickGetNumButtons( int ident )\r
987 {\r
988     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );\r
989     return fgJoystick[ ident ]->num_buttons;\r
990 }\r
991 int  glutJoystickNotWorking( int ident )\r
992 {\r
993     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );\r
994     return fgJoystick[ ident ]->error;\r
995 }\r
996 \r
997 float glutJoystickGetDeadBand( int ident, int axis )\r
998 {\r
999     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );\r
1000     return fgJoystick[ ident ]->dead_band [ axis ];\r
1001 }\r
1002 void  glutJoystickSetDeadBand( int ident, int axis, float db )\r
1003 {\r
1004     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );\r
1005     fgJoystick[ ident ]->dead_band[ axis ] = db;\r
1006 }\r
1007 \r
1008 float glutJoystickGetSaturation( int ident, int axis )\r
1009 {\r
1010     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );\r
1011     return fgJoystick[ ident ]->saturate[ axis ];\r
1012 }\r
1013 void  glutJoystickSetSaturation( int ident, int axis, float st )\r
1014 {\r
1015     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );\r
1016     fgJoystick[ ident ]->saturate [ axis ] = st;\r
1017 }\r
1018 \r
1019 void glutJoystickSetMinRange( int ident, float *axes )\r
1020 {\r
1021     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );\r
1022     memcpy( fgJoystick[ ident ]->min, axes,\r
1023             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1024 }\r
1025 void glutJoystickSetMaxRange( int ident, float *axes )\r
1026 {\r
1027     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );\r
1028     memcpy( fgJoystick[ ident ]->max, axes,\r
1029             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1030 }\r
1031 void glutJoystickSetCenter( int ident, float *axes )\r
1032 {\r
1033     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );\r
1034     memcpy( fgJoystick[ ident ]->center, axes,\r
1035             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1036 }\r
1037 \r
1038 void glutJoystickGetMinRange( int ident, float *axes )\r
1039 {\r
1040     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );\r
1041     memcpy( axes, fgJoystick[ ident ]->min,\r
1042             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1043 }\r
1044 void glutJoystickGetMaxRange( int ident, float *axes )\r
1045 {\r
1046     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );\r
1047     memcpy( axes, fgJoystick[ ident ]->max,\r
1048             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1049 }\r
1050 void glutJoystickGetCenter( int ident, float *axes )\r
1051 {\r
1052     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );\r
1053     memcpy( axes, fgJoystick[ ident ]->center,\r
1054             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1055 }\r
1056 \r
1057 /*** END OF FILE ***/\r