// 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) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // 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. #include #include #include STL_IOSTREAM #include STL_FSTREAM #include STL_STRING #include SG_USING_STD(fstream); SG_USING_STD(cout); SG_USING_STD(cin); SG_USING_STD(endl); SG_USING_STD(ios); SG_USING_STD(string); string axes_humannames[8] = { "Aileron", "Elevator", "Rudder", "Throttle", "Mixture", "Pitch", "View Direction", "View Elevation" }; string axes_propnames[8]={ "/controls/flight/aileron","/controls/flight/elevator", "/controls/flight/rudder","/controls/engines/engine[%d]/throttle", "/controls/engines/engine[%d]/mixture","/controls/engines/engine[%d]/pitch", "/sim/current-view/goal-heading-offset-deg", "/sim/current-view/goal-pitch-offset-deg" }; bool half_range[8]={ false,false,false,true,true,true,false,false }; bool repeatable[8]={ false,false,false,false,false,false,true,true }; bool invert[8]= { false,true,false,false,false,false,false,true }; string button_humannames[8]= { "Left Brake", "Right Brake", "Flaps Up", "Flaps Down", "Elevator Trim Up", "Elevator Trim Down", "Landing Gear Up", "Landing Gear Down" }; string button_propnames[8]={ "/controls/gear/brake-left", "/controls/gear/brake-right", "/controls/flight/flaps", "/controls/flight/flaps", "/controls/flight/elevator-trim", "/controls/flight/elevator-trim", "/controls/gear/gear-down", "/controls/gear/gear-down" }; bool button_modup[8]={ true,true,false,false,false,false,false,false }; bool button_boolean[8]={ false,false,false,false,false,false,true,true }; float button_step[8]={ 1.0, 1.0, 0.34, -0.34, 0.001, -0.001, 0.0, 1.0 }; string button_repeat[8]={ "false", "false", "false", "false", "true", "true", "false", "false" }; void writeAxisXML(fstream &fs, int control, int axis) { char axisline[16]; snprintf(axisline,16," ",axis); fs << axisline << endl; fs << " " << axes_humannames[control] << "" << endl; if (half_range[control]) { for (int i=0; i<=7; i++) { fs << " " << endl; fs << " property-scale" << endl; char propertyline[256]; snprintf(propertyline,256,axes_propnames[control].c_str(),i); fs << " " << propertyline << "" << endl; fs << " -1.0" << endl; fs << " -0.5" << endl; fs << " " << endl; } } else if (repeatable[control]) { fs << " " << endl; fs << " true" << endl; fs << " " << endl; fs << " property-adjust" << endl; fs << " " << axes_propnames[control] << "" << endl; if (invert[control]) { fs << " 1.0" << endl; } else { fs << " -1.0" << endl; } fs << " " << endl; fs << " " << endl; fs << " " << endl; fs << " true" << endl; fs << " " << endl; fs << " property-adjust" << endl; fs << " " << axes_propnames[control] << "" << endl; if (invert[control]) { fs << " -1.0" << endl; } else { fs << " 1.0" << endl; } fs << " " << endl; fs << " " << endl; } else { fs << " " << endl; fs << " property-scale" << endl; fs << " " << axes_propnames[control] << "" << endl; fs << " 0.02" << endl; fs << " 0.0" << endl; if (invert[control]) { fs << " -1.0" << endl; } else { fs << " 1.0" << endl; } fs << " " << endl; } fs << " " << endl << endl; } void writeAxisProperties(fstream &fs, int control,int joystick, int axis) { char jsDesc[80]; snprintf(jsDesc,80,"--prop:/input/joysticks/js[%d]/axis[%d]",joystick,axis); if( half_range[control] == true) { for (int i=0; i<=7; i++) { char bindno[64]; snprintf(bindno,64,"/binding[%d]",i); char propertyline[256]; snprintf(propertyline,256,axes_propnames[control].c_str(),i); fs << jsDesc << bindno << "/command=property-scale" << endl; fs << jsDesc << bindno << "/property=" << propertyline << endl; fs << jsDesc << bindno << "/offset=-1.0" << endl; fs << jsDesc << bindno << "/factor=-0.5" << endl; } } else if (repeatable[control]) { fs << jsDesc << "/low/repeatable=true" << endl; fs << jsDesc << "/low/binding/command=property-adjust" << endl; fs << jsDesc << "/low/binding/property=" << axes_propnames[control] << endl; if (invert[control]) { fs << jsDesc << "/low/binding/step=1.0" << endl; } else { fs << jsDesc << "/low/binding/step=-1.0" << endl; } fs << jsDesc << "/high/repeatable=true" << endl; fs << jsDesc << "/high/binding/command=property-adjust" << endl; fs << jsDesc << "/high/binding/property=" << axes_propnames[control] << endl; if (invert[control]) { fs << jsDesc << "/high/binding/step=-1.0" << endl; } else { fs << jsDesc << "/high/binding/step=1.0" << endl; } } else { fs << jsDesc << "/binding/command=property-scale" << endl; fs << jsDesc << "/binding/property=" << axes_propnames[control] << endl; fs << jsDesc << "/binding/dead-band=0.02" << endl; fs << jsDesc << "/binding/offset=0.0" << endl; if (invert[control]) { fs << jsDesc << "/binding/factor=-1.0" << endl; } else { fs << jsDesc << "/binding/factor=1.0" << endl; } } fs << endl; } void writeButtonXML(fstream &fs, int property, int button) { char buttonline[32]; snprintf(buttonline,32," " << endl << endl; } void writeButtonProperties(fstream &fs, int property,int joystick, int button) { char jsDesc[80]; snprintf(jsDesc,80,"--prop:/input/joysticks/js[%d]/button[%d]",joystick,button); if (property==-1) { fs << jsDesc << "/binding[0]/command=property-assign" << endl; fs << jsDesc << "/binding[0]/property=" << button_propnames[0] << endl; fs << jsDesc << "/binding[0]/value=" << button_step[0] << endl; fs << jsDesc << "/binding[1]/command=property-assign" << endl; fs << jsDesc << "/binding[1]/property=" << button_propnames[1] << endl; fs << jsDesc << "/binding[1]/value=" << button_step[1] << endl; fs << jsDesc << "/mod-up/binding[0]/command=property-assign" << endl; fs << jsDesc << "/mod-up/binding[0]/property=" << button_propnames[0] << endl; fs << jsDesc << "/mod-up/binding[0]/value=0" << endl; fs << jsDesc << "/mod-up/binding[1]/command=property-assign" << endl; fs << jsDesc << "/mod-up/binding[1]/property=" << button_propnames[1] << endl; fs << jsDesc << "/mod-up/binding[1]/value=0" << endl; fs << endl; } else if (button_modup[property]) { fs << jsDesc << "/binding/command=property-assign" << endl; fs << jsDesc << "/binding/property=" << button_propnames[property] << endl; fs << jsDesc << "/binding/value=" << button_step[property] << endl; fs << jsDesc << "/mod-up/binding/command=property-assign" << endl; fs << jsDesc << "/mod-up/binding/property=" << button_propnames[property] << endl; fs << jsDesc << "/mod-up/binding/value=0" << endl; fs << endl; } else if (button_boolean[property]) { fs << jsDesc << "/repeatable=" << button_repeat[property] << endl; fs << jsDesc << "/binding/command=property-assign" << endl; fs << jsDesc << "/binding/property=" << button_propnames[property] << endl; fs << jsDesc << "/binding/value="; if (button_step[property]==1) { fs << "true"; } else { fs << "false"; } fs << endl << endl; } else { fs << jsDesc << "/repeatable=" << button_repeat[property] << endl; fs << jsDesc << "/binding/command=property-adjust" << endl; fs << jsDesc << "/binding/property=" << button_propnames[property] << endl; fs << jsDesc << "/binding/step=" << button_step[property] << endl; fs << endl; } } int main( int argc, char *argv[] ) { bool usexml=true; float deadband=0; char answer[128]; int btninit=-2; for (int i=1; i\t\tSet deadband (for this program only, useful" << endl; cout << "\t\t\t\tfor 'twitchy' joysticks)" << endl; exit(0); } else if (strcmp("--prop",argv[i])==0) { usexml=false; btninit=-1; } else if (strcmp("--deadband",argv[i])==0) { i++; deadband=atoi(argv[i]); cout << "Deadband set to " << argv[i] << endl; } else if (strcmp("--xml",argv[i])!=0) { cout << "Unknown option \"" << argv[i] << "\"" << endl; exit(0); } } jsInit(); jsSuper *jss=new jsSuper(); jsInput *jsi=new jsInput(jss); jsi->displayValues(false); int control=0; cout << "Found " << jss->getNumJoysticks() << " joystick(s)" << endl; if(jss->getNumJoysticks() <= 0) { cout << "Can't find any joysticks ..." << endl; exit(1); } fstream fs; fstream xfs[jss->getNumJoysticks()]; if (!usexml) { fs.open("fgfsrc.js",ios::out); } jss->firstJoystick(); do { cout << "Joystick #" << jss->getCurrentJoystickId() << " \"" << jss->getJoystick()->getName() << "\" has " << jss->getJoystick()->getNumAxes() << " axes" << endl; for (int i=0; igetJoystick()->getNumAxes(); i++) { jss->getJoystick()->setDeadBand(i,deadband); } if (usexml) { char filename[16]; snprintf(filename,16,"js%i.xml",jss->getCurrentJoystickId()); xfs[jss->getCurrentJoystickId()].open(filename,ios::out); xfs[jss->getCurrentJoystickId()] << "" << endl << endl << "" << endl << endl << " " << jss->getJoystick()->getName() << "" << endl << endl; } } while( jss->nextJoystick() ); 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; bool badanswer=true; do { cout << "Is this correct? (y/n) $ "; cin >> answer; if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; } } while (badanswer); if (strcmp(answer,"n")==0) { control--; } else { if (usexml) { writeAxisXML( xfs[jsi->getInputJoystick()], control, jsi->getInputAxis() ); } else { writeAxisProperties( fs, control, jsi->getInputJoystick(), jsi->getInputAxis() ); } } } else { cout << "Skipping control" << endl; bool badanswer=true; do { cout << "Is this correct? (y/n) $ "; cin >> answer; if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; } } while (badanswer); if (strcmp(answer,"n")==0) { control--; } } cout << endl; } for(control=btninit;control<=7;control++) { if ( control == -2 ) { cout << "Press the button you wish to use for View Cycle" << endl; } else if ( control == -1 ) { cout << "Press the button you wish to use for Brakes" << endl; } else { cout << "Press the button you wish to use for " << button_humannames[control] << endl; } fflush( stdout ); jsi->getInput(); if(jsi->getInputButton() != -1) { cout << endl << "Assigned button " << jsi->getInputButton() << " on joystick " << jsi->getInputJoystick() << " to control "; if ( control == -2 ) { cout << "View Cycle" << endl; } else if ( control == -1 ) { cout << "Brakes" << endl; } else { cout << button_humannames[control] << endl; } bool badanswer=true; do { cout << "Is this correct? (y/n) $ "; cin >> answer; if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; } } while (badanswer); if (strcmp(answer,"n")==0) { control--; } else { if (usexml) { writeButtonXML( xfs[jsi->getInputJoystick()], control, jsi->getInputButton() ); } else { writeButtonProperties( fs, control, jsi->getInputJoystick(), jsi->getInputButton() ); } } } else { cout << "Skipping control" << endl; bool badanswer=true; do { cout << "Is this correct? (y/n) $ "; cin >> answer; if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; } } while (badanswer); if (strcmp(answer,"n")==0) { control--; } } cout << endl; } if (usexml) { for (int i=0; igetNumJoysticks(); i++) { xfs[i] << "" << endl << endl << "" << endl; xfs[i].close(); } } else { fs.close(); } delete jsi; delete jss; cout << "Your joystick settings are in "; if (usexml) { cout << "js0.xml, js1.xml, etc. depending on how many" << endl << "devices you have." << endl; } else { cout << "fgfsrc.js" << endl; } cout << endl << "Check and edit as desired. Once you are happy," << endl; if (usexml) { cout << "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; } else { cout << "append its contents to your .fgfsrc or system.fgfsrc" << endl; } return 1; }