]> git.mxchange.org Git - flightgear.git/blob - src/Input/fgjs.cxx
5a646cc99ee03084182376c405e5e05857f6f901
[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., 675 Mass Ave, Cambridge, MA 02139, 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 bool half_range[8]={ false,false,false,true,true,true,false,false };
54
55 bool repeatable[8]={ false,false,false,false,false,false,true,true };
56
57 bool invert[8]= { false,true,false,false,false,false,false,true };
58
59 string button_humannames[8]= { "Left Brake", "Right Brake",
60                                "Flaps Up", "Flaps Down",
61                                "Elevator Trim Up", "Elevator Trim Down",
62                                "Landing Gear Up", "Landing Gear Down"
63                              };
64
65 string button_propnames[8]={ "/controls/gear/brake-left",
66                              "/controls/gear/brake-right",
67                              "/controls/flight/flaps",
68                              "/controls/flight/flaps",
69                              "/controls/flight/elevator-trim",
70                              "/controls/flight/elevator-trim",
71                              "/controls/gear/gear-down",
72                              "/controls/gear/gear-down"
73                            };
74
75 bool button_modup[8]={ true,true,false,false,false,false,false,false };
76
77 bool button_boolean[8]={ false,false,false,false,false,false,true,true };
78
79 float button_step[8]={ 1.0, 1.0, 0.34, -0.34, 0.001, -0.001, 0.0, 1.0 };
80
81 string button_repeat[8]={ "false", "false", "false", "false", "true", "true", "false", "false" };
82
83
84 void writeAxisXML(fstream &fs, int control, int axis) {
85
86      char axisline[16];
87      snprintf(axisline,16," <axis n=\"%d\">",axis);
88
89      fs << axisline << endl;
90      fs << "  <desc>" << axes_humannames[control] << "</desc>" << endl;
91      if (half_range[control]) {
92        for (int i=0; i<=7; i++) {
93          fs << "  <binding>" << endl;
94          fs << "   <command>property-scale</command>" << endl;
95          char propertyline[256];
96          snprintf(propertyline,256,axes_propnames[control].c_str(),i);
97          fs << "   <property>" << propertyline << "</property>" << endl;
98          fs << "   <offset type=\"double\">-1.0</offset>" << endl;
99          fs << "   <factor type=\"double\">-0.5</factor>" << endl;
100          fs << "  </binding>" << endl;
101        }
102      } else if (repeatable[control]) {
103        fs << "  <low>" << endl;
104        fs << "   <repeatable>true</repeatable>" << endl;
105        fs << "   <binding>" << endl;
106        fs << "    <command>property-adjust</command>" << endl;
107        fs << "    <property>" << axes_propnames[control] << "</property>" << endl;
108        if (invert[control]) {
109          fs << "    <step type=\"double\">1.0</step>" << endl;
110        } else {
111          fs << "    <step type=\"double\">-1.0</step>" << endl;
112        }
113        fs << "   </binding>" << endl;
114        fs << "  </low>" << endl;
115        fs << "  <high>" << endl;
116        fs << "   <repeatable>true</repeatable>" << endl;
117        fs << "   <binding>" << endl;
118        fs << "    <command>property-adjust</command>" << endl;
119        fs << "    <property>" << axes_propnames[control] << "</property>" << endl;
120        if (invert[control]) {
121          fs << "    <step type=\"double\">-1.0</step>" << endl;
122        } else {
123          fs << "    <step type=\"double\">1.0</step>" << endl;
124        }
125        fs << "   </binding>" << endl;
126        fs << "  </high>" << endl;
127      } else {
128        fs << "  <binding>" << endl;
129        fs << "   <command>property-scale</command>" << endl;
130        fs << "   <property>" << axes_propnames[control] << "</property>" << endl;
131        fs << "   <dead-band type=\"double\">0.02</dead-band>" << endl;
132        fs << "   <offset type=\"double\">0.0</offset>" << endl;
133        if (invert[control]) {
134          fs << "   <factor type=\"double\">-1.0</factor>" << endl;
135        } else {
136          fs << "   <factor type=\"double\">1.0</factor>" << endl;
137        }
138        fs << "  </binding>" << endl;
139      }
140      fs << " </axis>" << endl << endl;
141 }
142
143 void writeAxisProperties(fstream &fs, int control,int joystick, int axis) {
144
145      char jsDesc[80];
146      snprintf(jsDesc,80,"--prop:/input/joysticks/js[%d]/axis[%d]",joystick,axis);
147      if( half_range[control] == true) {
148        for (int i=0; i<=7; i++) {
149          char bindno[64];
150          snprintf(bindno,64,"/binding[%d]",i);
151          char propertyline[256];
152          snprintf(propertyline,256,axes_propnames[control].c_str(),i);
153          fs << jsDesc << bindno << "/command=property-scale" << endl;
154          fs << jsDesc << bindno << "/property=" << propertyline << endl;
155          fs << jsDesc << bindno << "/offset=-1.0" << endl;
156          fs << jsDesc << bindno << "/factor=-0.5" << endl;
157        }
158      } else if (repeatable[control]) {
159        fs << jsDesc << "/low/repeatable=true" << endl;
160        fs << jsDesc << "/low/binding/command=property-adjust" << endl;
161        fs << jsDesc << "/low/binding/property=" << axes_propnames[control] << endl;
162        if (invert[control]) {
163          fs << jsDesc << "/low/binding/step=1.0" << endl;
164        } else {
165          fs << jsDesc << "/low/binding/step=-1.0" << endl;
166        }
167        fs << jsDesc << "/high/repeatable=true" << endl;
168        fs << jsDesc << "/high/binding/command=property-adjust" << endl;
169        fs << jsDesc << "/high/binding/property=" << axes_propnames[control] << endl;
170        if (invert[control]) {
171          fs << jsDesc << "/high/binding/step=-1.0" << endl;
172        } else {
173          fs << jsDesc << "/high/binding/step=1.0" << endl;
174        }
175      } else {
176        fs << jsDesc << "/binding/command=property-scale" << endl;
177        fs << jsDesc << "/binding/property=" << axes_propnames[control] << endl;
178        fs << jsDesc << "/binding/dead-band=0.02" << endl;
179        fs << jsDesc << "/binding/offset=0.0" << endl;
180        if (invert[control]) {
181          fs << jsDesc << "/binding/factor=-1.0" << endl;
182        } else {
183          fs << jsDesc << "/binding/factor=1.0" << endl;
184        }
185      }
186      fs << endl;
187 }
188
189 void writeButtonXML(fstream &fs, int property, int button) {
190
191      char buttonline[32];
192      snprintf(buttonline,32," <button n=\"%d\">",button);
193
194      fs << buttonline << endl;
195      if (property==-2) {
196        fs << "  <desc>View Cycle</desc>" << endl;
197        fs << "  <repeatable>false</repeatable>" << endl;
198        fs << "  <binding>" << endl;
199        fs << "   <command>view-cycle</command>" << endl;
200        fs << "   <step type=\"double\">1</step>" << endl;
201        fs << "  </binding>" << endl;
202      } else if (property==-1) {
203        fs << "  <desc>Brakes</desc>" << endl;
204        fs << "  <binding>" << endl;
205        fs << "   <command>property-assign</command>" << endl;
206        fs << "   <property>" << button_propnames[0] << "</property>" << endl;
207        fs << "   <value type=\"double\">" << button_step[0] << "</value>" << endl;
208        fs << "  </binding>" << endl;
209        fs << "  <binding>" << endl;
210        fs << "   <command>property-assign</command>" << endl;
211        fs << "   <property>" << button_propnames[1] << "</property>" << endl;
212        fs << "   <value type=\"double\">" << button_step[1] << "</value>" << endl;
213        fs << "  </binding>" << endl;
214        fs << "  <mod-up>" << endl;
215        fs << "   <binding>" << endl;
216        fs << "    <command>property-assign</command>" << endl;
217        fs << "    <property>" << button_propnames[0] << "</property>" << endl;
218        fs << "    <value type=\"double\">0</value>" << endl;
219        fs << "   </binding>" << endl;
220        fs << "   <binding>" << endl;
221        fs << "    <command>property-assign</command>" << endl;
222        fs << "    <property>" << button_propnames[1] << "</property>" << endl;
223        fs << "    <value type=\"double\">0</value>" << endl;
224        fs << "   </binding>" << endl;
225        fs << "  </mod-up>" << endl;
226      } else {
227        fs << "  <desc>" << button_humannames[property] << "</desc>" << endl;
228        if (button_modup[property]) {
229          fs << "  <binding>" << endl;
230          fs << "   <command>property-assign</command>" << endl;
231          fs << "   <property>" << button_propnames[property] << "</property>" << endl;
232          fs << "   <value type=\"double\">" << button_step[property] << "</value>" << endl;
233          fs << "  </binding>" << endl;
234          fs << "  <mod-up>" << endl;
235          fs << "   <binding>" << endl;
236          fs << "    <command>property-assign</command>" << endl;
237          fs << "    <property>" << button_propnames[property] << "</property>" << endl;
238          fs << "    <value type=\"double\">0</value>" << endl;
239          fs << "   </binding>" << endl;
240          fs << "  </mod-up>" << endl;
241        } else if (button_boolean[property]) {
242          fs << "  <repeatable>" << button_repeat[property] << "</repeatable>" << endl;
243          fs << "  <binding>" << endl;
244          fs << "   <command>property-assign</command>" << endl;
245          fs << "   <property>" << button_propnames[property] << "</property>" << endl;
246          fs << "   <value type=\"bool\">";
247          if (button_step[property]==1) {
248            fs << "true";
249          } else {
250            fs << "false";
251          }
252          fs << "</value>" << endl;
253          fs << "  </binding>" << endl;
254        } else {
255          fs << "  <repeatable>" << button_repeat[property] << "</repeatable>" << endl;
256          fs << "  <binding>" << endl;
257          fs << "   <command>property-adjust</command>" << endl;
258          fs << "   <property>" << button_propnames[property] << "</property>" << endl;
259          fs << "   <step type=\"double\">" << button_step[property] << "</step>" << endl;
260          fs << "  </binding>" << endl;
261        }
262      }
263      fs << " </button>" << endl << endl;
264 }
265
266 void writeButtonProperties(fstream &fs, int property,int joystick, int button) {
267
268      char jsDesc[80];
269      snprintf(jsDesc,80,"--prop:/input/joysticks/js[%d]/button[%d]",joystick,button);
270
271      if (property==-1) {
272        fs << jsDesc << "/binding[0]/command=property-assign" << endl; 
273        fs << jsDesc << "/binding[0]/property=" << button_propnames[0] << endl;
274        fs << jsDesc << "/binding[0]/value=" << button_step[0] << endl; 
275        fs << jsDesc << "/binding[1]/command=property-assign" << endl; 
276        fs << jsDesc << "/binding[1]/property=" << button_propnames[1] << endl;
277        fs << jsDesc << "/binding[1]/value=" << button_step[1] << endl; 
278        fs << jsDesc << "/mod-up/binding[0]/command=property-assign" << endl; 
279        fs << jsDesc << "/mod-up/binding[0]/property=" << button_propnames[0] << endl;
280        fs << jsDesc << "/mod-up/binding[0]/value=0" << endl; 
281        fs << jsDesc << "/mod-up/binding[1]/command=property-assign" << endl; 
282        fs << jsDesc << "/mod-up/binding[1]/property=" << button_propnames[1] << endl;
283        fs << jsDesc << "/mod-up/binding[1]/value=0" << endl; 
284        fs << endl; 
285      } else if (button_modup[property]) {
286        fs << jsDesc << "/binding/command=property-assign" << endl; 
287        fs << jsDesc << "/binding/property=" << button_propnames[property] << endl;
288        fs << jsDesc << "/binding/value=" << button_step[property] << endl; 
289        fs << jsDesc << "/mod-up/binding/command=property-assign" << endl; 
290        fs << jsDesc << "/mod-up/binding/property=" << button_propnames[property] << endl;
291        fs << jsDesc << "/mod-up/binding/value=0" << endl; 
292        fs << endl; 
293      } else if (button_boolean[property]) {
294        fs << jsDesc << "/repeatable=" << button_repeat[property] << endl;
295        fs << jsDesc << "/binding/command=property-assign" << endl; 
296        fs << jsDesc << "/binding/property=" << button_propnames[property] << endl;
297        fs << jsDesc << "/binding/value=";
298        if (button_step[property]==1) {
299          fs << "true";
300        } else {
301          fs << "false";
302        }
303        fs << endl << endl; 
304      } else {
305        fs << jsDesc << "/repeatable=" << button_repeat[property] << endl;
306        fs << jsDesc << "/binding/command=property-adjust" << endl; 
307        fs << jsDesc << "/binding/property=" << button_propnames[property] << endl;
308        fs << jsDesc << "/binding/step=" << button_step[property] << endl; 
309        fs << endl; 
310      }
311 }
312
313 int main( int argc, char *argv[] ) {
314
315   bool usexml=true;
316   float deadband=0;
317   char answer[128];
318   int btninit=-2;
319
320   for (int i=1; i<argc; i++) {
321     if (strcmp("--help",argv[i])==0) {
322       cout << "Usage:" << endl;
323       cout << "  --help\t\t\tShow this help" << endl;
324       cout << "  --prop\t\t\tOutput property list" << endl;
325       cout << "  --xml\t\t\t\tOutput xml (default)" << endl;
326       cout << "  --deadband <float>\t\tSet deadband (for this program only, useful" << endl;
327       cout << "\t\t\t\tfor 'twitchy' joysticks)" << endl;
328       exit(0);
329     } else if (strcmp("--prop",argv[i])==0) {
330       usexml=false;
331       btninit=-1;
332     } else if (strcmp("--deadband",argv[i])==0) {
333       i++;
334       deadband=atoi(argv[i]);
335       cout << "Deadband set to " << argv[i] << endl;
336     } else if (strcmp("--xml",argv[i])!=0) {
337       cout << "Unknown option \"" << argv[i] << "\"" << endl;
338       exit(0);
339     }
340   }
341
342   jsInit();
343
344   jsSuper *jss=new jsSuper();
345   jsInput *jsi=new jsInput(jss);
346   jsi->displayValues(false);
347   int control=0;
348
349   cout << "Found " << jss->getNumJoysticks() << " joystick(s)" << endl;
350
351   if(jss->getNumJoysticks() <= 0) { 
352     cout << "Can't find any joysticks ..." << endl;
353     exit(1);
354   }
355
356   fstream fs;
357   fstream *xfs = new fstream[jss->getNumJoysticks()];
358   if (!usexml) {
359     fs.open("fgfsrc.js",ios::out);
360   }
361
362   jss->firstJoystick();
363   do {
364     cout << "Joystick #" << jss->getCurrentJoystickId()
365          << " \"" << jss->getJoystick()->getName() << "\" has "
366          << jss->getJoystick()->getNumAxes() << " axes" << endl;
367     for (int i=0; i<jss->getJoystick()->getNumAxes(); i++) {
368       jss->getJoystick()->setDeadBand(i,deadband);
369     }
370     if (usexml) {
371       char filename[16];
372       snprintf(filename,16,"js%i.xml",jss->getCurrentJoystickId());
373       xfs[jss->getCurrentJoystickId()].open(filename,ios::out);
374       xfs[jss->getCurrentJoystickId()] << "<?xml version=\"1.0\" ?>" << endl
375       << endl << "<PropertyList>" << endl << endl << " <name>"
376       << jss->getJoystick()->getName() << "</name>" << endl << endl;
377     }
378   } while( jss->nextJoystick() ); 
379
380   for(control=0;control<=7;control++) {
381       cout << "Move the control you wish to use for " << axes_humannames[control]
382            << endl;
383       fflush( stdout );
384       jsi->getInput();
385
386       if(jsi->getInputAxis() != -1) {
387          cout << endl << "Assigned axis " << jsi->getInputAxis()
388               << " on joystick " << jsi->getInputJoystick()
389               << " to control " << axes_humannames[control]
390               << endl;
391          bool badanswer=true;
392          do {
393            cout << "Is this correct? (y/n) $ ";
394            cin >> answer;
395            if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
396          } while (badanswer);
397          if (strcmp(answer,"n")==0) {
398            control--;
399          } else {
400            if (usexml) {
401              writeAxisXML( xfs[jsi->getInputJoystick()], control, jsi->getInputAxis() );
402            } else {
403              writeAxisProperties( fs, control, jsi->getInputJoystick(), jsi->getInputAxis() );
404            }
405          }
406       } else {
407          cout << "Skipping control" << endl;
408          bool badanswer=true;
409          do {
410            cout << "Is this correct? (y/n) $ ";
411            cin >> answer;
412            if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
413          } while (badanswer);
414          if (strcmp(answer,"n")==0) { control--; }
415       }
416       cout << endl;
417   }
418
419   for(control=btninit;control<=7;control++) {
420       if ( control == -2 ) {
421         cout << "Press the button you wish to use for View Cycle" << endl;
422       } else if ( control == -1 ) {
423         cout << "Press the button you wish to use for Brakes" << endl;
424       } else {
425         cout << "Press the button you wish to use for " << button_humannames[control] << endl;
426       }
427       fflush( stdout );
428       jsi->getInput();
429       if(jsi->getInputButton() != -1) {
430          cout << endl << "Assigned button " << jsi->getInputButton()
431               << " on joystick " << jsi->getInputJoystick()
432               << " to control ";
433          if ( control == -2 ) { cout << "View Cycle" << endl; }
434          else if ( control == -1 ) { cout << "Brakes" << endl; }
435          else { cout << button_humannames[control] << endl; }
436          bool badanswer=true;
437          do {
438            cout << "Is this correct? (y/n) $ ";
439            cin >> answer;
440            if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
441          } while (badanswer);
442          if (strcmp(answer,"n")==0) {
443            control--;
444          } else {
445            if (usexml) {
446              writeButtonXML( xfs[jsi->getInputJoystick()], control, jsi->getInputButton() );
447            } else {
448              writeButtonProperties( fs, control, jsi->getInputJoystick(), jsi->getInputButton() );
449            }
450          }
451       } else {
452          cout << "Skipping control" << endl;
453          bool badanswer=true;
454          do {
455            cout << "Is this correct? (y/n) $ ";
456            cin >> answer;
457            if ((strcmp(answer,"y")==0) || (strcmp(answer,"n")==0)) { badanswer=false; }
458          } while (badanswer);
459          if (strcmp(answer,"n")==0) { control--; }
460       }
461
462       cout << endl;
463   }
464   if (usexml) {
465     for (int i=0; i<jss->getNumJoysticks(); i++) {
466       xfs[i] << "</PropertyList>" << endl << endl << "<!-- end of joystick.xml -->" << endl;
467       xfs[i].close();
468     }
469   } else {
470     fs.close();
471   }
472   delete jsi;
473   delete[] xfs;
474   delete jss;
475
476   cout << "Your joystick settings are in ";
477   if (usexml) {
478     cout << "js0.xml, js1.xml, etc. depending on how many" << endl << "devices you have." << endl;
479   } else {
480     cout << "fgfsrc.js" << endl;
481   }
482   cout << endl << "Check and edit as desired. Once you are happy," << endl;
483   if (usexml) {
484     cout << "move relevant js<n>.xml files to $FG_ROOT/Input/Joysticks/ (if you didn't use" << endl
485          << "an attached controller, you don't need to move the corresponding file)" << endl;
486   } else {
487     cout << "append its contents to your .fgfsrc or system.fgfsrc" << endl;
488   }
489
490   return 1;
491 }