1 // joystick.cxx -- joystick support
3 // Written by Curtis Olson, started October 1998.
4 // Amended by Alexander Perry, started May 2000, for lots of game controllers.
6 // Copyright (C) 1998 - 1999 Curtis L. Olson - curt@flightgear.org
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.
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.
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.
33 #include <simgear/debug/logstream.hxx>
35 #include <Aircraft/aircraft.hxx>
36 #include <Main/options.hxx>
37 #include <Main/views.hxx>
39 #if defined( ENABLE_PLIB_JOYSTICK )
40 # include <plib/js.h> // plib include
41 #elif defined( ENABLE_GLUT_JOYSTICK )
43 # include <simgear/xgl.h>
47 #include "joystick.hxx"
50 #if defined( ENABLE_PLIB_JOYSTICK )
52 // Maximum number of joystick devices
56 static jsJoystick *( js[jsN] );
58 // these will hold the values of the axes
59 // the first points to all the axes, the second by device
61 static float *js_ax_all, *( js_ax[jsN] );
62 static int js_buttons[jsN];
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;
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;
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 )
82 for ( a = 0; a < jsN; a++ ) {
83 if ( NULL != js[a] ) {
85 if ( axes == js[a]->getNumAxes() ) {
86 cout << "axis[" << a << "][" << axis << "] = "
87 << (js_ax[a])[axis] << endl;
88 if ( (js_ax[a])[axis] > 0.5 ) {
89 ( *ptr) = (js_ax[a]) + axis;
98 // this finds a specific button on a given controller device
99 static int to_find_D_zero;
100 void to_find_D ( int butnum, float *ana, int **butptr, int *mask )
103 (*butptr) = & to_find_D_zero;
104 (*mask) = ( (int) 1 ) << butnum;
106 for ( a=0; a < jsN; a++ )
107 if ( ( NULL != js[a] )
108 && ( ana >= js_ax[a] )
109 && ( ana - js_ax[a] <= js[a]->getNumAxes() )
110 ){ (*butptr) = & ( js_buttons[a] );
111 // printf ( "Button %i uses mask %i\n", butnum, *mask );
115 // this decides whether we believe the throttle is safe to use
116 static bool sync_throttle=false;
117 static float throttle_tmp=0;
119 #define SYNC_TOLERANCE 0.02
121 #elif defined( ENABLE_GLUT_JOYSTICK )
123 // Do we want these user settable ??
124 static float joy_scale = 1./1000;
126 // play with following to get your desired sensitivity
127 static int x_dead_zone = 50;
128 static int y_dead_zone = 2*x_dead_zone;
130 // Joystick support using glut -- William Riley -- riley@technologist.com
132 // Joystick fixed values for calibration and scaling
133 static float joy_x_max = joy_scale;
134 static float joy_y_max = joy_scale;
136 static int joy_z_min = 1000, /* joy_z_ctr=0, */ joy_z_max = -1000;
137 static int joy_z_dead_min = 100, joy_z_dead_max = -100;
139 #elif defined( MACOS )
140 # warning port me: no joystick support
142 # error port me: no joystick support
147 #if defined( ENABLE_GLUT_JOYSTICK )
149 // Function called by glutJoystickFunc(), adjusts read values and
150 // passes them to the necessary aircraft control functions
151 void joystick(unsigned int buttonMask, int js_x, int js_y, int js_z)
153 float joy_x, joy_y, joy_z;
154 // adjust the values to fgfs's scale and allow a 'dead zone' to
155 // reduce jitter code adapted from joystick.c by Michele
156 // F. America - nomimarketing@mail.telepac.pt
158 if( js_x > -x_dead_zone && js_x < x_dead_zone) {
161 joy_x = js_x * joy_scale;
164 if( js_y > -y_dead_zone && js_y < y_dead_zone) {
167 joy_y = js_y * joy_scale;
170 if( js_z >= joy_z_dead_min && js_z <= joy_z_dead_max ) {
173 joy_z = (float)js_z / (float)(joy_z_max - joy_z_min);
174 joy_z = (((joy_z*2.0)+1.0)/2);
176 // Pass the values to the control routines
177 controls.set_elevator( -joy_y );
178 controls.set_aileron( joy_x );
179 controls.set_throttle( FGControls::ALL_ENGINES, joy_z );
182 #endif // ENABLE_GLUT_JOYSTICK
185 // Initialize the joystick(s)
186 int fgJoystickInit( void ) {
190 FG_LOG( FG_INPUT, FG_INFO, "Initializing joystick" );
192 #if defined( ENABLE_PLIB_JOYSTICK )
195 for ( i = 0; i < jsN; i ++ )
196 { js[i] = new jsJoystick ( i );
197 if ( js[i]->notWorking () )
202 j = js[i]->getNumAxes();
203 FG_LOG ( FG_INPUT, FG_INFO,
204 " Joystick " << i << " detected with " << j << " axes" );
205 // Count axes and set all the deadbands
208 js[i]->setDeadBand( --j, 0.1 );
212 // allocate storage for axes values
213 js_ax_all = new float [ js_axes + 1 ];
215 for ( i = 0; i < jsN; i ++ )
217 { // Point to the memory
218 js_ax [i] = js_ax_all + j;
219 j += js[i]->getNumAxes();
222 // Warn user if we didn't find anything in the end
223 if ( js_axes == 0 ) {
224 FG_LOG ( FG_INPUT, FG_INFO, " No joysticks detected" );
227 // Guess channel assignments; do this once and save nightmares later
228 for ( i = 0; i < js_axes; i++ )
229 { js_ax_all[i] = 1.0;
233 to_find_A ( 1, &to_aileron , 6, 0 ); // Yoke
234 to_find_A ( 0, &to_aileron , 2, 0 ); // Analog JS
235 to_find_A ( 0, &to_aileron , 4, 0 ); // Gaming JS
237 to_find_A ( 1, &to_elevator , 6, 1 ); // Yoke
238 to_find_A ( 0, &to_elevator , 2, 1 ); // Analog JS
239 to_find_A ( 0, &to_elevator , 4, 1 ); // Gaming JS
241 to_find_A ( 1, &to_throttle , 6, 3 ); // Yoke
242 to_find_A ( 0, &to_throttle , 2, 0 ); // Analog JS, presume second
243 to_find_A ( 0, &to_throttle , 4, 3 ); // Game JS
245 to_find_A ( 1, &to_rudder , 4, 2 ); // Pedals or gaming joystick
246 to_find_A ( 0, &to_rudder , 2, 1 ); // Analog JS, maybe second
248 to_find_A ( 1, &to_viewhat , 6, 4 ); // Yoke
250 to_find_A ( 1, &to_brakeL , 4, 0 ); // Pedals
251 to_find_A ( 1, &to_brakeR , 4, 1 ); // Pedals
253 // Derive some of the interesting buttons from the channels
254 // 0 is the push-to-talk, 1 is gear switch
255 to_find_D ( 1, to_viewhat , &to_gearmove, &msk_gearmove );
256 // 2,3 are rudder trim
257 // 4,5 are spare buttons
259 to_find_D ( 9, to_viewhat , &to_flapup, &msk_flapup );
260 to_find_D ( 8, to_viewhat , &to_flapdn, &msk_flapdn );
261 to_find_D ( 6, to_viewhat , &to_eltrimup, &msk_eltrimup );
262 to_find_D ( 7, to_viewhat , &to_eltrimdn, &msk_eltrimdn );
264 // I hate doing this sort of thing, but it's overridable from the
265 // command line/config file. If the user hasn't specified an
266 // autocoordination preference, and if they only have two axes of
267 // joystick, then automatical enable auto_coordination.
269 if ( ( current_options.get_auto_coordination() ==
270 fgOPTIONS::FG_AUTO_COORD_NOT_SPECIFIED
276 current_options.set_auto_coordination(fgOPTIONS::FG_AUTO_COORD_ENABLED);
279 if(current_options.get_trim_mode() > 0) {
280 FG_LOG(FG_INPUT, FG_INFO,
281 " Waiting for user to synchronize throttle lever...");
286 #elif defined( ENABLE_GLUT_JOYSTICK )
288 glutJoystickFunc(joystick, 100);
290 #elif defined( MACOS )
291 # warning port me: no joystick support
293 # error port me: no joystick support
300 #if defined( ENABLE_PLIB_JOYSTICK )
302 // update the control parameters based on joystick intput
303 int fgJoystickRead( void ) {
306 // Go and fetch all the readings in one pass
307 for ( i = 0; i < jsN; i++ )
309 js[i]->read( & ( js_buttons[i] ), js_ax[i] ) ;
311 // These are the easy ones for now
312 if ( NULL != to_aileron ) controls.set_aileron ( * to_aileron );
313 if ( NULL != to_elevator ) controls.set_elevator( - * to_elevator );
314 if ( NULL != to_brakeL ) controls.set_brake ( 0, * to_brakeL );
315 if ( NULL != to_brakeR ) controls.set_brake ( 1, * to_brakeR );
316 // Good! Brakes need half travel to act.
318 // No gear implemented yet!
319 // if ( msk_gearmove & *to_gearmove ) controls.set_gear
320 // ( 1 - controls.get_gear());
322 if ( msk_flapup & *to_flapup ) controls.move_flaps ( 0.02 );
323 if ( msk_flapdn & *to_flapdn ) controls.move_flaps ( - 0.02 );
324 if ( msk_eltrimup & *to_eltrimup )
325 controls.move_elevator_trim ( 0.005 );
326 if ( msk_eltrimdn & *to_eltrimdn )
327 controls.move_elevator_trim ( - 0.005 );
329 // Added by William Riley -- riley@technologist.com
330 // Modified by Alex Perry
331 if ( NULL != to_throttle ) {
332 throttle_tmp=(- * to_throttle + 1) / 2;
334 if(sync_throttle == true) {
335 if (fabs(controls.get_throttle(0)-throttle_tmp)
338 FG_LOG(FG_INPUT, FG_INFO, " Throttle lever synchronized.");
339 controls.set_throttle(FGControls::ALL_ENGINES,throttle_tmp);
343 controls.set_throttle( FGControls::ALL_ENGINES,throttle_tmp );
347 if ( NULL != to_rudder )
349 if ( current_options.get_auto_coordination() !=
350 fgOPTIONS::FG_AUTO_COORD_ENABLED )
352 double dead_zone = 0.0; // 0.4;
353 double value = * to_rudder;
354 if (value < -dead_zone) {
356 value *= 1.0 / (1.0 - dead_zone);
357 } else if (value > dead_zone) {
359 value *= 1.0 / (1.0 - dead_zone);
363 controls.set_rudder(value);
366 // End of William's code
368 // Use hat to set view direction
369 // Alex Perry 2000-05-31, based on concept by dpm
370 if ( NULL != to_viewhat )
371 { double n = * ( to_viewhat + 1 );
372 double e = * ( to_viewhat );
374 if ( fabs(n) + fabs(e) > 0.1 )
375 { d = atan2 ( -e, -n );
376 if ( d < 0 ) d += 2 * FG_PI;
377 current_view.set_goal_view_offset ( d );
384 #endif // ENABLE_PLIB_JOYSTICK