]> git.mxchange.org Git - flightgear.git/blob - src/Joystick/joystick.cxx
Start of a ascii-file configurable joystick module.
[flightgear.git] / src / Joystick / joystick.cxx
1 // joystick.cxx -- joystick support
2 //
3 // Written by Curtis Olson, started October 1998.
4 // Amended by Alexander Perry, started May 2000, for lots of game controllers.
5 //
6 // Copyright (C) 1998 - 1999  Curtis L. Olson - curt@flightgear.org
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 //
22 // $Id$
23
24
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28
29 #ifdef HAVE_WINDOWS_H
30 #  include <windows.h>                     
31 #endif
32
33 #include <simgear/debug/logstream.hxx>
34
35 #include <Aircraft/aircraft.hxx>
36 #include <Main/options.hxx>
37 #include <Main/views.hxx>
38
39 #if defined( ENABLE_PLIB_JOYSTICK )
40 #  include <plib/js.h>          // plib include
41 #elif defined( ENABLE_GLUT_JOYSTICK )
42 #  include <GL/glut.h>
43 #  include <simgear/xgl.h>
44 #endif
45
46
47 #include "joystick.hxx"
48
49
50 #if defined( ENABLE_PLIB_JOYSTICK )
51
52 // Maximum number of joystick devices
53 #define jsN 8
54
55 // joystick classes
56 static jsJoystick *( js[jsN] );
57
58 // these will hold the values of the axes
59 // the first points to all the axes, the second by device
60 static int    js_axes;
61 static float *js_ax_all, *( js_ax[jsN] );
62 static int    js_buttons[jsN];
63
64 // these will point to elements of that float array
65 static float *to_elevator, *to_aileron, *to_rudder, *to_throttle;
66 static float *to_viewhat, *to_brakeL, *to_brakeR;
67
68 // these will point to elements of that digital button array
69 static int *to_gearmove, *to_flapup, *to_flapdn, *to_eltrimup, *to_eltrimdn;
70 static int msk_gearmove, msk_flapup, msk_flapdn, msk_eltrimup, msk_eltrimdn;
71
72 // this finds the first unused occasion of a specific channel number
73 // on a controller with a known number of channels.  Remember, when
74 // you use USB devices, they appear in a semi-random order.  Sigh.
75 void to_find_A ( int firstcall, float **ptr, int axes, int axis )
76 {       int a;
77         if (firstcall) (*ptr) = NULL;
78         for ( a = 0; a < jsN; a++ )
79         if ( ( NULL != js[a] )
80           && ( NULL == *ptr )
81           && ( axes == js[a]->getNumAxes() )
82           && ( (js_ax[a])[axis] > 0.5 )
83            )    {       ( *ptr) = (js_ax[a]) + axis;
84                         (**ptr) = 0;
85                 }
86 }
87
88 // this finds a specific button on a given controller device
89 static int to_find_D_zero;
90 void to_find_D ( int butnum, float *ana, int **butptr, int *mask )
91 {       int a;
92         to_find_D_zero = 0;
93         (*butptr) = & to_find_D_zero;
94         (*mask) = ( (int) 1 ) << butnum;
95         if ( NULL != ana )
96          for ( a=0; a < jsN; a++ )
97           if ( ( NULL != js[a] )
98             && ( ana >= js_ax[a] )
99             && ( ana -  js_ax[a] <= js[a]->getNumAxes() )
100                ){       (*butptr) = & ( js_buttons[a] );
101 //                      printf ( "Button %i uses mask %i\n", butnum, *mask );
102                 }
103 }
104
105 // this decides whether we believe the throttle is safe to use
106 static bool sync_throttle=false;
107 static float throttle_tmp=0;
108
109 #define SYNC_TOLERANCE 0.02
110
111 #elif defined( ENABLE_GLUT_JOYSTICK )
112
113 // Do we want these user settable ??
114 static float joy_scale = 1./1000;
115
116 // play with following to get your desired sensitivity
117 static int x_dead_zone = 50;
118 static int y_dead_zone = 2*x_dead_zone;
119
120 // Joystick support using glut -- William Riley -- riley@technologist.com
121
122 // Joystick fixed values for calibration and scaling
123 static float joy_x_max = joy_scale;
124 static float joy_y_max = joy_scale;
125
126 static int joy_z_min = 1000, /* joy_z_ctr=0, */ joy_z_max = -1000;
127 static int joy_z_dead_min = 100, joy_z_dead_max = -100;
128
129 #elif defined( MACOS )
130 #  warning port me: no joystick support
131 #else
132 #  error port me: no joystick support
133 #endif
134
135
136
137 #if defined( ENABLE_GLUT_JOYSTICK )
138
139 // Function called by glutJoystickFunc(), adjusts read values and
140 // passes them to the necessary aircraft control functions
141 void joystick(unsigned int buttonMask, int js_x, int js_y, int js_z)
142 {
143     float joy_x, joy_y, joy_z;
144     // adjust the values to fgfs's scale and allow a 'dead zone' to
145     // reduce jitter code adapted from joystick.c by Michele
146     // F. America - nomimarketing@mail.telepac.pt
147
148     if( js_x > -x_dead_zone && js_x < x_dead_zone) {
149         joy_x = 0.0;
150     } else {
151         joy_x = js_x * joy_scale;
152     }
153
154     if( js_y > -y_dead_zone && js_y < y_dead_zone) {
155         joy_y = 0.0;
156     } else {
157         joy_y = js_y * joy_scale;
158     }
159
160     if( js_z >= joy_z_dead_min && js_z <= joy_z_dead_max ) {
161         joy_z = 0.0;
162     }
163     joy_z = (float)js_z / (float)(joy_z_max - joy_z_min);
164     joy_z = (((joy_z*2.0)+1.0)/2);
165
166     // Pass the values to the control routines
167     controls.set_elevator( -joy_y );
168     controls.set_aileron( joy_x );
169     controls.set_throttle( FGControls::ALL_ENGINES, joy_z );
170 }
171
172 #endif // ENABLE_GLUT_JOYSTICK
173
174
175 // Initialize the joystick(s)
176 int fgJoystickInit( void ) {
177
178     int i, j;
179
180     FG_LOG( FG_INPUT, FG_INFO, "Initializing joystick" );
181
182 #if defined( ENABLE_PLIB_JOYSTICK )
183
184     js_axes = 0;
185     for ( i = 0; i < jsN; i ++ )
186     {   js[i] = new jsJoystick ( i );
187         if ( js[i]->notWorking () ) 
188         {   // not working
189 //BUG       free ( js[i] );
190             js[i] = NULL;
191         } else {
192             j = js[i]->getNumAxes();
193             FG_LOG ( FG_INPUT, FG_INFO, 
194                  "  Joystick " << i  << " detected with " << j << " axes" );
195             // Count axes and set all the deadbands
196             js_axes += j;
197             while ( j>0 )
198                 js[i]->setDeadBand( --j, 0.1 );
199         }
200     }
201
202     // allocate storage for axes values
203     js_ax_all = new float [ js_axes + 1 ];
204     j = 0;
205     for ( i = 0; i < jsN; i ++ )
206       if ( js[i] != NULL )
207       { // Point to the memory
208         js_ax [i] = js_ax_all + j;
209         j += js[i]->getNumAxes();
210       }
211
212     // Warn user if we didn't find anything in the end
213     if ( js_axes == 0 ) {
214         FG_LOG ( FG_INPUT, FG_INFO, "  No joysticks detected" );
215     }
216
217     // Guess channel assignments; do this once and save nightmares later
218     for ( i = 0; i < js_axes; i++ )
219     {   js_ax_all[i] = 1.0;
220         js_buttons[i] = 0;
221     }
222
223     to_find_A ( 1, &to_aileron  , 6, 0 );       // Yoke
224     to_find_A ( 0, &to_aileron  , 2, 0 );       // Analog JS
225     to_find_A ( 1, &to_elevator , 6, 1 );       // Yoke
226     to_find_A ( 0, &to_elevator , 2, 1 );       // Analog JS
227
228     to_find_A ( 1, &to_throttle , 6, 3 );       // Yoke
229     to_find_A ( 0, &to_throttle , 2, 0 );       // Analog JS, presume second
230     to_find_A ( 0, &to_throttle , 4, 2 );       // gaming joystick
231
232     to_find_A ( 1, &to_rudder   , 4, 3 );       // Pedals or gaming joystick
233     to_find_A ( 0, &to_rudder   , 2, 1 );       // Analog JS, maybe second
234
235     to_find_A ( 1, &to_viewhat  , 6, 4 );       // Yoke
236
237     to_find_A ( 1, &to_brakeL   , 4, 0 );       // Pedals
238     to_find_A ( 1, &to_brakeR   , 4, 1 );       // Pedals
239
240     // Derive some of the interesting buttons from the channels
241         // 0 is the push-to-talk, 1 is gear switch
242     to_find_D (  1, to_viewhat  , &to_gearmove, &msk_gearmove );
243         // 2,3 are rudder trim
244         // 4,5 are spare buttons
245         // 8,9 are flaps
246     to_find_D (  9, to_viewhat  , &to_flapup,   &msk_flapup );
247     to_find_D (  8, to_viewhat  , &to_flapdn,   &msk_flapdn );
248     to_find_D (  6, to_viewhat  , &to_eltrimup, &msk_eltrimup );
249     to_find_D (  7, to_viewhat  , &to_eltrimdn, &msk_eltrimdn );
250
251     // I hate doing this sort of thing, but it's overridable from the
252     // command line/config file.  If the user hasn't specified an
253     // autocoordination preference, and if they only have two axes of
254     // joystick, then automatical enable auto_coordination.
255
256     if ( ( current_options.get_auto_coordination() == 
257            fgOPTIONS::FG_AUTO_COORD_NOT_SPECIFIED
258          )
259       && ( js_axes > 0 )
260       && ( js_axes < 3 )
261        )
262     {
263         current_options.set_auto_coordination(fgOPTIONS::FG_AUTO_COORD_ENABLED);
264     }
265     
266     if(current_options.get_trim_mode() > 0) {
267         FG_LOG(FG_INPUT, FG_INFO,
268                "  Waiting for user to synchronize throttle lever...");
269         sync_throttle=true;
270     }
271
272
273 #elif defined( ENABLE_GLUT_JOYSTICK )
274
275     glutJoystickFunc(joystick, 100);
276
277 #elif defined( MACOS )
278 #  warning port me: no joystick support
279 #else
280 #  error port me: no joystick support
281 #endif
282
283     return 1;
284 }
285
286
287 #if defined( ENABLE_PLIB_JOYSTICK )
288
289 // update the control parameters based on joystick intput
290 int fgJoystickRead( void ) {
291     int i;
292
293     // Go and fetch all the readings in one pass
294     for ( i = 0; i < jsN; i++ )
295         if ( NULL != js[i] )
296             js[i]->read( & ( js_buttons[i] ), js_ax[i] ) ;
297
298     // These are the easy ones for now
299     if ( NULL != to_aileron )   controls.set_aileron (   * to_aileron  );
300     if ( NULL != to_elevator )  controls.set_elevator( - * to_elevator );
301     if ( NULL != to_brakeL )    controls.set_brake   ( 0, * to_brakeL );
302     if ( NULL != to_brakeR )    controls.set_brake   ( 1, * to_brakeR );
303                                 // Good! Brakes need half travel to act.
304
305 // No gear implemented yet!
306 //  if ( msk_gearmove & *to_gearmove )  controls.set_gear 
307 //                                              ( 1 - controls.get_gear());
308
309     if ( msk_flapup & *to_flapup )      controls.move_flaps (   0.02 );
310     if ( msk_flapdn & *to_flapdn )      controls.move_flaps ( - 0.02 );
311     if ( msk_eltrimup & *to_eltrimup )
312                                 controls.move_elevator_trim (   0.005 );
313     if ( msk_eltrimdn & *to_eltrimdn )
314                                 controls.move_elevator_trim ( - 0.005 );
315
316     //  Added by William Riley -- riley@technologist.com
317     //  Modified by Alex Perry
318     if ( NULL != to_throttle ) {
319             throttle_tmp=(- * to_throttle + 1) / 2;
320
321             if(sync_throttle == true) {
322                 if (fabs(controls.get_throttle(0)-throttle_tmp)
323                     < SYNC_TOLERANCE)
324                 {
325                     FG_LOG(FG_INPUT, FG_INFO, "  Throttle lever synchronized.");
326                     controls.set_throttle(FGControls::ALL_ENGINES,throttle_tmp);
327                     sync_throttle=false;
328                 }    
329             } else {
330                 controls.set_throttle( FGControls::ALL_ENGINES,throttle_tmp );
331             }
332         } 
333
334     if ( NULL != to_rudder )
335         {
336             if ( current_options.get_auto_coordination() !=
337                   fgOPTIONS::FG_AUTO_COORD_ENABLED ) 
338             {
339                 double dead_zone = 0.0; // 0.4;
340                 double value = * to_rudder;
341                 if (value < -dead_zone) {
342                   value += dead_zone;
343                   value *= 1.0 / (1.0 - dead_zone);
344                 } else if (value > dead_zone) {
345                   value -= dead_zone;
346                   value *= 1.0 / (1.0 - dead_zone);
347                 } else {
348                   value = 0.0;
349                 }
350                 controls.set_rudder(value);
351             }
352         }
353     //  End of William's code
354
355     // Use hat to set view direction
356     // Alex Perry 2000-05-31, based on concept by dpm
357     if ( NULL != to_viewhat )
358     {     double n = * ( to_viewhat + 1 );
359           double e = * ( to_viewhat     );
360           double d;
361           if ( fabs(n) + fabs(e) > 0.1 )
362           {     d = atan2 ( -e, -n );
363                 if ( d < 0 ) d += 2 * FG_PI;
364                 current_view.set_goal_view_offset ( d );
365           }
366     }
367
368     return 1;
369 }
370
371 #endif // ENABLE_PLIB_JOYSTICK
372