1 /*
2 * fg_joystick_x11.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 * Copied for Platform code by Evan Felix <karcaw at gmail.com>
9 * Creation date: Thur Feb 2 2012
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
25 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 */
28
29 /*
30 * FreeBSD port by Stephen Montgomery-Smith <stephen@math.missouri.edu>
31 *
32 * Redone by John Fay 2/4/04 with another look from the PLIB "js" library.
33 * Many thanks for Steve Baker for permission to pull from that library.
34 */
35
36 #include <GL/freeglut.h>
37 #include "../fg_internal.h"
38 #ifdef HAVE_SYS_PARAM_H
39 # include <sys/param.h>
40 #endif
41
42 #include <fcntl.h>
43
44 /*this should be defined in a header file */
45 #define MAX_NUM_JOYSTICKS 2
46
47 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
48 # ifdef HAVE_USB_JS
49 /*
50 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
51 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
52 * return the uhidN device name. We warn the user of this situation once.
53 */
fghJoystickWalkUSBdev(int f,char * dev,char * out,int outlen)54 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
55 {
56 #if __FreeBSD_version < 800061
57 struct usb_device_info di;
58 int i, a;
59 char *cp;
60
61 for (a = 1; a < USB_MAX_DEVICES; a++) {
62 di.udi_addr = a;
63 if (ioctl(f, USB_DEVICEINFO, &di) != 0)
64 return NULL;
65 for (i = 0; i < USB_MAX_DEVNAMES; i++)
66 if (di.udi_devnames[i][0] &&
67 strcmp(di.udi_devnames[i], dev) == 0) {
68 cp = calloc( 1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
69 strcpy(cp, di.udi_vendor);
70 strcat(cp, " ");
71 strcat(cp, di.udi_product);
72 strncpy(out, cp, outlen - 1);
73 out[outlen - 1] = 0;
74 free( cp );
75 return out;
76 }
77 }
78 #endif
79 return NULL;
80 }
81
fghJoystickFindUSBdev(char * name,char * out,int outlen)82 static int fghJoystickFindUSBdev(char *name, char *out, int outlen)
83 {
84 int i, f;
85 char buf[50];
86 char *cp;
87 static int protection_warned = 0;
88
89 for (i = 0; i < 16; i++) {
90 snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
91 f = open(buf, O_RDONLY);
92 if (f >= 0) {
93 cp = fghJoystickWalkUSBdev(f, name, out, outlen);
94 close(f);
95 if (cp)
96 return 1;
97 }
98 else if (errno == EACCES) {
99 if (!protection_warned) {
100 fgWarning ( "Can't open %s for read!", buf );
101 protection_warned = 1;
102 }
103 }
104 }
105 return 0;
106 }
107
fghJoystickInitializeHID(struct os_specific_s * os,int * num_axes,int * num_buttons)108 static int fghJoystickInitializeHID(struct os_specific_s *os,
109 int *num_axes, int *num_buttons)
110 {
111 int size, is_joystick;
112 # ifdef HAVE_USBHID_H
113 int report_id = 0;
114 # endif
115 struct hid_data *d;
116 struct hid_item h;
117 report_desc_t rd;
118
119 if ( ( rd = hid_get_report_desc( os->fd ) ) == 0 )
120 {
121 fgWarning ( "error: %s: %s", os->fname, strerror( errno ) );
122 return FALSE;
123 }
124
125 os->hids = NULL;
126
127 # ifdef HAVE_USBHID_H
128 if( ioctl( os->fd, USB_GET_REPORT_ID, &report_id ) < 0)
129 {
130 /*** XXX {report_id} may not be the right variable? ***/
131 fgWarning ( "error: %s%d: %s", UHIDDEV, report_id, strerror( errno ) );
132 return FALSE;
133 }
134
135 size = hid_report_size( rd, hid_input, report_id );
136 # else
137 size = hid_report_size( rd, 0, hid_input );
138 # endif
139 os->hid_data_buf = calloc( 1, size );
140 os->hid_dlen = size;
141
142 is_joystick = 0;
143 # ifdef HAVE_USBHID_H
144 d = hid_start_parse( rd, 1 << hid_input, report_id );
145 # else
146 d = hid_start_parse( rd, 1 << hid_input );
147 # endif
148 while( hid_get_item( d, &h ) )
149 {
150 int usage, page, interesting_hid;
151
152 page = HID_PAGE( h.usage );
153 usage = HID_USAGE( h.usage );
154
155 /* This test is somewhat too simplistic, but this is how MicroSoft
156 * does, so I guess it works for all joysticks/game pads. */
157 is_joystick = is_joystick ||
158 ( h.kind == hid_collection &&
159 page == HUP_GENERIC_DESKTOP &&
160 ( usage == HUG_JOYSTICK || usage == HUG_GAME_PAD ) );
161
162 if( h.kind != hid_input )
163 continue;
164
165 if( !is_joystick )
166 continue;
167
168 interesting_hid = TRUE;
169 if( page == HUP_GENERIC_DESKTOP )
170 {
171 switch( usage )
172 {
173 case HUG_X:
174 case HUG_RX:
175 case HUG_Y:
176 case HUG_RY:
177 case HUG_Z:
178 case HUG_RZ:
179 case HUG_SLIDER:
180 if( *num_axes < _JS_MAX_AXES )
181 {
182 os->axes_usage[ *num_axes ] = usage;
183 ( *num_axes )++;
184 }
185 break;
186 case HUG_HAT_SWITCH:
187 /* Allocate two axes for a hat */
188 if( *num_axes + 1 < _JS_MAX_AXES )
189 {
190 os->axes_usage[ *num_axes ] = usage;
191 (*num_axes)++;
192 os->axes_usage[ *num_axes ] = usage;
193 (*num_axes)++;
194 }
195 break;
196 default:
197 interesting_hid = FALSE;
198 break;
199 }
200 }
201 else if( page == HUP_BUTTON )
202 {
203 interesting_hid = ( usage > 0 ) &&
204 ( usage <= _JS_MAX_BUTTONS );
205
206 if( interesting_hid && usage - 1 > *num_buttons )
207 *num_buttons = usage - 1;
208 }
209
210 if( interesting_hid )
211 {
212 h.next = os->hids;
213 os->hids = calloc( 1, sizeof ( struct hid_item ) );
214 *os->hids = h;
215 }
216 }
217 hid_end_parse( d );
218
219 return os->hids != NULL;
220 }
221 # endif /* HAVE_USB_JS */
222 #endif /* FreeBSD or NetBSD */
223
224
225 extern SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
226
fgPlatformJoystickRawRead(SFG_Joystick * joy,int * buttons,float * axes)227 void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
228 {
229 int status;
230
231 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
232 int len;
233
234 if ( joy->pJoystick.os->is_analog )
235 {
236 int status = read ( joy->pJoystick.os->fd, &joy->pJoystick.os->ajs, sizeof(joy->pJoystick.os->ajs) );
237 if ( status != sizeof(joy->pJoystick.os->ajs) ) {
238 perror ( joy->pJoystick.os->fname );
239 joy->error = GL_TRUE;
240 return;
241 }
242 if ( buttons != NULL )
243 *buttons = ( joy->pJoystick.os->ajs.b1 ? 1 : 0 ) | ( joy->pJoystick.os->ajs.b2 ? 2 : 0 );
244
245 if ( axes != NULL )
246 {
247 axes[0] = (float) joy->pJoystick.os->ajs.x;
248 axes[1] = (float) joy->pJoystick.os->ajs.y;
249 }
250
251 return;
252 }
253
254 # ifdef HAVE_USB_JS
255 while ( ( len = read ( joy->pJoystick.os->fd, joy->pJoystick.os->hid_data_buf, joy->pJoystick.os->hid_dlen ) ) == joy->pJoystick.os->hid_dlen )
256 {
257 struct hid_item *h;
258
259 for ( h = joy->pJoystick.os->hids; h; h = h->next )
260 {
261 int d = hid_get_data ( joy->pJoystick.os->hid_data_buf, h );
262
263 int page = HID_PAGE ( h->usage );
264 int usage = HID_USAGE ( h->usage );
265
266 if ( page == HUP_GENERIC_DESKTOP )
267 {
268 int i;
269 for ( i = 0; i < joy->num_axes; i++ )
270 if (joy->pJoystick.os->axes_usage[i] == usage)
271 {
272 if (usage == HUG_HAT_SWITCH)
273 {
274 if (d < 0 || d > 8)
275 d = 0; /* safety */
276 joy->pJoystick.os->cache_axes[i] = (float)hatmap_x[d];
277 joy->pJoystick.os->cache_axes[i + 1] = (float)hatmap_y[d];
278 }
279 else
280 {
281 joy->pJoystick.os->cache_axes[i] = (float)d;
282 }
283 break;
284 }
285 }
286 else if (page == HUP_BUTTON)
287 {
288 if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
289 {
290 if (d)
291 joy->pJoystick.os->cache_buttons |= (1 << ( usage - 1 ));
292 else
293 joy->pJoystick.os->cache_buttons &= ~(1 << ( usage - 1 ));
294 }
295 }
296 }
297 }
298 if ( len < 0 && errno != EAGAIN )
299 {
300 perror( joy->pJoystick.os->fname );
301 joy->error = 1;
302 }
303 if ( buttons != NULL ) *buttons = joy->pJoystick.os->cache_buttons;
304 if ( axes != NULL )
305 memcpy ( axes, joy->pJoystick.os->cache_axes, sizeof(float) * joy->num_axes );
306 # endif
307 #endif
308
309 #ifdef JS_NEW
310
311 while ( 1 )
312 {
313 status = read ( joy->pJoystick.fd, &joy->pJoystick.js, sizeof(struct js_event) );
314
315 if ( status != sizeof( struct js_event ) )
316 {
317 if ( errno == EAGAIN )
318 {
319 /* Use the old values */
320 if ( buttons )
321 *buttons = joy->pJoystick.tmp_buttons;
322 if ( axes )
323 memcpy( axes, joy->pJoystick.tmp_axes,
324 sizeof( float ) * joy->num_axes );
325 return;
326 }
327
328 fgWarning ( "%s", joy->pJoystick.fname );
329 joy->error = GL_TRUE;
330 return;
331 }
332
333 switch ( joy->pJoystick.js.type & ~JS_EVENT_INIT )
334 {
335 case JS_EVENT_BUTTON:
336 if( joy->pJoystick.js.value == 0 ) /* clear the flag */
337 joy->pJoystick.tmp_buttons &= ~( 1 << joy->pJoystick.js.number );
338 else
339 joy->pJoystick.tmp_buttons |= ( 1 << joy->pJoystick.js.number );
340 break;
341
342 case JS_EVENT_AXIS:
343 if ( joy->pJoystick.js.number < joy->num_axes )
344 {
345 joy->pJoystick.tmp_axes[ joy->pJoystick.js.number ] = ( float )joy->pJoystick.js.value;
346
347 if( axes )
348 memcpy( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
349 }
350 break;
351
352 default:
353 fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
354
355 /* use the old values */
356
357 if ( buttons != NULL ) *buttons = joy->pJoystick.tmp_buttons;
358 if ( axes != NULL )
359 memcpy ( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
360
361 return;
362 }
363
364 if( buttons )
365 *buttons = joy->pJoystick.tmp_buttons;
366 }
367 #else
368
369 status = read( joy->pJoystick.fd, &joy->pJoystick.js, JS_RETURN );
370
371 if ( status != JS_RETURN )
372 {
373 fgWarning( "%s", joy->pJoystick.fname );
374 joy->error = GL_TRUE;
375 return;
376 }
377
378 if ( buttons )
379 # if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
380 *buttons = ( joy->pJoystick.js.b1 ? 1 : 0 ) | ( joy->pJoystick.js.b2 ? 2 : 0 ); /* XXX Should not be here -- BSD is handled earlier */
381 # else
382 *buttons = joy->pJoystick.js.buttons;
383 # endif
384
385 if ( axes )
386 {
387 axes[ 0 ] = (float) joy->pJoystick.js.x;
388 axes[ 1 ] = (float) joy->pJoystick.js.y;
389 }
390 #endif
391 }
392
393
fgPlatformJoystickOpen(SFG_Joystick * joy)394 void fgPlatformJoystickOpen( SFG_Joystick* joy )
395 {
396 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
397 int i = 0;
398 char *cp;
399 #endif
400 #ifdef JS_NEW
401 unsigned char u;
402 int i=0;
403 #else
404 # if defined( __linux__ ) || TARGET_HOST_SOLARIS
405 int i = 0;
406 int counter = 0;
407 # endif
408 #endif
409
410 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
411 for( i = 0; i < _JS_MAX_AXES; i++ )
412 joy->pJoystick.os->cache_axes[ i ] = 0.0f;
413
414 joy->pJoystick.os->cache_buttons = 0;
415
416 joy->pJoystick.os->fd = open( joy->pJoystick.os->fname, O_RDONLY | O_NONBLOCK);
417
418 if( joy->pJoystick.os->fd < 0 && errno == EACCES )
419 fgWarning ( "%s exists but is not readable by you", joy->pJoystick.os->fname );
420
421 joy->error =( joy->pJoystick.os->fd < 0 );
422
423 if( joy->error )
424 return;
425
426 joy->num_axes = 0;
427 joy->num_buttons = 0;
428 if( joy->pJoystick.os->is_analog )
429 {
430 FILE *joyfile;
431 char joyfname[ 1024 ];
432 int noargs, in_no_axes;
433
434 float axes [ _JS_MAX_AXES ];
435 int buttons[ _JS_MAX_AXES ];
436
437 joy->num_axes = 2;
438 joy->num_buttons = 32;
439
440 fgJoystickRawRead( joy, buttons, axes );
441 joy->error = axes[ 0 ] < -1000000000.0f;
442 if( joy->error )
443 return;
444
445 snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
446
447 joyfile = fopen( joyfname, "r" );
448 joy->error =( joyfile == NULL );
449 if( joy->error )
450 return;
451
452 noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
453 &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
454 &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
455 joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
456 fclose( joyfile );
457 if( joy->error )
458 return;
459
460 for( i = 0; i < _JS_MAX_AXES; i++ )
461 {
462 joy->dead_band[ i ] = 0.0f;
463 joy->saturate [ i ] = 1.0f;
464 }
465
466 return; /* End of analog code */
467 }
468
469 # ifdef HAVE_USB_JS
470 if( ! fghJoystickInitializeHID( joy->pJoystick.os, &joy->num_axes,
471 &joy->num_buttons ) )
472 {
473 close( joy->pJoystick.os->fd );
474 joy->error = GL_TRUE;
475 return;
476 }
477
478 cp = strrchr( joy->pJoystick.os->fname, '/' );
479 if( cp )
480 {
481 if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
482 0 )
483 strcpy( joy->name, &cp[1] );
484 }
485
486 if( joy->num_axes > _JS_MAX_AXES )
487 joy->num_axes = _JS_MAX_AXES;
488
489 for( i = 0; i < _JS_MAX_AXES; i++ )
490 {
491 /* We really should get this from the HID, but that data seems
492 * to be quite unreliable for analog-to-USB converters. Punt for
493 * now.
494 */
495 if( joy->pJoystick.os->axes_usage[ i ] == HUG_HAT_SWITCH )
496 {
497 joy->max [ i ] = 1.0f;
498 joy->center[ i ] = 0.0f;
499 joy->min [ i ] = -1.0f;
500 }
501 else
502 {
503 joy->max [ i ] = 255.0f;
504 joy->center[ i ] = 127.0f;
505 joy->min [ i ] = 0.0f;
506 }
507
508 joy->dead_band[ i ] = 0.0f;
509 joy->saturate[ i ] = 1.0f;
510 }
511 # endif
512 #endif
513
514 #if defined( __linux__ ) || TARGET_HOST_SOLARIS
515 /* Default for older Linux systems. */
516 joy->num_axes = 2;
517 joy->num_buttons = 32;
518
519 # ifdef JS_NEW
520 for( i = 0; i < _JS_MAX_AXES; i++ )
521 joy->pJoystick.tmp_axes[ i ] = 0.0f;
522
523 joy->pJoystick.tmp_buttons = 0;
524 # endif
525
526 joy->pJoystick.fd = open( joy->pJoystick.fname, O_RDONLY );
527
528 joy->error =( joy->pJoystick.fd < 0 );
529
530 if( joy->error )
531 return;
532
533 /* Set the correct number of axes for the linux driver */
534 # ifdef JS_NEW
535 /* Melchior Franz's fixes for big-endian Linuxes since writing
536 * to the upper byte of an uninitialized word doesn't work.
537 * 9 April 2003
538 */
539 if(ioctl(joy->pJoystick.fd, JSIOCGAXES, &u) != -1)
540 joy->num_axes = u;
541 if(ioctl(joy->pJoystick.fd, JSIOCGBUTTONS, &u) != -1)
542 joy->num_buttons = u;
543 ioctl( joy->pJoystick.fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
544 fcntl(joy->pJoystick.fd, F_SETFL, fcntl(joy->pJoystick.fd, F_GETFL) | O_NONBLOCK);
545 # endif
546
547 /*
548 * The Linux driver seems to return 512 for all axes
549 * when no stick is present - but there is a chance
550 * that could happen by accident - so it's gotta happen
551 * on both axes for at least 100 attempts.
552 *
553 * PWO: shouldn't be that done somehow wiser on the kernel level?
554 */
555 # ifndef JS_NEW
556 counter = 0;
557
558 do
559 {
560 fgJoystickRawRead( joy, NULL, joy->center );
561 counter++;
562 } while( !joy->error &&
563 counter < 100 &&
564 joy->center[ 0 ] == 512.0f &&
565 joy->center[ 1 ] == 512.0f );
566
567 if ( counter >= 100 )
568 joy->error = GL_TRUE;
569 # endif
570
571 for( i = 0; i < _JS_MAX_AXES; i++ )
572 {
573 # ifdef JS_NEW
574 joy->max [ i ] = 32767.0f;
575 joy->center[ i ] = 0.0f;
576 joy->min [ i ] = -32767.0f;
577 # else
578 joy->max[ i ] = joy->center[ i ] * 2.0f;
579 joy->min[ i ] = 0.0f;
580 # endif
581 joy->dead_band[ i ] = 0.0f;
582 joy->saturate [ i ] = 1.0f;
583 }
584 #endif
585 }
586
fgJoystickRawRead(SFG_Joystick * joy,int * buttons,float * axes)587 void fgJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
588 {
589 int i;
590
591 /* Defaults */
592 if( buttons )
593 *buttons = 0;
594
595 if( axes )
596 for( i = 0; i < joy->num_axes; i++ )
597 axes[ i ] = 1500.0f;
598
599 if( joy->error )
600 return;
601
602 fgPlatformJoystickRawRead ( joy, buttons, axes );
603 }
604
fgPlatformJoystickInit(SFG_Joystick * fgJoystick[],int ident)605 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
606 {
607 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
608 fgJoystick[ ident ]->id = ident;
609 fgJoystick[ ident ]->error = GL_FALSE;
610
611 fgJoystick[ ident ]->pJoystick.os = calloc( 1, sizeof( struct os_specific_s ) );
612 memset( fgJoystick[ ident ]->pJoystick.os, 0, sizeof( struct os_specific_s ) );
613 if( ident < USB_IDENT_OFFSET )
614 fgJoystick[ ident ]->pJoystick.os->is_analog = 1;
615 if( fgJoystick[ ident ]->pJoystick.os->is_analog )
616 snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", AJSDEV, ident );
617 else
618 snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", UHIDDEV,
619 ident - USB_IDENT_OFFSET );
620 #elif defined( __linux__ )
621 fgJoystick[ ident ]->id = ident;
622 fgJoystick[ ident ]->error = GL_FALSE;
623
624 snprintf( fgJoystick[ident]->pJoystick.fname, sizeof(fgJoystick[ident]->pJoystick.fname), "/dev/input/js%d", ident );
625
626 if( access( fgJoystick[ ident ]->pJoystick.fname, F_OK ) != 0 )
627 snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d", ident );
628 #endif
629 }
630
631
fgPlatformJoystickClose(int ident)632 void fgPlatformJoystickClose ( int ident )
633 {
634 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
635 if( fgJoystick[ident]->pJoystick.os )
636 {
637 if( ! fgJoystick[ ident ]->error )
638 close( fgJoystick[ ident ]->pJoystick.os->fd );
639 #ifdef HAVE_USB_JS
640 if( fgJoystick[ ident ]->pJoystick.os->hids )
641 free (fgJoystick[ ident ]->pJoystick.os->hids);
642 if( fgJoystick[ ident ]->pJoystick.os->hid_data_buf )
643 free( fgJoystick[ ident ]->pJoystick.os->hid_data_buf );
644 #endif
645 free( fgJoystick[ident]->pJoystick.os );
646 }
647 #endif
648
649 if( ! fgJoystick[ident]->error )
650 close( fgJoystick[ ident ]->pJoystick.fd );
651 }
652
653