Applied a patch from "extraeme@netbsd.org" to add joystick support.
[freeglut] / src / freeglut_joystick.c
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  * PWO: this is not exactly what Steve Baker has done for PLIB, as I had to convert
29  *      it from C++ to C. And I've also reformatted it a bit (that's my little
30  *      personal deviation :]) I don't really know if it is still portable...
31  *      Steve: could you please add some comments to the code? :)
32  *
33  * FreeBSD port - courtesy of Stephen Montgomery-Smith <stephen@math.missouri.edu>
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #define G_LOG_DOMAIN "freeglut-joystick"
41
42 #include "../include/GL/freeglut.h"
43 #include "freeglut_internal.h"
44
45 /*
46  * PWO: I don't like it at all. It's a mess. Could it be cleared?
47  */
48 #ifdef WIN32
49 #   include <windows.h>
50 #   if defined( __CYGWIN32__ ) || defined( __CYGWIN__ )
51 #       define NEAR /* */
52 #       define FAR  /* */
53 #   endif
54 #   include <mmsystem.h>
55 #   include <string.h>
56 #else
57 #   include <unistd.h>
58 #   include <fcntl.h>
59 #   if defined(__FreeBSD__) || defined(__NetBSD__)
60 #   if __FreeBSD_version >= 500000
61 #       include <sys/joystick.h>
62 #   else
63 #       include <machine/joystick.h>
64 #   endif
65 #       define JS_DATA_TYPE joystick
66 #       define JS_RETURN (sizeof(struct JS_DATA_TYPE))
67 #   elif defined(__linux__)
68 #       include <sys/ioctl.h>
69 #       include <linux/joystick.h>
70 #       include <errno.h>
71
72         /*
73          * Check the joystick driver version
74          */
75 #       ifdef JS_VERSION
76 #           if JS_VERSION >= 0x010000
77 #               define JS_NEW
78 #           endif
79 #       endif
80 #   else
81 #       ifndef JS_DATA_TYPE
82
83             /*
84              * Not Windoze and no joystick driver...
85              *
86              * Well - we'll put these values in and that should
87              * allow the code to at least compile. The JS open
88              * routine should error out and shut off all the code
89              * downstream anyway
90              */
91             struct JS_DATA_TYPE
92             {
93                 int buttons;
94                 int x;
95                 int y;
96             };
97
98 #           define JS_RETURN (sizeof(struct JS_DATA_TYPE))
99 #       endif
100 #   endif
101 #endif
102
103 #ifdef WIN32
104 #   define _JS_MAX_AXES 6
105 #else
106 #   ifdef __FreeBSD__
107 #       define _JS_MAX_AXES 2
108 #   else
109 #       define _JS_MAX_AXES 6
110 #   endif
111 #endif
112
113 typedef struct tagSFG_Joystick SFG_Joystick;
114 struct tagSFG_Joystick
115 {
116 #ifdef __FreeBSD__
117     int         id;
118 #endif
119
120 #ifdef WIN32
121     JOYINFOEX   js;
122     UINT        js_id;
123 #else
124 #   ifdef JS_NEW
125         struct js_event js;
126         int         tmp_buttons;
127         float       tmp_axes[ _JS_MAX_AXES ];
128 #   else
129         struct JS_DATA_TYPE js;
130 #   endif
131
132     char fname[ 128 ];
133     int  fd;
134 #endif
135
136     GLboolean error;
137     int       num_axes;
138     int       num_buttons;
139
140     float dead_band[ _JS_MAX_AXES ];
141     float saturate [ _JS_MAX_AXES ];
142     float center   [ _JS_MAX_AXES ];
143     float max      [ _JS_MAX_AXES ];
144     float min      [ _JS_MAX_AXES ];
145 };
146
147 /*
148  * The static joystick structure pointer
149  */
150 static SFG_Joystick* fgJoystick = NULL;
151
152 /*
153  * Read the raw joystick data
154  */
155 static void fghJoystickRawRead ( SFG_Joystick* joy, int* buttons, float* axes )
156 {
157 #ifdef WIN32
158     MMRESULT status;
159 #else
160     int status;
161 #endif
162
163     int i;
164
165     if( joy->error )
166     {
167         if( buttons )
168             *buttons = 0 ;
169
170         if( axes )
171             for( i=0; i<joy->num_axes; i++ )
172                 axes[ i ] = 1500.0f;
173
174         return;
175     }
176
177 #ifdef WIN32
178     status = joyGetPosEx( joy->js_id, &joy->js );
179
180     if( status != JOYERR_NOERROR )
181     {
182         joy->error = TRUE;
183         return;
184     }
185
186     if( buttons )
187         *buttons = joy->js.dwButtons;
188
189     if( axes )
190     {
191         /*
192          * WARNING - Fall through case clauses!!
193          */
194         switch( joy->num_axes )
195         {
196             case 6: axes[5] = (float) joy->js.dwVpos;
197             case 5: axes[4] = (float) joy->js.dwUpos;
198             case 4: axes[3] = (float) joy->js.dwRpos;
199             case 3: axes[2] = (float) joy->js.dwZpos;
200             case 2: axes[1] = (float) joy->js.dwYpos;
201             case 1: axes[0] = (float) joy->js.dwXpos;
202         }
203     }
204 #else
205 #   ifdef JS_NEW
206
207     while( 1 )
208     {
209         status = read( joy->fd, &joy->js, sizeof(struct js_event) );
210
211         if( status != sizeof(struct js_event) )
212         {
213                 if( errno == EAGAIN )
214                 {
215                     /*
216                      * Use the old values
217                      */
218                 if( buttons ) *buttons = joy->tmp_buttons;
219                     if( axes    ) memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
220                 return;
221                 }
222
223                 fgWarning( "%s", joy->fname );
224                 joy->error = TRUE;
225                 return;
226         }
227
228         switch( joy->js.type & ~JS_EVENT_INIT )
229         {
230             case JS_EVENT_BUTTON:
231                 if ( joy->js.value == 0 ) /* clear the flag */
232                     joy->tmp_buttons &= ~(1 << joy->js.number);
233             else
234                     joy->tmp_buttons |= (1 << joy->js.number);
235                 break;
236
237             case JS_EVENT_AXIS:
238                 joy->tmp_axes[ joy->js.number ] = (float) joy->js.value;
239
240                 if( axes )
241                     memcpy( axes, joy->tmp_axes, sizeof(float) * joy->num_axes );
242                 break;
243         }
244
245         if( buttons )
246                 *buttons = joy->tmp_buttons;
247     }
248 #   else
249
250     status = read( joy->fd, &joy->js, JS_RETURN );
251
252     if( status != JS_RETURN )
253     {
254         fgWarning( "%s", joy->fname );
255         joy->error = TRUE;
256         return;
257     }
258
259     if( buttons )
260 #       if defined(__FreeBSD__) || defined(__NetBSD__)
261         *buttons = (joy->js.b1 ? 1 : 0) | (joy->js.b2 ? 2 : 0);
262 #       else
263         *buttons = joy->js.buttons;
264 #       endif
265
266     if( axes )
267     {
268         axes[ 0 ] = (float) joy->js.x;
269         axes[ 1 ] = (float) joy->js.y;
270     }
271 #   endif
272 #endif
273 }
274
275 /*
276  * Correct the joystick axis data
277  */
278 static float fghJoystickFudgeAxis( SFG_Joystick* joy, float value, int axis )
279 {
280     if( value < joy->center[ axis ] )
281     {
282         float xx = (value - joy->center[ axis ]) / (joy->center[ axis ] - joy->min[ axis ]);
283
284         if( xx < -joy->saturate[ axis ] )
285                 return( -1.0f );
286
287         if( xx > -joy->dead_band [ axis ] )
288                 return(  0.0f );
289
290         xx = (xx + joy->dead_band[ axis ]) / (joy->saturate[ axis ] - joy->dead_band[ axis ]);
291
292         return( ( xx < -1.0f ) ? -1.0f : xx );
293     }
294     else
295     {
296         float xx = (value - joy->center [ axis ]) / (joy->max[ axis ] - joy->center[ axis ]);
297
298         if( xx > joy->saturate[ axis ] )
299             return 1.0f ;
300
301         if( xx < joy->dead_band[ axis ] )
302                 return 0.0f ;
303
304         xx = (xx - joy->dead_band[ axis ]) / (joy->saturate[ axis ] - joy->dead_band[ axis ]);
305
306         return( ( xx > 1.0f ) ? 1.0f : xx );
307     }
308 }
309
310 /*
311  * Read the corrected joystick data
312  */
313 static void fghJoystickRead( SFG_Joystick* joy, int* buttons, float* axes )
314 {
315     float raw_axes[ _JS_MAX_AXES ];
316     int  i;
317
318     if( joy->error )
319     {
320         if( buttons )
321             *buttons = 0;
322
323         if( axes )
324             for ( i=0; i<joy->num_axes ; i++ )
325                 axes[ i ] = 0.0f ;
326     }
327
328     fghJoystickRawRead( joy, buttons, raw_axes );
329
330     if( axes )
331         for( i=0 ; i<joy->num_axes ; i++ )
332             axes[ i ] = fghJoystickFudgeAxis( joy, raw_axes[ i ], i );
333 }
334
335 /*
336  * Happy happy happy joy joy joy (happy new year toudi :D)
337  */
338 static void fghJoystickOpen( SFG_Joystick* joy )
339 {
340 #ifdef WIN32
341     JOYCAPS jsCaps;
342     int     i;
343
344     joy->js.dwFlags = JOY_RETURNALL;
345     joy->js.dwSize  = sizeof( joy->js );
346
347     memset( &jsCaps, 0, sizeof(jsCaps) );
348
349     joy->error    = (joyGetDevCaps( joy->js_id, &jsCaps, sizeof(jsCaps) ) != JOYERR_NOERROR);
350     joy->num_axes = (jsCaps.wNumAxes < _JS_MAX_AXES ) ? jsCaps.wNumAxes : _JS_MAX_AXES;
351
352     /*
353      * WARNING - Fall through case clauses!!
354      */
355     switch( joy->num_axes )
356     {
357     case 6 : joy->min[ 5 ] = (float) jsCaps.wVmin; joy->max[ 5 ] = (float) jsCaps.wVmax;
358     case 5 : joy->min[ 4 ] = (float) jsCaps.wUmin; joy->max[ 4 ] = (float) jsCaps.wUmax;
359     case 4 : joy->min[ 3 ] = (float) jsCaps.wRmin; joy->max[ 3 ] = (float) jsCaps.wRmax;
360     case 3 : joy->min[ 2 ] = (float) jsCaps.wZmin; joy->max[ 2 ] = (float) jsCaps.wZmax;
361     case 2 : joy->min[ 1 ] = (float) jsCaps.wYmin; joy->max[ 1 ] = (float) jsCaps.wYmax;
362     case 1 : joy->min[ 0 ] = (float) jsCaps.wXmin; joy->max[ 0 ] = (float) jsCaps.wXmax; break;
363
364     /*
365      * I guess we have no axes at all
366      */
367     default: joy->error = TRUE; break;
368     }
369
370     /*
371      * Guess all the rest judging on the axes extremals
372      */
373     for( i=0 ; i<joy->num_axes ; i++ )
374     {
375         joy->center   [ i ] = (joy->max[i] + joy->min[i]) * 0.5f;
376         joy->dead_band[ i ] = 0.0f;
377         joy->saturate [ i ] = 1.0f;
378     }
379
380 #else
381 #   ifdef __FreeBSD__
382     int   buttons[ _JS_MAX_AXES ];
383     float axes[ _JS_MAX_AXES ];
384     int   noargs, in_no_axes;
385     char  joyfname[ 1024 ];
386     FILE* joyfile;
387 #   else
388 #       ifndef JS_NEW
389     int counter;
390 #       endif
391 #   endif
392     int i;
393
394     /*
395      * Default for older Linux systems.
396      */
397     joy->num_axes    =  2;
398     joy->num_buttons = 32;
399
400 #   ifdef JS_NEW
401     for( i=0 ; i<_JS_MAX_AXES ; i++ )
402         joy->tmp_axes[ i ] = 0.0f ;
403
404     joy->tmp_buttons = 0 ;
405 #   endif
406
407     joy->fd = open( joy->fname, O_RDONLY );
408
409     joy->error = (joy->fd < 0);
410
411     if( joy->error )
412         return;
413
414 #   ifdef __FreeBSD__
415     fghJoystickRawRead(joy, buttons, axes );
416     joy->error = axes[ 0 ] < -1000000000.0f;
417     if( joy->error )
418       return ;
419
420     sprintf( joyfname, "%s/.joy%drc", getenv( "HOME" ), joy->id );
421
422     joyfile = fopen( joyfname, "r" );
423     joy->error = (joyfile == NULL);
424     if( joy->error )
425       return;
426
427     noargs = fscanf(
428         joyfile, "%d%f%f%f%f%f%f",
429         &in_no_axes,
430         &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
431         &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ]
432     );
433
434     joy->error = (noargs != 7) || (in_no_axes != _JS_MAX_AXES);
435     fclose( joyfile );
436     if( joy->error )
437       return;
438
439     for( i=0 ; i<_JS_MAX_AXES ; i++ )
440     {
441         joy->dead_band[ i ] = 0.0f;
442         joy->saturate [ i ] = 1.0f;
443     }
444 #   else
445
446     /*
447      * Set the correct number of axes for the linux driver
448      */
449 #       ifdef JS_NEW
450     ioctl( joy->fd, JSIOCGAXES   , &joy->num_axes    );
451     ioctl( joy->fd, JSIOCGBUTTONS, &joy->num_buttons );
452     fcntl( joy->fd, F_SETFL, O_NONBLOCK );
453
454 #       endif
455
456     /*
457      * The Linux driver seems to return 512 for all axes
458      * when no stick is present - but there is a chance
459      * that could happen by accident - so it's gotta happen
460      * on both axes for at least 100 attempts.
461      *
462      * PWO: shouldn't be that done somehow wiser on the kernel level?
463      */
464 #       ifndef JS_NEW
465     counter = 0 ;
466
467     do
468     { 
469         fghJoystickRawRead( joy, NULL, joy->center );
470         counter++;
471     } while( !joy->error && counter < 100 && joy->center[ 0 ] == 512.0f && joy->center[ 1 ] == 512.0f );
472    
473     if( counter >= 100 )
474         joy->error = TRUE;
475 #       endif
476
477     for( i=0 ; i<_JS_MAX_AXES ; i++ )
478     {
479 #       ifdef JS_NEW
480         joy->max   [ i ] =  32767.0f;
481         joy->center[ i ] =      0.0f;
482         joy->min   [ i ] = -32767.0f;
483 #       else
484         joy->max[ i ] = joy->center[ i ] * 2.0f;
485         joy->min[ i ] = 0.0f;
486 #       endif
487         joy->dead_band[ i ] = 0.0f ;
488         joy->saturate [ i ] = 1.0f ;
489     }
490 #   endif
491 #endif
492 }
493
494 /*
495  *
496  */
497 void fgJoystickInit( int ident )
498 {
499     /*
500      * Make sure we don't get reinitialized
501      */
502     if( fgJoystick != NULL )
503         fgError( "illegal attemp to initialize joystick device" );
504
505     /*
506      * Have the global joystick structure created
507      */
508     fgJoystick = (SFG_Joystick *)calloc( sizeof(SFG_Joystick), 1 );
509
510 #ifdef WIN32
511     switch( ident )
512     {
513     case 0:  fgJoystick->js_id    = JOYSTICKID1; fghJoystickOpen( fgJoystick ); break;
514     case 1:  fgJoystick->js_id    = JOYSTICKID2; fghJoystickOpen( fgJoystick ); break;
515     default: fgJoystick->num_axes = 0; fgJoystick->error = TRUE;                break;
516     }
517 #else
518
519 #   ifdef __FreeBSD__
520     fgJoystick->id = ident;
521     sprintf( fgJoystick->fname, "/dev/joy%d", ident );
522 #   else
523     sprintf( fgJoystick->fname, "/dev/js%d", ident );
524 #   endif
525
526     /*
527      * Let's try opening the joystick device now:
528      */
529     fghJoystickOpen( fgJoystick );
530 #endif
531 }
532
533 /*
534  *
535  */
536 void fgJoystickClose( void )
537 {
538     if( fgJoystick == NULL )
539         fgError( "illegal attempt to deinitialize joystick device" );
540
541 #ifndef WIN32
542     if( fgJoystick->error != TRUE )
543         close( fgJoystick->fd );
544 #endif
545
546     free ( fgJoystick ) ;
547     fgJoystick = NULL ;  /* show joystick has been deinitialized */
548 }
549
550 /*
551  * Polls the joystick and executes the joystick callback hooked to the
552  * window specified in the function's parameter:
553  */
554 void fgJoystickPollWindow( SFG_Window* window )
555 {
556     float axes[ _JS_MAX_AXES ];
557     int buttons;
558
559     /*
560      * Make sure the joystick device is initialized, the window seems valid
561      * and that there is a joystick callback hooked to it:
562      */
563     freeglut_return_if_fail( fgJoystick != NULL && window != NULL );
564     freeglut_return_if_fail( window->Callbacks.Joystick != NULL );
565
566     /*
567      * Poll the joystick now:
568      */
569     fghJoystickRead( fgJoystick, &buttons, axes );
570
571     /*
572      * Execute the freeglut joystick callback now
573      */
574     window->Callbacks.Joystick(
575         buttons,
576         (int) (axes[ 0 ] * 1000.0f),
577         (int) (axes[ 1 ] * 1000.0f),
578         (int) (axes[ 2 ] * 1000.0f)
579     );
580 }
581
582 /*
583  * PWO: These jsJoystick class methods have not been implemented.
584  *      We might consider adding such functions to freeglut-2.0.
585  */
586 #if 0
587   int  getNumAxes () { return num_axes ; }
588   int  notWorking () { return error ;    }
589
590   float getDeadBand ( int axis )             { return dead_band [ axis ] ; }
591   void  setDeadBand ( int axis, float db )   { dead_band [ axis ] = db   ; }
592
593   float getSaturation ( int axis )           { return saturate [ axis ]  ; }
594   void  setSaturation ( int axis, float st ) { saturate [ axis ] = st    ; }
595
596   void setMinRange ( float *axes ) { memcpy ( min   , axes, num_axes * sizeof(float) ) ; }
597   void setMaxRange ( float *axes ) { memcpy ( max   , axes, num_axes * sizeof(float) ) ; }
598   void setCenter   ( float *axes ) { memcpy ( center, axes, num_axes * sizeof(float) ) ; }
599
600   void getMinRange ( float *axes ) { memcpy ( axes, min   , num_axes * sizeof(float) ) ; }
601   void getMaxRange ( float *axes ) { memcpy ( axes, max   , num_axes * sizeof(float) ) ; }
602   void getCenter   ( float *axes ) { memcpy ( axes, center, num_axes * sizeof(float) ) ; }
603 #endif
604
605 /*** END OF FILE ***/
606
607
608
609