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