1 // fgjs.cxx -- assign joystick axes to flightgear properties
3 // Updated to allow xml output & added a few bits & pieces
4 // Laurie Bradshaw, Jun 2005
6 // Written by Tony Peden, started May 2001
8 // Copyright (C) 2001 Tony Peden (apeden@earthlink.net)
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License as
12 // published by the Free Software Foundation; either version 2 of the
13 // License, or (at your option) any later version.
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include <simgear/compiler.h>
34 SG_USING_STD(fstream);
41 string axes_humannames[8] = { "Aileron", "Elevator", "Rudder", "Throttle",
42 "Mixture", "Pitch", "View Direction",
46 string axes_propnames[8]={ "/controls/flight/aileron","/controls/flight/elevator",
47 "/controls/flight/rudder","/controls/engines/engine[%d]/throttle",
48 "/controls/engines/engine[%d]/mixture","/controls/engines/engine[%d]/pitch",
49 "/sim/current-view/goal-heading-offset-deg",
50 "/sim/current-view/goal-pitch-offset-deg"
53 string axis_posdir[8]= { "right", "down/forward", "right", "forward", "forward", "forward", "left", "upward" };
56 bool half_range[8]={ false,false,false,true,true,true,false,false };
58 bool repeatable[8]={ false,false,false,false,false,false,true,true };
60 bool invert[8]= { false,false,false,false,false,false,false,false };
62 string button_humannames[8]= { "Left Brake", "Right Brake",
63 "Flaps Up", "Flaps Down",
64 "Elevator Trim Forward", "Elevator Trim Backward",
65 "Landing Gear Up", "Landing Gear Down"
68 string button_propnames[8]={ "/controls/gear/brake-left",
69 "/controls/gear/brake-right",
70 "/controls/flight/flaps",
71 "/controls/flight/flaps",
72 "/controls/flight/elevator-trim",
73 "/controls/flight/elevator-trim",
74 "/controls/gear/gear-down",
75 "/controls/gear/gear-down"
78 bool button_modup[8]={ true,true,false,false,false,false,false,false };
80 bool button_boolean[8]={ false,false,false,false,false,false,true,true };
82 float button_step[8]={ 1.0, 1.0, -0.34, 0.34, 0.001, -0.001, 0.0, 1.0 };
84 string button_repeat[8]={ "false", "false", "false", "false", "true", "true", "false", "false" };
87 void writeAxisXML(fstream &fs, int control, int axis) {
90 snprintf(axisline,16," <axis n=\"%d\">",axis);
92 fs << axisline << endl;
93 fs << " <desc>" << axes_humannames[control] << "</desc>" << endl;
94 if (half_range[control]) {
95 for (int i=0; i<=7; i++) {
96 fs << " <binding>" << endl;
97 fs << " <command>property-scale</command>" << endl;
98 char propertyline[256];
99 snprintf(propertyline,256,axes_propnames[control].c_str(),i);
100 fs << " <property>" << propertyline << "</property>" << endl;
101 fs << " <offset type=\"double\">-1.0</offset>" << endl;
102 fs << " <factor type=\"double\">-0.5</factor>" << endl;
103 fs << " </binding>" << endl;
105 } else if (repeatable[control]) {
106 fs << " <low>" << endl;
107 fs << " <repeatable>true</repeatable>" << endl;
108 fs << " <binding>" << endl;
109 fs << " <command>property-adjust</command>" << endl;
110 fs << " <property>" << axes_propnames[control] << "</property>" << endl;
111 if (invert[control]) {
112 fs << " <step type=\"double\">1.0</step>" << endl;
114 fs << " <step type=\"double\">-1.0</step>" << endl;
116 fs << " </binding>" << endl;
117 fs << " </low>" << endl;
118 fs << " <high>" << endl;
119 fs << " <repeatable>true</repeatable>" << endl;
120 fs << " <binding>" << endl;
121 fs << " <command>property-adjust</command>" << endl;
122 fs << " <property>" << axes_propnames[control] << "</property>" << endl;
123 if (invert[control]) {
124 fs << " <step type=\"double\">-1.0</step>" << endl;
126 fs << " <step type=\"double\">1.0</step>" << endl;
128 fs << " </binding>" << endl;
129 fs << " </high>" << endl;
131 fs << " <binding>" << endl;
132 fs << " <command>property-scale</command>" << endl;
133 fs << " <property>" << axes_propnames[control] << "</property>" << endl;
134 fs << " <dead-band type=\"double\">0.02</dead-band>" << endl;
135 fs << " <offset type=\"double\">0.0</offset>" << endl;
136 if (invert[control]) {
137 fs << " <factor type=\"double\">-1.0</factor>" << endl;
139 fs << " <factor type=\"double\">1.0</factor>" << endl;
141 fs << " </binding>" << endl;
143 fs << " </axis>" << endl << endl;
146 void writeAxisProperties(fstream &fs, int control,int joystick, int axis) {
149 snprintf(jsDesc,80,"--prop:/input/joysticks/js[%d]/axis[%d]",joystick,axis);
150 if( half_range[control] == true) {
151 for (int i=0; i<=7; i++) {
153 snprintf(bindno,64,"/binding[%d]",i);
154 char propertyline[256];
155 snprintf(propertyline,256,axes_propnames[control].c_str(),i);
156 fs << jsDesc << bindno << "/command=property-scale" << endl;
157 fs << jsDesc << bindno << "/property=" << propertyline << endl;
158 fs << jsDesc << bindno << "/offset=-1.0" << endl;
159 fs << jsDesc << bindno << "/factor=-0.5" << endl;
161 } else if (repeatable[control]) {
162 fs << jsDesc << "/low/repeatable=true" << endl;
163 fs << jsDesc << "/low/binding/command=property-adjust" << endl;
164 fs << jsDesc << "/low/binding/property=" << axes_propnames[control] << endl;
165 if (invert[control]) {
166 fs << jsDesc << "/low/binding/step=1.0" << endl;
168 fs << jsDesc << "/low/binding/step=-1.0" << endl;
170 fs << jsDesc << "/high/repeatable=true" << endl;
171 fs << jsDesc << "/high/binding/command=property-adjust" << endl;
172 fs << jsDesc << "/high/binding/property=" << axes_propnames[control] << endl;
173 if (invert[control]) {
174 fs << jsDesc << "/high/binding/step=-1.0" << endl;
176 fs << jsDesc << "/high/binding/step=1.0" << endl;
179 fs << jsDesc << "/binding/command=property-scale" << endl;
180 fs << jsDesc << "/binding/property=" << axes_propnames[control] << endl;
181 fs << jsDesc << "/binding/dead-band=0.02" << endl;
182 fs << jsDesc << "/binding/offset=0.0" << endl;
183 if (invert[control]) {
184 fs << jsDesc << "/binding/factor=-1.0" << endl;
186 fs << jsDesc << "/binding/factor=1.0" << endl;
192 void writeButtonXML(fstream &fs, int property, int button) {
195 snprintf(buttonline,32," <button n=\"%d\">",button);
197 fs << buttonline << endl;
199 fs << " <desc>View Cycle</desc>" << endl;
200 fs << " <repeatable>false</repeatable>" << endl;
201 fs << " <binding>" << endl;
202 fs << " <command>view-cycle</command>" << endl;
203 fs << " <step type=\"double\">1</step>" << endl;
204 fs << " </binding>" << endl;
205 } else if (property==-1) {
206 fs << " <desc>Brakes</desc>" << endl;
207 fs << " <binding>" << endl;
208 fs << " <command>property-assign</command>" << endl;
209 fs << " <property>" << button_propnames[0] << "</property>" << endl;
210 fs << " <value type=\"double\">" << button_step[0] << "</value>" << endl;
211 fs << " </binding>" << endl;
212 fs << " <binding>" << endl;
213 fs << " <command>property-assign</command>" << endl;
214 fs << " <property>" << button_propnames[1] << "</property>" << endl;
215 fs << " <value type=\"double\">" << button_step[1] << "</value>" << endl;
216 fs << " </binding>" << endl;
217 fs << " <mod-up>" << endl;
218 fs << " <binding>" << endl;
219 fs << " <command>property-assign</command>" << endl;
220 fs << " <property>" << button_propnames[0] << "</property>" << endl;
221 fs << " <value type=\"double\">0</value>" << endl;
222 fs << " </binding>" << endl;
223 fs << " <binding>" << endl;
224 fs << " <command>property-assign</command>" << endl;
225 fs << " <property>" << button_propnames[1] << "</property>" << endl;
226 fs << " <value type=\"double\">0</value>" << endl;
227 fs << " </binding>" << endl;
228 fs << " </mod-up>" << endl;
230 fs << " <desc>" << button_humannames[property] << "</desc>" << endl;
231 if (button_modup[property]) {
232 fs << " <binding>" << endl;
233 fs << " <command>property-assign</command>" << endl;
234 fs << " <property>" << button_propnames[property] << "</property>" << endl;
235 fs << " <value type=\"double\">" << button_step[property] << "</value>" << endl;
236 fs << " </binding>" << endl;
237 fs << " <mod-up>" << endl;
238 fs << " <binding>" << endl;
239 fs << " <command>property-assign</command>" << endl;
240 fs << " <property>" << button_propnames[property] << "</property>" << endl;
241 fs << " <value type=\"double\">0</value>" << endl;
242 fs << " </binding>" << endl;
243 fs << " </mod-up>" << endl;
244 } else if (button_boolean[property]) {
245 fs << " <repeatable>" << button_repeat[property] << "</repeatable>" << endl;
246 fs << " <binding>" << endl;
247 fs << " <command>property-assign</command>" << endl;
248 fs << " <property>" << button_propnames[property] << "</property>" << endl;
249 fs << " <value type=\"bool\">";
250 if (button_step[property]==1) {
255 fs << "</value>" << endl;
256 fs << " </binding>" << endl;
258 fs << " <repeatable>" << button_repeat[property] << "</repeatable>" << endl;
259 fs << " <binding>" << endl;
260 fs << " <command>property-adjust</command>" << endl;
261 fs << " <property>" << button_propnames[property] << "</property>" << endl;
262 fs << " <step type=\"double\">" << button_step[property] << "</step>" << endl;
263 fs << " </binding>" << endl;
266 fs << " </button>" << endl << endl;
269 void writeButtonProperties(fstream &fs, int property,int joystick, int button) {
272 snprintf(jsDesc,80,"--prop:/input/joysticks/js[%d]/button[%d]",joystick,button);
275 fs << jsDesc << "/binding[0]/command=property-assign" << endl;
276 fs << jsDesc << "/binding[0]/property=" << button_propnames[0] << endl;
277 fs << jsDesc << "/binding[0]/value=" << button_step[0] << endl;
278 fs << jsDesc << "/binding[1]/command=property-assign" << endl;
279 fs << jsDesc << "/binding[1]/property=" << button_propnames[1] << endl;
280 fs << jsDesc << "/binding[1]/value=" << button_step[1] << endl;
281 fs << jsDesc << "/mod-up/binding[0]/command=property-assign" << endl;
282 fs << jsDesc << "/mod-up/binding[0]/property=" << button_propnames[0] << endl;
283 fs << jsDesc << "/mod-up/binding[0]/value=0" << endl;
284 fs << jsDesc << "/mod-up/binding[1]/command=property-assign" << endl;
285 fs << jsDesc << "/mod-up/binding[1]/property=" << button_propnames[1] << endl;
286 fs << jsDesc << "/mod-up/binding[1]/value=0" << endl;
288 } else if (button_modup[property]) {
289 fs << jsDesc << "/binding/command=property-assign" << endl;
290 fs << jsDesc << "/binding/property=" << button_propnames[property] << endl;
291 fs << jsDesc << "/binding/value=" << button_step[property] << endl;
292 fs << jsDesc << "/mod-up/binding/command=property-assign" << endl;
293 fs << jsDesc << "/mod-up/binding/property=" << button_propnames[property] << endl;
294 fs << jsDesc << "/mod-up/binding/value=0" << endl;
296 } else if (button_boolean[property]) {
297 fs << jsDesc << "/repeatable=" << button_repeat[property] << endl;
298 fs << jsDesc << "/binding/command=property-assign" << endl;
299 fs << jsDesc << "/binding/property=" << button_propnames[property] << endl;
300 fs << jsDesc << "/binding/value=";
301 if (button_step[property]==1) {
308 fs << jsDesc << "/repeatable=" << button_repeat[property] << endl;
309 fs << jsDesc << "/binding/command=property-adjust" << endl;
310 fs << jsDesc << "/binding/property=" << button_propnames[property] << endl;
311 fs << jsDesc << "/binding/step=" << button_step[property] << endl;
316 int main( int argc, char *argv[] ) {
323 for (int i=1; i<argc; i++) {
324 if (strcmp("--help",argv[i])==0) {
325 cout << "Usage:" << endl;
326 cout << " --help\t\t\tShow this help" << endl;
327 cout << " --prop\t\t\tOutput property list" << endl;
328 cout << " --xml\t\t\t\tOutput xml (default)" << endl;
329 cout << " --deadband <float>\t\tSet deadband (for this program only, useful" << endl;
330 cout << "\t\t\t\tfor 'twitchy' joysticks)" << endl;
332 } else if (strcmp("--prop",argv[i])==0) {
335 } else if (strcmp("--deadband",argv[i])==0) {
337 deadband=atoi(argv[i]);
338 cout << "Deadband set to " << argv[i] << endl;
339 } else if (strcmp("--xml",argv[i])!=0) {
340 cout << "Unknown option \"" << argv[i] << "\"" << endl;
347 jsSuper *jss=new jsSuper();
348 jsInput *jsi=new jsInput(jss);
349 jsi->displayValues(false);
352 cout << "Found " << jss->getNumJoysticks() << " joystick(s)" << endl;
354 if(jss->getNumJoysticks() <= 0) {
355 cout << "Can't find any joysticks ..." << endl;
360 fstream *xfs = new fstream[jss->getNumJoysticks()];
362 fs.open("fgfsrc.js",ios::out);
365 jss->firstJoystick();
367 cout << "Joystick #" << jss->getCurrentJoystickId()
368 << " \"" << jss->getJoystick()->getName() << "\" has "
369 << jss->getJoystick()->getNumAxes() << " axes" << endl;
370 for (int i=0; i<jss->getJoystick()->getNumAxes(); i++) {
371 jss->getJoystick()->setDeadBand(i,deadband);
375 snprintf(filename,16,"js%i.xml",jss->getCurrentJoystickId());
376 xfs[jss->getCurrentJoystickId()].open(filename,ios::out);
377 xfs[jss->getCurrentJoystickId()] << "<?xml version=\"1.0\" ?>" << endl
378 << endl << "<PropertyList>" << endl << endl << " <name>"
379 << jss->getJoystick()->getName() << "</name>" << endl << endl;
381 } while( jss->nextJoystick() );
383 for(control=0;control<=7;control++) {
384 cout << "Move the control you wish to use for " << axes_humannames[control]
385 << " " << axis_posdir[control] << endl;
386 cout << "Pressing a button skips this axis\n";
390 if(jsi->getInputAxis() != -1) {
391 cout << endl << "Assigned axis " << jsi->getInputAxis()
392 << " on joystick " << jsi->getInputJoystick()
393 << " to control " << axes_humannames[control]
397 cout << "Is this correct? (y/n) $ ";
399 if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
401 if (strcmp(answer,"n")==0) {
404 invert[control]=!jsi->getInputAxisPositive();
406 writeAxisXML( xfs[jsi->getInputJoystick()], control, jsi->getInputAxis() );
408 writeAxisProperties( fs, control, jsi->getInputJoystick(), jsi->getInputAxis() );
412 cout << "Skipping control" << endl;
415 cout << "Is this correct? (y/n) $ ";
417 if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
419 if (strcmp(answer,"n")==0) { control--; }
424 for(control=btninit;control<=7;control++) {
425 if ( control == -2 ) {
426 cout << "Press the button you wish to use for View Cycle" << endl;
427 } else if ( control == -1 ) {
428 cout << "Press the button you wish to use for Brakes" << endl;
430 cout << "Press the button you wish to use for " << button_humannames[control] << endl;
432 cout << "Moving a joystick axis skips this button\n";
435 if(jsi->getInputButton() != -1) {
436 cout << endl << "Assigned button " << jsi->getInputButton()
437 << " on joystick " << jsi->getInputJoystick()
439 if ( control == -2 ) { cout << "View Cycle" << endl; }
440 else if ( control == -1 ) { cout << "Brakes" << endl; }
441 else { cout << button_humannames[control] << endl; }
444 cout << "Is this correct? (y/n) $ ";
446 if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
448 if (strcmp(answer,"n")==0) {
452 writeButtonXML( xfs[jsi->getInputJoystick()], control, jsi->getInputButton() );
454 writeButtonProperties( fs, control, jsi->getInputJoystick(), jsi->getInputButton() );
458 cout << "Skipping control" << endl;
461 cout << "Is this correct? (y/n) $ ";
463 if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
465 if (strcmp(answer,"n")==0) { control--; }
471 for (int i=0; i<jss->getNumJoysticks(); i++) {
472 xfs[i] << "</PropertyList>" << endl << endl << "<!-- end of joystick.xml -->" << endl;
482 cout << "Your joystick settings are in ";
484 cout << "js0.xml, js1.xml, etc. depending on how many" << endl << "devices you have." << endl;
486 cout << "fgfsrc.js" << endl;
488 cout << endl << "Check and edit as desired. Once you are happy," << endl;
490 cout << "move relevant js<n>.xml files to $FG_ROOT/Input/Joysticks/ (if you didn't use" << endl
491 << "an attached controller, you don't need to move the corresponding file)" << endl;
493 cout << "append its contents to your .fgfsrc or system.fgfsrc" << endl;