]> git.mxchange.org Git - flightgear.git/blob - src/Input/fgjs.cxx
74a89620572eaf715774ec20f8231af6371d9ffb
[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 // Copyright (C) 2006  Stefan Seifert
10 //
11 // This program is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU General Public License as
13 // published by the Free Software Foundation; either version 2 of the
14 // License, or (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful, but
17 // WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 // General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28
29 #include <simgear/compiler.h>
30
31 #ifdef _MSC_VER
32 #include <Winsock2.h>
33 #endif
34
35 #include <math.h>
36
37 #include STL_IOSTREAM
38 #include STL_FSTREAM
39 #include STL_STRING
40
41 SG_USING_STD(fstream);
42 SG_USING_STD(cout);
43 SG_USING_STD(cin);
44 SG_USING_STD(endl);
45 SG_USING_STD(ios);
46 SG_USING_STD(string);
47
48 #include <simgear/constants.h>
49 #include <simgear/debug/logstream.hxx>
50 #include <simgear/misc/sg_path.hxx>
51 #include <simgear/misc/sgstream.hxx>
52 #include <simgear/structure/exception.hxx>
53
54 #include <Main/fg_io.hxx>
55 #include <Main/fg_props.hxx>
56 #include <Main/globals.hxx>
57
58 #include "jsinput.h"
59
60
61 bool confirmAnswer() {
62     char answer;
63     do {
64         cout << "Is this correct? (y/n) $ ";
65         cin >> answer;
66         cin.ignore(256, '\n');
67         if (answer == 'y')
68             return true;
69         if (answer == 'n')
70             return false;
71     } while (true);
72 }
73
74 string getFGRoot( int argc, char *argv[] );
75
76 int main( int argc, char *argv[] ) {
77
78     for (int i = 1; i < argc; i++) {
79         if (strcmp("--help", argv[i]) == 0) {
80             cout << "Usage:" << endl;
81             cout << "  --help\t\t\tShow this help" << endl;
82             exit(0);
83         } else {
84             cout << "Unknown option \"" << argv[i] << "\"" << endl;
85             exit(0);
86         }
87     }
88
89     jsInit();
90
91     jsSuper *jss = new jsSuper();
92     jsInput *jsi = new jsInput(jss);
93     jsi->displayValues(false);
94
95     cout << "Found " << jss->getNumJoysticks() << " joystick(s)" << endl;
96
97     if(jss->getNumJoysticks() <= 0) {
98         cout << "Can't find any joysticks ..." << endl;
99         exit(1);
100     }
101     cout << endl << "Now measuring the dead band of your joystick. The dead band is the area " << endl
102                  << "where the joystick is centered and should not generate any input. Move all " << endl
103                  << "axes around in this dead zone during the ten seconds this test will take." << endl;
104     cout << "Press enter to continue." << endl;
105     cin.ignore(1024, '\n');
106     jsi->findDeadBand();
107     cout << endl << "Dead band calibration finished. Press enter to start control assignment." << endl;
108     cin.ignore(1024, '\n');
109
110     jss->firstJoystick();
111     fstream *xfs = new fstream[ jss->getNumJoysticks() ];
112     SGPropertyNode_ptr *jstree = new SGPropertyNode_ptr[ jss->getNumJoysticks() ];
113     do {
114         cout << "Joystick #" << jss->getCurrentJoystickId()
115              << " \"" << jss->getJoystick()->getName() << "\" has "
116              << jss->getJoystick()->getNumAxes() << " axes" << endl;
117
118         char filename[16];
119         snprintf(filename, 16, "js%i.xml", jss->getCurrentJoystickId());
120         xfs[ jss->getCurrentJoystickId() ].open(filename, ios::out);
121         jstree[ jss->getCurrentJoystickId() ] = new SGPropertyNode();
122     } while ( jss->nextJoystick() );
123
124     SGPath templatefile( getFGRoot(argc, argv) );
125     templatefile.append("Input");
126     templatefile.append("Joysticks");
127     templatefile.append("template.xml");
128
129     SGPropertyNode *templatetree = new SGPropertyNode();
130     try {
131         readProperties(templatefile.str().c_str(), templatetree);
132     } catch (sg_io_exception e) {
133         cout << e.getFormattedMessage ();
134     }
135
136     vector<SGPropertyNode_ptr> axes = templatetree->getChildren("axis");
137     for(vector<SGPropertyNode_ptr>::iterator iter = axes.begin(); iter != axes.end(); iter++) {
138         cout << "Move the control you wish to use for " << (*iter)->getStringValue("desc")
139              << " " << (*iter)->getStringValue("direction") << endl;
140         cout << "Pressing a button skips this axis" << endl;
141         fflush( stdout );
142         jsi->getInput();
143         if (jsi->getInputAxis() != -1) {
144             cout << endl << "Assigned axis " << jsi->getInputAxis()
145                  << " on joystick " << jsi->getInputJoystick()
146                  << " to control " << (*iter)->getStringValue("desc") << endl;
147             if ( confirmAnswer() ) {
148                 SGPropertyNode *axis = jstree[ jsi->getInputJoystick() ]->getChild("axis", jsi->getInputAxis(), true);
149                 copyProperties(*iter, axis);
150                 axis->setDoubleValue("dead-band", jss->getJoystick(jsi->getInputJoystick())
151                         ->getDeadBand(jsi->getInputAxis()));
152                 axis->setDoubleValue("binding/factor", jsi->getInputAxisPositive() ? 1.0 : -1.0);
153             } else {
154                 iter--;
155             }
156         } else {
157             cout << "Skipping control" << endl;
158             if ( ! confirmAnswer() )
159                 iter--;
160         }
161         cout << endl;
162     }
163
164     vector<SGPropertyNode_ptr> buttons = templatetree->getChildren("button");
165     for(vector<SGPropertyNode_ptr>::iterator iter = buttons.begin(); iter != buttons.end(); iter++) {
166         cout << "Press the button you wish to use for " << (*iter)->getStringValue("desc") << endl;
167         cout << "Moving a joystick axis skips this button" << endl;
168         fflush( stdout );
169         jsi->getInput();
170         if (jsi->getInputButton() != -1) {
171             cout << endl << "Assigned button " << jsi->getInputButton()
172                  << " on joystick " << jsi->getInputJoystick()
173                  << " to control " << (*iter)->getStringValue("desc") << endl;
174             if ( confirmAnswer() ) {
175                 SGPropertyNode *button = jstree[ jsi->getInputJoystick() ]->getChild("button", jsi->getInputButton(), true);
176                 copyProperties(*iter, button);
177             } else {
178                 iter--;
179             }
180         } else {
181             cout << "Skipping control" << endl;
182             if (! confirmAnswer())
183                 iter--;
184         }
185         cout << endl;
186     }
187
188     cout << "Your joystick settings are in ";
189     for (int i = 0; i < jss->getNumJoysticks(); i++) {
190         try {
191             cout << "js" << i << ".xml";
192             if (i + 2 < jss->getNumJoysticks())
193                 cout << ", ";
194             else if (i + 1 < jss->getNumJoysticks())
195                 cout << " and ";
196
197             jstree[i]->setStringValue("name", jss->getJoystick(i)->getName());
198             writeProperties(xfs[i], jstree[i], true);
199         } catch (sg_io_exception e) {
200             cout << e.getFormattedMessage ();
201         }
202         xfs[i].close();
203     }
204     cout << "." << endl << "Check and edit as desired. Once you are happy," << endl
205          << "move relevant js<n>.xml files to $FG_ROOT/Input/Joysticks/ (if you didn't use" << endl
206          << "an attached controller, you don't need to move the corresponding file)" << endl;
207
208     delete jsi;
209     delete[] xfs;
210     delete jss;
211
212     return 1;
213 }
214
215 char *homedir = ::getenv( "HOME" );
216 char *hostname = ::getenv( "HOSTNAME" );
217 bool free_hostname = false;
218
219 // Scan the command line options for the specified option and return
220 // the value.
221 static string fgScanForOption( const string& option, int argc, char **argv ) {
222     int i = 1;
223
224     if (hostname == NULL)
225     {
226         char _hostname[256];
227         gethostname(_hostname, 256);
228         hostname = strdup(_hostname);
229         free_hostname = true;
230     }
231
232     SG_LOG(SG_GENERAL, SG_INFO, "Scanning command line for: " << option );
233
234     int len = option.length();
235
236     while ( i < argc ) {
237         SG_LOG( SG_GENERAL, SG_DEBUG, "argv[" << i << "] = " << argv[i] );
238
239         string arg = argv[i];
240         if ( arg.find( option ) == 0 ) {
241             return arg.substr( len );
242         }
243
244         i++;
245     }
246
247     return "";
248 }
249
250 // Scan the user config files for the specified option and return
251 // the value.
252 static string fgScanForOption( const string& option, const string& path ) {
253     sg_gzifstream in( path );
254     if ( !in.is_open() ) {
255         return "";
256     }
257
258     SG_LOG( SG_GENERAL, SG_INFO, "Scanning " << path << " for: " << option );
259
260     int len = option.length();
261
262     in >> skipcomment;
263 #ifndef __MWERKS__
264     while ( ! in.eof() ) {
265 #else
266     char c = '\0';
267     while ( in.get(c) && c != '\0' ) {
268         in.putback(c);
269 #endif
270         string line;
271
272 #if defined( macintosh )
273         getline( in, line, '\r' );
274 #else
275         getline( in, line, '\n' );
276 #endif
277
278         // catch extraneous (DOS) line ending character
279         if ( line[line.length() - 1] < 32 ) {
280             line = line.substr( 0, line.length()-1 );
281         }
282
283         if ( line.find( option ) == 0 ) {
284             return line.substr( len );
285         }
286
287         in >> skipcomment;
288     }
289
290     return "";
291 }
292
293 // Scan the user config files for the specified option and return
294 // the value.
295 static string fgScanForOption( const string& option ) {
296     string arg("");
297
298 #if defined( unix ) || defined( __CYGWIN__ )
299     // Next check home directory for .fgfsrc.hostname file
300     if ( arg.empty() ) {
301         if ( homedir != NULL ) {
302             SGPath config( homedir );
303             config.append( ".fgfsrc" );
304             config.concat( "." );
305             config.concat( hostname );
306             arg = fgScanForOption( option, config.str() );
307         }
308     }
309 #endif
310
311     // Next check home directory for .fgfsrc file
312     if ( arg.empty() ) {
313         if ( homedir != NULL ) {
314             SGPath config( homedir );
315             config.append( ".fgfsrc" );
316             arg = fgScanForOption( option, config.str() );
317         }
318     }
319
320     return arg;
321 }
322
323 // Read in configuration (files and command line options) but only set
324 // fg_root
325 string getFGRoot ( int argc, char **argv ) {
326     string root;
327
328     // First parse command line options looking for --fg-root=, this
329     // will override anything specified in a config file
330     root = fgScanForOption( "--fg-root=", argc, argv);
331
332     // Check in one of the user configuration files.
333     if (root.empty() )
334         root = fgScanForOption( "--fg-root=" );
335
336     // Next check if fg-root is set as an env variable
337     if ( root.empty() ) {
338         char *envp = ::getenv( "FG_ROOT" );
339         if ( envp != NULL ) {
340             root = envp;
341         }
342     }
343
344     // Otherwise, default to a random compiled-in location if we can't
345     // find fg-root any other way.
346     if ( root.empty() ) {
347 #if defined( __CYGWIN__ )
348         root = "/FlightGear";
349 #elif defined( WIN32 )
350         root = "\\FlightGear";
351 #elif defined(OSX_BUNDLE)
352         /* the following code looks for the base package directly inside
353             the application bundle. This can be changed fairly easily by
354             fiddling with the code below. And yes, I know it's ugly and verbose.
355         */
356         CFBundleRef appBundle = CFBundleGetMainBundle();
357         CFURLRef appUrl = CFBundleCopyBundleURL(appBundle);
358         CFRelease(appBundle);
359
360         // look for a 'data' subdir directly inside the bundle : is there
361         // a better place? maybe in Resources? I don't know ...
362         CFURLRef dataDir = CFURLCreateCopyAppendingPathComponent(NULL, appUrl, CFSTR("data"), true);
363
364         // now convert down to a path, and the a c-string
365         CFStringRef path = CFURLCopyFileSystemPath(dataDir, kCFURLPOSIXPathStyle);
366         root = CFStringGetCStringPtr(path, CFStringGetSystemEncoding());
367
368         // tidy up.
369         CFRelease(appBundle);
370         CFRelease(dataDir);
371         CFRelease(path);
372 #else
373         root = PKGLIBDIR;
374 #endif
375     }
376
377     SG_LOG(SG_INPUT, SG_INFO, "fg_root = " << root );
378
379     return root;
380 }