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