X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FInput%2Ffgjs.cxx;h=9391c022afd879447c73db2e4790c698f4664a86;hb=e7625515247552f6a45627926522d306176e6302;hp=a5dcffa6cf195c9b41f93df01e1acc4da02a9559;hpb=71f08e795dca21cac1293ce89abb62024d4a16cc;p=flightgear.git diff --git a/src/Input/fgjs.cxx b/src/Input/fgjs.cxx index a5dcffa6c..9391c022a 100644 --- a/src/Input/fgjs.cxx +++ b/src/Input/fgjs.cxx @@ -1,8 +1,12 @@ // fgjs.cxx -- assign joystick axes to flightgear properties // +// Updated to allow xml output & added a few bits & pieces +// Laurie Bradshaw, Jun 2005 +// // Written by Tony Peden, started May 2001 // // Copyright (C) 2001 Tony Peden (apeden@earthlink.net) +// Copyright (C) 2006 Stefan Seifert // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -16,192 +20,354 @@ // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +# include +#endif #include +#ifdef _WIN32 +# include +#endif + #include -#include STL_IOSTREAM -#include STL_FSTREAM -#include STL_STRING - -#include - -SG_USING_STD(fstream); -SG_USING_STD(cout); -SG_USING_STD(endl); -SG_USING_STD(ios); -SG_USING_STD(string); - -string axes_humannames[8] = { "elevator", "ailerons", "rudder", "throttle", - "mixture","propller pitch", "lateral view", - "longitudinal view" - }; - -string axes_propnames[8]={ "/controls/elevator","/controls/aileron", - "/controls/rudder","/controls/throttle", - "/controls/mixture","/controls/pitch", - "/sim/views/axes/lat","/sim/views/axes/long" - }; - -bool half_range[8]={ false,false,false,true,true,true,false,false }; - - -string button_humannames[7]= { "apply all brakes", "apply left brake", - "apply right brake", "step flaps up", - "step flaps down","apply nose-up trim", - "apply nose-down trim" - }; - -string button_propnames[7]={ "/controls/brakes/all", "/controls/brakes[0]", - "/controls/brakes[1]", "/controls/flaps", - "/controls/flaps","/controls/elevator-trim", - "/controls/elevator-trim" - }; - - -float button_step[7]={ 1.0, 1.0, 1.0, 0.34, -0.34, 0.001, -0.001 }; - -string button_repeat[7]={ "false", "false", "false", "false", "false", - "true", "true" }; - - -void waitForButton(jsSuper *jss, int wait_ms) { - int b,lastb; - float axes[_JS_MAX_AXES]; - b=0; - ulMilliSecondSleep(wait_ms); - do { - lastb=b; - do { - jss->getJoystick()->read ( &b, axes ) ; - } while( jss->nextJoystick()); - - ulMilliSecondSleep(1); - - }while( lastb == b ); - ulMilliSecondSleep(wait_ms); +#include +#include +#include + +using std::fstream; +using std::cout; +using std::cin; +using std::endl; +using std::ios; +using std::string; + +#include +#include +#include +#include +#include +#include + +#include
+#include
+#include
+ +#include "jsinput.h" + + +bool confirmAnswer() { + char answer; + do { + cout << "Is this correct? (y/n) $ "; + cin >> answer; + cin.ignore(256, '\n'); + if (answer == 'y') + return true; + if (answer == 'n') + return false; + } while (true); } -void writeAxisProperties(fstream &fs, int control,int joystick, int axis) { - - char jsDesc[80]; - snprintf(jsDesc,80,"--prop:/input/joysticks/js[%d]/axis[%d]/binding",joystick,axis); - fs << jsDesc << "/command=property-scale" << endl; - fs << jsDesc << "/property=" << axes_propnames[control] << endl; - - fs << jsDesc << "/dead-band=0.02" << endl; - - if( half_range[control] == true) { - fs << jsDesc << "/offset=-1.0" << endl; - fs << jsDesc << "/factor=-0.5" << endl; - } else { - fs << jsDesc << "/offset=0.0" << endl; - fs << jsDesc << "/factor=1.0" << endl; - } - fs << endl; -} - -void writeButtonProperties(fstream &fs, int property,int joystick, int button) { - - char jsDesc[80]; - snprintf(jsDesc,80,"--prop:/input/joysticks/js[%d]/button[%d]/binding",joystick,button); - - fs << jsDesc << "/repeatable=" << button_repeat[property] << endl; - fs << jsDesc << "/command=property-adjust" << endl; - fs << jsDesc << "/property=" << button_propnames[property] << endl; - fs << jsDesc << "/step=" << button_step[property] << endl; - fs << endl; -} - - - - -int main(void) { - jsSuper *jss=new jsSuper(); - jsInput *jsi=new jsInput(jss); - jsi->displayValues(false); - // int i; - int control=0; - - - cout << "Found " << jss->getNumJoysticks() << " joystick(s)" << endl; - - if(jss->getNumJoysticks() <= 0) { - cout << "Can't find any joysticks ..." << endl; - exit(1); - } - - jss->firstJoystick(); - do { - cout << "Joystick " << jss->getCurrentJoystickId() << " has " - << jss->getJoystick()->getNumAxes() << " axes" << endl; - } while( jss->nextJoystick() ); - - fstream fs("fgfsrc.js",ios::out); - - - for(control=0;control<=7;control++) { - cout << "Move the control you wish to use for " << axes_humannames[control] - << endl; - fflush( stdout ); - jsi->getInput(); - - if(jsi->getInputAxis() != -1) { - cout << endl << "Assigned axis " << jsi->getInputAxis() - << " on joystick " << jsi->getInputJoystick() - << " to control " << axes_humannames[control] - << endl; - - writeAxisProperties( fs, control, jsi->getInputJoystick(), - jsi->getInputAxis() ); - } else { - cout << "Skipping Axis" << endl; - } - - cout << "Press any button for next control" << endl; - - waitForButton(jss,500); - cout << endl; - } - - for(control=0;control<=6;control++) { - cout << "Press the button you wish to use to " - << button_humannames[control] - << endl; - fflush( stdout ); - jsi->getInput(); - if(jsi->getInputButton() != -1) { - - cout << endl << "Assigned button " << jsi->getInputButton() - << " on joystick " << jsi->getInputJoystick() - << " to control " << button_humannames[control] - << endl; - - writeButtonProperties( fs, control, jsi->getInputJoystick(), - jsi->getInputButton() ); - } else { - cout << "Skipping..." << endl; - } - - cout << "Press any button for next axis" << endl; - - waitForButton(jss,500); - cout << endl; - } - - - delete jsi; - delete jss; - - cout << "Your joystick settings are in the file fgfsrc.js" << endl; - cout << "Check and edit as desired (especially important if you are" - << " using a hat switch" << endl; - cout << "as this program will, most likely, not get those right). "; - - cout << "Once you are happy, " << endl - << "append its contents to your .fgfsrc or system.fgfsrc" << endl; - - return 1; -} +string getFGRoot( int argc, char *argv[] ); + +int main( int argc, char *argv[] ) { + + for (int i = 1; i < argc; i++) { + if (strcmp("--help", argv[i]) == 0) { + cout << "Usage:" << endl; + cout << " --help\t\t\tShow this help" << endl; + exit(0); + } else if (strncmp("--fg-root=", argv[i], 10) == 0) { + // used later + } else { + cout << "Unknown option \"" << argv[i] << "\"" << endl; + exit(0); + } + } + + jsInit(); + + jsSuper *jss = new jsSuper(); + jsInput *jsi = new jsInput(jss); + jsi->displayValues(false); + + cout << "Found " << jss->getNumJoysticks() << " joystick(s)" << endl; + + if(jss->getNumJoysticks() <= 0) { + cout << "Can't find any joysticks ..." << endl; + exit(1); + } + cout << endl << "Now measuring the dead band of your joystick. The dead band is the area " << endl + << "where the joystick is centered and should not generate any input. Move all " << endl + << "axes around in this dead zone during the ten seconds this test will take." << endl; + cout << "Press enter to continue." << endl; + cin.ignore(1024, '\n'); + jsi->findDeadBand(); + cout << endl << "Dead band calibration finished. Press enter to start control assignment." << endl; + cin.ignore(1024, '\n'); + + jss->firstJoystick(); + fstream *xfs = new fstream[ jss->getNumJoysticks() ]; + SGPropertyNode_ptr *jstree = new SGPropertyNode_ptr[ jss->getNumJoysticks() ]; + do { + cout << "Joystick #" << jss->getCurrentJoystickId() + << " \"" << jss->getJoystick()->getName() << "\" has " + << jss->getJoystick()->getNumAxes() << " axes" << endl; + + char filename[16]; + snprintf(filename, 16, "js%i.xml", jss->getCurrentJoystickId()); + xfs[ jss->getCurrentJoystickId() ].open(filename, ios::out); + jstree[ jss->getCurrentJoystickId() ] = new SGPropertyNode(); + } while ( jss->nextJoystick() ); + + SGPath templatefile( getFGRoot(argc, argv) ); + templatefile.append("Input"); + templatefile.append("Joysticks"); + templatefile.append("template.xml"); + + SGPropertyNode *templatetree = new SGPropertyNode(); + try { + readProperties(templatefile.str().c_str(), templatetree); + } catch (sg_io_exception e) { + cout << e.getFormattedMessage (); + } + + vector axes = templatetree->getChildren("axis"); + for(vector::iterator iter = axes.begin(); iter != axes.end(); iter++) { + cout << "Move the control you wish to use for " << (*iter)->getStringValue("desc") + << " " << (*iter)->getStringValue("direction") << endl; + cout << "Pressing a button skips this axis" << endl; + fflush( stdout ); + jsi->getInput(); + if (jsi->getInputAxis() != -1) { + cout << endl << "Assigned axis " << jsi->getInputAxis() + << " on joystick " << jsi->getInputJoystick() + << " to control " << (*iter)->getStringValue("desc") << endl; + if ( confirmAnswer() ) { + SGPropertyNode *axis = jstree[ jsi->getInputJoystick() ]->getChild("axis", jsi->getInputAxis(), true); + copyProperties(*iter, axis); + axis->setDoubleValue("dead-band", jss->getJoystick(jsi->getInputJoystick()) + ->getDeadBand(jsi->getInputAxis())); + axis->setDoubleValue("binding/factor", jsi->getInputAxisPositive() ? 1.0 : -1.0); + } else { + iter--; + } + } else { + cout << "Skipping control" << endl; + if ( ! confirmAnswer() ) + iter--; + } + cout << endl; + } + + vector buttons = templatetree->getChildren("button"); + for(vector::iterator iter = buttons.begin(); iter != buttons.end(); iter++) { + cout << "Press the button you wish to use for " << (*iter)->getStringValue("desc") << endl; + cout << "Moving a joystick axis skips this button" << endl; + fflush( stdout ); + jsi->getInput(); + if (jsi->getInputButton() != -1) { + cout << endl << "Assigned button " << jsi->getInputButton() + << " on joystick " << jsi->getInputJoystick() + << " to control " << (*iter)->getStringValue("desc") << endl; + if ( confirmAnswer() ) { + SGPropertyNode *button = jstree[ jsi->getInputJoystick() ]->getChild("button", jsi->getInputButton(), true); + copyProperties(*iter, button); + } else { + iter--; + } + } else { + cout << "Skipping control" << endl; + if (! confirmAnswer()) + iter--; + } + cout << endl; + } + + cout << "Your joystick settings are in "; + for (int i = 0; i < jss->getNumJoysticks(); i++) { + try { + cout << "js" << i << ".xml"; + if (i + 2 < jss->getNumJoysticks()) + cout << ", "; + else if (i + 1 < jss->getNumJoysticks()) + cout << " and "; + + jstree[i]->setStringValue("name", jss->getJoystick(i)->getName()); + writeProperties(xfs[i], jstree[i], true); + } catch (sg_io_exception e) { + cout << e.getFormattedMessage (); + } + xfs[i].close(); + } + cout << "." << endl << "Check and edit as desired. Once you are happy," << endl + << "move relevant js.xml files to $FG_ROOT/Input/Joysticks/ (if you didn't use" << endl + << "an attached controller, you don't need to move the corresponding file)" << endl; + + delete jsi; + delete[] xfs; + delete jss; + delete[] jstree; + + return 1; +} + +char *homedir = ::getenv( "HOME" ); +char *hostname = ::getenv( "HOSTNAME" ); +bool free_hostname = false; + +// Scan the command line options for the specified option and return +// the value. +static string fgScanForOption( const string& option, int argc, char **argv ) { + int i = 1; + + if (hostname == NULL) + { + char _hostname[256]; + gethostname(_hostname, 256); + hostname = strdup(_hostname); + free_hostname = true; + } + + SG_LOG(SG_GENERAL, SG_INFO, "Scanning command line for: " << option ); + + int len = option.length(); + + while ( i < argc ) { + SG_LOG( SG_GENERAL, SG_DEBUG, "argv[" << i << "] = " << argv[i] ); + + string arg = argv[i]; + if ( arg.find( option ) == 0 ) { + return arg.substr( len ); + } + + i++; + } + + return ""; +} + +// Scan the user config files for the specified option and return +// the value. +static string fgScanForOption( const string& option, const string& path ) { + sg_gzifstream in( path ); + if ( !in.is_open() ) { + return ""; + } + + SG_LOG( SG_GENERAL, SG_INFO, "Scanning " << path << " for: " << option ); + + int len = option.length(); + + in >> skipcomment; + while ( ! in.eof() ) { + string line; + getline( in, line, '\n' ); + + // catch extraneous (DOS) line ending character + if ( line[line.length() - 1] < 32 ) { + line = line.substr( 0, line.length()-1 ); + } + + if ( line.find( option ) == 0 ) { + return line.substr( len ); + } + + in >> skipcomment; + } + + return ""; +} + +// Scan the user config files for the specified option and return +// the value. +static string fgScanForOption( const string& option ) { + string arg(""); + +#if defined( unix ) || defined( __CYGWIN__ ) + // Next check home directory for .fgfsrc.hostname file + if ( arg.empty() ) { + if ( homedir != NULL ) { + SGPath config( homedir ); + config.append( ".fgfsrc" ); + config.concat( "." ); + config.concat( hostname ); + arg = fgScanForOption( option, config.str() ); + } + } +#endif + + // Next check home directory for .fgfsrc file + if ( arg.empty() ) { + if ( homedir != NULL ) { + SGPath config( homedir ); + config.append( ".fgfsrc" ); + arg = fgScanForOption( option, config.str() ); + } + } + + return arg; +} + +// Read in configuration (files and command line options) but only set +// fg_root +string getFGRoot ( int argc, char **argv ) { + string root; + + // First parse command line options looking for --fg-root=, this + // will override anything specified in a config file + root = fgScanForOption( "--fg-root=", argc, argv); + + // Check in one of the user configuration files. + if (root.empty() ) + root = fgScanForOption( "--fg-root=" ); + + // Next check if fg-root is set as an env variable + if ( root.empty() ) { + char *envp = ::getenv( "FG_ROOT" ); + if ( envp != NULL ) { + root = envp; + } + } + + // Otherwise, default to a random compiled-in location if we can't + // find fg-root any other way. + if ( root.empty() ) { +#if defined( __CYGWIN__ ) + root = "../data"; +#elif defined( _WIN32 ) + root = "..\\data"; +#elif defined(OSX_BUNDLE) + /* the following code looks for the base package directly inside + the application bundle. This can be changed fairly easily by + fiddling with the code below. And yes, I know it's ugly and verbose. + */ + CFBundleRef appBundle = CFBundleGetMainBundle(); + CFURLRef appUrl = CFBundleCopyBundleURL(appBundle); + CFRelease(appBundle); + + // look for a 'data' subdir directly inside the bundle : is there + // a better place? maybe in Resources? I don't know ... + CFURLRef dataDir = CFURLCreateCopyAppendingPathComponent(NULL, appUrl, CFSTR("data"), true); + + // now convert down to a path, and the a c-string + CFStringRef path = CFURLCopyFileSystemPath(dataDir, kCFURLPOSIXPathStyle); + root = CFStringGetCStringPtr(path, CFStringGetSystemEncoding()); + + // tidy up. + CFRelease(appBundle); + CFRelease(dataDir); + CFRelease(path); +#else + root = PKGLIBDIR; +#endif + } + + SG_LOG(SG_INPUT, SG_INFO, "fg_root = " << root ); + + return root; +}