1/*
2 * fg_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 <GL/freeglut.h>
35#include "fg_internal.h"
36#ifdef HAVE_SYS_PARAM_H
37# include <sys/param.h>
38#endif
39
40#define JS_TRUE 1
41#define JS_FALSE 0
42
43/* BSD defines from "jsBSD.cxx" around lines 42-270 */
44
45#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
46
47# ifdef HAVE_USB_JS
48# if defined(__NetBSD__)
49/* XXX The below hack is done until freeglut's autoconf is updated. */
50# define HAVE_USBHID_H 1
51# ifdef HAVE_USBHID_H
52# include <usbhid.h>
53# else
54# include <usb.h>
55# endif
56# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
57# ifdef HAVE_USBHID_H
58# include <usbhid.h>
59# else
60# include <libusbhid.h>
61# endif
62# endif
63# include <legacy/dev/usb/usb.h>
64# include <dev/usb/usbhid.h>
65
66/* Compatibility with older usb.h revisions */
67# if !defined(USB_MAX_DEVNAMES) && defined(MAXDEVNAMES)
68# define USB_MAX_DEVNAMES MAXDEVNAMES
69# endif
70# endif
71
72static int hatmap_x[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
73static int hatmap_y[9] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
74struct os_specific_s {
75 char fname [128 ];
76 int fd;
77 int is_analog;
78 /* The following structure members are specific to analog joysticks */
79 struct joystick ajs;
80# ifdef HAVE_USB_JS
81 /* The following structure members are specific to USB joysticks */
82 struct hid_item *hids;
83 int hid_dlen;
84 int hid_offset;
85 char *hid_data_buf;
86 int axes_usage [ _JS_MAX_AXES ];
87# endif
88 /* We keep button and axes state ourselves, as they might not be updated
89 * on every read of a USB device
90 */
91 int cache_buttons;
92 float cache_axes [ _JS_MAX_AXES ];
93};
94
95/* Idents lower than USB_IDENT_OFFSET are for analog joysticks. */
96# define USB_IDENT_OFFSET 2
97
98# define USBDEV "/dev/usb"
99# define UHIDDEV "/dev/uhid"
100# define AJSDEV "/dev/joy"
101
102# ifdef HAVE_USB_JS
103/*
104 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
105 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
106 * return the uhidN device name. We warn the user of this situation once.
107 */
108static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
109{
110 struct usb_device_info di;
111 int i, a;
112 char *cp;
113
114 for (a = 1; a < USB_MAX_DEVICES; a++) {
115 di.udi_addr = a;
116 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
117 return NULL;
118 for (i = 0; i < USB_MAX_DEVNAMES; i++)
119 if (di.udi_devnames[i][0] &&
120 strcmp(di.udi_devnames[i], dev) == 0) {
121 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
122 strcpy(cp, di.udi_vendor);
123 strcat(cp, " ");
124 strcat(cp, di.udi_product);
125 strncpy(out, cp, outlen - 1);
126 out[outlen - 1] = 0;
127 free( cp );
128 return out;
129 }
130 }
131 return NULL;
132}
133
134static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
135{
136 int i, f;
137 char buf[50];
138 char *cp;
139 static int protection_warned = 0;
140
141 for (i = 0; i < 16; i++) {
142 snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
143 f = open(buf, O_RDONLY);
144 if (f >= 0) {
145 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
146 close(f);
147 if (cp)
148 return 1;
149 }
150 else if (errno == EACCES) {
151 if (!protection_warned) {
152 fgWarning ( "Can't open %s for read!", buf );
153 protection_warned = 1;
154 }
155 }
156 }
157 return 0;
158}
159
160static int fghJoystickInitializeHID(struct os_specific_s *os,
161 int *num_axes, int *num_buttons)
162{
163 int size, is_joystick;
164# ifdef HAVE_USBHID_H
165 int report_id = 0;
166# endif
167 struct hid_data *d;
168 struct hid_item h;
169 report_desc_t rd;
170
171 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
172 {
173 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
174 return FALSE;
175 }
176
177 os->hids = NULL;
178
179# ifdef HAVE_USBHID_H
180 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
181 {
182 /*** XXX {report_id} may not be the right variable? ***/
183 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
184 return FALSE;
185 }
186
187 size = hid_report_size( rd, hid_input, report_id );
188# else
189 size = hid_report_size( rd, 0, hid_input );
190# endif
191 os->hid_data_buf = calloc( 1, size );
192 os->hid_dlen = size;
193
194 is_joystick = 0;
195# ifdef HAVE_USBHID_H
196 d = hid_start_parse( rd, 1 << hid_input, report_id );
197# else
198 d = hid_start_parse( rd, 1 << hid_input );
199# endif
200 while( hid_get_item( d, &h ) )
201 {
202 int usage, page, interesting_hid;
203
204 page = HID_PAGE( h.usage );
205 usage = HID_USAGE( h.usage );
206
207 /* This test is somewhat too simplistic, but this is how MicroSoft
208 * does, so I guess it works for all joysticks/game pads. */
209 is_joystick = is_joystick ||
210 ( h.kind == hid_collection &&
211 page == HUP_GENERIC_DESKTOP &&
212 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
213
214 if( h.kind != hid_input )
215 continue;
216
217 if( !is_joystick )
218 continue;
219
220 interesting_hid = TRUE;
221 if( page == HUP_GENERIC_DESKTOP )
222 {
223 switch( usage )
224 {
225 case HUG_X:
226 case HUG_RX:
227 case HUG_Y:
228 case HUG_RY:
229 case HUG_Z:
230 case HUG_RZ:
231 case HUG_SLIDER:
232 if( *num_axes < _JS_MAX_AXES )
233 {
234 os->axes_usage[ *num_axes ] = usage;
235 ( *num_axes )++;
236 }
237 break;
238 case HUG_HAT_SWITCH:
239 /* Allocate two axes for a hat */
240 if( *num_axes + 1 < _JS_MAX_AXES )
241 {
242 os->axes_usage[ *num_axes ] = usage;
243 (*num_axes)++;
244 os->axes_usage[ *num_axes ] = usage;
245 (*num_axes)++;
246 }
247 break;
248 default:
249 interesting_hid = FALSE;
250 break;
251 }
252 }
253 else if( page == HUP_BUTTON )
254 {
255 interesting_hid = ( usage > 0 ) &&
256 ( usage <= _JS_MAX_BUTTONS );
257
258 if( interesting_hid && usage - 1 > *num_buttons )
259 *num_buttons = usage - 1;
260 }
261
262 if( interesting_hid )
263 {
264 h.next = os->hids;
265 os->hids = calloc( 1, sizeof ( struct hid_item ) );
266 *os->hids = h;
267 }
268 }
269 hid_end_parse( d );
270
271 return os->hids != NULL;
272}
273# endif
274#endif
275
276/*
277 * Functions associated with the "jsJoystick" class in PLIB
278 */
279#if TARGET_HOST_MAC_OSX
280#define K_NUM_DEVICES 32
281int numDevices;
282io_object_t ioDevices[K_NUM_DEVICES];
283
284static void fghJoystickFindDevices ( SFG_Joystick* joy, mach_port_t );
285static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick* joy, io_object_t );
286
287static void fghJoystickEnumerateElements ( SFG_Joystick* joy, CFTypeRef element );
288/* callback for CFArrayApply */
289static void fghJoystickElementEnumerator ( SFG_Joystick* joy, void *element, void* vjs );
290
291static void fghJoystickAddAxisElement ( SFG_Joystick* joy, CFDictionaryRef axis );
292static void fghJoystickAddButtonElement ( SFG_Joystick* joy, CFDictionaryRef button );
293static void fghJoystickAddHatElement ( SFG_Joystick* joy, CFDictionaryRef hat );
294#endif
295
296
297/* External function declarations (mostly platform-specific) */
298extern void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes );
299extern void fgPlatformJoystickOpen( SFG_Joystick* joy );
300extern void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident );
301extern void fgPlatformJoystickClose ( int ident );
302
303/*
304 * The static joystick structure pointer
305 */
306#define MAX_NUM_JOYSTICKS 2
307SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
308
309/*
310 * Read the raw joystick data
311 */
312static void fghJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
313{
314 int i;
315
316 /* Defaults */
317 if( buttons )
318 *buttons = 0;
319
320 if( axes )
321 for( i = 0; i < joy->num_axes; i++ )
322 axes[ i ] = 1500.0f;
323
324 if( joy->error )
325 return;
326
327 fgPlatformJoystickRawRead ( joy, buttons, axes );
328}
329
330/*
331 * Correct the joystick axis data
332 */
333static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
334{
335 if( value < joy->center[ axis ] )
336 {
337 float xx = ( value - joy->center[ axis ] ) / ( joy->center[ axis ] -
338 joy->min[ axis ] );
339
340 if( xx < -joy->saturate[ axis ] )
341 return -1.0f;
342
343 if( xx > -joy->dead_band [ axis ] )
344 return 0.0f;
345
346 xx = ( xx + joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
347 joy->dead_band[ axis ] );
348
349 return ( xx < -1.0f ) ? -1.0f : xx;
350 }
351 else
352 {
353 float xx = ( value - joy->center [ axis ] ) / ( joy->max[ axis ] -
354 joy->center[ axis ] );
355
356 if( xx > joy->saturate[ axis ] )
357 return 1.0f;
358
359 if( xx < joy->dead_band[ axis ] )
360 return 0.0f;
361
362 xx = ( xx - joy->dead_band[ axis ] ) / ( joy->saturate[ axis ] -
363 joy->dead_band[ axis ] );
364
365 return ( xx > 1.0f ) ? 1.0f : xx;
366 }
367}
368
369/*
370 * Read the corrected joystick data
371 */
372static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
373{
374 float raw_axes[ _JS_MAX_AXES ];
375 int i;
376
377 if( joy->error )
378 {
379 if( buttons )
380 *buttons = 0;
381
382 if( axes )
383 for ( i=0; i<joy->num_axes; i++ )
384 axes[ i ] = 0.0f;
385 }
386
387 fghJoystickRawRead( joy, buttons, raw_axes );
388
389 if( axes )
390 for( i=0; i<joy->num_axes; i++ )
391 axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
392}
393
394/*
395 * Happy happy happy joy joy joy (happy new year toudi :D)
396 */
397
398
399#if TARGET_HOST_MAC_OSX
400/** open the IOKit connection, enumerate all the HID devices, add their
401interface references to the static array. We then use the array index
402as the device number when we come to open() the joystick. */
403static int fghJoystickFindDevices ( SFG_Joystick *joy, mach_port_t masterPort )
404{
405 CFMutableDictionaryRef hidMatch = NULL;
406 IOReturn rv = kIOReturnSuccess;
407
408 io_iterator_t hidIterator;
409 io_object_t ioDev;
410
411 /* build a dictionary matching HID devices */
412 hidMatch = IOServiceMatching(kIOHIDDeviceKey);
413
414 rv = IOServiceGetMatchingServices(masterPort, hidMatch, &hidIterator);
415 if (rv != kIOReturnSuccess || !hidIterator) {
416 fgWarning( "no joystick (HID) devices found" );
417 return;
418 }
419
420 /* iterate */
421 while ((ioDev = IOIteratorNext(hidIterator))) {
422 /* filter out keyboard and mouse devices */
423 CFDictionaryRef properties = getCFProperties(ioDev);
424 long usage, page;
425
426 CFTypeRef refPage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsagePageKey));
427 CFTypeRef refUsage = CFDictionaryGetValue (properties, CFSTR(kIOHIDPrimaryUsageKey));
428 CFNumberGetValue((CFNumberRef) refUsage, kCFNumberLongType, &usage);
429 CFNumberGetValue((CFNumberRef) refPage, kCFNumberLongType, &page);
430
431 /* keep only joystick devices */
432 if ( ( page == kHIDPage_GenericDesktop ) && (
433 (usage == kHIDUsage_GD_Joystick)
434 || (usage == kHIDUsage_GD_GamePad)
435 || (usage == kHIDUsage_GD_MultiAxisController)
436 || (usage == kHIDUsage_GD_Hatswitch) /* last two necessary ? */
437 /* add it to the array */
438 ioDevices[numDevices++] = ioDev;
439 }
440
441 IOObjectRelease(hidIterator);
442}
443
444static CFDictionaryRef fghJoystickGetCFProperties ( SFG_Joystick *joy, io_object_t ioDev )
445{
446 IOReturn rv;
447 CFMutableDictionaryRef cfProperties;
448
449#if 0
450 /* comment copied from darwin/SDL_sysjoystick.c */
451 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
452 * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
453 */
454
455 io_registry_entry_t parent1, parent2;
456
457 rv = IORegistryEntryGetParentEntry (ioDev, kIOServicePlane, &parent1);
458 if (rv != kIOReturnSuccess) {
459 fgWarning ( "error getting device entry parent");
460 return NULL;
461 }
462
463 rv = IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2);
464 if (rv != kIOReturnSuccess) {
465 fgWarning ( "error getting device entry parent 2");
466 return NULL;
467 }
468#endif
469
470 rv = IORegistryEntryCreateCFProperties( ioDev /*parent2*/,
471 &cfProperties, kCFAllocatorDefault, kNilOptions);
472 if (rv != kIOReturnSuccess || !cfProperties) {
473 fgWarning ( "error getting device properties");
474 return NULL;
475 }
476
477 return cfProperties;
478}
479
480static void fghJoystickElementEnumerator ( SFG_Joystick *joy, void *element, void* vjs )
481{
482 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
483 fgError ( "%s", "element enumerator passed non-dictionary value");
484 return;
485 }
486
487 static_cast<jsJoystick*>(vjs)->parseElement ( (CFDictionaryRef) element );
488}
489
490/** element enumerator function : pass NULL for top-level*/
491static void fghJoystickEnumerateElements ( SFG_Joystick *joy, CFTypeRef element )
492{
493 FREEGLUT_INTERNAL_ERROR_EXIT( (CFGetTypeID(element) == CFArrayGetTypeID(),
494 "Joystick element type mismatch",
495 "fghJoystickEnumerateElements" );
496
497 CFRange range = {0, CFArrayGetCount ((CFArrayRef)element)};
498 CFArrayApplyFunction((CFArrayRef) element, range,
499 &fghJoystickElementEnumerator, joy );
500}
501
502static void fghJoystickAddAxisElement ( SFG_Joystick *joy, CFDictionaryRef axis )
503{
504 long cookie, lmin, lmax;
505 int index = joy->num_axes++;
506
507 CFNumberGetValue ((CFNumberRef)
508 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementCookieKey) ),
509 kCFNumberLongType, &cookie);
510
511 joy->pJoystick.axisCookies[index] = (IOHIDElementCookie) cookie;
512
513 CFNumberGetValue ((CFNumberRef)
514 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMinKey) ),
515 kCFNumberLongType, &lmin);
516
517 CFNumberGetValue ((CFNumberRef)
518 CFDictionaryGetValue ( axis, CFSTR(kIOHIDElementMaxKey) ),
519 kCFNumberLongType, &lmax);
520
521 joy->min[index] = lmin;
522 joy->max[index] = lmax;
523 joy->dead_band[index] = 0.0;
524 joy->saturate[index] = 1.0;
525 joy->center[index] = (lmax + lmin) * 0.5;
526}
527
528static void fghJoystickAddButtonElement ( SFG_Joystick *joy, CFDictionaryRef button )
529{
530 long cookie;
531 CFNumberGetValue ((CFNumberRef)
532 CFDictionaryGetValue ( button, CFSTR(kIOHIDElementCookieKey) ),
533 kCFNumberLongType, &cookie);
534
535 joy->pJoystick.buttonCookies[num_buttons++] = (IOHIDElementCookie) cookie;
536 /* anything else for buttons? */
537}
538
539static void fghJoystickAddHatElement ( SFG_Joystick *joy, CFDictionaryRef button )
540{
541 /* hatCookies[num_hats++] = (IOHIDElementCookie) cookie; */
542 /* do we map hats to axes or buttons? */
543}
544#endif
545
546/*
547 * Platform-Specific Code
548 */
549
550#if TARGET_HOST_MACINTOSH
551void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
552{
553 int i;
554
555 if ( buttons )
556 {
557 *buttons = 0;
558
559 for ( i = 0; i < joy->num_buttons; i++ )
560 {
561 UInt32 state;
562 int err = ISpElement_GetSimpleState ( joy->pJoystick.isp_elem [ i + ISP_NUM_AXIS ], &state);
563 ISP_CHECK_ERR(err)
564
565 *buttons |= state << i;
566 }
567 }
568
569 if ( axes )
570 {
571 for ( i = 0; i < joy->num_axes; i++ )
572 {
573 UInt32 state;
574 int err = ISpElement_GetSimpleState ( joy->pJoystick.isp_elem [ i ], &state );
575 ISP_CHECK_ERR(err)
576
577 axes [i] = (float) state;
578 }
579 }
580}
581
582
583void fgPlatformJoystickOpen( SFG_Joystick* joy )
584{
585 int i = 0;
586 OSStatus err;
587
588 /* XXX FIXME: get joystick name in Mac */
589
590 err = ISpStartup( );
591
592 if( err == noErr )
593 {
594#define ISP_CHECK_ERR(x) if( x != noErr ) { joy->error = GL_TRUE; return; }
595
596 joy->error = GL_TRUE;
597
598 /* initialize the needs structure */
599 ISpNeed temp_isp_needs[ ISP_NUM_NEEDS ] =
600 {
601 { "\pX-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
602 { "\pY-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
603 { "\pZ-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
604 { "\pR-Axis", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
605 { "\pAxis 4", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
606 { "\pAxis 5", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
607 { "\pAxis 6", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
608 { "\pAxis 7", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
609 { "\pAxis 8", 128, 0, 0, kISpElementKind_Axis, kISpElementLabel_None, 0, 0, 0, 0 },
610
611 { "\pButton 0", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
612 { "\pButton 1", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
613 { "\pButton 2", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
614 { "\pButton 3", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
615 { "\pButton 4", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
616 { "\pButton 5", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
617 { "\pButton 6", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
618 { "\pButton 7", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
619 { "\pButton 8", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
620 { "\pButton 9", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
621 { "\pButton 10", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
622 { "\pButton 11", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
623 { "\pButton 12", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
624 { "\pButton 13", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
625 { "\pButton 14", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
626 { "\pButton 15", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
627 { "\pButton 16", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
628 { "\pButton 17", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
629 { "\pButton 18", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
630 { "\pButton 19", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
631 { "\pButton 20", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
632 { "\pButton 21", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
633 { "\pButton 22", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
634 { "\pButton 23", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
635 { "\pButton 24", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
636 { "\pButton 25", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
637 { "\pButton 26", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
638 { "\pButton 27", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
639 { "\pButton 28", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
640 { "\pButton 29", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
641 { "\pButton 30", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
642 { "\pButton 31", 128, 0, 0, kISpElementKind_Button, kISpElementLabel_Btn_Select, 0, 0, 0, 0 },
643 };
644
645 memcpy( joy->pJoystick.isp_needs, temp_isp_needs, sizeof (temp_isp_needs ) );
646
647
648 /* next two calls allow keyboard and mouse to emulate other input
649 * devices (gamepads, joysticks, etc)
650 */
651 /*
652 err = ISpDevices_ActivateClass ( kISpDeviceClass_Keyboard );
653 ISP_CHECK_ERR(err)
654
655
656 err = ISpDevices_ActivateClass ( kISpDeviceClass_Mouse );
657 ISP_CHECK_ERR(err)
658 */
659
660 err = ISpElement_NewVirtualFromNeeds( ISP_NUM_NEEDS,
661 joy->pJoystick.isp_needs, joy->pJoystick.isp_elem,
662 0 );
663 ISP_CHECK_ERR( err )
664
665 err = ISpInit( ISP_NUM_NEEDS, joy->pJoystick.isp_needs, joy->pJoystick.isp_elem,
666 'freeglut', nil, 0, 128, 0 );
667 ISP_CHECK_ERR( err )
668
669 joy->num_buttons = ISP_NUM_NEEDS - ISP_NUM_AXIS;
670 joy->num_axes = ISP_NUM_AXIS;
671
672 for( i = 0; i < joy->num_axes; i++ )
673 {
674 joy->dead_band[ i ] = 0;
675 joy->saturate [ i ] = 1;
676 joy->center [ i ] = kISpAxisMiddle;
677 joy->max [ i ] = kISpAxisMaximum;
678 joy->min [ i ] = kISpAxisMinimum;
679 }
680
681 joy->error = GL_FALSE;
682 }
683 else
684 joy->num_buttons = joy->num_axes = 0;
685}
686
687
688void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
689{
690 fgJoystick[ ident ]->id = ident;
691 snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d", ident ); /* FIXME */
692 fgJoystick[ ident ]->error = GL_FALSE;
693}
694
695
696void fgPlatformJoystickClose ( int ident )
697{
698 ISpSuspend( );
699 ISpStop( );
700 ISpShutdown( );
701}
702#endif
703
704#if TARGET_HOST_MAC_OSX
705void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
706{
707 int i;
708
709 if ( buttons != NULL )
710 {
711 *buttons = 0;
712
713 for ( i = 0; i < joy->num_buttons; i++ )
714 {
715 IOHIDEventStruct hidEvent;
716 (*(joy->pJoystick.hidDev))->getElementValue ( joy->pJoystick.hidDev, joy->pJoystick.buttonCookies[i], &hidEvent );
717 if ( hidEvent.value )
718 *buttons |= 1 << i;
719 }
720 }
721
722 if ( axes != NULL )
723 {
724 for ( i = 0; i < joy->num_axes; i++ )
725 {
726 IOHIDEventStruct hidEvent;
727 (*(joy->pJoystick.hidDev))->getElementValue ( joy->pJoystick.hidDev, joy->pJoystick.axisCookies[i], &hidEvent );
728 axes[i] = hidEvent.value;
729 }
730 }
731}
732
733
734void fgPlatformJoystickOpen( SFG_Joystick* joy )
735{
736 IOReturn rv;
737 SInt32 score;
738 IOCFPlugInInterface **plugin;
739
740 HRESULT pluginResult;
741
742 CFDictionaryRef props;
743 CFTypeRef topLevelElement;
744
745 if( joy->id >= numDevices )
746 {
747 fgWarning( "device index out of range in fgJoystickOpen()" );
748 return;
749 }
750
751 /* create device interface */
752 rv = IOCreatePlugInInterfaceForService( ioDevices[ joy->id ],
753 kIOHIDDeviceUserClientTypeID,
754 kIOCFPlugInInterfaceID,
755 &plugin, &score );
756
757 if( rv != kIOReturnSuccess )
758 {
759 fgWarning( "error creating plugin for io device" );
760 return;
761 }
762
763 pluginResult = ( *plugin )->QueryInterface(
764 plugin,
765 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
766 &( LPVOID )joy->pJoystick.hidDev
767 );
768
769 if( pluginResult != S_OK )
770 fgWarning ( "QI-ing IO plugin to HID Device interface failed" );
771
772 ( *plugin )->Release( plugin ); /* don't leak a ref */
773 if( joy->pJoystick.hidDev == NULL )
774 return;
775
776 /* store the interface in this instance */
777 rv = ( *( joy->pJoystick.hidDev ) )->open( joy->pJoystick.hidDev, 0 );
778 if( rv != kIOReturnSuccess )
779 {
780 fgWarning( "error opening device interface");
781 return;
782 }
783
784 props = getCFProperties( ioDevices[ joy->id ] );
785
786 /* recursively enumerate all the bits */
787 CFTypeRef topLevelElement =
788 CFDictionaryGetValue( props, CFSTR( kIOHIDElementKey ) );
789 enumerateElements( topLevelElement );
790
791 CFRelease( props );
792}
793
794
795void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
796{
797 fgJoystick[ ident ]->id = ident;
798 fgJoystick[ ident ]->error = GL_FALSE;
799 fgJoystick[ ident ]->num_axes = 0;
800 fgJoystick[ ident ]->num_buttons = 0;
801
802 if( numDevices < 0 )
803 {
804 /* do first-time init (since we can't over-ride jsInit, hmm */
805 numDevices = 0;
806
807 mach_port_t masterPort;
808 IOReturn rv = IOMasterPort( bootstrap_port, &masterPort );
809 if( rv != kIOReturnSuccess )
810 {
811 fgWarning( "error getting master Mach port" );
812 return;
813 }
814 fghJoystickFindDevices( masterPort );
815 }
816
817 if ( ident >= numDevices )
818 {
819 fgJoystick[ ident ]->error = GL_TRUE;
820 return;
821 }
822
823 /* get the name now too */
824 CFDictionaryRef properties = getCFProperties( ioDevices[ ident ] );
825 CFTypeRef ref = CFDictionaryGetValue( properties,
826 CFSTR( kIOHIDProductKey ) );
827 if (!ref)
828 ref = CFDictionaryGetValue(properties, CFSTR( "USB Product Name" ) );
829
830 if( !ref ||
831 !CFStringGetCString( ( CFStringRef )ref, name, 128,
832 CFStringGetSystemEncoding( ) ) )
833 {
834 fgWarning( "error getting device name" );
835 name[ 0 ] = '\0';
836 }
837}
838
839
840void fgPlatformJoystickClose ( int ident )
841{
842 ( *( fgJoystick[ ident ]->pJoystick.hidDev ) )->
843 close( fgJoystick[ ident ]->pJoystick.hidDev );
844}
845#endif
846
847
848
849
850static void fghJoystickOpen( SFG_Joystick* joy )
851{
852 /*
853 * Default values (for no joystick -- each conditional will reset the
854 * error flag)
855 */
856 joy->error = TRUE;
857 joy->num_axes = joy->num_buttons = 0;
858 joy->name[ 0 ] = '\0';
859
860 fgPlatformJoystickOpen ( joy );
861
862}
863
864/*
865 * This function replaces the constructor method in the JS library.
866 */
867static void fghJoystickInit( int ident )
868{
869 if( ident >= MAX_NUM_JOYSTICKS )
870 fgError( "Too large a joystick number: %d", ident );
871
872 if( fgJoystick[ ident ] )
873 fgError( "illegal attempt to initialize joystick device again" );
874
875 fgJoystick[ ident ] =
876 ( SFG_Joystick * )calloc( sizeof( SFG_Joystick ), 1 );
877
878 /* Set defaults */
879 fgJoystick[ ident ]->num_axes = fgJoystick[ ident ]->num_buttons = 0;
880 fgJoystick[ ident ]->error = GL_TRUE;
881
882 fgPlatformJoystickInit( fgJoystick, ident );
883
884 fghJoystickOpen( fgJoystick[ ident ] );
885}
886
887/*
888 * Try initializing all the joysticks (well, both of them)
889 */
890void fgInitialiseJoysticks ( void )
891{
892 if( !fgState.JoysticksInitialised )
893 {
894 int ident ;
895 for ( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
896 fghJoystickInit( ident );
897
898 fgState.JoysticksInitialised = GL_TRUE;
899 }
900}
901
902
903void fgJoystickClose( void )
904{
905 int ident ;
906 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
907 {
908 if( fgJoystick[ ident ] )
909 {
910 fgPlatformJoystickClose ( ident );
911
912 free( fgJoystick[ ident ] );
913 fgJoystick[ ident ] = NULL;
914 /* show joystick has been deinitialized */
915 }
916 }
917}
918
919/*
920 * Polls the joystick and executes the joystick callback hooked to the
921 * window specified in the function's parameter:
922 */
923void fgJoystickPollWindow( SFG_Window* window )
924{
925 float axes[ _JS_MAX_AXES ];
926 int buttons;
927 int ident;
928
929 freeglut_return_if_fail( window );
930 freeglut_return_if_fail( FETCH_WCB( *window, Joystick ) );
931
932 for( ident = 0; ident < MAX_NUM_JOYSTICKS; ident++ )
933 {
934 if( fgJoystick[ident] )
935 {
936 fghJoystickRead( fgJoystick[ident], &buttons, axes );
937
938 if( !fgJoystick[ident]->error )
939 INVOKE_WCB( *window, Joystick,
940 ( buttons,
941 (int) ( axes[ 0 ] * 1000.0f ),
942 (int) ( axes[ 1 ] * 1000.0f ),
943 (int) ( axes[ 2 ] * 1000.0f ) )
944 );
945 }
946 }
947}
948
949/*
950 * Implementation for glutDeviceGet(GLUT_HAS_JOYSTICK)
951 */
952int fgJoystickDetect( void )
953{
954 int ident;
955
956 fgInitialiseJoysticks ();
957
958 if ( !fgState.JoysticksInitialised )
959 return 0;
960
961 for( ident=0; ident<MAX_NUM_JOYSTICKS; ident++ )
962 if( fgJoystick[ident] && !fgJoystick[ident]->error )
963 return 1;
964
965 return 0;
966}
967
968/*
969 * Joystick information, setup and execution functions
970 */
971
972/*
973 * Forces the joystick callback to be executed
974 */
975void FGAPIENTRY glutForceJoystickFunc( void )
976{
977 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutForceJoystickFunc" );
978#if !defined(_WIN32_WCE)
979 freeglut_return_if_fail( fgStructure.CurrentWindow != NULL );
980 freeglut_return_if_fail( FETCH_WCB( *( fgStructure.CurrentWindow ), Joystick ) );
981 fgJoystickPollWindow( fgStructure.CurrentWindow );
982#endif /* !defined(_WIN32_WCE) */
983}
984int glutJoystickGetNumAxes( int ident )
985{
986 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumAxes" );
987 return fgJoystick[ ident ]->num_axes;
988}
989int glutJoystickGetNumButtons( int ident )
990{
991 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetNumButtons" );
992 return fgJoystick[ ident ]->num_buttons;
993}
994int glutJoystickNotWorking( int ident )
995{
996 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickNotWorking" );
997 return fgJoystick[ ident ]->error;
998}
999
1000float glutJoystickGetDeadBand( int ident, int axis )
1001{
1002 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetDeadBand" );
1003 return fgJoystick[ ident ]->dead_band [ axis ];
1004}
1005void glutJoystickSetDeadBand( int ident, int axis, float db )
1006{
1007 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetDeadBand" );
1008 fgJoystick[ ident ]->dead_band[ axis ] = db;
1009}
1010
1011float glutJoystickGetSaturation( int ident, int axis )
1012{
1013 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetSaturation" );
1014 return fgJoystick[ ident ]->saturate[ axis ];
1015}
1016void glutJoystickSetSaturation( int ident, int axis, float st )
1017{
1018 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetSaturation" );
1019 fgJoystick[ ident ]->saturate [ axis ] = st;
1020}
1021
1022void glutJoystickSetMinRange( int ident, float *axes )
1023{
1024 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMinRange" );
1025 memcpy( fgJoystick[ ident ]->min, axes,
1026 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1027}
1028void glutJoystickSetMaxRange( int ident, float *axes )
1029{
1030 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetMaxRange" );
1031 memcpy( fgJoystick[ ident ]->max, axes,
1032 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1033}
1034void glutJoystickSetCenter( int ident, float *axes )
1035{
1036 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickSetCenter" );
1037 memcpy( fgJoystick[ ident ]->center, axes,
1038 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1039}
1040
1041void glutJoystickGetMinRange( int ident, float *axes )
1042{
1043 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMinRange" );
1044 memcpy( axes, fgJoystick[ ident ]->min,
1045 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1046}
1047void glutJoystickGetMaxRange( int ident, float *axes )
1048{
1049 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetMaxRange" );
1050 memcpy( axes, fgJoystick[ ident ]->max,
1051 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1052}
1053void glutJoystickGetCenter( int ident, float *axes )
1054{
1055 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickGetCenter" );
1056 memcpy( axes, fgJoystick[ ident ]->center,
1057 fgJoystick[ ident ]->num_axes * sizeof( float ) );
1058}
1059
1060/*** END OF FILE ***/
1061