5d686b5de9bc48264b8635976d4894b2ed8736c3
[freeglut] / src / x11 / fg_joystick_x11.c
1 /*
2  * freeglut_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
43 /*this should be defined in a header file */
44 #define MAX_NUM_JOYSTICKS  2   
45 extern SFG_Joystick *fgJoystick [ MAX_NUM_JOYSTICKS ];
46
47 void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
48 {
49     int status;
50
51     int i;
52
53 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
54     int len;
55
56     if ( joy->pJoystick.os->is_analog )
57     {
58         int status = read ( joy->pJoystick.os->fd, &joy->pJoystick.os->ajs, sizeof(joy->pJoystick.os->ajs) );
59         if ( status != sizeof(joy->pJoystick.os->ajs) ) {
60             perror ( joy->pJoystick.os->fname );
61             joy->error = GL_TRUE;
62             return;
63         }
64         if ( buttons != NULL )
65             *buttons = ( joy->pJoystick.os->ajs.b1 ? 1 : 0 ) | ( joy->pJoystick.os->ajs.b2 ? 2 : 0 );
66
67         if ( axes != NULL )
68         {
69             axes[0] = (float) joy->pJoystick.os->ajs.x;
70             axes[1] = (float) joy->pJoystick.os->ajs.y;
71         }
72
73         return;
74     }
75
76 #  ifdef HAVE_USB_JS
77     while ( ( len = read ( joy->pJoystick.os->fd, joy->pJoystick.os->hid_data_buf, joy->pJoystick.os->hid_dlen ) ) == joy->pJoystick.os->hid_dlen )
78     {
79         struct hid_item *h;
80
81         for  ( h = joy->pJoystick.os->hids; h; h = h->next )
82         {
83             int d = hid_get_data ( joy->pJoystick.os->hid_data_buf, h );
84
85             int page = HID_PAGE ( h->usage );
86             int usage = HID_USAGE ( h->usage );
87
88             if ( page == HUP_GENERIC_DESKTOP )
89             {
90                 int i;
91                 for ( i = 0; i < joy->num_axes; i++ )
92                     if (joy->pJoystick.os->axes_usage[i] == usage)
93                     {
94                         if (usage == HUG_HAT_SWITCH)
95                         {
96                             if (d < 0 || d > 8)
97                                 d = 0;  /* safety */
98                             joy->pJoystick.os->cache_axes[i] = (float)hatmap_x[d];
99                             joy->pJoystick.os->cache_axes[i + 1] = (float)hatmap_y[d];
100                         }
101                         else
102                         {
103                             joy->pJoystick.os->cache_axes[i] = (float)d;
104                         }
105                         break;
106                     }
107             }
108             else if (page == HUP_BUTTON)
109             {
110                if (usage > 0 && usage < _JS_MAX_BUTTONS + 1)
111                {
112                    if (d)
113                        joy->pJoystick.os->cache_buttons |=  (1 << ( usage - 1 ));
114                    else
115                        joy->pJoystick.os->cache_buttons &= ~(1 << ( usage - 1 ));
116                }
117             }
118         }
119     }
120 #    ifdef HAVE_ERRNO_H
121     if ( len < 0 && errno != EAGAIN )
122 #    else
123     if ( len < 0 )
124 #    endif
125     {
126         perror( joy->pJoystick.os->fname );
127         joy->error = 1;
128     }
129     if ( buttons != NULL ) *buttons = joy->pJoystick.os->cache_buttons;
130     if ( axes    != NULL )
131         memcpy ( axes, joy->pJoystick.os->cache_axes, sizeof(float) * joy->num_axes );
132 #  endif
133 #endif
134
135 #ifdef JS_NEW
136
137     while ( 1 )
138     {
139         status = read ( joy->pJoystick.fd, &joy->pJoystick.js, sizeof(struct js_event) );
140
141         if ( status != sizeof( struct js_event ) )
142         {
143 #  ifdef HAVE_ERRNO_H
144             if ( errno == EAGAIN )
145             {
146                 /* Use the old values */
147                 if ( buttons )
148                     *buttons = joy->pJoystick.tmp_buttons;
149                 if ( axes )
150                     memcpy( axes, joy->pJoystick.tmp_axes,
151                             sizeof( float ) * joy->num_axes );
152                 return;
153             }
154 #  endif
155
156             fgWarning ( "%s", joy->pJoystick.fname );
157             joy->error = GL_TRUE;
158             return;
159         }
160
161         switch ( joy->pJoystick.js.type & ~JS_EVENT_INIT )
162         {
163         case JS_EVENT_BUTTON:
164             if( joy->pJoystick.js.value == 0 ) /* clear the flag */
165                 joy->pJoystick.tmp_buttons &= ~( 1 << joy->pJoystick.js.number );
166             else
167                 joy->pJoystick.tmp_buttons |= ( 1 << joy->pJoystick.js.number );
168             break;
169
170         case JS_EVENT_AXIS:
171             if ( joy->pJoystick.js.number < joy->num_axes )
172             {
173                 joy->pJoystick.tmp_axes[ joy->pJoystick.js.number ] = ( float )joy->pJoystick.js.value;
174
175                 if( axes )
176                     memcpy( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
177             }
178             break;
179
180         default:
181             fgWarning ( "PLIB_JS: Unrecognised /dev/js return!?!" );
182
183             /* use the old values */
184
185             if ( buttons != NULL ) *buttons = joy->pJoystick.tmp_buttons;
186             if ( axes    != NULL )
187                 memcpy ( axes, joy->pJoystick.tmp_axes, sizeof(float) * joy->num_axes );
188
189             return;
190         }
191
192         if( buttons )
193             *buttons = joy->pJoystick.tmp_buttons;
194     }
195 #else
196
197     status = read( joy->pJoystick.fd, &joy->pJoystick.js, JS_RETURN );
198
199     if ( status != JS_RETURN )
200     {
201         fgWarning( "%s", joy->pJoystick.fname );
202         joy->error = GL_TRUE;
203         return;
204     }
205
206     if ( buttons )
207 #    if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
208         *buttons = ( joy->pJoystick.js.b1 ? 1 : 0 ) | ( joy->pJoystick.js.b2 ? 2 : 0 );  /* XXX Should not be here -- BSD is handled earlier */
209 #    else
210         *buttons = joy->pJoystick.js.buttons;
211 #    endif
212
213     if ( axes )
214     {
215         axes[ 0 ] = (float) joy->pJoystick.js.x;
216         axes[ 1 ] = (float) joy->pJoystick.js.y;
217     }
218 #endif
219 }
220
221
222 void fgPlatformJoystickOpen( SFG_Joystick* joy )
223 {
224 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
225         int i = 0;
226        char *cp;
227 #endif
228 #ifdef JS_NEW
229        unsigned char u;
230         int i=0;
231 #else
232 #  if defined( __linux__ ) || TARGET_HOST_SOLARIS
233         int i = 0;
234     int counter = 0;
235 #  endif
236 #endif
237
238 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
239     for( i = 0; i < _JS_MAX_AXES; i++ )
240         joy->pJoystick.os->cache_axes[ i ] = 0.0f;
241
242     joy->pJoystick.os->cache_buttons = 0;
243
244     joy->pJoystick.os->fd = open( joy->pJoystick.os->fname, O_RDONLY | O_NONBLOCK);
245
246 #ifdef HAVE_ERRNO_H
247     if( joy->pJoystick.os->fd < 0 && errno == EACCES )
248         fgWarning ( "%s exists but is not readable by you", joy->pJoystick.os->fname );
249 #endif
250
251     joy->error =( joy->pJoystick.os->fd < 0 );
252
253     if( joy->error )
254         return;
255
256     joy->num_axes = 0;
257     joy->num_buttons = 0;
258     if( joy->pJoystick.os->is_analog )
259     {
260         FILE *joyfile;
261         char joyfname[ 1024 ];
262         int noargs, in_no_axes;
263
264         float axes [ _JS_MAX_AXES ];
265         int buttons[ _JS_MAX_AXES ];
266
267         joy->num_axes    =  2;
268         joy->num_buttons = 32;
269
270         fghJoystickRawRead( joy, buttons, axes );
271         joy->error = axes[ 0 ] < -1000000000.0f;
272         if( joy->error )
273             return;
274
275         snprintf( joyfname, sizeof(joyfname), "%s/.joy%drc", getenv( "HOME" ), joy->id );
276
277         joyfile = fopen( joyfname, "r" );
278         joy->error =( joyfile == NULL );
279         if( joy->error )
280             return;
281
282         noargs = fscanf( joyfile, "%d%f%f%f%f%f%f", &in_no_axes,
283                          &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
284                          &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ] );
285         joy->error = noargs != 7 || in_no_axes != _JS_MAX_AXES;
286         fclose( joyfile );
287         if( joy->error )
288             return;
289
290         for( i = 0; i < _JS_MAX_AXES; i++ )
291         {
292             joy->dead_band[ i ] = 0.0f;
293             joy->saturate [ i ] = 1.0f;
294         }
295
296         return;    /* End of analog code */
297     }
298
299 #    ifdef HAVE_USB_JS
300     if( ! fghJoystickInitializeHID( joy->pJoystick.os, &joy->num_axes,
301                                     &joy->num_buttons ) )
302     {
303         close( joy->pJoystick.os->fd );
304         joy->error = GL_TRUE;
305         return;
306     }
307
308     cp = strrchr( joy->pJoystick.os->fname, '/' );
309     if( cp )
310     {
311         if( fghJoystickFindUSBdev( &cp[1], joy->name, sizeof( joy->name ) ) ==
312             0 )
313             strcpy( joy->name, &cp[1] );
314     }
315
316     if( joy->num_axes > _JS_MAX_AXES )
317         joy->num_axes = _JS_MAX_AXES;
318
319     for( i = 0; i < _JS_MAX_AXES; i++ )
320     {
321         /* We really should get this from the HID, but that data seems
322          * to be quite unreliable for analog-to-USB converters. Punt for
323          * now.
324          */
325         if( joy->pJoystick.os->axes_usage[ i ] == HUG_HAT_SWITCH )
326         {
327             joy->max   [ i ] = 1.0f;
328             joy->center[ i ] = 0.0f;
329             joy->min   [ i ] = -1.0f;
330         }
331         else
332         {
333             joy->max   [ i ] = 255.0f;
334             joy->center[ i ] = 127.0f;
335             joy->min   [ i ] = 0.0f;
336         }
337
338         joy->dead_band[ i ] = 0.0f;
339         joy->saturate[ i ] = 1.0f;
340     }
341 #    endif
342 #endif
343
344 #if defined( __linux__ ) || TARGET_HOST_SOLARIS
345     /* Default for older Linux systems. */
346     joy->num_axes    =  2;
347     joy->num_buttons = 32;
348
349 #    ifdef JS_NEW
350     for( i = 0; i < _JS_MAX_AXES; i++ )
351         joy->pJoystick.tmp_axes[ i ] = 0.0f;
352
353     joy->pJoystick.tmp_buttons = 0;
354 #    endif
355
356     joy->pJoystick.fd = open( joy->pJoystick.fname, O_RDONLY );
357
358     joy->error =( joy->pJoystick.fd < 0 );
359
360     if( joy->error )
361         return;
362
363     /* Set the correct number of axes for the linux driver */
364 #    ifdef JS_NEW
365     /* Melchior Franz's fixes for big-endian Linuxes since writing
366      *  to the upper byte of an uninitialized word doesn't work.
367      *  9 April 2003
368      */
369     ioctl( joy->pJoystick.fd, JSIOCGAXES, &u );
370     joy->num_axes = u;
371     ioctl( joy->pJoystick.fd, JSIOCGBUTTONS, &u );
372     joy->num_buttons = u;
373     ioctl( joy->pJoystick.fd, JSIOCGNAME( sizeof( joy->name ) ), joy->name );
374     fcntl( joy->pJoystick.fd, F_SETFL, O_NONBLOCK );
375 #    endif
376
377     /*
378      * The Linux driver seems to return 512 for all axes
379      * when no stick is present - but there is a chance
380      * that could happen by accident - so it's gotta happen
381      * on both axes for at least 100 attempts.
382      *
383      * PWO: shouldn't be that done somehow wiser on the kernel level?
384      */
385 #    ifndef JS_NEW
386     counter = 0;
387
388     do
389     {
390         fghJoystickRawRead( joy, NULL, joy->center );
391         counter++;
392     } while( !joy->error &&
393              counter < 100 &&
394              joy->center[ 0 ] == 512.0f &&
395              joy->center[ 1 ] == 512.0f );
396
397     if ( counter >= 100 )
398         joy->error = GL_TRUE;
399 #    endif
400
401     for( i = 0; i < _JS_MAX_AXES; i++ )
402     {
403 #    ifdef JS_NEW
404         joy->max   [ i ] =  32767.0f;
405         joy->center[ i ] =      0.0f;
406         joy->min   [ i ] = -32767.0f;
407 #    else
408         joy->max[ i ] = joy->center[ i ] * 2.0f;
409         joy->min[ i ] = 0.0f;
410 #    endif
411         joy->dead_band[ i ] = 0.0f;
412         joy->saturate [ i ] = 1.0f;
413     }
414 #endif
415 }
416
417
418 void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
419 {
420 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
421     fgJoystick[ ident ]->id = ident;
422     fgJoystick[ ident ]->error = GL_FALSE;
423
424     fgJoystick[ ident ]->pJoystick.os = calloc( 1, sizeof( struct os_specific_s ) );
425     memset( fgJoystick[ ident ]->pJoystick.os, 0, sizeof( struct os_specific_s ) );
426     if( ident < USB_IDENT_OFFSET )
427         fgJoystick[ ident ]->pJoystick.os->is_analog = 1;
428     if( fgJoystick[ ident ]->pJoystick.os->is_analog )
429         snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", AJSDEV, ident );
430     else
431         snprintf( fgJoystick[ ident ]->pJoystick.os->fname, sizeof(fgJoystick[ ident ]->pJoystick.os->fname), "%s%d", UHIDDEV,
432                  ident - USB_IDENT_OFFSET );
433 #elif defined( __linux__ )
434     fgJoystick[ ident ]->id = ident;
435     fgJoystick[ ident ]->error = GL_FALSE;
436
437     snprintf( fgJoystick[ident]->pJoystick.fname, sizeof(fgJoystick[ident]->pJoystick.fname), "/dev/input/js%d", ident );
438
439     if( access( fgJoystick[ ident ]->pJoystick.fname, F_OK ) != 0 )
440         snprintf( fgJoystick[ ident ]->pJoystick.fname, sizeof(fgJoystick[ ident ]->pJoystick.fname), "/dev/js%d", ident );
441 #endif
442 }
443
444
445 void fgPlatformJoystickClose ( int ident )
446 {
447 #if defined( __FreeBSD__ ) || defined(__FreeBSD_kernel__) || defined( __NetBSD__ )
448     if( fgJoystick[ident]->pJoystick.os )
449     {
450         if( ! fgJoystick[ ident ]->error )
451             close( fgJoystick[ ident ]->pJoystick.os->fd );
452 #ifdef HAVE_USB_JS
453         if( fgJoystick[ ident ]->pJoystick.os->hids )
454             free (fgJoystick[ ident ]->pJoystick.os->hids);
455         if( fgJoystick[ ident ]->pJoystick.os->hid_data_buf )
456             free( fgJoystick[ ident ]->pJoystick.os->hid_data_buf );
457 #endif
458         free( fgJoystick[ident]->pJoystick.os );
459         }
460 #endif
461
462     if( ! fgJoystick[ident]->error )
463          close( fgJoystick[ ident ]->pJoystick.fd );
464 }
465