]> git.mxchange.org Git - flightgear.git/blob - src/Input/fgjs.cxx
new FSF address
[flightgear.git] / src / Input / fgjs.cxx
1 // fgjs.cxx -- assign joystick axes to flightgear properties
2 //
3 // Updated to allow xml output & added a few bits & pieces
4 // Laurie Bradshaw, Jun 2005
5 //
6 // Written by Tony Peden, started May 2001
7 //
8 // Copyright (C) 2001  Tony Peden (apeden@earthlink.net)
9 //
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.
14 //
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.
19 //
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
23
24 #include <simgear/compiler.h>
25
26 #include <math.h>
27
28 #include STL_IOSTREAM
29 #include STL_FSTREAM
30 #include STL_STRING
31
32 #include <jsinput.h>
33
34 SG_USING_STD(fstream);
35 SG_USING_STD(cout);
36 SG_USING_STD(cin);
37 SG_USING_STD(endl);
38 SG_USING_STD(ios);
39 SG_USING_STD(string);
40
41 string axes_humannames[8] = { "Aileron", "Elevator", "Rudder", "Throttle",
42                               "Mixture", "Pitch", "View Direction",
43                               "View Elevation"
44                             };
45
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"
51                          };
52
53 string axis_posdir[8]= { "right", "down/forward", "right", "forward", "forward", "forward", "left", "upward" };
54
55
56 bool half_range[8]={ false,false,false,true,true,true,false,false };
57
58 bool repeatable[8]={ false,false,false,false,false,false,true,true };
59
60 bool invert[8]= { false,false,false,false,false,false,false,false };
61
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"
66                              };
67
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"
76                            };
77
78 bool button_modup[8]={ true,true,false,false,false,false,false,false };
79
80 bool button_boolean[8]={ false,false,false,false,false,false,true,true };
81
82 float button_step[8]={ 1.0, 1.0, -0.34, 0.34, 0.001, -0.001, 0.0, 1.0 };
83
84 string button_repeat[8]={ "false", "false", "false", "false", "true", "true", "false", "false" };
85
86
87 void writeAxisXML(fstream &fs, int control, int axis) {
88
89      char axisline[16];
90      snprintf(axisline,16," <axis n=\"%d\">",axis);
91
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;
104        }
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;
113        } else {
114          fs << "    <step type=\"double\">-1.0</step>" << endl;
115        }
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;
125        } else {
126          fs << "    <step type=\"double\">1.0</step>" << endl;
127        }
128        fs << "   </binding>" << endl;
129        fs << "  </high>" << endl;
130      } else {
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;
138        } else {
139          fs << "   <factor type=\"double\">1.0</factor>" << endl;
140        }
141        fs << "  </binding>" << endl;
142      }
143      fs << " </axis>" << endl << endl;
144 }
145
146 void writeAxisProperties(fstream &fs, int control,int joystick, int axis) {
147
148      char jsDesc[80];
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++) {
152          char bindno[64];
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;
160        }
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;
167        } else {
168          fs << jsDesc << "/low/binding/step=-1.0" << endl;
169        }
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;
175        } else {
176          fs << jsDesc << "/high/binding/step=1.0" << endl;
177        }
178      } else {
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;
185        } else {
186          fs << jsDesc << "/binding/factor=1.0" << endl;
187        }
188      }
189      fs << endl;
190 }
191
192 void writeButtonXML(fstream &fs, int property, int button) {
193
194      char buttonline[32];
195      snprintf(buttonline,32," <button n=\"%d\">",button);
196
197      fs << buttonline << endl;
198      if (property==-2) {
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;
229      } else {
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) {
251            fs << "true";
252          } else {
253            fs << "false";
254          }
255          fs << "</value>" << endl;
256          fs << "  </binding>" << endl;
257        } else {
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;
264        }
265      }
266      fs << " </button>" << endl << endl;
267 }
268
269 void writeButtonProperties(fstream &fs, int property,int joystick, int button) {
270
271      char jsDesc[80];
272      snprintf(jsDesc,80,"--prop:/input/joysticks/js[%d]/button[%d]",joystick,button);
273
274      if (property==-1) {
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; 
287        fs << 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; 
295        fs << 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) {
302          fs << "true";
303        } else {
304          fs << "false";
305        }
306        fs << endl << endl; 
307      } else {
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; 
312        fs << endl; 
313      }
314 }
315
316 int main( int argc, char *argv[] ) {
317
318   bool usexml=true;
319   float deadband=0;
320   char answer[128];
321   int btninit=-2;
322
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;
331       exit(0);
332     } else if (strcmp("--prop",argv[i])==0) {
333       usexml=false;
334       btninit=-1;
335     } else if (strcmp("--deadband",argv[i])==0) {
336       i++;
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;
341       exit(0);
342     }
343   }
344
345   jsInit();
346
347   jsSuper *jss=new jsSuper();
348   jsInput *jsi=new jsInput(jss);
349   jsi->displayValues(false);
350   int control=0;
351
352   cout << "Found " << jss->getNumJoysticks() << " joystick(s)" << endl;
353
354   if(jss->getNumJoysticks() <= 0) { 
355     cout << "Can't find any joysticks ..." << endl;
356     exit(1);
357   }
358
359   fstream fs;
360   fstream *xfs = new fstream[jss->getNumJoysticks()];
361   if (!usexml) {
362     fs.open("fgfsrc.js",ios::out);
363   }
364
365   jss->firstJoystick();
366   do {
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);
372     }
373     if (usexml) {
374       char filename[16];
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;
380     }
381   } while( jss->nextJoystick() ); 
382
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";
387       fflush( stdout );
388       jsi->getInput();
389
390       if(jsi->getInputAxis() != -1) {
391          cout << endl << "Assigned axis " << jsi->getInputAxis()
392               << " on joystick " << jsi->getInputJoystick()
393               << " to control " << axes_humannames[control]
394               << endl;
395          bool badanswer=true;
396          do {
397            cout << "Is this correct? (y/n) $ ";
398            cin >> answer;
399            if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
400          } while (badanswer);
401          if (strcmp(answer,"n")==0) {
402            control--;
403          } else {
404            invert[control]=!jsi->getInputAxisPositive();
405            if (usexml) {
406              writeAxisXML( xfs[jsi->getInputJoystick()], control, jsi->getInputAxis() );
407            } else {
408              writeAxisProperties( fs, control, jsi->getInputJoystick(), jsi->getInputAxis() );
409            }
410          }
411       } else {
412          cout << "Skipping control" << endl;
413          bool badanswer=true;
414          do {
415            cout << "Is this correct? (y/n) $ ";
416            cin >> answer;
417            if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
418          } while (badanswer);
419          if (strcmp(answer,"n")==0) { control--; }
420       }
421       cout << endl;
422   }
423
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;
429       } else {
430         cout << "Press the button you wish to use for " << button_humannames[control] << endl;
431       }
432       cout << "Moving a joystick axis skips this button\n";
433       fflush( stdout );
434       jsi->getInput();
435       if(jsi->getInputButton() != -1) {
436          cout << endl << "Assigned button " << jsi->getInputButton()
437               << " on joystick " << jsi->getInputJoystick()
438               << " to control ";
439          if ( control == -2 ) { cout << "View Cycle" << endl; }
440          else if ( control == -1 ) { cout << "Brakes" << endl; }
441          else { cout << button_humannames[control] << endl; }
442          bool badanswer=true;
443          do {
444            cout << "Is this correct? (y/n) $ ";
445            cin >> answer;
446            if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
447          } while (badanswer);
448          if (strcmp(answer,"n")==0) {
449            control--;
450          } else {
451            if (usexml) {
452              writeButtonXML( xfs[jsi->getInputJoystick()], control, jsi->getInputButton() );
453            } else {
454              writeButtonProperties( fs, control, jsi->getInputJoystick(), jsi->getInputButton() );
455            }
456          }
457       } else {
458          cout << "Skipping control" << endl;
459          bool badanswer=true;
460          do {
461            cout << "Is this correct? (y/n) $ ";
462            cin >> answer;
463            if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
464          } while (badanswer);
465          if (strcmp(answer,"n")==0) { control--; }
466       }
467
468       cout << endl;
469   }
470   if (usexml) {
471     for (int i=0; i<jss->getNumJoysticks(); i++) {
472       xfs[i] << "</PropertyList>" << endl << endl << "<!-- end of joystick.xml -->" << endl;
473       xfs[i].close();
474     }
475   } else {
476     fs.close();
477   }
478   delete jsi;
479   delete[] xfs;
480   delete jss;
481
482   cout << "Your joystick settings are in ";
483   if (usexml) {
484     cout << "js0.xml, js1.xml, etc. depending on how many" << endl << "devices you have." << endl;
485   } else {
486     cout << "fgfsrc.js" << endl;
487   }
488   cout << endl << "Check and edit as desired. Once you are happy," << endl;
489   if (usexml) {
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;
492   } else {
493     cout << "append its contents to your .fgfsrc or system.fgfsrc" << endl;
494   }
495
496   return 1;
497 }