962b0f179c646076cc44c9aa676d8fea4307d79b
[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 fgPlatformJoystickOpen( SFG_Joystick* joy );\r
392 extern void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident );\r
393 extern void fgPlatformJoystickClose ( int ident );\r
394 \r
395 /*\r
396  * The static joystick structure pointer\r
397  */\r
398 #define MAX_NUM_JOYSTICKS  2\r
399 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];\r
400 \r
401 /*\r
402  * Read the raw joystick data\r
403  */\r
404 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )\r
405 {\r
406 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)\r
407     MMRESULT status;\r
408 #else\r
409     int status;\r
410 #endif\r
411 \r
412 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)\r
413     int len;\r
414 #endif\r
415 \r
416     int i;\r
417 \r
418     /* Defaults */\r
419     if( buttons )\r
420         *buttons = 0;\r
421 \r
422     if( axes )\r
423         for( i = 0; i < joy->num_axes; i++ )\r
424             axes[ i ] = 1500.0f;\r
425 \r
426     if( joy->error )\r
427         return;\r
428 \r
429 #if TARGET_HOST_MACINTOSH\r
430     if ( buttons )\r
431     {\r
432         *buttons = 0;\r
433 \r
434         for ( i = 0; i < joy->num_buttons; i++ )\r
435         {\r
436             UInt32 state;\r
437             int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);\r
438             ISP_CHECK_ERR(err)\r
439 \r
440             *buttons |= state << i;\r
441         }\r
442     }\r
443 \r
444     if ( axes )\r
445     {\r
446         for ( i = 0; i < joy->num_axes; i++ )\r
447         {\r
448             UInt32 state;\r
449             int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );\r
450             ISP_CHECK_ERR(err)\r
451 \r
452             axes [i] = (float) state;\r
453         }\r
454     }\r
455 #endif\r
456 \r
457 #if TARGET_HOST_MAC_OSX\r
458     if ( buttons != NULL )\r
459     {\r
460         *buttons = 0;\r
461 \r
462         for ( i = 0; i < joy->num_buttons; i++ )\r
463         {\r
464             IOHIDEventStruct hidEvent;\r
465             (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );\r
466             if ( hidEvent.value )\r
467                 *buttons |= 1 << i;\r
468         }\r
469     }\r
470 \r
471     if ( axes != NULL )\r
472     {\r
473         for ( i = 0; i < joy->num_axes; i++ )\r
474         {\r
475             IOHIDEventStruct hidEvent;\r
476             (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );\r
477             axes[i] = hidEvent.value;\r
478         }\r
479     }\r
480 #endif\r
481 \r
482 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)\r
483     status = joyGetPosEx( joy->js_id, &joy->js );\r
484 \r
485     if ( status != JOYERR_NOERROR )\r
486     {\r
487         joy->error = GL_TRUE;\r
488         return;\r
489     }\r
490 \r
491     if ( buttons )\r
492         *buttons = joy->js.dwButtons;\r
493 \r
494     if ( axes )\r
495     {\r
496         /*\r
497          * WARNING - Fall through case clauses!!\r
498          */\r
499         switch ( joy->num_axes )\r
500         {\r
501         case 8:\r
502             /* Generate two POV axes from the POV hat angle.\r
503              * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in\r
504              *   hundredths of a degree, or 0xFFFF when idle.\r
505              */\r
506             if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )\r
507             {\r
508               axes [ 6 ] = 0.0;\r
509               axes [ 7 ] = 0.0;\r
510             }\r
511             else\r
512             {\r
513               /* This is the contentious bit: how to convert angle to X/Y.\r
514                *    wk: I know of no define for PI that we could use here:\r
515                *    SG_PI would pull in sg, M_PI is undefined for MSVC\r
516                * But the accuracy of the value of PI is very unimportant at\r
517                * this point.\r
518                */\r
519               float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );\r
520               float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );\r
521 \r
522               /* Convert to coordinates on a square so that North-East\r
523                * is (1,1) not (.7,.7), etc.\r
524                * s and c cannot both be zero so we won't divide by zero.\r
525                */\r
526               if ( fabs ( s ) < fabs ( c ) )\r
527               {\r
528                 axes [ 6 ] = ( c < 0.0 ) ? -s/c  : s/c ;\r
529                 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;\r
530               }\r
531               else\r
532               {\r
533                 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;\r
534                 axes [ 7 ] = ( s < 0.0 ) ? -c/s  : c/s ;\r
535               }\r
536             }\r
537 \r
538         case 6: axes[5] = (float) joy->js.dwVpos;\r
539         case 5: axes[4] = (float) joy->js.dwUpos;\r
540         case 4: axes[3] = (float) joy->js.dwRpos;\r
541         case 3: axes[2] = (float) joy->js.dwZpos;\r
542         case 2: axes[1] = (float) joy->js.dwYpos;\r
543         case 1: axes[0] = (float) joy->js.dwXpos;\r
544         }\r
545     }\r
546 #endif\r
547 \r
548 #if TARGET_HOST_POSIX_X11\r
549 #    if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)\r
550     if ( joy->os->is_analog )\r
551     {\r
552         int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );\r
553         if ( status != sizeof(joy->os->ajs) ) {\r
554             perror ( joy->os->fname );\r
555             joy->error = GL_TRUE;\r
556             return;\r
557         }\r
558         if ( buttons != NULL )\r
559             *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );\r
560 \r
561         if ( axes != NULL )\r
562         {\r
563             axes[0] = (float) joy->os->ajs.x;\r
564             axes[1] = (float) joy->os->ajs.y;\r
565         }\r
566 \r
567         return;\r
568     }\r
569 \r
570 #        ifdef HAVE_USB_JS\r
571     while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )\r
572     {\r
573         struct hid_item *h;\r
574 \r
575         for  ( h = joy->os->hids; h; h = h->next )\r
576         {\r
577             int d = hid_get_data ( joy->os->hid_data_buf, h );\r
578 \r
579             int page = HID_PAGE ( h->usage );\r
580             int usage = HID_USAGE ( h->usage );\r
581 \r
582             if ( page == HUP_GENERIC_DESKTOP )\r
583             {\r
584                 int i;\r
585                 for ( i = 0; i < joy->num_axes; i++ )\r
586                     if (joy->os->axes_usage[i] == usage)\r
587                     {\r
588                         if (usage == HUG_HAT_SWITCH)\r
589                         {\r
590                             if (d < 0 || d > 8)\r
591                                 d = 0;  /* safety */\r
592                             joy->os->cache_axes[i] = (float)hatmap_x[d];\r
593                             joy->os->cache_axes[i + 1] = (float)hatmap_y[d];\r
594                         }\r
595                         else\r
596                         {\r
597                             joy->os->cache_axes[i] = (float)d;\r
598                         }\r
599                         break;\r
600                     }\r
601             }\r
602             else if (page == HUP_BUTTON)\r
603             {\r
604                if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)\r
605                {\r
606                    if (d)\r
607                        joy->os->cache_buttons |=  (1 << ( usage - 1 ));\r
608                    else\r
609                        joy->os->cache_buttons &= ~(1 << ( usage - 1 ));\r
610                }\r
611             }\r
612         }\r
613     }\r
614 #ifdef HAVE_ERRNO_H\r
615     if ( len < 0 && errno != EAGAIN )\r
616 #else\r
617     if ( len < 0 )\r
618 #endif\r
619     {\r
620         perror( joy->os->fname );\r
621         joy->error = 1;\r
622     }\r
623     if ( buttons != NULL ) *buttons = joy->os->cache_buttons;\r
624     if ( axes    != NULL )\r
625         memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );\r
626 #        endif\r
627 #    endif\r
628 \r
629 #    ifdef JS_NEW\r
630 \r
631     while ( 1 )\r
632     {\r
633         status = read ( joy->fd, &joy->js, sizeof(struct js_event) );\r
634 \r
635         if ( status != sizeof( struct js_event ) )\r
636         {\r
637 #ifdef HAVE_ERRNO_H\r
638             if ( errno == EAGAIN )\r
639             {\r
640                 /* Use the old values */\r
641                 if ( buttons )\r
642                     *buttons = joy->tmp_buttons;\r
643                 if ( axes )\r
644                     memcpy( axes, joy->tmp_axes,\r
645                             sizeof( float ) * joy->num_axes );\r
646                 return;\r
647             }\r
648 #endif\r
649 \r
650             fgWarning ( "%s", joy->fname );\r
651             joy->error = GL_TRUE;\r
652             return;\r
653         }\r
654 \r
655         switch ( joy->js.type & ~JS_EVENT_INIT )\r
656         {\r
657         case JS_EVENT_BUTTON:\r
658             if( joy->js.value == 0 ) /* clear the flag */\r
659                 joy->tmp_buttons &= ~( 1 << joy->js.number );\r
660             else\r
661                 joy->tmp_buttons |= ( 1 << joy->js.number );\r
662             break;\r
663 \r
664         case JS_EVENT_AXIS:\r
665             if ( joy->js.number < joy->num_axes )\r
666             {\r
667                 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;\r
668 \r
669                 if( axes )\r
670                     memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );\r
671             }\r
672             break;\r
673 \r
674         default:\r
675             fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );\r
676 \r
677             /* use the old values */\r
678 \r
679             if ( buttons != NULL ) *buttons = joy->tmp_buttons;\r
680             if ( axes    != NULL )\r
681                 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );\r
682 \r
683             return;\r
684         }\r
685 \r
686         if( buttons )\r
687             *buttons = joy->tmp_buttons;\r
688     }\r
689 #    else\r
690 \r
691     status = read( joy->fd, &joy->js, JS_RETURN );\r
692 \r
693     if ( status != JS_RETURN )\r
694     {\r
695         fgWarning( "%s", joy->fname );\r
696         joy->error = GL_TRUE;\r
697         return;\r
698     }\r
699 \r
700     if ( buttons )\r
701 #        if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )\r
702         *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 );  /* XXX Should not be here -- BSD is handled earlier */\r
703 #        else\r
704         *buttons = joy->js.buttons;\r
705 #        endif\r
706 \r
707     if ( axes )\r
708     {\r
709         axes[ 0 ] = (float) joy->js.x;\r
710         axes[ 1 ] = (float) joy->js.y;\r
711     }\r
712 #    endif\r
713 #endif\r
714 }\r
715 \r
716 /*\r
717  * Correct the joystick axis data\r
718  */\r
719 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )\r
720 {\r
721     if( value < joy->center[ axis ] )\r
722     {\r
723         float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -\r
724                                                        joy->min[ axis ] );\r
725 \r
726         if( xx < -joy->saturate[ axis ] )\r
727             return -1.0f;\r
728 \r
729         if( xx > -joy->dead_band [ axis ] )\r
730             return 0.0f;\r
731 \r
732         xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -\r
733                                                  joy->dead_band[ axis ] );\r
734 \r
735         return ( xx < -1.0f ) ? -1.0f : xx;\r
736     }\r
737     else\r
738     {\r
739         float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -\r
740                                                         joy->center[ axis ] );\r
741 \r
742         if( xx > joy->saturate[ axis ] )\r
743             return 1.0f;\r
744 \r
745         if( xx < joy->dead_band[ axis ] )\r
746             return 0.0f;\r
747 \r
748         xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -\r
749                                                  joy->dead_band[ axis ] );\r
750 \r
751         return ( xx > 1.0f ) ? 1.0f : xx;\r
752     }\r
753 }\r
754 \r
755 /*\r
756  * Read the corrected joystick data\r
757  */\r
758 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )\r
759 {\r
760     float raw_axes[ _JS_MAX_AXES ];\r
761     int  i;\r
762 \r
763     if( joy->error )\r
764     {\r
765         if( buttons )\r
766             *buttons = 0;\r
767 \r
768         if( axes )\r
769             for ( i=0; i<joy->num_axes; i++ )\r
770                 axes[ i ] = 0.0f;\r
771     }\r
772 \r
773     fghJoystickRawRead( joy, buttons, raw_axes );\r
774 \r
775     if( axes )\r
776         for( i=0; i<joy->num_axes; i++ )\r
777             axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );\r
778 }\r
779 \r
780 /*\r
781  * Happy happy happy joy joy joy (happy new year toudi :D)\r
782  */\r
783 \r
784 \r
785 #if TARGET_HOST_MAC_OSX\r
786 /** open the IOKit connection, enumerate all the HID devices, add their\r
787 interface references to the static array. We then use the array index\r
788 as the device number when we come to open() the joystick. */\r
789 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )\r
790 {\r
791     CFMutableDictionaryRef hidMatch = NULL;\r
792     IOReturn rv = kIOReturnSuccess;\r
793 \r
794     io_iterator_t hidIterator;\r
795     io_object_t ioDev;\r
796 \r
797     /* build a dictionary matching HID devices */\r
798     hidMatch = IOServiceMatching(kIOHIDDeviceKey);\r
799 \r
800     rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);\r
801     if (rv != kIOReturnSuccess || !hidIterator) {\r
802       fgWarning( "no joystick (HID) devices found" );\r
803       return;\r
804     }\r
805 \r
806     /* iterate */\r
807     while ((ioDev = IOIteratorNext(hidIterator))) {\r
808         /* filter out keyboard and mouse devices */\r
809         CFDictionaryRef properties = getCFProperties(ioDev);\r
810         long usage, page;\r
811 \r
812         CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));\r
813         CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));\r
814         CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);\r
815         CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);\r
816 \r
817         /* keep only joystick devices */\r
818         if ( ( page == kHIDPage_GenericDesktop ) && (\r
819                             (usage == kHIDUsage_GD_Joystick)\r
820                          || (usage == kHIDUsage_GD_GamePad)\r
821                          || (usage == kHIDUsage_GD_MultiAxisController)\r
822                          || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */\r
823             /* add it to the array */\r
824             ioDevices[numDevices++] = ioDev;\r
825     }\r
826 \r
827     IOObjectRelease(hidIterator);\r
828 }\r
829 \r
830 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )\r
831 {\r
832     IOReturn rv;\r
833     CFMutableDictionaryRef cfProperties;\r
834 \r
835 #if 0\r
836     /* comment copied from darwin/SDL_sysjoystick.c */\r
837     /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also\r
838      * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties\r
839      */\r
840 \r
841     io_registry_entry_t parent1, parent2;\r
842 \r
843     rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);\r
844     if (rv != kIOReturnSuccess) {\r
845         fgWarning ( "error getting device entry parent");\r
846         return NULL;\r
847     }\r
848 \r
849     rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);\r
850     if (rv != kIOReturnSuccess) {\r
851         fgWarning ( "error getting device entry parent 2");\r
852         return NULL;\r
853     }\r
854 #endif\r
855 \r
856     rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,\r
857         &cfProperties, kCFAllocatorDefault, kNilOptions);\r
858     if (rv != kIOReturnSuccess || !cfProperties) {\r
859         fgWarning ( "error getting device properties");\r
860         return NULL;\r
861     }\r
862 \r
863     return cfProperties;\r
864 }\r
865 \r
866 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )\r
867 {\r
868       if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {\r
869             fgError ( "%s", "element enumerator passed non-dictionary value");\r
870             return;\r
871     }\r
872 \r
873       static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );\r
874 }\r
875 \r
876 /** element enumerator function : pass NULL for top-level*/\r
877 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )\r
878 {\r
879       FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),\r
880                                     "Joystick element type mismatch",\r
881                                     "fghJoystickEnumerateElements" );\r
882 \r
883       CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};\r
884       CFArrayApplyFunction((CFArrayRef) element, range,\r
885             &fghJoystickElementEnumerator, joy );\r
886 }\r
887 \r
888 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )\r
889 {\r
890     long cookie, lmin, lmax;\r
891     int index = joy->num_axes++;\r
892 \r
893     CFNumberGetValue ((CFNumberRef)\r
894         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),\r
895         kCFNumberLongType, &cookie);\r
896 \r
897     axisCookies[index] = (IOHIDElementCookie) cookie;\r
898 \r
899     CFNumberGetValue ((CFNumberRef)\r
900         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),\r
901         kCFNumberLongType, &lmin);\r
902 \r
903     CFNumberGetValue ((CFNumberRef)\r
904         CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),\r
905         kCFNumberLongType, &lmax);\r
906 \r
907     joy->min[index] = lmin;\r
908     joy->max[index] = lmax;\r
909     joy->dead_band[index] = 0.0;\r
910     joy->saturate[index] = 1.0;\r
911     joy->center[index] = (lmax + lmin) * 0.5;\r
912 }\r
913 \r
914 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )\r
915 {\r
916     long cookie;\r
917     CFNumberGetValue ((CFNumberRef)\r
918             CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),\r
919             kCFNumberLongType, &cookie);\r
920 \r
921     joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;\r
922     /* anything else for buttons? */\r
923 }\r
924 \r
925 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )\r
926 {\r
927     /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */\r
928     /* do we map hats to axes or buttons? */\r
929 }\r
930 #endif\r
931 \r
932 /*\r
933  *  Platform-Specific Code\r
934  */\r
935 \r
936 #if TARGET_HOST_MACINTOSH\r
937 void fgPlatformJoystickOpen( SFG_Joystick* joy )\r
938 {\r
939         int i = 0;\r
940     OSStatus err;\r
941 \r
942     /* XXX FIXME: get joystick name in Mac */\r
943 \r
944     err = ISpStartup( );\r
945 \r
946     if( err == noErr )\r
947     {\r
948 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }\r
949 \r
950         joy->error = GL_TRUE;\r
951 \r
952         /* initialize the needs structure */\r
953         ISpNeed temp_isp_needs[ isp_num_needs ] =\r
954         {\r
955           { "\pX-Axis",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },\r
956           { "\pY-Axis",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },\r
957           { "\pZ-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },\r
958           { "\pR-Axis",    128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },\r
959           { "\pAxis   4",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },\r
960           { "\pAxis   5",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },\r
961           { "\pAxis   6",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },\r
962           { "\pAxis   7",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },\r
963           { "\pAxis   8",  128, 0, 0, kISpElementKind_Axis,   kISpElementLabel_None, 0, 0, 0, 0 },\r
964 \r
965           { "\pButton 0",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
966           { "\pButton 1",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
967           { "\pButton 2",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
968           { "\pButton 3",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
969           { "\pButton 4",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
970           { "\pButton 5",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
971           { "\pButton 6",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
972           { "\pButton 7",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
973           { "\pButton 8",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
974           { "\pButton 9",  128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
975           { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
976           { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
977           { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
978           { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
979           { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
980           { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
981           { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
982           { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
983           { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
984           { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
985           { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
986           { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
987           { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
988           { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
989           { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
990           { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
991           { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
992           { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
993           { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
994           { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
995           { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
996           { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },\r
997         };\r
998 \r
999         memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );\r
1000 \r
1001 \r
1002         /* next two calls allow keyboard and mouse to emulate other input\r
1003          * devices (gamepads, joysticks, etc)\r
1004          */\r
1005         /*\r
1006           err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );\r
1007           ISP_CHECK_ERR(err)\r
1008 \r
1009 \r
1010           err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );\r
1011           ISP_CHECK_ERR(err)\r
1012         */\r
1013 \r
1014         err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,\r
1015                                               joy->isp_needs, joy->isp_elem,\r
1016                                               0 );\r
1017         ISP_CHECK_ERR( err )\r
1018 \r
1019         err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,\r
1020                        'freeglut', nil, 0, 128, 0 );\r
1021         ISP_CHECK_ERR( err )\r
1022 \r
1023         joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;\r
1024         joy->num_axes    = joy->isp_num_axis;\r
1025 \r
1026         for( i = 0; i < joy->num_axes; i++ )\r
1027         {\r
1028             joy->dead_band[ i ] = 0;\r
1029             joy->saturate [ i ] = 1;\r
1030             joy->center   [ i ] = kISpAxisMiddle;\r
1031             joy->max      [ i ] = kISpAxisMaximum;\r
1032             joy->min      [ i ] = kISpAxisMinimum;\r
1033         }\r
1034 \r
1035         joy->error = GL_FALSE;\r
1036     }\r
1037     else\r
1038         joy->num_buttons = joy->num_axes = 0;\r
1039 }\r
1040 \r
1041 \r
1042 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )\r
1043 {\r
1044     fgJoystick[ ident ]->id = ident;\r
1045     snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident ); /* FIXME */\r
1046     fgJoystick[ ident ]->error = GL_FALSE;\r
1047 }\r
1048 \r
1049 \r
1050 void fgPlatformJoystickClose ( int ident )\r
1051 {\r
1052     ISpSuspend( );\r
1053     ISpStop( );\r
1054     ISpShutdown( );\r
1055 }\r
1056 #endif\r
1057 \r
1058 #if TARGET_HOST_MAC_OSX\r
1059 void fgPlatformJoystickOpen( SFG_Joystick* joy )\r
1060 {\r
1061     IOReturn rv;\r
1062     SInt32 score;\r
1063     IOCFPlugInInterface **plugin;\r
1064 \r
1065     HRESULT pluginResult;\r
1066 \r
1067     CFDictionaryRef props;\r
1068     CFTypeRef topLevelElement;\r
1069 \r
1070     if( joy->id >= numDevices )\r
1071     {\r
1072         fgWarning( "device index out of range in fgJoystickOpen()" );\r
1073         return;\r
1074     }\r
1075 \r
1076     /* create device interface */\r
1077     rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],\r
1078                                             kIOHIDDeviceUserClientTypeID,\r
1079                                             kIOCFPlugInInterfaceID,\r
1080                                             &plugin, &score );\r
1081 \r
1082     if( rv != kIOReturnSuccess )\r
1083     {\r
1084         fgWarning( "error creating plugin for io device" );\r
1085         return;\r
1086     }\r
1087 \r
1088     pluginResult = ( *plugin )->QueryInterface(\r
1089         plugin,\r
1090         CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),\r
1091         &( LPVOID )joy->hidDev\r
1092     );\r
1093 \r
1094     if( pluginResult != S_OK )\r
1095         fgWarning ( "QI-ing IO plugin to HID Device interface failed" );\r
1096 \r
1097     ( *plugin )->Release( plugin ); /* don't leak a ref */\r
1098     if( joy->hidDev == NULL )\r
1099         return;\r
1100 \r
1101     /* store the interface in this instance */\r
1102     rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );\r
1103     if( rv != kIOReturnSuccess )\r
1104     {\r
1105         fgWarning( "error opening device interface");\r
1106         return;\r
1107     }\r
1108 \r
1109     props = getCFProperties( ioDevices[ joy->id ] );\r
1110 \r
1111     /* recursively enumerate all the bits */\r
1112     CFTypeRef topLevelElement =\r
1113         CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );\r
1114     enumerateElements( topLevelElement );\r
1115 \r
1116     CFRelease( props );\r
1117 }\r
1118 \r
1119 \r
1120 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )\r
1121 {\r
1122     fgJoystick[ ident ]->id = ident;\r
1123     fgJoystick[ ident ]->error = GL_FALSE;\r
1124     fgJoystick[ ident ]->num_axes = 0;\r
1125     fgJoystick[ ident ]->num_buttons = 0;\r
1126 \r
1127     if( numDevices < 0 )\r
1128     {\r
1129         /* do first-time init (since we can't over-ride jsInit, hmm */\r
1130         numDevices = 0;\r
1131 \r
1132         mach_port_t masterPort;\r
1133         IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );\r
1134         if( rv != kIOReturnSuccess )\r
1135         {\r
1136             fgWarning( "error getting master Mach port" );\r
1137             return;\r
1138         }\r
1139         fghJoystickFindDevices( masterPort );\r
1140     }\r
1141 \r
1142     if ( ident >= numDevices )\r
1143     {\r
1144         fgJoystick[ ident ]->error = GL_TRUE;\r
1145         return;\r
1146     }\r
1147 \r
1148     /* get the name now too */\r
1149     CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );\r
1150     CFTypeRef ref = CFDictionaryGetValue( properties,\r
1151                                           CFSTR( kIOHIDProductKey ) );\r
1152     if (!ref)\r
1153         ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );\r
1154 \r
1155     if( !ref ||\r
1156         !CFStringGetCString( ( CFStringRef )ref, name, 128,\r
1157                              CFStringGetSystemEncoding( ) ) )\r
1158     {\r
1159         fgWarning( "error getting device name" );\r
1160         name[ 0 ] = '\0';\r
1161     }\r
1162 }\r
1163 \r
1164 \r
1165 void fgPlatformJoystickClose ( int ident )\r
1166 {\r
1167     ( *( fgJoystick[ ident ]->hidDev ) )->\r
1168         close( fgJoystick[ ident ]->hidDev );\r
1169 }\r
1170 #endif\r
1171 \r
1172 #if TARGET_HOST_POSIX_X11\r
1173 void fgPlatformJoystickOpen( SFG_Joystick* joy )\r
1174 {\r
1175 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )\r
1176         int i = 0;\r
1177        char *cp;\r
1178 #endif\r
1179 #ifdef JS_NEW\r
1180        unsigned char u;\r
1181 #else\r
1182 #  if defined( __linux__ ) || TARGET_HOST_SOLARIS\r
1183         int i = 0;\r
1184     int counter = 0;\r
1185 #  endif\r
1186 #endif\r
1187 \r
1188 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )\r
1189     for( i = 0; i < _JS_MAX_AXES; i++ )\r
1190         joy->os->cache_axes[ i ] = 0.0f;\r
1191 \r
1192     joy->os->cache_buttons = 0;\r
1193 \r
1194     joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);\r
1195 \r
1196 #ifdef HAVE_ERRNO_H\r
1197     if( joy->os->fd < 0 && errno == EACCES )\r
1198         fgWarning ( "%s exists but is not readable by you", joy->os->fname );\r
1199 #endif\r
1200 \r
1201     joy->error =( joy->os->fd < 0 );\r
1202 \r
1203     if( joy->error )\r
1204         return;\r
1205 \r
1206     joy->num_axes = 0;\r
1207     joy->num_buttons = 0;\r
1208     if( joy->os->is_analog )\r
1209     {\r
1210         FILE *joyfile;\r
1211         char joyfname[ 1024 ];\r
1212         int noargs, in_no_axes;\r
1213 \r
1214         float axes [ _JS_MAX_AXES ];\r
1215         int buttons[ _JS_MAX_AXES ];\r
1216 \r
1217         joy->num_axes    =  2;\r
1218         joy->num_buttons = 32;\r
1219 \r
1220         fghJoystickRawRead( joy, buttons, axes );\r
1221         joy->error = axes[ 0 ] < -1000000000.0f;\r
1222         if( joy->error )\r
1223             return;\r
1224 \r
1225         snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );\r
1226 \r
1227         joyfile = fopen( joyfname, "r" );\r
1228         joy->error =( joyfile == NULL );\r
1229         if( joy->error )\r
1230             return;\r
1231 \r
1232         noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,\r
1233                          &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],\r
1234                          &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );\r
1235         joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;\r
1236         fclose( joyfile );\r
1237         if( joy->error )\r
1238             return;\r
1239 \r
1240         for( i = 0; i < _JS_MAX_AXES; i++ )\r
1241         {\r
1242             joy->dead_band[ i ] = 0.0f;\r
1243             joy->saturate [ i ] = 1.0f;\r
1244         }\r
1245 \r
1246         return;    /* End of analog code */\r
1247     }\r
1248 \r
1249 #    ifdef HAVE_USB_JS\r
1250     if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,\r
1251                                     &joy->num_buttons ) )\r
1252     {\r
1253         close( joy->os->fd );\r
1254         joy->error = GL_TRUE;\r
1255         return;\r
1256     }\r
1257 \r
1258     cp = strrchr( joy->os->fname, '/' );\r
1259     if( cp )\r
1260     {\r
1261         if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==\r
1262             0 )\r
1263             strcpy( joy->name, &cp[1] );\r
1264     }\r
1265 \r
1266     if( joy->num_axes > _JS_MAX_AXES )\r
1267         joy->num_axes = _JS_MAX_AXES;\r
1268 \r
1269     for( i = 0; i < _JS_MAX_AXES; i++ )\r
1270     {\r
1271         /* We really should get this from the HID, but that data seems\r
1272          * to be quite unreliable for analog-to-USB converters. Punt for\r
1273          * now.\r
1274          */\r
1275         if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )\r
1276         {\r
1277             joy->max   [ i ] = 1.0f;\r
1278             joy->center[ i ] = 0.0f;\r
1279             joy->min   [ i ] = -1.0f;\r
1280         }\r
1281         else\r
1282         {\r
1283             joy->max   [ i ] = 255.0f;\r
1284             joy->center[ i ] = 127.0f;\r
1285             joy->min   [ i ] = 0.0f;\r
1286         }\r
1287 \r
1288         joy->dead_band[ i ] = 0.0f;\r
1289         joy->saturate[ i ] = 1.0f;\r
1290     }\r
1291 #    endif\r
1292 #endif\r
1293 \r
1294 #if defined( __linux__ ) || TARGET_HOST_SOLARIS\r
1295     /* Default for older Linux systems. */\r
1296     joy->num_axes    =  2;\r
1297     joy->num_buttons = 32;\r
1298 \r
1299 #    ifdef JS_NEW\r
1300     for( i = 0; i < _JS_MAX_AXES; i++ )\r
1301         joy->tmp_axes[ i ] = 0.0f;\r
1302 \r
1303     joy->tmp_buttons = 0;\r
1304 #    endif\r
1305 \r
1306     joy->fd = open( joy->fname, O_RDONLY );\r
1307 \r
1308     joy->error =( joy->fd < 0 );\r
1309 \r
1310     if( joy->error )\r
1311         return;\r
1312 \r
1313     /* Set the correct number of axes for the linux driver */\r
1314 #    ifdef JS_NEW\r
1315     /* Melchior Franz's fixes for big-endian Linuxes since writing\r
1316      *  to the upper byte of an uninitialized word doesn't work.\r
1317      *  9 April 2003\r
1318      */\r
1319     ioctl( joy->fd, JSIOCGAXES, &u );\r
1320     joy->num_axes = u;\r
1321     ioctl( joy->fd, JSIOCGBUTTONS, &u );\r
1322     joy->num_buttons = u;\r
1323     ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );\r
1324     fcntl( joy->fd, F_SETFL, O_NONBLOCK );\r
1325 #    endif\r
1326 \r
1327     /*\r
1328      * The Linux driver seems to return 512 for all axes\r
1329      * when no stick is present - but there is a chance\r
1330      * that could happen by accident - so it's gotta happen\r
1331      * on both axes for at least 100 attempts.\r
1332      *\r
1333      * PWO: shouldn't be that done somehow wiser on the kernel level?\r
1334      */\r
1335 #    ifndef JS_NEW\r
1336     counter = 0;\r
1337 \r
1338     do\r
1339     {\r
1340         fghJoystickRawRead( joy, NULL, joy->center );\r
1341         counter++;\r
1342     } while( !joy->error &&\r
1343              counter < 100 &&\r
1344              joy->center[ 0 ] == 512.0f &&\r
1345              joy->center[ 1 ] == 512.0f );\r
1346 \r
1347     if ( counter >= 100 )\r
1348         joy->error = GL_TRUE;\r
1349 #    endif\r
1350 \r
1351     for( i = 0; i < _JS_MAX_AXES; i++ )\r
1352     {\r
1353 #    ifdef JS_NEW\r
1354         joy->max   [ i ] =  32767.0f;\r
1355         joy->center[ i ] =      0.0f;\r
1356         joy->min   [ i ] = -32767.0f;\r
1357 #    else\r
1358         joy->max[ i ] = joy->center[ i ] * 2.0f;\r
1359         joy->min[ i ] = 0.0f;\r
1360 #    endif\r
1361         joy->dead_band[ i ] = 0.0f;\r
1362         joy->saturate [ i ] = 1.0f;\r
1363     }\r
1364 #endif\r
1365 }\r
1366 \r
1367 \r
1368 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )\r
1369 {\r
1370 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )\r
1371     fgJoystick[ ident ]->id = ident;\r
1372     fgJoystick[ ident ]->error = GL_FALSE;\r
1373 \r
1374     fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );\r
1375     memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );\r
1376     if( ident < USB_IDENT_OFFSET )\r
1377         fgJoystick[ ident ]->os->is_analog = 1;\r
1378     if( fgJoystick[ ident ]->os->is_analog )\r
1379         snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", AJSDEV, ident );\r
1380     else\r
1381         snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", UHIDDEV,\r
1382                  ident - USB_IDENT_OFFSET );\r
1383 #elif defined( __linux__ )\r
1384     fgJoystick[ ident ]->id = ident;\r
1385     fgJoystick[ ident ]->error = GL_FALSE;\r
1386 \r
1387     snprintf( fgJoystick[ident]->fname, sizeof(fgJoystick[ident]->fname), "/dev/input/js%d", ident );\r
1388 \r
1389     if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )\r
1390         snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident );\r
1391 #endif\r
1392 }\r
1393 \r
1394 \r
1395 void fgPlatformJoystickClose ( int ident )\r
1396 {\r
1397 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )\r
1398     if( fgJoystick[ident]->os )\r
1399     {\r
1400         if( ! fgJoystick[ ident ]->error )\r
1401             close( fgJoystick[ ident ]->os->fd );\r
1402 #ifdef HAVE_USB_JS\r
1403         if( fgJoystick[ ident ]->os->hids )\r
1404             free (fgJoystick[ ident ]->os->hids);\r
1405         if( fgJoystick[ ident ]->os->hid_data_buf )\r
1406             free( fgJoystick[ ident ]->os->hid_data_buf );\r
1407 #endif\r
1408         free( fgJoystick[ident]->os );\r
1409         }\r
1410 #endif\r
1411 \r
1412     if( ! fgJoystick[ident]->error )\r
1413          close( fgJoystick[ ident ]->fd );\r
1414 }\r
1415 #endif\r
1416 \r
1417 \r
1418 \r
1419 \r
1420 \r
1421 \r
1422 static void fghJoystickOpen( SFG_Joystick* joy )\r
1423 {\r
1424     /*\r
1425      * Default values (for no joystick -- each conditional will reset the\r
1426      * error flag)\r
1427      */\r
1428     joy->error = TRUE;\r
1429     joy->num_axes = joy->num_buttons = 0;\r
1430     joy->name[ 0 ] = '\0';\r
1431 \r
1432         fgPlatformJoystickOpen ( joy );\r
1433 \r
1434 }\r
1435 \r
1436 /*\r
1437  * This function replaces the constructor method in the JS library.\r
1438  */\r
1439 static void fghJoystickInit( int ident )\r
1440 {\r
1441     if( ident >= MAX_NUM_JOYSTICKS )\r
1442       fgError( "Too large a joystick number: %d", ident );\r
1443 \r
1444     if( fgJoystick[ ident ] )\r
1445         fgError( "illegal attempt to initialize joystick device again" );\r
1446 \r
1447     fgJoystick[ ident ] =\r
1448         ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );\r
1449 \r
1450     /* Set defaults */\r
1451     fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;\r
1452     fgJoystick[ ident ]->error = GL_TRUE;\r
1453 \r
1454         fgPlatformJoystickInit( fgJoystick, ident );\r
1455 \r
1456     fghJoystickOpen( fgJoystick[ ident  ] );\r
1457 }\r
1458 \r
1459 /*\r
1460  * Try initializing all the joysticks (well, both of them)\r
1461  */\r
1462 void fgInitialiseJoysticks ( void )\r
1463 {\r
1464     if( !fgState.JoysticksInitialised )\r
1465     {\r
1466         int ident ;\r
1467         for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )\r
1468             fghJoystickInit( ident );\r
1469 \r
1470         fgState.JoysticksInitialised = GL_TRUE;\r
1471     }\r
1472 }\r
1473 \r
1474 \r
1475 void fgJoystickClose( void )\r
1476 {\r
1477     int ident ;\r
1478     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )\r
1479     {\r
1480         if( fgJoystick[ ident ] )\r
1481         {\r
1482                         fgPlatformJoystickClose ( ident );\r
1483 \r
1484             free( fgJoystick[ ident ] );\r
1485             fgJoystick[ ident ] = NULL;\r
1486             /* show joystick has been deinitialized */\r
1487         }\r
1488     }\r
1489 }\r
1490 \r
1491 /*\r
1492  * Polls the joystick and executes the joystick callback hooked to the\r
1493  * window specified in the function's parameter:\r
1494  */\r
1495 void fgJoystickPollWindow( SFG_Window* window )\r
1496 {\r
1497     float axes[ _JS_MAX_AXES ];\r
1498     int buttons;\r
1499     int ident;\r
1500 \r
1501     freeglut_return_if_fail( window );\r
1502     freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );\r
1503 \r
1504     for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )\r
1505     {\r
1506         if( fgJoystick[ident] )\r
1507         {\r
1508             fghJoystickRead( fgJoystick[ident], &buttons, axes );\r
1509 \r
1510             if( !fgJoystick[ident]->error )\r
1511                 INVOKE_WCB( *window, Joystick,\r
1512                             ( buttons,\r
1513                               (int) ( axes[ 0 ] * 1000.0f ),\r
1514                               (int) ( axes[ 1 ] * 1000.0f ),\r
1515                               (int) ( axes[ 2 ] * 1000.0f ) )\r
1516                 );\r
1517         }\r
1518     }\r
1519 }\r
1520 \r
1521 /*\r
1522  * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)\r
1523  */\r
1524 int fgJoystickDetect( void )\r
1525 {\r
1526     int ident;\r
1527 \r
1528     fgInitialiseJoysticks ();\r
1529 \r
1530     if ( !fgState.JoysticksInitialised )\r
1531         return 0;\r
1532 \r
1533     for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )\r
1534         if( fgJoystick[ident] && !fgJoystick[ident]->error )\r
1535             return 1;\r
1536 \r
1537     return 0;\r
1538 }\r
1539 \r
1540 /*\r
1541  * Joystick information functions\r
1542  */\r
1543 int  glutJoystickGetNumAxes( int ident )\r
1544 {\r
1545     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );\r
1546     return fgJoystick[ ident ]->num_axes;\r
1547 }\r
1548 int  glutJoystickGetNumButtons( int ident )\r
1549 {\r
1550     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );\r
1551     return fgJoystick[ ident ]->num_buttons;\r
1552 }\r
1553 int  glutJoystickNotWorking( int ident )\r
1554 {\r
1555     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );\r
1556     return fgJoystick[ ident ]->error;\r
1557 }\r
1558 \r
1559 float glutJoystickGetDeadBand( int ident, int axis )\r
1560 {\r
1561     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );\r
1562     return fgJoystick[ ident ]->dead_band [ axis ];\r
1563 }\r
1564 void  glutJoystickSetDeadBand( int ident, int axis, float db )\r
1565 {\r
1566     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );\r
1567     fgJoystick[ ident ]->dead_band[ axis ] = db;\r
1568 }\r
1569 \r
1570 float glutJoystickGetSaturation( int ident, int axis )\r
1571 {\r
1572     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );\r
1573     return fgJoystick[ ident ]->saturate[ axis ];\r
1574 }\r
1575 void  glutJoystickSetSaturation( int ident, int axis, float st )\r
1576 {\r
1577     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );\r
1578     fgJoystick[ ident ]->saturate [ axis ] = st;\r
1579 }\r
1580 \r
1581 void glutJoystickSetMinRange( int ident, float *axes )\r
1582 {\r
1583     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );\r
1584     memcpy( fgJoystick[ ident ]->min, axes,\r
1585             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1586 }\r
1587 void glutJoystickSetMaxRange( int ident, float *axes )\r
1588 {\r
1589     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );\r
1590     memcpy( fgJoystick[ ident ]->max, axes,\r
1591             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1592 }\r
1593 void glutJoystickSetCenter( int ident, float *axes )\r
1594 {\r
1595     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );\r
1596     memcpy( fgJoystick[ ident ]->center, axes,\r
1597             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1598 }\r
1599 \r
1600 void glutJoystickGetMinRange( int ident, float *axes )\r
1601 {\r
1602     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );\r
1603     memcpy( axes, fgJoystick[ ident ]->min,\r
1604             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1605 }\r
1606 void glutJoystickGetMaxRange( int ident, float *axes )\r
1607 {\r
1608     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );\r
1609     memcpy( axes, fgJoystick[ ident ]->max,\r
1610             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1611 }\r
1612 void glutJoystickGetCenter( int ident, float *axes )\r
1613 {\r
1614     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );\r
1615     memcpy( axes, fgJoystick[ ident ]->center,\r
1616             fgJoystick[ ident ]->num_axes * sizeof( float ) );\r
1617 }\r
1618 \r
1619 /*** END OF FILE ***/\r