Initial revision
[freeglut] / freeglut-1.3 / 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  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
26  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
27  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29  */
30
31 /*
32  * PWO: this is not exactly what Steve Baker has done for PLIB, as I had to convert
33  *      it from C++ to C. And I've also reformatted it a bit (that's my little
34  *      personal deviation :]) I don't really know if it is still portable...
35  *      Steve: could you please add some comments to the code? :)
36  *
37  * FreeBSD port - courtesy of Stephen Montgomery-Smith <stephen@math.missouri.edu>
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #define G_LOG_DOMAIN "freeglut-joystick"
45
46 #include "../include/GL/freeglut.h"
47 #include "../include/GL/freeglut_internal.h"
48
49 /*
50  * PWO: I don't like it at all. It's a mess. Could it be cleared?
51  */
52 #ifdef WIN32
53 #   include <windows.h>
54 #   if defined( __CYGWIN32__ ) || defined( __CYGWIN__ )
55 #       define NEAR /* */
56 #       define FAR  /* */
57 #   endif
58 #   include <mmsystem.h>
59 #   include <string.h>
60 #else
61 #   include <unistd.h>
62 #   include <fcntl.h>
63 #   ifdef __FreeBSD__
64 #       include <machine/joystick.h>
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     gint        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         gint        tmp_buttons;
127         float       tmp_axes[ _JS_MAX_AXES ];
128 #   else
129         JS_DATA_TYPE js;
130 #   endif
131
132     gchar fname[ 128 ];
133     gint  fd;
134 #endif
135
136     gboolean  error;
137     gint      num_axes;
138     gint      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, gint* buttons, float* axes )
156 {
157 #ifdef WIN32
158     MMRESULT status;
159 #else
160     gint status;
161 #endif
162
163     gint 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 = (int) 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         gint 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                 g_warning( 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         g_warning( fname );
255         joy->error = TRUE;
256         return;
257     }
258
259     if( buttons )
260 #       ifdef __FreeBSD__
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, gint 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, gint* buttons, float* axes )
314 {
315     float raw_axes[ _JS_MAX_AXES ];
316     gint  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     gint    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     gint buttons[ _JS_MAX_AXES ];
383     float axes[ _JS_MAX_AXES ];
384     gint  noargs, in_no_axes;
385     gchar joyfname[ 1024 ];
386     FILE* joyfile;
387 #   endif
388
389     gint i, counter;
390
391     /*
392      * Default for older Linux systems.
393      */
394     joy->num_axes    =  2;
395     joy->num_buttons = 32;
396
397 #   ifdef JS_NEW
398     for( i=0 ; i<_JS_MAX_AXES ; i++ )
399         joy->tmp_axes[ i ] = 0.0f ;
400
401     joy->tmp_buttons = 0 ;
402 #   endif
403
404     joy->fd = open( joy->fname, O_RDONLY );
405
406     joy->error = (joy->fd < 0);
407
408     if( joy->error )
409         return;
410
411 #   ifdef __FreeBSD__
412     fghJoystickRawRead( buttons, axes );
413     joy->error = axes[ 0 ] < -1000000000.0f;
414     if( joy->error )
415       return ;
416
417     sprintf( joyfname, "%s/.joy%drc", g_getenv( "HOME" ), id );
418
419     joyfile = fopen( joyfname, "r" );
420     joy->error = (joyfile == NULL);
421     if( joy->error )
422       return;
423
424     noargs = fscanf(
425         joyfile, "%d%f%f%f%f%f%f",
426         &in_no_axes,
427         &joy->min[ 0 ], &joy->center[ 0 ], &joy->max[ 0 ],
428         &joy->min[ 1 ], &joy->center[ 1 ], &joy->max[ 1 ]
429     );
430
431     joy->error = (noargs != 7) || (in_no_axes != _JS_MAX_AXES);
432     fclose( joyfile );
433     if( joy->error )
434       return;
435
436     for( i=0 ; i<_JS_MAX_AXES ; i++ )
437     {
438         dead_band[ i ] = 0.0f;
439         saturate [ i ] = 1.0f;
440     }
441 #   else
442
443     /*
444      * Set the correct number of axes for the linux driver
445      */
446 #       ifdef JS_NEW
447     ioctl( joy->fd, JSIOCGAXES   , &joy->num_axes    );
448     ioctl( joy->fd, JSIOCGBUTTONS, &joy->num_buttons );
449     fcntl( joy->fd, F_SETFL, O_NONBLOCK );
450
451 #       endif
452
453     /*
454      * The Linux driver seems to return 512 for all axes
455      * when no stick is present - but there is a chance
456      * that could happen by accident - so it's gotta happen
457      * on both axes for at least 100 attempts.
458      *
459      * PWO: shouldn't be that done somehow wiser on the kernel level?
460      */
461 #       ifndef JS_NEW
462     counter = 0 ;
463
464     do
465     { 
466         fghJoystickRawRead( joy, NULL, center );
467         counter++;
468     } while( !joy->error && counter < 100 && center[ 0 ] == 512.0f && center[ 1 ] == 512.0f );
469    
470     if( counter >= 100 )
471         joy->error = TRUE;
472 #       endif
473
474     for( i=0 ; i<_JS_MAX_AXES ; i++ )
475     {
476 #       ifdef JS_NEW
477         joy->max   [ i ] =  32767.0f;
478         joy->center[ i ] =      0.0f;
479         joy->min   [ i ] = -32767.0f;
480 #       else
481         joy->max[ i ] = center[ i ] * 2.0f;
482         joy->min[ i ] = 0.0f;
483 #       endif
484         joy->dead_band[ i ] = 0.0f ;
485         joy->saturate [ i ] = 1.0f ;
486     }
487 #   endif
488 #endif
489 }
490
491 /*
492  *
493  */
494 void fgJoystickInit( gint ident )
495 {
496     /*
497      * Make sure we don't get reinitialized
498      */
499     if( fgJoystick != NULL )
500         g_error( "illegal attemp to initialize joystick device" );
501
502     /*
503      * Have the global joystick structure created
504      */
505     fgJoystick = g_new0( SFG_Joystick, 1 );
506
507 #ifdef WIN32
508     switch( ident )
509     {
510     case 0:  fgJoystick->js_id    = JOYSTICKID1; fghJoystickOpen( fgJoystick ); break;
511     case 1:  fgJoystick->js_id    = JOYSTICKID2; fghJoystickOpen( fgJoystick ); break;
512     default: fgJoystick->num_axes = 0; fgJoystick->error = TRUE;                break;
513     }
514 #else
515
516 #   ifdef __FreeBSD__
517     fgJoystick->id = ident;
518     sprintf( fgJoystick->fname, "/dev/joy%d", ident );
519 #   else
520     sprintf( fgJoystick->fname, "/dev/js%d", ident );
521 #   endif
522
523     /*
524      * Let's try opening the joystick device now:
525      */
526     fghJoystickOpen( fgJoystick );
527 #endif
528 }
529
530 /*
531  *
532  */
533 void fgJoystickClose( void )
534 {
535     if( fgJoystick == NULL )
536         g_error( "illegal attempt to deinitialize joystick device" );
537
538 #ifndef WIN32
539     if( fgJoystick->error != TRUE )
540         close( fgJoystick->fd );
541 #endif
542 }
543
544 /*
545  * Polls the joystick and executes the joystick callback hooked to the
546  * window specified in the function's parameter:
547  */
548 void fgJoystickPollWindow( SFG_Window* window )
549 {
550     float axes[ _JS_MAX_AXES ];
551     gint buttons;
552
553     /*
554      * Make sure the joystick device is initialized, the window seems valid
555      * and that there is a joystick callback hooked to it:
556      */
557     freeglut_return_if_fail( fgJoystick == NULL || window == NULL );
558     freeglut_return_if_fail( window->Callbacks.Joystick == NULL );
559
560     /*
561      * Poll the joystick now:
562      */
563     fghJoystickRead( fgJoystick, &buttons, axes );
564
565     /*
566      * Execute the freeglut joystick callback now
567      */
568     window->Callbacks.Joystick(
569         buttons,
570         (gint) (axes[ 0 ] * 1000.0f),
571         (gint) (axes[ 1 ] * 1000.0f),
572         (gint) (axes[ 2 ] * 1000.0f)
573     );
574 }
575
576 /*
577  * PWO: These jsJoystick class methods have not been implemented.
578  *      We might consider adding such functions to freeglut-2.0.
579  */
580 #if 0
581   int  getNumAxes () { return num_axes ; }
582   int  notWorking () { return error ;    }
583
584   float getDeadBand ( int axis )             { return dead_band [ axis ] ; }
585   void  setDeadBand ( int axis, float db )   { dead_band [ axis ] = db   ; }
586
587   float getSaturation ( int axis )           { return saturate [ axis ]  ; }
588   void  setSaturation ( int axis, float st ) { saturate [ axis ] = st    ; }
589
590   void setMinRange ( float *axes ) { memcpy ( min   , axes, num_axes * sizeof(float) ) ; }
591   void setMaxRange ( float *axes ) { memcpy ( max   , axes, num_axes * sizeof(float) ) ; }
592   void setCenter   ( float *axes ) { memcpy ( center, axes, num_axes * sizeof(float) ) ; }
593
594   void getMinRange ( float *axes ) { memcpy ( axes, min   , num_axes * sizeof(float) ) ; }
595   void getMaxRange ( float *axes ) { memcpy ( axes, max   , num_axes * sizeof(float) ) ; }
596   void getCenter   ( float *axes ) { memcpy ( axes, center, num_axes * sizeof(float) ) ; }
597 #endif
598
599 /*** END OF FILE ***/
600
601
602
603