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