From e7d90b98d2f89dc3bd2b2acff4c110884e4cd1c0 Mon Sep 17 00:00:00 2001 From: curt Date: Thu, 6 Jul 2000 16:26:56 +0000 Subject: [PATCH] Rewrite from David Megginson to use property registry to allow the joystick to be runtime configurable. --- src/Joystick/joystick.cxx | 585 +++++++++++++++++--------------------- src/Joystick/joystick.hxx | 7 +- 2 files changed, 257 insertions(+), 335 deletions(-) diff --git a/src/Joystick/joystick.cxx b/src/Joystick/joystick.cxx index 4da93fa8e..49a1655d3 100644 --- a/src/Joystick/joystick.cxx +++ b/src/Joystick/joystick.cxx @@ -1,9 +1,9 @@ // joystick.cxx -- joystick support // -// Written by Curtis Olson, started October 1998. -// Amended by Alexander Perry, started May 2000, for lots of game controllers. +// Original module written by Curtis Olson, started October 1998. +// Completely rewritten by David Megginson, July 2000. // -// Copyright (C) 1998 - 1999 Curtis L. Olson - curt@flightgear.org +// Copyright (C) 1998 - 2000 Curtis L. Olson - curt@flightgear.org // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -30,356 +30,281 @@ # include #endif -#include - -#include -#include
-#include
- -#if defined( ENABLE_PLIB_JOYSTICK ) -# include // plib include -#elif defined( ENABLE_GLUT_JOYSTICK ) -# include -# include -#endif - +#include +#include +#include +#include #include "joystick.hxx" - -#if defined( ENABLE_PLIB_JOYSTICK ) - -// Maximum number of joystick devices -#define jsN 8 - -// joystick classes -static jsJoystick *( js[jsN] ); - -// these will hold the values of the axes -// the first points to all the axes, the second by device -static int js_axes; -static float *js_ax_all, *( js_ax[jsN] ); -static int js_buttons[jsN]; - -// these will point to elements of that float array -static float *to_elevator, *to_aileron, *to_rudder, *to_throttle; -static float *to_viewhat, *to_brakeL, *to_brakeR; - -// these will point to elements of that digital button array -static int *to_gearmove, *to_flapup, *to_flapdn, *to_eltrimup, *to_eltrimdn; -static int msk_gearmove, msk_flapup, msk_flapdn, msk_eltrimup, msk_eltrimdn; - -// this finds the first unused occasion of a specific channel number -// on a controller with a known number of channels. Remember, when -// you use USB devices, they appear in a semi-random order. Sigh. -void to_find_A ( int firstcall, float **ptr, int axes, int axis ) -{ int a; - - if (firstcall) { - (*ptr) = NULL; - } - - for ( a = 0; a < jsN; a++ ) { - if ( NULL != js[a] ) { - if ( NULL == *ptr ) { - if ( axes == js[a]->getNumAxes() ) { - cout << "axis[" << a << "][" << axis << "] = " - << (js_ax[a])[axis] << endl; - if ( (js_ax[a])[axis] > 0.5 ) { - ( *ptr) = (js_ax[a]) + axis; - (**ptr) = 0; - } - } - } - } - } -} - -// this finds a specific button on a given controller device -static int to_find_D_zero; -void to_find_D ( int butnum, float *ana, int **butptr, int *mask ) -{ int a; - to_find_D_zero = 0; - (*butptr) = & to_find_D_zero; - (*mask) = ( (int) 1 ) << butnum; - if ( NULL != ana ) - for ( a=0; a < jsN; a++ ) - if ( ( NULL != js[a] ) - && ( ana >= js_ax[a] ) - && ( ana - js_ax[a] <= js[a]->getNumAxes() ) - ){ (*butptr) = & ( js_buttons[a] ); -// printf ( "Button %i uses mask %i\n", butnum, *mask ); - } -} - -// this decides whether we believe the throttle is safe to use -static bool sync_throttle=false; -static float throttle_tmp=0; - -#define SYNC_TOLERANCE 0.02 - -#elif defined( ENABLE_GLUT_JOYSTICK ) - -// Do we want these user settable ?? -static float joy_scale = 1./1000; - -// play with following to get your desired sensitivity -static int x_dead_zone = 50; -static int y_dead_zone = 2*x_dead_zone; - -// Joystick support using glut -- William Riley -- riley@technologist.com - -// Joystick fixed values for calibration and scaling -static float joy_x_max = joy_scale; -static float joy_y_max = joy_scale; - -static int joy_z_min = 1000, /* joy_z_ctr=0, */ joy_z_max = -1000; -static int joy_z_dead_min = 100, joy_z_dead_max = -100; - -#elif defined( MACOS ) -# warning port me: no joystick support -#else -# error port me: no joystick support -#endif - - - -#if defined( ENABLE_GLUT_JOYSTICK ) - -// Function called by glutJoystickFunc(), adjusts read values and -// passes them to the necessary aircraft control functions -void joystick(unsigned int buttonMask, int js_x, int js_y, int js_z) +using std::string; + +static const int MAX_JOYSTICKS = 10; +static const int MAX_AXES = 10; + + +/** + * Property names for joysticks and axes. + */ +static const char * jsNames[] = { + "js0", "js1", "js2", "js3", "js4", + "js5", "js6", "js7", "js8", "js9" +}; +static const char * axisNames[] = { + "axis0", "axis1", "axis2", "axis3", "axis4", + "axis5", "axis6", "axis7", "axis8", "axis9" +}; + + +/** + * Settings for a single axis. + */ +struct axis { + axis () : value(0), offset(0.0), factor(1.0), + last_value(9999999), tolerance(0.002) {} + FGValue * value; + float offset; + float factor; + float last_value; + float tolerance; +}; + + +/** + * Settings for a single joystick. + */ +struct joystick { + virtual ~joystick () { delete js; delete axes; } + jsJoystick * js; + axis * axes; +}; + + +/** + * Array of joystick settings. + */ +static joystick joysticks[MAX_JOYSTICKS]; + + +/** + * Set up default values if properties are not specified. + */ +static void +setupDefaults () { - float joy_x, joy_y, joy_z; - // adjust the values to fgfs's scale and allow a 'dead zone' to - // reduce jitter code adapted from joystick.c by Michele - // F. America - nomimarketing@mail.telepac.pt - - if( js_x > -x_dead_zone && js_x < x_dead_zone) { - joy_x = 0.0; - } else { - joy_x = js_x * joy_scale; - } - - if( js_y > -y_dead_zone && js_y < y_dead_zone) { - joy_y = 0.0; - } else { - joy_y = js_y * joy_scale; - } - - if( js_z >= joy_z_dead_min && js_z <= joy_z_dead_max ) { - joy_z = 0.0; - } - joy_z = (float)js_z / (float)(joy_z_max - joy_z_min); - joy_z = (((joy_z*2.0)+1.0)/2); - - // Pass the values to the control routines - controls.set_elevator( -joy_y ); - controls.set_aileron( joy_x ); - controls.set_throttle( FGControls::ALL_ENGINES, joy_z ); + FGPropertyList &props = current_properties; + + // Default axis 0 to aileron + if (!props.getValue("/input/js0/axis0/control")) { + props.setStringValue("/input/js0/axis0/control", "/controls/aileron"); + props.setFloatValue("/input/js0/axis0/dead-band", 0.1); + } + + // Default axis 1 to elevator + if (!props.getValue("/input/js0/axis1/control")) { + props.setStringValue("/input/js0/axis1/control", "/controls/elevator"); + props.setFloatValue("/input/js0/axis1/dead-band", 0.1); + props.setFloatValue("/input/js0/axis1/factor", -1.0); + } + + // Default axis 2 to throttle + // We need to fiddle with the offset + // and factor to make it work + if (!props.getValue("/input/js0/axis2/control")) { + props.setStringValue("/input/js0/axis2/control", "/controls/throttle"); + props.setFloatValue("/input/js0/axis2/dead-band", 0.0); + props.setFloatValue("/input/js0/axis2/offset", -1.0); + props.setFloatValue("/input/js0/axis2/factor", -0.5); + } + + // Default axis 3 to rudder + if (!props.getValue("/input/js0/axis3/control")) { + props.setStringValue("/input/js0/axis3/control", "/controls/rudder"); + props.setFloatValue("/input/js0/axis3/dead-band", 0.3); + } } -#endif // ENABLE_GLUT_JOYSTICK - - -// Initialize the joystick(s) -int fgJoystickInit( void ) { - int i, j; +/** + * Initialize any joysticks found. + */ +int +fgJoystickInit() +{ + bool seen_joystick = false; - FG_LOG( FG_INPUT, FG_INFO, "Initializing joystick" ); + FG_LOG(FG_INPUT, FG_INFO, "Initializing joysticks"); -#if defined( ENABLE_PLIB_JOYSTICK ) + setupDefaults(); - js_axes = 0; - for ( i = 0; i < jsN; i ++ ) - { js[i] = new jsJoystick ( i ); - if ( js[i]->notWorking () ) - { - delete js[i]; - js[i] = NULL; - } else { - j = js[i]->getNumAxes(); - FG_LOG ( FG_INPUT, FG_INFO, - " Joystick " << i << " detected with " << j << " axes" ); - // Count axes and set all the deadbands - js_axes += j; - while ( j>0 ) - js[i]->setDeadBand( --j, 0.1 ); - } + for (int i = 0; i < MAX_JOYSTICKS; i++) { + jsJoystick * js = new jsJoystick(i); + joysticks[i].js = js; + if (js->notWorking()) { + FG_LOG(FG_INPUT, FG_INFO, "Joystick " << i << " not found"); + continue; } - // allocate storage for axes values - js_ax_all = new float [ js_axes + 1 ]; - j = 0; - for ( i = 0; i < jsN; i ++ ) - if ( js[i] != NULL ) - { // Point to the memory - js_ax [i] = js_ax_all + j; - j += js[i]->getNumAxes(); + FG_LOG(FG_INPUT, FG_INFO, "Initializing joystick " << i); + seen_joystick = true; + + // Set up range arrays + float minRange[js->getNumAxes()]; + float maxRange[js->getNumAxes()]; + float center[js->getNumAxes()]; + + // Initialize with default values + js->getMinRange(minRange); + js->getMaxRange(maxRange); + js->getCenter(center); + + // Allocate axes + joysticks[i].axes = new axis[js->getNumAxes()]; + + for (int j = 0; j < min(js->getNumAxes(), MAX_AXES); j++) { + string base = "/input/"; + base += jsNames[i]; + base += '/'; + base += axisNames[j]; + FG_LOG(FG_INPUT, FG_INFO, " Axis " << j << ':'); + + // Control property + string name = base; + name += "/control"; + FGValue * value = current_properties.getValue(name); + if (value == 0) { + FG_LOG(FG_INPUT, FG_INFO, " no control defined"); + continue; } - - // Warn user if we didn't find anything in the end - if ( js_axes == 0 ) { - FG_LOG ( FG_INPUT, FG_INFO, " No joysticks detected" ); + const string &control = value->getStringValue(); + joysticks[i].axes[j].value = current_properties.getValue(control, true); + FG_LOG(FG_INPUT, FG_INFO, " using control " << control); + + // Dead band + name = base; + name += "/dead-band"; + value = current_properties.getValue(name); + if (value != 0) + js->setDeadBand(j, value->getFloatValue()); + FG_LOG(FG_INPUT, FG_INFO, " dead-band is " << js->getDeadBand(j)); + + // Offset + name = base; + name += "/offset"; + value = current_properties.getValue(name); + if (value != 0) + joysticks[i].axes[j].offset = value->getFloatValue(); + FG_LOG(FG_INPUT, FG_INFO, " offset is " + << joysticks[i].axes[j].offset); + + + // Factor + name = base; + name += "/factor"; + value = current_properties.getValue(name); + if (value != 0) + joysticks[i].axes[j].factor = value->getFloatValue(); + FG_LOG(FG_INPUT, FG_INFO, " factor is " + << joysticks[i].axes[j].factor); + + + // Tolerance + name = base; + name += "/tolerance"; + value = current_properties.getValue(name); + if (value != 0) + joysticks[i].axes[j].tolerance = value->getFloatValue(); + FG_LOG(FG_INPUT, FG_INFO, " tolerance is " + << joysticks[i].axes[j].tolerance); + + + // Saturation + name = base; + name += "/saturation"; + value = current_properties.getValue(name); + if (value != 0) + js->setSaturation(j, value->getFloatValue()); + FG_LOG(FG_INPUT, FG_INFO, " saturation is " << js->getSaturation(j)); + + // Minimum range + name = base; + name += "/min-range"; + value = current_properties.getValue(name); + if (value != 0) + minRange[j] = value->getFloatValue(); + FG_LOG(FG_INPUT, FG_INFO, " min-range is " << minRange[j]); + + // Maximum range + name = base; + name += "/max-range"; + value = current_properties.getValue(name); + if (value != 0) + maxRange[j] = value->getFloatValue(); + FG_LOG(FG_INPUT, FG_INFO, " max-range is " << maxRange[j]); + + // Center + name = base; + name += "/center"; + value = current_properties.getValue(name); + if (value != 0) + center[j] = value->getFloatValue(); + FG_LOG(FG_INPUT, FG_INFO, " center is " << center[j]); } - // Guess channel assignments; do this once and save nightmares later - for ( i = 0; i < js_axes; i++ ) - { js_ax_all[i] = 1.0; - js_buttons[i] = 0; - } - - to_find_A ( 1, &to_aileron , 6, 0 ); // Yoke - to_find_A ( 0, &to_aileron , 2, 0 ); // Analog JS - to_find_A ( 0, &to_aileron , 4, 0 ); // Gaming JS - - to_find_A ( 1, &to_elevator , 6, 1 ); // Yoke - to_find_A ( 0, &to_elevator , 2, 1 ); // Analog JS - to_find_A ( 0, &to_elevator , 4, 1 ); // Gaming JS - - to_find_A ( 1, &to_throttle , 6, 3 ); // Yoke - to_find_A ( 0, &to_throttle , 2, 0 ); // Analog JS, presume second - to_find_A ( 0, &to_throttle , 4, 3 ); // Game JS - - to_find_A ( 1, &to_rudder , 4, 2 ); // Pedals or gaming joystick - to_find_A ( 0, &to_rudder , 2, 1 ); // Analog JS, maybe second - - to_find_A ( 1, &to_viewhat , 6, 4 ); // Yoke - - to_find_A ( 1, &to_brakeL , 4, 0 ); // Pedals - to_find_A ( 1, &to_brakeR , 4, 1 ); // Pedals - - // Derive some of the interesting buttons from the channels - // 0 is the push-to-talk, 1 is gear switch - to_find_D ( 1, to_viewhat , &to_gearmove, &msk_gearmove ); - // 2,3 are rudder trim - // 4,5 are spare buttons - // 8,9 are flaps - to_find_D ( 9, to_viewhat , &to_flapup, &msk_flapup ); - to_find_D ( 8, to_viewhat , &to_flapdn, &msk_flapdn ); - to_find_D ( 6, to_viewhat , &to_eltrimup, &msk_eltrimup ); - to_find_D ( 7, to_viewhat , &to_eltrimdn, &msk_eltrimdn ); - - // I hate doing this sort of thing, but it's overridable from the - // command line/config file. If the user hasn't specified an - // autocoordination preference, and if they only have two axes of - // joystick, then automatical enable auto_coordination. - - if ( ( current_options.get_auto_coordination() == - fgOPTIONS::FG_AUTO_COORD_NOT_SPECIFIED - ) - && ( js_axes > 0 ) - && ( js_axes < 3 ) - ) - { - current_options.set_auto_coordination(fgOPTIONS::FG_AUTO_COORD_ENABLED); - } - - if(current_options.get_trim_mode() > 0) { - FG_LOG(FG_INPUT, FG_INFO, - " Waiting for user to synchronize throttle lever..."); - sync_throttle=true; - } + js->setMinRange(minRange); + js->setMaxRange(maxRange); + js->setCenter(center); + } + if (seen_joystick) + FG_LOG(FG_INPUT, FG_INFO, "Done initializing joysticks"); + else + FG_LOG(FG_INPUT, FG_ALERT, "No joysticks detected"); -#elif defined( ENABLE_GLUT_JOYSTICK ) + return seen_joystick; +} - glutJoystickFunc(joystick, 100); -#elif defined( MACOS ) -# warning port me: no joystick support -#else -# error port me: no joystick support -#endif - - return 1; -} +/** + * Update property values based on the joystick state(s). + */ +int +fgJoystickRead() +{ + int buttons; + for (int i = 0; i < MAX_JOYSTICKS; i++) { + jsJoystick * js = joysticks[i].js; + float axis_values[js->getNumAxes()]; + if (js->notWorking()) { + continue; + } -#if defined( ENABLE_PLIB_JOYSTICK ) - -// update the control parameters based on joystick intput -int fgJoystickRead( void ) { - int i; - - // Go and fetch all the readings in one pass - for ( i = 0; i < jsN; i++ ) - if ( NULL != js[i] ) - js[i]->read( & ( js_buttons[i] ), js_ax[i] ) ; - - // These are the easy ones for now - if ( NULL != to_aileron ) controls.set_aileron ( * to_aileron ); - if ( NULL != to_elevator ) controls.set_elevator( - * to_elevator ); - if ( NULL != to_brakeL ) controls.set_brake ( 0, * to_brakeL ); - if ( NULL != to_brakeR ) controls.set_brake ( 1, * to_brakeR ); - // Good! Brakes need half travel to act. - -// No gear implemented yet! -// if ( msk_gearmove & *to_gearmove ) controls.set_gear -// ( 1 - controls.get_gear()); - - if ( msk_flapup & *to_flapup ) controls.move_flaps ( 0.02 ); - if ( msk_flapdn & *to_flapdn ) controls.move_flaps ( - 0.02 ); - if ( msk_eltrimup & *to_eltrimup ) - controls.move_elevator_trim ( 0.005 ); - if ( msk_eltrimdn & *to_eltrimdn ) - controls.move_elevator_trim ( - 0.005 ); - - // Added by William Riley -- riley@technologist.com - // Modified by Alex Perry - if ( NULL != to_throttle ) { - throttle_tmp=(- * to_throttle + 1) / 2; - - if(sync_throttle == true) { - if (fabs(controls.get_throttle(0)-throttle_tmp) - < SYNC_TOLERANCE) - { - FG_LOG(FG_INPUT, FG_INFO, " Throttle lever synchronized."); - controls.set_throttle(FGControls::ALL_ENGINES,throttle_tmp); - sync_throttle=false; - } - } else { - controls.set_throttle( FGControls::ALL_ENGINES,throttle_tmp ); - } - } - - if ( NULL != to_rudder ) - { - if ( current_options.get_auto_coordination() != - fgOPTIONS::FG_AUTO_COORD_ENABLED ) - { - double dead_zone = 0.0; // 0.4; - double value = * to_rudder; - if (value < -dead_zone) { - value += dead_zone; - value *= 1.0 / (1.0 - dead_zone); - } else if (value > dead_zone) { - value -= dead_zone; - value *= 1.0 / (1.0 - dead_zone); - } else { - value = 0.0; - } - controls.set_rudder(value); - } - } - // End of William's code - - // Use hat to set view direction - // Alex Perry 2000-05-31, based on concept by dpm - if ( NULL != to_viewhat ) - { double n = * ( to_viewhat + 1 ); - double e = * ( to_viewhat ); - double d; - if ( fabs(n) + fabs(e) > 0.1 ) - { d = atan2 ( -e, -n ); - if ( d < 0 ) d += 2 * FG_PI; - current_view.set_goal_view_offset ( d ); - } + js->read(&buttons, axis_values); + + for (int j = 0; j < min(MAX_AXES, js->getNumAxes()); j++) { + + // If the axis hasn't changed, don't + // force the value. + if (fabs(axis_values[j] - joysticks[i].axes[j].last_value) <= + joysticks[i].axes[j].tolerance) + continue; + else + joysticks[i].axes[j].last_value = axis_values[j]; + + FGValue * value = joysticks[i].axes[j].value; + if (value) { + if (!value->setDoubleValue((axis_values[j] + + joysticks[i].axes[j].offset) * + joysticks[i].axes[j].factor)) + FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick " + << i << ", axis " << j); + } } + } - return 1; + return true; // FIXME } -#endif // ENABLE_PLIB_JOYSTICK +// end of joystick.cxx diff --git a/src/Joystick/joystick.hxx b/src/Joystick/joystick.hxx index aa8e2e639..bb4ff66bc 100644 --- a/src/Joystick/joystick.hxx +++ b/src/Joystick/joystick.hxx @@ -33,11 +33,8 @@ // Initialize the joystick(s) int fgJoystickInit( void ); -#if defined( ENABLE_PLIB_JOYSTICK ) - // update the control parameters based on joystick intput - int fgJoystickRead( void ); -#endif // ENABLE_PLIB_JOYSTICK - +// update the control parameters based on joystick intput +int fgJoystickRead( void ); #endif // _JOYSTICK_HXX -- 2.39.5