2 * freeglut_joystick.c
\r
4 * Joystick handling code
\r
6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
\r
7 * Written by Steve Baker, <sjbaker1@airmail.net>
\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
16 * The above copyright notice and this permission notice shall be included
\r
17 * in all copies or substantial portions of the Software.
\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
28 * FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu>
\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
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
41 * Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
\r
45 /* XXX It might be better to poll the operating system for the numbers of buttons and
\r
46 * XXX axes and then dynamically allocate the arrays.
\r
48 #define _JS_MAX_BUTTONS 32
\r
50 #if TARGET_HOST_MACINTOSH
\r
51 # define _JS_MAX_AXES 9
\r
52 # include <InputSprocket.h>
\r
55 #if TARGET_HOST_MAC_OSX
\r
56 # define _JS_MAX_AXES 16
\r
57 # include <mach/mach.h>
\r
58 # include <IOKit/IOkitLib.h>
\r
59 # include <IOKit/hid/IOHIDLib.h>
\r
62 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
\r
63 # define _JS_MAX_AXES 8
\r
64 # include <windows.h>
\r
65 # include <mmsystem.h>
\r
66 # include <regstr.h>
\r
70 #if TARGET_HOST_POSIX_X11
\r
71 # define _JS_MAX_AXES 16
\r
72 # ifdef HAVE_SYS_IOCTL_H
\r
73 # include <sys/ioctl.h>
\r
75 # ifdef HAVE_FCNTL_H
\r
78 # ifdef HAVE_ERRNO_H
\r
80 # include <string.h>
\r
82 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
\r
83 /* XXX The below hack is done until freeglut's autoconf is updated. */
\r
84 # define HAVE_USB_JS 1
\r
86 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
\r
87 # include <sys/joystick.h>
\r
90 * XXX NetBSD/amd64 systems may find that they have to steal the
\r
91 * XXX /usr/include/machine/joystick.h from a NetBSD/i386 system.
\r
92 * XXX I cannot comment whether that works for the interface, but
\r
93 * XXX it lets you compile...(^& I do not think that we can do away
\r
94 * XXX with this header.
\r
96 # include <machine/joystick.h> /* For analog joysticks */
\r
98 # define JS_DATA_TYPE joystick
\r
99 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
\r
102 # if defined(__linux__)
\r
103 # include <linux/joystick.h>
\r
105 /* check the joystick driver version */
\r
106 # if defined(JS_VERSION) && JS_VERSION >= 0x010000
\r
109 # else /* Not BSD or Linux */
\r
113 * We'll put these values in and that should
\r
114 * allow the code to at least compile when there is
\r
115 * no support. The JS open routine should error out
\r
116 * and shut off all the code downstream anyway and if
\r
117 * the application doesn't use a joystick we'll be fine.
\r
120 struct JS_DATA_TYPE
\r
127 # define JS_RETURN (sizeof(struct JS_DATA_TYPE))
\r
135 /* BSD defines from "jsBSD.cxx" around lines 42-270 */
\r
137 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
\r
139 # ifdef HAVE_USB_JS
\r
140 # if defined(__NetBSD__)
\r
141 /* XXX The below hack is done until freeglut's autoconf is updated. */
\r
142 # define HAVE_USBHID_H 1
\r
143 # ifdef HAVE_USBHID_H
\r
144 # include <usbhid.h>
\r
148 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
\r
149 # ifdef HAVE_USBHID_H
\r
150 # include <usbhid.h>
\r
152 # include <libusbhid.h>
\r
155 # include <legacy/dev/usb/usb.h>
\r
156 # include <dev/usb/usbhid.h>
\r
158 /* Compatibility with older usb.h revisions */
\r
159 # if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
\r
160 # define USB_MAX_DEVNAMES MAXDEVNAMES
\r
164 static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
\r
165 static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
\r
166 struct os_specific_s {
\r
170 /* The following structure members are specific to analog joysticks */
\r
171 struct joystick ajs;
\r
172 # ifdef HAVE_USB_JS
\r
173 /* The following structure members are specific to USB joysticks */
\r
174 struct hid_item *hids;
\r
177 char *hid_data_buf;
\r
178 int axes_usage [ _JS_MAX_AXES ];
\r
180 /* We keep button and axes state ourselves, as they might not be updated
\r
181 * on every read of a USB device
\r
184 float cache_axes [ _JS_MAX_AXES ];
\r
187 /* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
\r
188 # define USB_IDENT_OFFSET 2
\r
190 # define USBDEV "/dev/usb"
\r
191 # define UHIDDEV "/dev/uhid"
\r
192 # define AJSDEV "/dev/joy"
\r
194 # ifdef HAVE_USB_JS
\r
196 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
\r
197 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
\r
198 * return the uhidN device name. We warn the user of this situation once.
\r
200 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
\r
202 struct usb_device_info di;
\r
206 for (a = 1; a < USB_MAX_DEVICES; a++) {
\r
208 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
\r
210 for (i = 0; i < USB_MAX_DEVNAMES; i++)
\r
211 if (di.udi_devnames[i][0] &&
\r
212 strcmp(di.udi_devnames[i], dev) == 0) {
\r
213 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
\r
214 strcpy(cp, di.udi_vendor);
\r
216 strcat(cp, di.udi_product);
\r
217 strncpy(out, cp, outlen - 1);
\r
218 out[outlen - 1] = 0;
\r
226 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
\r
231 static int protection_warned = 0;
\r
233 for (i = 0; i < 16; i++) {
\r
234 snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
\r
235 f = open(buf, O_RDONLY);
\r
237 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
\r
242 #ifdef HAVE_ERRNO_H
\r
243 else if (errno == EACCES) {
\r
244 if (!protection_warned) {
\r
245 fgWarning ( "Can't open %s for read!", buf );
\r
246 protection_warned = 1;
\r
254 static int fghJoystickInitializeHID(struct os_specific_s *os,
\r
255 int *num_axes, int *num_buttons)
\r
257 int size, is_joystick;
\r
258 # ifdef HAVE_USBHID_H
\r
261 struct hid_data *d;
\r
265 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
\r
267 #ifdef HAVE_ERRNO_H
\r
268 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
\r
270 fgWarning ( "error: %s", os->fname );
\r
277 # ifdef HAVE_USBHID_H
\r
278 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
\r
280 /*** XXX {report_id} may not be the right variable? ***/
\r
281 #ifdef HAVE_ERRNO_H
\r
282 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
\r
284 fgWarning ( "error: %s%d", UHIDDEV, report_id );
\r
289 size = hid_report_size( rd, hid_input, report_id );
\r
291 size = hid_report_size( rd, 0, hid_input );
\r
293 os->hid_data_buf = calloc( 1, size );
\r
294 os->hid_dlen = size;
\r
297 # ifdef HAVE_USBHID_H
\r
298 d = hid_start_parse( rd, 1 << hid_input, report_id );
\r
300 d = hid_start_parse( rd, 1 << hid_input );
\r
302 while( hid_get_item( d, &h ) )
\r
304 int usage, page, interesting_hid;
\r
306 page = HID_PAGE( h.usage );
\r
307 usage = HID_USAGE( h.usage );
\r
309 /* This test is somewhat too simplistic, but this is how MicroSoft
\r
310 * does, so I guess it works for all joysticks/game pads. */
\r
311 is_joystick = is_joystick ||
\r
312 ( h.kind == hid_collection &&
\r
313 page == HUP_GENERIC_DESKTOP &&
\r
314 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
\r
316 if( h.kind != hid_input )
\r
322 interesting_hid = TRUE;
\r
323 if( page == HUP_GENERIC_DESKTOP )
\r
334 if( *num_axes < _JS_MAX_AXES )
\r
336 os->axes_usage[ *num_axes ] = usage;
\r
340 case HUG_HAT_SWITCH:
\r
341 /* Allocate two axes for a hat */
\r
342 if( *num_axes + 1 < _JS_MAX_AXES )
\r
344 os->axes_usage[ *num_axes ] = usage;
\r
346 os->axes_usage[ *num_axes ] = usage;
\r
351 interesting_hid = FALSE;
\r
355 else if( page == HUP_BUTTON )
\r
357 interesting_hid = ( usage > 0 ) &&
\r
358 ( usage <= _JS_MAX_BUTTONS );
\r
360 if( interesting_hid && usage - 1 > *num_buttons )
\r
361 *num_buttons = usage - 1;
\r
364 if( interesting_hid )
\r
367 os->hids = calloc( 1, sizeof ( struct hid_item ) );
\r
371 hid_end_parse( d );
\r
373 return os->hids != NULL;
\r
379 * Definition of "SFG_Joystick" structure -- based on JS's "jsJoystick" object class.
\r
380 * See "js.h" lines 80-178.
\r
382 typedef struct tagSFG_Joystick SFG_Joystick;
\r
383 struct tagSFG_Joystick
\r
385 #if TARGET_HOST_MACINTOSH
\r
386 #define ISP_NUM_AXIS 9
\r
387 #define ISP_NUM_NEEDS 41
\r
388 ISpElementReference isp_elem [ ISP_NUM_NEEDS ];
\r
389 ISpNeed isp_needs [ ISP_NUM_NEEDS ];
\r
392 #if TARGET_HOST_MAC_OSX
\r
393 IOHIDDeviceInterface ** hidDev;
\r
394 IOHIDElementCookie buttonCookies[41];
\r
395 IOHIDElementCookie axisCookies[_JS_MAX_AXES];
\r
396 long minReport[_JS_MAX_AXES],
\r
397 maxReport[_JS_MAX_AXES];
\r
400 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
\r
407 #if TARGET_HOST_POSIX_X11
\r
408 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
\r
409 struct os_specific_s *os;
\r
413 struct js_event js;
\r
415 float tmp_axes [ _JS_MAX_AXES ];
\r
417 struct JS_DATA_TYPE js;
\r
420 char fname [ 128 ];
\r
430 float dead_band[ _JS_MAX_AXES ];
\r
431 float saturate [ _JS_MAX_AXES ];
\r
432 float center [ _JS_MAX_AXES ];
\r
433 float max [ _JS_MAX_AXES ];
\r
434 float min [ _JS_MAX_AXES ];
\r
438 * Functions associated with the "jsJoystick" class in PLIB
\r
440 #if TARGET_HOST_MAC_OSX
\r
441 #define K_NUM_DEVICES 32
\r
443 io_object_t ioDevices[K_NUM_DEVICES];
\r
445 static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
\r
446 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
\r
448 static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
\r
449 /* callback for CFArrayApply */
\r
450 static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
\r
452 static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
\r
453 static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
\r
454 static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
\r
458 /* External function declarations (mostly platform-specific) */
\r
459 extern void fgPlatformJoystickClose ( int ident );
\r
462 * The static joystick structure pointer
\r
464 #define MAX_NUM_JOYSTICKS 2
\r
465 static SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
\r
468 * Platform-Specific Code
\r
471 #if TARGET_HOST_MACINTOSH
\r
472 void fgPlatformJoystickClose ( int ident )
\r
480 #if TARGET_HOST_MAC_OSX
\r
481 void fgPlatformJoystickClose ( int ident )
\r
483 ( *( fgJoystick[ ident ]->hidDev ) )->
\r
484 close( fgJoystick[ ident ]->hidDev );
\r
488 #if TARGET_HOST_POSIX_X11
\r
489 void fgPlatformJoystickClose ( int ident )
\r
491 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
\r
492 if( fgJoystick[ident]->os )
\r
494 if( ! fgJoystick[ ident ]->error )
\r
495 close( fgJoystick[ ident ]->os->fd );
\r
497 if( fgJoystick[ ident ]->os->hids )
\r
498 free (fgJoystick[ ident ]->os->hids);
\r
499 if( fgJoystick[ ident ]->os->hid_data_buf )
\r
500 free( fgJoystick[ ident ]->os->hid_data_buf );
\r
502 free( fgJoystick[ident]->os );
\r
506 if( ! fgJoystick[ident]->error )
\r
507 close( fgJoystick[ ident ]->fd );
\r
514 * Read the raw joystick data
\r
516 static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
\r
518 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
\r
524 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
\r
535 for( i = 0; i < joy->num_axes; i++ )
\r
536 axes[ i ] = 1500.0f;
\r
541 #if TARGET_HOST_MACINTOSH
\r
546 for ( i = 0; i < joy->num_buttons; i++ )
\r
549 int err = ISpElement_GetSimpleState ( isp_elem [ i + isp_num_axis ], &state);
\r
552 *buttons |= state << i;
\r
558 for ( i = 0; i < joy->num_axes; i++ )
\r
561 int err = ISpElement_GetSimpleState ( isp_elem [ i ], &state );
\r
564 axes [i] = (float) state;
\r
569 #if TARGET_HOST_MAC_OSX
\r
570 if ( buttons != NULL )
\r
574 for ( i = 0; i < joy->num_buttons; i++ )
\r
576 IOHIDEventStruct hidEvent;
\r
577 (*(joy->hidDev))->getElementValue ( joy->hidDev, buttonCookies[i], &hidEvent );
\r
578 if ( hidEvent.value )
\r
579 *buttons |= 1 << i;
\r
583 if ( axes != NULL )
\r
585 for ( i = 0; i < joy->num_axes; i++ )
\r
587 IOHIDEventStruct hidEvent;
\r
588 (*(joy->hidDev))->getElementValue ( joy->hidDev, axisCookies[i], &hidEvent );
\r
589 axes[i] = hidEvent.value;
\r
594 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
\r
595 status = joyGetPosEx( joy->js_id, &joy->js );
\r
597 if ( status != JOYERR_NOERROR )
\r
599 joy->error = GL_TRUE;
\r
604 *buttons = joy->js.dwButtons;
\r
609 * WARNING - Fall through case clauses!!
\r
611 switch ( joy->num_axes )
\r
614 /* Generate two POV axes from the POV hat angle.
\r
615 * Low 16 bits of js.dwPOV gives heading (clockwise from ahead) in
\r
616 * hundredths of a degree, or 0xFFFF when idle.
\r
618 if ( ( joy->js.dwPOV & 0xFFFF ) == 0xFFFF )
\r
625 /* This is the contentious bit: how to convert angle to X/Y.
\r
626 * wk: I know of no define for PI that we could use here:
\r
627 * SG_PI would pull in sg, M_PI is undefined for MSVC
\r
628 * But the accuracy of the value of PI is very unimportant at
\r
631 float s = (float) sin ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
\r
632 float c = (float) cos ( ( joy->js.dwPOV & 0xFFFF ) * ( 0.01 * 3.1415926535f / 180.0f ) );
\r
634 /* Convert to coordinates on a square so that North-East
\r
635 * is (1,1) not (.7,.7), etc.
\r
636 * s and c cannot both be zero so we won't divide by zero.
\r
638 if ( fabs ( s ) < fabs ( c ) )
\r
640 axes [ 6 ] = ( c < 0.0 ) ? -s/c : s/c ;
\r
641 axes [ 7 ] = ( c < 0.0 ) ? -1.0f : 1.0f;
\r
645 axes [ 6 ] = ( s < 0.0 ) ? -1.0f : 1.0f;
\r
646 axes [ 7 ] = ( s < 0.0 ) ? -c/s : c/s ;
\r
650 case 6: axes[5] = (float) joy->js.dwVpos;
\r
651 case 5: axes[4] = (float) joy->js.dwUpos;
\r
652 case 4: axes[3] = (float) joy->js.dwRpos;
\r
653 case 3: axes[2] = (float) joy->js.dwZpos;
\r
654 case 2: axes[1] = (float) joy->js.dwYpos;
\r
655 case 1: axes[0] = (float) joy->js.dwXpos;
\r
660 #if TARGET_HOST_POSIX_X11
\r
661 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
\r
662 if ( joy->os->is_analog )
\r
664 int status = read ( joy->os->fd, &joy->os->ajs, sizeof(joy->os->ajs) );
\r
665 if ( status != sizeof(joy->os->ajs) ) {
\r
666 perror ( joy->os->fname );
\r
667 joy->error = GL_TRUE;
\r
670 if ( buttons != NULL )
\r
671 *buttons = ( joy->os->ajs.b1 ? 1 : 0 ) | ( joy->os->ajs.b2 ? 2 : 0 );
\r
673 if ( axes != NULL )
\r
675 axes[0] = (float) joy->os->ajs.x;
\r
676 axes[1] = (float) joy->os->ajs.y;
\r
682 # ifdef HAVE_USB_JS
\r
683 while ( ( len = read ( joy->os->fd, joy->os->hid_data_buf, joy->os->hid_dlen ) ) == joy->os->hid_dlen )
\r
685 struct hid_item *h;
\r
687 for ( h = joy->os->hids; h; h = h->next )
\r
689 int d = hid_get_data ( joy->os->hid_data_buf, h );
\r
691 int page = HID_PAGE ( h->usage );
\r
692 int usage = HID_USAGE ( h->usage );
\r
694 if ( page == HUP_GENERIC_DESKTOP )
\r
697 for ( i = 0; i < joy->num_axes; i++ )
\r
698 if (joy->os->axes_usage[i] == usage)
\r
700 if (usage == HUG_HAT_SWITCH)
\r
702 if (d < 0 || d > 8)
\r
703 d = 0; /* safety */
\r
704 joy->os->cache_axes[i] = (float)hatmap_x[d];
\r
705 joy->os->cache_axes[i + 1] = (float)hatmap_y[d];
\r
709 joy->os->cache_axes[i] = (float)d;
\r
714 else if (page == HUP_BUTTON)
\r
716 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
\r
719 joy->os->cache_buttons |= (1 << ( usage - 1 ));
\r
721 joy->os->cache_buttons &= ~(1 << ( usage - 1 ));
\r
726 #ifdef HAVE_ERRNO_H
\r
727 if ( len < 0 && errno != EAGAIN )
\r
732 perror( joy->os->fname );
\r
735 if ( buttons != NULL ) *buttons = joy->os->cache_buttons;
\r
736 if ( axes != NULL )
\r
737 memcpy ( axes, joy->os->cache_axes, sizeof(float) * joy->num_axes );
\r
745 status = read ( joy->fd, &joy->js, sizeof(struct js_event) );
\r
747 if ( status != sizeof( struct js_event ) )
\r
749 #ifdef HAVE_ERRNO_H
\r
750 if ( errno == EAGAIN )
\r
752 /* Use the old values */
\r
754 *buttons = joy->tmp_buttons;
\r
756 memcpy( axes, joy->tmp_axes,
\r
757 sizeof( float ) * joy->num_axes );
\r
762 fgWarning ( "%s", joy->fname );
\r
763 joy->error = GL_TRUE;
\r
767 switch ( joy->js.type & ~JS_EVENT_INIT )
\r
769 case JS_EVENT_BUTTON:
\r
770 if( joy->js.value == 0 ) /* clear the flag */
\r
771 joy->tmp_buttons &= ~( 1 << joy->js.number );
\r
773 joy->tmp_buttons |= ( 1 << joy->js.number );
\r
776 case JS_EVENT_AXIS:
\r
777 if ( joy->js.number < joy->num_axes )
\r
779 joy->tmp_axes[ joy->js.number ] = ( float )joy->js.value;
\r
782 memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
\r
787 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
\r
789 /* use the old values */
\r
791 if ( buttons != NULL ) *buttons = joy->tmp_buttons;
\r
792 if ( axes != NULL )
\r
793 memcpy ( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
\r
799 *buttons = joy->tmp_buttons;
\r
803 status = read( joy->fd, &joy->js, JS_RETURN );
\r
805 if ( status != JS_RETURN )
\r
807 fgWarning( "%s", joy->fname );
\r
808 joy->error = GL_TRUE;
\r
813 # if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
\r
814 *buttons = ( joy->js.b1 ? 1 : 0 ) | ( joy->js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
\r
816 *buttons = joy->js.buttons;
\r
821 axes[ 0 ] = (float) joy->js.x;
\r
822 axes[ 1 ] = (float) joy->js.y;
\r
829 * Correct the joystick axis data
\r
831 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
\r
833 if( value < joy->center[ axis ] )
\r
835 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
\r
836 joy->min[ axis ] );
\r
838 if( xx < -joy->saturate[ axis ] )
\r
841 if( xx > -joy->dead_band [ axis ] )
\r
844 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
\r
845 joy->dead_band[ axis ] );
\r
847 return ( xx < -1.0f ) ? -1.0f : xx;
\r
851 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
\r
852 joy->center[ axis ] );
\r
854 if( xx > joy->saturate[ axis ] )
\r
857 if( xx < joy->dead_band[ axis ] )
\r
860 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
\r
861 joy->dead_band[ axis ] );
\r
863 return ( xx > 1.0f ) ? 1.0f : xx;
\r
868 * Read the corrected joystick data
\r
870 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
\r
872 float raw_axes[ _JS_MAX_AXES ];
\r
881 for ( i=0; i<joy->num_axes; i++ )
\r
885 fghJoystickRawRead( joy, buttons, raw_axes );
\r
888 for( i=0; i<joy->num_axes; i++ )
\r
889 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
\r
893 * Happy happy happy joy joy joy (happy new year toudi :D)
\r
897 #if TARGET_HOST_MAC_OSX
\r
898 /** open the IOKit connection, enumerate all the HID devices, add their
\r
899 interface references to the static array. We then use the array index
\r
900 as the device number when we come to open() the joystick. */
\r
901 static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
\r
903 CFMutableDictionaryRef hidMatch = NULL;
\r
904 IOReturn rv = kIOReturnSuccess;
\r
906 io_iterator_t hidIterator;
\r
909 /* build a dictionary matching HID devices */
\r
910 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
\r
912 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
\r
913 if (rv != kIOReturnSuccess || !hidIterator) {
\r
914 fgWarning( "no joystick (HID) devices found" );
\r
919 while ((ioDev = IOIteratorNext(hidIterator))) {
\r
920 /* filter out keyboard and mouse devices */
\r
921 CFDictionaryRef properties = getCFProperties(ioDev);
\r
924 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
\r
925 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
\r
926 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
\r
927 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
\r
929 /* keep only joystick devices */
\r
930 if ( ( page == kHIDPage_GenericDesktop ) && (
\r
931 (usage == kHIDUsage_GD_Joystick)
\r
932 || (usage == kHIDUsage_GD_GamePad)
\r
933 || (usage == kHIDUsage_GD_MultiAxisController)
\r
934 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
\r
935 /* add it to the array */
\r
936 ioDevices[numDevices++] = ioDev;
\r
939 IOObjectRelease(hidIterator);
\r
942 static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
\r
945 CFMutableDictionaryRef cfProperties;
\r
948 /* comment copied from darwin/SDL_sysjoystick.c */
\r
949 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
\r
950 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
\r
953 io_registry_entry_t parent1, parent2;
\r
955 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
\r
956 if (rv != kIOReturnSuccess) {
\r
957 fgWarning ( "error getting device entry parent");
\r
961 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
\r
962 if (rv != kIOReturnSuccess) {
\r
963 fgWarning ( "error getting device entry parent 2");
\r
968 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
\r
969 &cfProperties, kCFAllocatorDefault, kNilOptions);
\r
970 if (rv != kIOReturnSuccess || !cfProperties) {
\r
971 fgWarning ( "error getting device properties");
\r
975 return cfProperties;
\r
978 static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
\r
980 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
\r
981 fgError ( "%s", "element enumerator passed non-dictionary value");
\r
985 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
\r
988 /** element enumerator function : pass NULL for top-level*/
\r
989 static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
\r
991 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
\r
992 "Joystick element type mismatch",
\r
993 "fghJoystickEnumerateElements" );
\r
995 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
\r
996 CFArrayApplyFunction((CFArrayRef) element, range,
\r
997 &fghJoystickElementEnumerator, joy );
\r
1000 static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
\r
1002 long cookie, lmin, lmax;
\r
1003 int index = joy->num_axes++;
\r
1005 CFNumberGetValue ((CFNumberRef)
\r
1006 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
\r
1007 kCFNumberLongType, &cookie);
\r
1009 axisCookies[index] = (IOHIDElementCookie) cookie;
\r
1011 CFNumberGetValue ((CFNumberRef)
\r
1012 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
\r
1013 kCFNumberLongType, &lmin);
\r
1015 CFNumberGetValue ((CFNumberRef)
\r
1016 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
\r
1017 kCFNumberLongType, &lmax);
\r
1019 joy->min[index] = lmin;
\r
1020 joy->max[index] = lmax;
\r
1021 joy->dead_band[index] = 0.0;
\r
1022 joy->saturate[index] = 1.0;
\r
1023 joy->center[index] = (lmax + lmin) * 0.5;
\r
1026 static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
\r
1029 CFNumberGetValue ((CFNumberRef)
\r
1030 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
\r
1031 kCFNumberLongType, &cookie);
\r
1033 joy->buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
\r
1034 /* anything else for buttons? */
\r
1037 static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
\r
1039 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
\r
1040 /* do we map hats to axes or buttons? */
\r
1044 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
\r
1046 http://msdn.microsoft.com/archive/en-us/dnargame/html/msdn_sidewind3d.asp
\r
1048 # if FREEGLUT_LIB_PRAGMAS
\r
1049 # pragma comment (lib, "advapi32.lib")
\r
1052 static int fghJoystickGetOEMProductName ( SFG_Joystick* joy, char *buf, int buf_sz )
\r
1054 char buffer [ 256 ];
\r
1056 char OEMKey [ 256 ];
\r
1065 /* Open .. MediaResources\CurrentJoystickSettings */
\r
1066 _snprintf ( buffer, sizeof(buffer), "%s\\%s\\%s",
\r
1067 REGSTR_PATH_JOYCONFIG, joy->jsCaps.szRegKey,
\r
1068 REGSTR_KEY_JOYCURR );
\r
1070 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
\r
1072 if ( lr != ERROR_SUCCESS ) return 0;
\r
1074 /* Get OEM Key name */
\r
1075 dwcb = sizeof(OEMKey);
\r
1077 /* JOYSTICKID1-16 is zero-based; registry entries for VJOYD are 1-based. */
\r
1078 _snprintf ( buffer, sizeof(buffer), "Joystick%d%s", joy->js_id + 1, REGSTR_VAL_JOYOEMNAME );
\r
1080 lr = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEMKey, &dwcb);
\r
1081 RegCloseKey ( hKey );
\r
1083 if ( lr != ERROR_SUCCESS ) return 0;
\r
1085 /* Open OEM Key from ...MediaProperties */
\r
1086 _snprintf ( buffer, sizeof(buffer), "%s\\%s", REGSTR_PATH_JOYOEM, OEMKey );
\r
1088 lr = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey );
\r
1090 if ( lr != ERROR_SUCCESS ) return 0;
\r
1092 /* Get OEM Name */
\r
1095 lr = RegQueryValueEx ( hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buf,
\r
1097 RegCloseKey ( hKey );
\r
1099 if ( lr != ERROR_SUCCESS ) return 0;
\r
1106 static void fghJoystickOpen( SFG_Joystick* joy )
\r
1109 #if TARGET_HOST_MACINTOSH
\r
1112 #if TARGET_HOST_MAC_OSX
\r
1115 IOCFPlugInInterface **plugin;
\r
1117 HRESULT pluginResult;
\r
1119 CFDictionaryRef props;
\r
1120 CFTypeRef topLevelElement;
\r
1122 #if TARGET_HOST_POSIX_X11
\r
1123 # if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
\r
1129 # if defined( __linux__ ) || TARGET_HOST_SOLARIS
\r
1135 /* Silence gcc, the correct #ifdefs would be too fragile... */
\r
1139 * Default values (for no joystick -- each conditional will reset the
\r
1142 joy->error = TRUE;
\r
1143 joy->num_axes = joy->num_buttons = 0;
\r
1144 joy->name[ 0 ] = '\0';
\r
1146 #if TARGET_HOST_MACINTOSH
\r
1147 /* XXX FIXME: get joystick name in Mac */
\r
1149 err = ISpStartup( );
\r
1151 if( err == noErr )
\r
1153 #define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
\r
1155 joy->error = GL_TRUE;
\r
1157 /* initialize the needs structure */
\r
1158 ISpNeed temp_isp_needs[ isp_num_needs ] =
\r
1160 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
\r
1161 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
\r
1162 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
\r
1163 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
\r
1164 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
\r
1165 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
\r
1166 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
\r
1167 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
\r
1168 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
\r
1170 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1171 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1172 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1173 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1174 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1175 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1176 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1177 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1178 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1179 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1180 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1181 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1182 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1183 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1184 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1185 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1186 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1187 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1188 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1189 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1190 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1191 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1192 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1193 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1194 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1195 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1196 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1197 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1198 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1199 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1200 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1201 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
\r
1204 memcpy( joy->isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
\r
1207 /* next two calls allow keyboard and mouse to emulate other input
\r
1208 * devices (gamepads, joysticks, etc)
\r
1211 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
\r
1212 ISP_CHECK_ERR(err)
\r
1215 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
\r
1216 ISP_CHECK_ERR(err)
\r
1219 err = ISpElement_NewVirtualFromNeeds( joy->isp_num_needs,
\r
1220 joy->isp_needs, joy->isp_elem,
\r
1222 ISP_CHECK_ERR( err )
\r
1224 err = ISpInit( joy->isp_num_needs, joy->isp_needs, joy->isp_elem,
\r
1225 'freeglut', nil, 0, 128, 0 );
\r
1226 ISP_CHECK_ERR( err )
\r
1228 joy->num_buttons = joy->isp_num_needs - joy->isp_num_axis;
\r
1229 joy->num_axes = joy->isp_num_axis;
\r
1231 for( i = 0; i < joy->num_axes; i++ )
\r
1233 joy->dead_band[ i ] = 0;
\r
1234 joy->saturate [ i ] = 1;
\r
1235 joy->center [ i ] = kISpAxisMiddle;
\r
1236 joy->max [ i ] = kISpAxisMaximum;
\r
1237 joy->min [ i ] = kISpAxisMinimum;
\r
1240 joy->error = GL_FALSE;
\r
1243 joy->num_buttons = joy->num_axes = 0;
\r
1246 #if TARGET_HOST_MAC_OSX
\r
1247 if( joy->id >= numDevices )
\r
1249 fgWarning( "device index out of range in fgJoystickOpen()" );
\r
1253 /* create device interface */
\r
1254 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
\r
1255 kIOHIDDeviceUserClientTypeID,
\r
1256 kIOCFPlugInInterfaceID,
\r
1257 &plugin, &score );
\r
1259 if( rv != kIOReturnSuccess )
\r
1261 fgWarning( "error creating plugin for io device" );
\r
1265 pluginResult = ( *plugin )->QueryInterface(
\r
1267 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
\r
1268 &( LPVOID )joy->hidDev
\r
1271 if( pluginResult != S_OK )
\r
1272 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
\r
1274 ( *plugin )->Release( plugin ); /* don't leak a ref */
\r
1275 if( joy->hidDev == NULL )
\r
1278 /* store the interface in this instance */
\r
1279 rv = ( *( joy->hidDev ) )->open( joy->hidDev, 0 );
\r
1280 if( rv != kIOReturnSuccess )
\r
1282 fgWarning( "error opening device interface");
\r
1286 props = getCFProperties( ioDevices[ joy->id ] );
\r
1288 /* recursively enumerate all the bits */
\r
1289 CFTypeRef topLevelElement =
\r
1290 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
\r
1291 enumerateElements( topLevelElement );
\r
1293 CFRelease( props );
\r
1296 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
\r
1297 joy->js.dwFlags = JOY_RETURNALL;
\r
1298 joy->js.dwSize = sizeof( joy->js );
\r
1300 memset( &joy->jsCaps, 0, sizeof( joy->jsCaps ) );
\r
1303 ( joyGetDevCaps( joy->js_id, &joy->jsCaps, sizeof( joy->jsCaps ) ) !=
\r
1306 if( joy->jsCaps.wNumAxes == 0 )
\r
1308 joy->num_axes = 0;
\r
1309 joy->error = GL_TRUE;
\r
1313 /* Device name from jsCaps is often "Microsoft PC-joystick driver",
\r
1314 * at least for USB. Try to get the real name from the registry.
\r
1316 if ( ! fghJoystickGetOEMProductName( joy, joy->name,
\r
1317 sizeof( joy->name ) ) )
\r
1319 fgWarning( "JS: Failed to read joystick name from registry" );
\r
1320 strncpy( joy->name, joy->jsCaps.szPname, sizeof( joy->name ) );
\r
1323 /* Windows joystick drivers may provide any combination of
\r
1324 * X,Y,Z,R,U,V,POV - not necessarily the first n of these.
\r
1326 if( joy->jsCaps.wCaps & JOYCAPS_HASPOV )
\r
1328 joy->num_axes = _JS_MAX_AXES;
\r
1329 joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */
\r
1330 joy->min[ 6 ] = -1.0; joy->max[ 6 ] = 1.0; /* POV X */
\r
1333 joy->num_axes = 6;
\r
1335 joy->min[ 5 ] = ( float )joy->jsCaps.wVmin;
\r
1336 joy->max[ 5 ] = ( float )joy->jsCaps.wVmax;
\r
1337 joy->min[ 4 ] = ( float )joy->jsCaps.wUmin;
\r
1338 joy->max[ 4 ] = ( float )joy->jsCaps.wUmax;
\r
1339 joy->min[ 3 ] = ( float )joy->jsCaps.wRmin;
\r
1340 joy->max[ 3 ] = ( float )joy->jsCaps.wRmax;
\r
1341 joy->min[ 2 ] = ( float )joy->jsCaps.wZmin;
\r
1342 joy->max[ 2 ] = ( float )joy->jsCaps.wZmax;
\r
1343 joy->min[ 1 ] = ( float )joy->jsCaps.wYmin;
\r
1344 joy->max[ 1 ] = ( float )joy->jsCaps.wYmax;
\r
1345 joy->min[ 0 ] = ( float )joy->jsCaps.wXmin;
\r
1346 joy->max[ 0 ] = ( float )joy->jsCaps.wXmax;
\r
1349 /* Guess all the rest judging on the axes extremals */
\r
1350 for( i = 0; i < joy->num_axes; i++ )
\r
1352 joy->center [ i ] = ( joy->max[ i ] + joy->min[ i ] ) * 0.5f;
\r
1353 joy->dead_band[ i ] = 0.0f;
\r
1354 joy->saturate [ i ] = 1.0f;
\r
1358 #if TARGET_HOST_POSIX_X11
\r
1359 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
\r
1360 for( i = 0; i < _JS_MAX_AXES; i++ )
\r
1361 joy->os->cache_axes[ i ] = 0.0f;
\r
1363 joy->os->cache_buttons = 0;
\r
1365 joy->os->fd = open( joy->os->fname, O_RDONLY | O_NONBLOCK);
\r
1367 #ifdef HAVE_ERRNO_H
\r
1368 if( joy->os->fd < 0 && errno == EACCES )
\r
1369 fgWarning ( "%s exists but is not readable by you", joy->os->fname );
\r
1372 joy->error =( joy->os->fd < 0 );
\r
1377 joy->num_axes = 0;
\r
1378 joy->num_buttons = 0;
\r
1379 if( joy->os->is_analog )
\r
1382 char joyfname[ 1024 ];
\r
1383 int noargs, in_no_axes;
\r
1385 float axes [ _JS_MAX_AXES ];
\r
1386 int buttons[ _JS_MAX_AXES ];
\r
1388 joy->num_axes = 2;
\r
1389 joy->num_buttons = 32;
\r
1391 fghJoystickRawRead( joy, buttons, axes );
\r
1392 joy->error = axes[ 0 ] < -1000000000.0f;
\r
1396 snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
\r
1398 joyfile = fopen( joyfname, "r" );
\r
1399 joy->error =( joyfile == NULL );
\r
1403 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
\r
1404 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
\r
1405 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
\r
1406 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
\r
1407 fclose( joyfile );
\r
1411 for( i = 0; i < _JS_MAX_AXES; i++ )
\r
1413 joy->dead_band[ i ] = 0.0f;
\r
1414 joy->saturate [ i ] = 1.0f;
\r
1417 return; /* End of analog code */
\r
1420 # ifdef HAVE_USB_JS
\r
1421 if( ! fghJoystickInitializeHID( joy->os, &joy->num_axes,
\r
1422 &joy->num_buttons ) )
\r
1424 close( joy->os->fd );
\r
1425 joy->error = GL_TRUE;
\r
1429 cp = strrchr( joy->os->fname, '/' );
\r
1432 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
\r
1434 strcpy( joy->name, &cp[1] );
\r
1437 if( joy->num_axes > _JS_MAX_AXES )
\r
1438 joy->num_axes = _JS_MAX_AXES;
\r
1440 for( i = 0; i < _JS_MAX_AXES; i++ )
\r
1442 /* We really should get this from the HID, but that data seems
\r
1443 * to be quite unreliable for analog-to-USB converters. Punt for
\r
1446 if( joy->os->axes_usage[ i ] == HUG_HAT_SWITCH )
\r
1448 joy->max [ i ] = 1.0f;
\r
1449 joy->center[ i ] = 0.0f;
\r
1450 joy->min [ i ] = -1.0f;
\r
1454 joy->max [ i ] = 255.0f;
\r
1455 joy->center[ i ] = 127.0f;
\r
1456 joy->min [ i ] = 0.0f;
\r
1459 joy->dead_band[ i ] = 0.0f;
\r
1460 joy->saturate[ i ] = 1.0f;
\r
1465 #if defined( __linux__ ) || TARGET_HOST_SOLARIS
\r
1466 /* Default for older Linux systems. */
\r
1467 joy->num_axes = 2;
\r
1468 joy->num_buttons = 32;
\r
1471 for( i = 0; i < _JS_MAX_AXES; i++ )
\r
1472 joy->tmp_axes[ i ] = 0.0f;
\r
1474 joy->tmp_buttons = 0;
\r
1477 joy->fd = open( joy->fname, O_RDONLY );
\r
1479 joy->error =( joy->fd < 0 );
\r
1484 /* Set the correct number of axes for the linux driver */
\r
1486 /* Melchior Franz's fixes for big-endian Linuxes since writing
\r
1487 * to the upper byte of an uninitialized word doesn't work.
\r
1490 ioctl( joy->fd, JSIOCGAXES, &u );
\r
1491 joy->num_axes = u;
\r
1492 ioctl( joy->fd, JSIOCGBUTTONS, &u );
\r
1493 joy->num_buttons = u;
\r
1494 ioctl( joy->fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
\r
1495 fcntl( joy->fd, F_SETFL, O_NONBLOCK );
\r
1499 * The Linux driver seems to return 512 for all axes
\r
1500 * when no stick is present - but there is a chance
\r
1501 * that could happen by accident - so it's gotta happen
\r
1502 * on both axes for at least 100 attempts.
\r
1504 * PWO: shouldn't be that done somehow wiser on the kernel level?
\r
1511 fghJoystickRawRead( joy, NULL, joy->center );
\r
1513 } while( !joy->error &&
\r
1515 joy->center[ 0 ] == 512.0f &&
\r
1516 joy->center[ 1 ] == 512.0f );
\r
1518 if ( counter >= 100 )
\r
1519 joy->error = GL_TRUE;
\r
1522 for( i = 0; i < _JS_MAX_AXES; i++ )
\r
1525 joy->max [ i ] = 32767.0f;
\r
1526 joy->center[ i ] = 0.0f;
\r
1527 joy->min [ i ] = -32767.0f;
\r
1529 joy->max[ i ] = joy->center[ i ] * 2.0f;
\r
1530 joy->min[ i ] = 0.0f;
\r
1532 joy->dead_band[ i ] = 0.0f;
\r
1533 joy->saturate [ i ] = 1.0f;
\r
1540 * This function replaces the constructor method in the JS library.
\r
1542 static void fghJoystickInit( int ident )
\r
1544 if( ident >= MAX_NUM_JOYSTICKS )
\r
1545 fgError( "Too large a joystick number: %d", ident );
\r
1547 if( fgJoystick[ ident ] )
\r
1548 fgError( "illegal attempt to initialize joystick device again" );
\r
1550 fgJoystick[ ident ] =
\r
1551 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
\r
1553 /* Set defaults */
\r
1554 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
\r
1555 fgJoystick[ ident ]->error = GL_TRUE;
\r
1557 #if TARGET_HOST_MACINTOSH
\r
1558 fgJoystick[ ident ]->id = ident;
\r
1559 snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident ); /* FIXME */
\r
1560 fgJoystick[ ident ]->error = GL_FALSE;
\r
1563 #if TARGET_HOST_MAC_OSX
\r
1564 fgJoystick[ ident ]->id = ident;
\r
1565 fgJoystick[ ident ]->error = GL_FALSE;
\r
1566 fgJoystick[ ident ]->num_axes = 0;
\r
1567 fgJoystick[ ident ]->num_buttons = 0;
\r
1569 if( numDevices < 0 )
\r
1571 /* do first-time init (since we can't over-ride jsInit, hmm */
\r
1574 mach_port_t masterPort;
\r
1575 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
\r
1576 if( rv != kIOReturnSuccess )
\r
1578 fgWarning( "error getting master Mach port" );
\r
1581 fghJoystickFindDevices( masterPort );
\r
1584 if ( ident >= numDevices )
\r
1586 fgJoystick[ ident ]->error = GL_TRUE;
\r
1590 /* get the name now too */
\r
1591 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
\r
1592 CFTypeRef ref = CFDictionaryGetValue( properties,
\r
1593 CFSTR( kIOHIDProductKey ) );
\r
1595 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
\r
1598 !CFStringGetCString( ( CFStringRef )ref, name, 128,
\r
1599 CFStringGetSystemEncoding( ) ) )
\r
1601 fgWarning( "error getting device name" );
\r
1606 #if TARGET_HOST_MS_WINDOWS && !defined(_WIN32_WCE)
\r
1610 fgJoystick[ ident ]->js_id = JOYSTICKID1;
\r
1611 fgJoystick[ ident ]->error = GL_FALSE;
\r
1614 fgJoystick[ ident ]->js_id = JOYSTICKID2;
\r
1615 fgJoystick[ ident ]->error = GL_FALSE;
\r
1618 fgJoystick[ ident ]->num_axes = 0;
\r
1619 fgJoystick[ ident ]->error = GL_TRUE;
\r
1624 #if TARGET_HOST_POSIX_X11
\r
1625 # if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
\r
1626 fgJoystick[ ident ]->id = ident;
\r
1627 fgJoystick[ ident ]->error = GL_FALSE;
\r
1629 fgJoystick[ ident ]->os = calloc( 1, sizeof( struct os_specific_s ) );
\r
1630 memset( fgJoystick[ ident ]->os, 0, sizeof( struct os_specific_s ) );
\r
1631 if( ident < USB_IDENT_OFFSET )
\r
1632 fgJoystick[ ident ]->os->is_analog = 1;
\r
1633 if( fgJoystick[ ident ]->os->is_analog )
\r
1634 snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", AJSDEV, ident );
\r
1636 snprintf( fgJoystick[ ident ]->os->fname, sizeof(fgJoystick[ ident ]->os->fname), "%s%d", UHIDDEV,
\r
1637 ident - USB_IDENT_OFFSET );
\r
1638 # elif defined( __linux__ )
\r
1639 fgJoystick[ ident ]->id = ident;
\r
1640 fgJoystick[ ident ]->error = GL_FALSE;
\r
1642 snprintf( fgJoystick[ident]->fname, sizeof(fgJoystick[ident]->fname), "/dev/input/js%d", ident );
\r
1644 if( access( fgJoystick[ ident ]->fname, F_OK ) != 0 )
\r
1645 snprintf( fgJoystick[ ident ]->fname, sizeof(fgJoystick[ ident ]->fname), "/dev/js%d", ident );
\r
1649 fghJoystickOpen( fgJoystick[ ident ] );
\r
1653 * Try initializing all the joysticks (well, both of them)
\r
1655 void fgInitialiseJoysticks ( void )
\r
1657 if( !fgState.JoysticksInitialised )
\r
1660 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
\r
1661 fghJoystickInit( ident );
\r
1663 fgState.JoysticksInitialised = GL_TRUE;
\r
1668 void fgJoystickClose( void )
\r
1671 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
\r
1673 if( fgJoystick[ ident ] )
\r
1675 fgPlatformJoystickClose ( ident );
\r
1677 free( fgJoystick[ ident ] );
\r
1678 fgJoystick[ ident ] = NULL;
\r
1679 /* show joystick has been deinitialized */
\r
1685 * Polls the joystick and executes the joystick callback hooked to the
\r
1686 * window specified in the function's parameter:
\r
1688 void fgJoystickPollWindow( SFG_Window* window )
\r
1690 float axes[ _JS_MAX_AXES ];
\r
1694 freeglut_return_if_fail( window );
\r
1695 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
\r
1697 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
\r
1699 if( fgJoystick[ident] )
\r
1701 fghJoystickRead( fgJoystick[ident], &buttons, axes );
\r
1703 if( !fgJoystick[ident]->error )
\r
1704 INVOKE_WCB( *window, Joystick,
\r
1706 (int) ( axes[ 0 ] * 1000.0f ),
\r
1707 (int) ( axes[ 1 ] * 1000.0f ),
\r
1708 (int) ( axes[ 2 ] * 1000.0f ) )
\r
1715 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
\r
1717 int fgJoystickDetect( void )
\r
1721 fgInitialiseJoysticks ();
\r
1723 if ( !fgState.JoysticksInitialised )
\r
1726 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
\r
1727 if( fgJoystick[ident] && !fgJoystick[ident]->error )
\r
1734 * Joystick information functions
\r
1736 int glutJoystickGetNumAxes( int ident )
\r
1738 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
\r
1739 return fgJoystick[ ident ]->num_axes;
\r
1741 int glutJoystickGetNumButtons( int ident )
\r
1743 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
\r
1744 return fgJoystick[ ident ]->num_buttons;
\r
1746 int glutJoystickNotWorking( int ident )
\r
1748 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
\r
1749 return fgJoystick[ ident ]->error;
\r
1752 float glutJoystickGetDeadBand( int ident, int axis )
\r
1754 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
\r
1755 return fgJoystick[ ident ]->dead_band [ axis ];
\r
1757 void glutJoystickSetDeadBand( int ident, int axis, float db )
\r
1759 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
\r
1760 fgJoystick[ ident ]->dead_band[ axis ] = db;
\r
1763 float glutJoystickGetSaturation( int ident, int axis )
\r
1765 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
\r
1766 return fgJoystick[ ident ]->saturate[ axis ];
\r
1768 void glutJoystickSetSaturation( int ident, int axis, float st )
\r
1770 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
\r
1771 fgJoystick[ ident ]->saturate [ axis ] = st;
\r
1774 void glutJoystickSetMinRange( int ident, float *axes )
\r
1776 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
\r
1777 memcpy( fgJoystick[ ident ]->min, axes,
\r
1778 fgJoystick[ ident ]->num_axes * sizeof( float ) );
\r
1780 void glutJoystickSetMaxRange( int ident, float *axes )
\r
1782 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
\r
1783 memcpy( fgJoystick[ ident ]->max, axes,
\r
1784 fgJoystick[ ident ]->num_axes * sizeof( float ) );
\r
1786 void glutJoystickSetCenter( int ident, float *axes )
\r
1788 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
\r
1789 memcpy( fgJoystick[ ident ]->center, axes,
\r
1790 fgJoystick[ ident ]->num_axes * sizeof( float ) );
\r
1793 void glutJoystickGetMinRange( int ident, float *axes )
\r
1795 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
\r
1796 memcpy( axes, fgJoystick[ ident ]->min,
\r
1797 fgJoystick[ ident ]->num_axes * sizeof( float ) );
\r
1799 void glutJoystickGetMaxRange( int ident, float *axes )
\r
1801 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
\r
1802 memcpy( axes, fgJoystick[ ident ]->max,
\r
1803 fgJoystick[ ident ]->num_axes * sizeof( float ) );
\r
1805 void glutJoystickGetCenter( int ident, float *axes )
\r
1807 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
\r
1808 memcpy( axes, fgJoystick[ ident ]->center,
\r
1809 fgJoystick[ ident ]->num_axes * sizeof( float ) );
\r
1812 /*** END OF FILE ***/
\r