30ad4b5133a1c61b25ee21c71fa8c95cf9a69737
[freeglut] / src / x11 / fg_joystick_x11.c
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
45 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
46
47 static int hatmap_x[9] = {0, 0, 1, 1, 1, 0, -1, -1, -1};
48 static int hatmap_y[9] = {0, 1, 1, 0, -1, -1, -1, 0, 1};
49
50 #    ifdef HAVE_USB_JS
51 /*
52 * fghJoystickFindUSBdev (and its helper, fghJoystickWalkUSBdev) try to locate
53 * the full name of a USB device. If /dev/usbN isn't readable, we punt and
54 * return the uhidN device name. We warn the user of this situation once.
55 */
56 static char *fghJoystickWalkUSBdev(int f, char *dev, char *out, int outlen)
57 {
58     struct usb_device_info di;
59     int i, a;
60     char *cp;
61
62     for (a = 1; a < USB_MAX_DEVICES; a++) {
63         di.udi_addr = a;
64         if (ioctl(f, USB_DEVICEINFO, &di) != 0)
65             return NULL;
66         for (i = 0; i < USB_MAX_DEVNAMES; i++)
67             if (di.udi_devnames[i][0] &&
68                 strcmp(di.udi_devnames[i], dev) == 0) {
69                 cp = calloc(1, strlen(di.udi_vendor) + strlen(di.udi_product) + 2);
70                 strcpy(cp, di.udi_vendor);
71                 strcat(cp, " ");
72                 strcat(cp, di.udi_product);
73                 strncpy(out, cp, outlen - 1);
74                 out[outlen - 1] = 0;
75                 free(cp);
76                 return out;
77             }
78     }
79     return NULL;
80 }
81
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
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
222 #endif
223
224
225 /*this should be defined in a header file */
226 #define MAX_NUM_JOYSTICKS  2
227 extern SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
228
229 void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
230 {
231     int status;
232
233 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
234     int len;
235
236     if ( joy->pJoystick.os->is_analog )
237     {
238         int status = read ( joy->pJoystick.os->fd, &joy->pJoystick.os->ajs, sizeof(joy->pJoystick.os->ajs) );
239         if ( status != sizeof(joy->pJoystick.os->ajs) ) {
240             perror ( joy->pJoystick.os->fname );
241             joy->error = GL_TRUE;
242             return;
243         }
244         if ( buttons != NULL )
245             *buttons = ( joy->pJoystick.os->ajs.b1 ? 1 : 0 ) | ( joy->pJoystick.os->ajs.b2 ? 2 : 0 );
246
247         if ( axes != NULL )
248         {
249             axes[0] = (float) joy->pJoystick.os->ajs.x;
250             axes[1] = (float) joy->pJoystick.os->ajs.y;
251         }
252
253         return;
254     }
255
256 #  ifdef HAVE_USB_JS
257     while ( ( len = read ( joy->pJoystick.os->fd, joy->pJoystick.os->hid_data_buf, joy->pJoystick.os->hid_dlen ) ) == joy->pJoystick.os->hid_dlen )
258     {
259         struct hid_item *h;
260
261         for  ( h = joy->pJoystick.os->hids; h; h = h->next )
262         {
263             int d = hid_get_data ( joy->pJoystick.os->hid_data_buf, h );
264
265             int page = HID_PAGE ( h->usage );
266             int usage = HID_USAGE ( h->usage );
267
268             if ( page == HUP_GENERIC_DESKTOP )
269             {
270                 int i;
271                 for ( i = 0; i < joy->num_axes; i++ )
272                     if (joy->pJoystick.os->axes_usage[i] == usage)
273                     {
274                         if (usage == HUG_HAT_SWITCH)
275                         {
276                             if (d < 0 || d > 8)
277                                 d = 0;  /* safety */
278                             joy->pJoystick.os->cache_axes[i] = (float)hatmap_x[d];
279                             joy->pJoystick.os->cache_axes[i + 1] = (float)hatmap_y[d];
280                         }
281                         else
282                         {
283                             joy->pJoystick.os->cache_axes[i] = (float)d;
284                         }
285                         break;
286                     }
287             }
288             else if (page == HUP_BUTTON)
289             {
290                if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
291                {
292                    if (d)
293                        joy->pJoystick.os->cache_buttons |=  (1 << ( usage - 1 ));
294                    else
295                        joy->pJoystick.os->cache_buttons &= ~(1 << ( usage - 1 ));
296                }
297             }
298         }
299     }
300     if ( len < 0 && errno != EAGAIN )
301     {
302         perror( joy->pJoystick.os->fname );
303         joy->error = 1;
304     }
305     if ( buttons != NULL ) *buttons = joy->pJoystick.os->cache_buttons;
306     if ( axes    != NULL )
307         memcpy ( axes, joy->pJoystick.os->cache_axes, sizeof(float) * joy->num_axes );
308 #  endif
309 #endif
310
311 #ifdef JS_NEW
312
313     while ( 1 )
314     {
315         status = read ( joy->pJoystick.fd, &joy->pJoystick.js, sizeof(struct js_event) );
316
317         if ( status != sizeof( struct js_event ) )
318         {
319             if ( errno == EAGAIN )
320             {
321                 /* Use the old values */
322                 if ( buttons )
323                     *buttons = joy->pJoystick.tmp_buttons;
324                 if ( axes )
325                     memcpy( axes, joy->pJoystick.tmp_axes,
326                             sizeof( float ) * joy->num_axes );
327                 return;
328             }
329
330             fgWarning ( "%s", joy->pJoystick.fname );
331             joy->error = GL_TRUE;
332             return;
333         }
334
335         switch ( joy->pJoystick.js.type & ~JS_EVENT_INIT )
336         {
337         case JS_EVENT_BUTTON:
338             if( joy->pJoystick.js.value == 0 ) /* clear the flag */
339                 joy->pJoystick.tmp_buttons &= ~( 1 << joy->pJoystick.js.number );
340             else
341                 joy->pJoystick.tmp_buttons |= ( 1 << joy->pJoystick.js.number );
342             break;
343
344         case JS_EVENT_AXIS:
345             if ( joy->pJoystick.js.number < joy->num_axes )
346             {
347                 joy->pJoystick.tmp_axes[ joy->pJoystick.js.number ] = ( float )joy->pJoystick.js.value;
348
349                 if( axes )
350                     memcpy( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
351             }
352             break;
353
354         default:
355             fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
356
357             /* use the old values */
358
359             if ( buttons != NULL ) *buttons = joy->pJoystick.tmp_buttons;
360             if ( axes    != NULL )
361                 memcpy ( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
362
363             return;
364         }
365
366         if( buttons )
367             *buttons = joy->pJoystick.tmp_buttons;
368     }
369 #else
370
371     status = read( joy->pJoystick.fd, &joy->pJoystick.js, JS_RETURN );
372
373     if ( status != JS_RETURN )
374     {
375         fgWarning( "%s", joy->pJoystick.fname );
376         joy->error = GL_TRUE;
377         return;
378     }
379
380     if ( buttons )
381 #    if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
382         *buttons = ( joy->pJoystick.js.b1 ? 1 : 0 ) | ( joy->pJoystick.js.b2 ? 2 : 0 );  /* XXX Should not be here -- BSD is handled earlier */
383 #    else
384         *buttons = joy->pJoystick.js.buttons;
385 #    endif
386
387     if ( axes )
388     {
389         axes[ 0 ] = (float) joy->pJoystick.js.x;
390         axes[ 1 ] = (float) joy->pJoystick.js.y;
391     }
392 #endif
393 }
394
395
396 void fgPlatformJoystickOpen( SFG_Joystick* joy )
397 {
398 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
399     int i = 0;
400        char *cp;
401 #endif
402 #ifdef JS_NEW
403        unsigned char u;
404     int i=0;
405 #else
406 #  if defined( __linux__ ) || TARGET_HOST_SOLARIS
407     int i = 0;
408     int counter = 0;
409 #  endif
410 #endif
411
412 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
413     for( i = 0; i < _JS_MAX_AXES; i++ )
414         joy->pJoystick.os->cache_axes[ i ] = 0.0f;
415
416     joy->pJoystick.os->cache_buttons = 0;
417
418     joy->pJoystick.os->fd = open( joy->pJoystick.os->fname, O_RDONLY | O_NONBLOCK);
419
420     if( joy->pJoystick.os->fd < 0 && errno == EACCES )
421         fgWarning ( "%s exists but is not readable by you", joy->pJoystick.os->fname );
422
423     joy->error =( joy->pJoystick.os->fd < 0 );
424
425     if( joy->error )
426         return;
427
428     joy->num_axes = 0;
429     joy->num_buttons = 0;
430     if( joy->pJoystick.os->is_analog )
431     {
432         FILE *joyfile;
433         char joyfname[ 1024 ];
434         int noargs, in_no_axes;
435
436         float axes [ _JS_MAX_AXES ];
437         int buttons[ _JS_MAX_AXES ];
438
439         joy->num_axes    =  2;
440         joy->num_buttons = 32;
441
442         fghJoystickRawRead( joy, buttons, axes );
443         joy->error = axes[ 0 ] < -1000000000.0f;
444         if( joy->error )
445             return;
446
447         snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
448
449         joyfile = fopen( joyfname, "r" );
450         joy->error =( joyfile == NULL );
451         if( joy->error )
452             return;
453
454         noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
455                          &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
456                          &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
457         joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
458         fclose( joyfile );
459         if( joy->error )
460             return;
461
462         for( i = 0; i < _JS_MAX_AXES; i++ )
463         {
464             joy->dead_band[ i ] = 0.0f;
465             joy->saturate [ i ] = 1.0f;
466         }
467
468         return;    /* End of analog code */
469     }
470
471 #    ifdef HAVE_USB_JS
472     if( ! fghJoystickInitializeHID( joy->pJoystick.os, &joy->num_axes,
473                                     &joy->num_buttons ) )
474     {
475         close( joy->pJoystick.os->fd );
476         joy->error = GL_TRUE;
477         return;
478     }
479
480     cp = strrchr( joy->pJoystick.os->fname, '/' );
481     if( cp )
482     {
483         if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
484             0 )
485             strcpy( joy->name, &cp[1] );
486     }
487
488     if( joy->num_axes > _JS_MAX_AXES )
489         joy->num_axes = _JS_MAX_AXES;
490
491     for( i = 0; i < _JS_MAX_AXES; i++ )
492     {
493         /* We really should get this from the HID, but that data seems
494          * to be quite unreliable for analog-to-USB converters. Punt for
495          * now.
496          */
497         if( joy->pJoystick.os->axes_usage[ i ] == HUG_HAT_SWITCH )
498         {
499             joy->max   [ i ] = 1.0f;
500             joy->center[ i ] = 0.0f;
501             joy->min   [ i ] = -1.0f;
502         }
503         else
504         {
505             joy->max   [ i ] = 255.0f;
506             joy->center[ i ] = 127.0f;
507             joy->min   [ i ] = 0.0f;
508         }
509
510         joy->dead_band[ i ] = 0.0f;
511         joy->saturate[ i ] = 1.0f;
512     }
513 #    endif
514 #endif
515
516 #if defined( __linux__ ) || TARGET_HOST_SOLARIS
517     /* Default for older Linux systems. */
518     joy->num_axes    =  2;
519     joy->num_buttons = 32;
520
521 #    ifdef JS_NEW
522     for( i = 0; i < _JS_MAX_AXES; i++ )
523         joy->pJoystick.tmp_axes[ i ] = 0.0f;
524
525     joy->pJoystick.tmp_buttons = 0;
526 #    endif
527
528     joy->pJoystick.fd = open( joy->pJoystick.fname, O_RDONLY );
529
530     joy->error =( joy->pJoystick.fd < 0 );
531
532     if( joy->error )
533         return;
534
535     /* Set the correct number of axes for the linux driver */
536 #    ifdef JS_NEW
537     /* Melchior Franz's fixes for big-endian Linuxes since writing
538      *  to the upper byte of an uninitialized word doesn't work.
539      *  9 April 2003
540      */
541     ioctl( joy->pJoystick.fd, JSIOCGAXES, &u );
542     joy->num_axes = u;
543     ioctl( joy->pJoystick.fd, JSIOCGBUTTONS, &u );
544     joy->num_buttons = u;
545     ioctl( joy->pJoystick.fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
546     fcntl( joy->pJoystick.fd, F_SETFL, O_NONBLOCK );
547 #    endif
548
549     /*
550      * The Linux driver seems to return 512 for all axes
551      * when no stick is present - but there is a chance
552      * that could happen by accident - so it's gotta happen
553      * on both axes for at least 100 attempts.
554      *
555      * PWO: shouldn't be that done somehow wiser on the kernel level?
556      */
557 #    ifndef JS_NEW
558     counter = 0;
559
560     do
561     {
562         fghJoystickRawRead( joy, NULL, joy->center );
563         counter++;
564     } while( !joy->error &&
565              counter < 100 &&
566              joy->center[ 0 ] == 512.0f &&
567              joy->center[ 1 ] == 512.0f );
568
569     if ( counter >= 100 )
570         joy->error = GL_TRUE;
571 #    endif
572
573     for( i = 0; i < _JS_MAX_AXES; i++ )
574     {
575 #    ifdef JS_NEW
576         joy->max   [ i ] =  32767.0f;
577         joy->center[ i ] =      0.0f;
578         joy->min   [ i ] = -32767.0f;
579 #    else
580         joy->max[ i ] = joy->center[ i ] * 2.0f;
581         joy->min[ i ] = 0.0f;
582 #    endif
583         joy->dead_band[ i ] = 0.0f;
584         joy->saturate [ i ] = 1.0f;
585     }
586 #endif
587 }
588
589
590 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
591 {
592 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
593     fgJoystick[ ident ]->id = ident;
594     fgJoystick[ ident ]->error = GL_FALSE;
595
596     fgJoystick[ ident ]->pJoystick.os = calloc( 1, sizeof( struct os_specific_s ) );
597     memset( fgJoystick[ ident ]->pJoystick.os, 0, sizeof( struct os_specific_s ) );
598     if( ident < USB_IDENT_OFFSET )
599         fgJoystick[ ident ]->pJoystick.os->is_analog = 1;
600     if( fgJoystick[ ident ]->pJoystick.os->is_analog )
601         snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", AJSDEV, ident );
602     else
603         snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", UHIDDEV,
604                  ident - USB_IDENT_OFFSET );
605 #elif defined( __linux__ )
606     fgJoystick[ ident ]->id = ident;
607     fgJoystick[ ident ]->error = GL_FALSE;
608
609     snprintf( fgJoystick[ident]->pJoystick.fname, sizeof(fgJoystick[ident]->pJoystick.fname), "/dev/input/js%d", ident );
610
611     if( access( fgJoystick[ ident ]->pJoystick.fname, F_OK ) != 0 )
612         snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d", ident );
613 #endif
614 }
615
616
617 void fgPlatformJoystickClose ( int ident )
618 {
619 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__)
620     if( fgJoystick[ident]->pJoystick.os )
621     {
622         if( ! fgJoystick[ ident ]->error )
623             close( fgJoystick[ ident ]->pJoystick.os->fd );
624 #ifdef HAVE_USB_JS
625         if( fgJoystick[ ident ]->pJoystick.os->hids )
626             free (fgJoystick[ ident ]->pJoystick.os->hids);
627         if( fgJoystick[ ident ]->pJoystick.os->hid_data_buf )
628             free( fgJoystick[ ident ]->pJoystick.os->hid_data_buf );
629 #endif
630         free( fgJoystick[ident]->pJoystick.os );
631     }
632 #endif
633
634     if( ! fgJoystick[ident]->error )
635          close( fgJoystick[ ident ]->pJoystick.fd );
636 }
637