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