2 // Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License as
6 // published by the Free Software Foundation; either version 2 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29 # include <CoreFoundation/CoreFoundation.h>
32 #include "FGGLApplication.hxx"
33 #include "FGPanelApplication.hxx"
35 #include <OpenGL/gl.h>
36 #include <GLUT/glut.h>
42 #include <simgear/math/SGMisc.hxx>
43 #include <simgear/misc/sg_path.hxx>
44 #include <simgear/props/props_io.hxx>
45 #include <simgear/structure/exception.hxx>
46 #include <simgear/misc/ResourceManager.hxx>
50 #include "panel_io.hxx"
51 #include "ApplicationProperties.hxx"
55 inline static string ParseArgs( int argc, char ** argv, const string & token )
57 for( int i = 0; i < argc; i++ ) {
59 if( arg.find( token ) == 0 )
60 return arg.substr( token.length() );
65 inline static string ParseArgs( int argc, char ** argv, const char * token )
68 return ParseArgs( argc, argv, s );
72 // define default location of fgdata (use the same as for fgfs)
73 #if defined(__CYGWIN__)
74 inline static string platformDefaultRoot()
80 inline static string platformDefaultRoot()
84 #elif defined(__APPLE__)
85 inline static string platformDefaultRoot()
88 The following code looks for the base package inside the application
89 bundle, in the standard Contents/Resources location.
91 CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
93 // look for a 'data' subdir
94 CFURLRef dataDir = CFURLCreateCopyAppendingPathComponent(NULL, resourcesUrl, CFSTR("data"), true);
96 // now convert down to a path, and the a c-string
97 CFStringRef path = CFURLCopyFileSystemPath(dataDir, kCFURLPOSIXPathStyle);
98 string root = CFStringGetCStringPtr(path, CFStringGetSystemEncoding());
100 CFRelease(resourcesUrl);
107 inline static string platformDefaultRoot()
113 #include "FGPNGTextureLoader.hxx"
114 #include "FGRGBTextureLoader.hxx"
115 static FGPNGTextureLoader pngTextureLoader;
116 static FGRGBTextureLoader rgbTextureLoader;
118 FGPanelApplication::FGPanelApplication( int argc, char ** argv ) :
119 FGGLApplication( "FlightGear Panel", argc, argv )
121 sglog().setLogLevels( SG_ALL, SG_WARN );
122 FGCroppedTexture::registerTextureLoader( "png", &pngTextureLoader );
123 FGCroppedTexture::registerTextureLoader( "rgb", &rgbTextureLoader );
125 ApplicationProperties::root = platformDefaultRoot();
127 string panelFilename;
130 for( int i = 1; i < argc; i++ ) {
131 panelFilename = ParseArgs( argc, argv, "--panel=" );
132 fgRoot = ParseArgs( argc, argv, "--fg-root=" );
135 if( fgRoot.length() > 0 )
136 ApplicationProperties::root = fgRoot;
138 simgear::ResourceManager::instance()->addBasePath(ApplicationProperties::root);
140 if( panelFilename.length() == 0 ) {
141 cerr << "Need a panel filename. Use --panel=path_to_filename" << endl;
145 // see if we got a valid fgdata path
146 SGPath BaseCheck(ApplicationProperties::root);
147 BaseCheck.append("version");
148 if (!BaseCheck.exists())
150 cerr << "Missing base package. Use --fg-root=path_to_fgdata" << endl;
155 SGPath tpath = ApplicationProperties::GetRootPath( panelFilename.c_str() );
156 readProperties( tpath.str(), ApplicationProperties::Properties );
158 catch( sg_io_exception & e ) {
159 cerr << e.getFormattedMessage() << endl;
163 for( int i = 1; i < argc; i++ ) {
164 string arg = argv[i];
165 if( arg.find( "--prop:" ) == 0 ) {
166 string s2 = arg.substr( 7 );
167 string::size_type p = s2.find( "=" );
168 if( p != string::npos ) {
169 string propertyName = s2.substr( 0, p );
170 string propertyValue = s2.substr( p+1 );
171 ApplicationProperties::Properties->getNode( propertyName.c_str(), true )->setValue( propertyValue.c_str() );
176 SGPropertyNode_ptr n;
177 if( (n = ApplicationProperties::Properties->getNode( "panel" )) != NULL )
178 panel = FGReadablePanel::read( n );
180 protocol = new FGPanelProtocol( ApplicationProperties::Properties->getNode( "communication", true ) );
184 FGPanelApplication::~FGPanelApplication()
188 void FGPanelApplication::Run()
190 int mode = GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE;
191 int w = panel == NULL ? 0 : panel->getWidth();
192 int h = panel == NULL ? 0 : panel->getHeight();
193 if( w == 0 && h == 0 ) {
196 } else if( w == 0 ) {
198 } else if( h == 0 ) {
202 bool gameMode = ApplicationProperties::Properties->getNode( "game-mode", true )->getBoolValue();
203 FGGLApplication::Run( mode, gameMode, w, h );
206 void FGPanelApplication::Init()
208 glAlphaFunc(GL_GREATER, 0.1);
209 glutSetCursor( GLUT_CURSOR_NONE );
210 ApplicationProperties::fontCache.initializeFonts();
213 void FGPanelApplication::Reshape( int width, int height )
216 this->height = height;
217 glViewport(0, 0, (GLsizei) width, (GLsizei) height);
220 void FGPanelApplication::Idle()
222 double d = glutGet(GLUT_ELAPSED_TIME);
233 if( protocol != NULL )
234 protocol->update( dt );
236 static double dsum = 0.0;
237 static unsigned cnt = 0;
238 dsum += glutGet(GLUT_ELAPSED_TIME)-d;
240 if( dsum > 1000.0 ) {
241 ApplicationProperties::Properties->getNode( "/sim/frame-rate", true )->setDoubleValue(cnt*1000.0/dsum );
247 void FGPanelApplication::Key( unsigned char key, int x, int y )
256 double FGPanelApplication::Sleep()
258 SGTimeStamp current_time_stamp;
259 static SGTimeStamp last_time_stamp;
261 if ( last_time_stamp.get_seconds() == 0 )
262 last_time_stamp.stamp();
264 double model_hz = 60;
265 double throttle_hz = ApplicationProperties::getDouble("/sim/frame-rate-throttle-hz", 0.0);
266 if ( throttle_hz > 0.0 ) {
267 // optionally throttle the frame rate (to get consistent frame
268 // rates or reduce cpu usage.
270 double frame_us = 1.0e6 / throttle_hz;
272 // sleep based timing loop.
274 // Calling sleep, even usleep() on linux is less accurate than
275 // we like, but it does free up the cpu for other tasks during
276 // the sleep so it is desirable. Because of the way sleep()
277 // is implemented in consumer operating systems like windows
278 // and linux, you almost always sleep a little longer than the
281 // To combat the problem of sleeping too long, we calculate the
282 // desired wait time and shorten it by 2000us (2ms) to avoid
283 // [hopefully] over-sleep'ing. The 2ms value was arrived at
284 // via experimentation. We follow this up at the end with a
285 // simple busy-wait loop to get the final pause timing exactly
288 // Assuming we don't oversleep by more than 2000us, this
289 // should be a reasonable compromise between sleep based
290 // waiting, and busy waiting.
292 // sleep() will always overshoot by a bit so undersleep by
293 // 2000us in the hopes of never oversleeping.
295 if ( frame_us < 0.0 ) {
298 current_time_stamp.stamp();
301 double elapsed_us = (current_time_stamp - last_time_stamp).toUSecs();
302 if ( elapsed_us < frame_us ) {
303 double requested_us = frame_us - elapsed_us;
305 ::Sleep ((int)(requested_us / 1000.0)) ;
307 usleep ( (useconds_t)(requested_us ) ) ;
310 // busy wait timing loop.
312 // This yields the most accurate timing. If the previous
313 // usleep() call is omitted this will peg the cpu
314 // (which is just fine if FG is the only app you care about.)
315 current_time_stamp.stamp();
316 SGTimeStamp next_time_stamp = last_time_stamp;
317 next_time_stamp += SGTimeStamp::fromSec(1e-6*frame_us);
318 while ( current_time_stamp < next_time_stamp ) {
319 current_time_stamp.stamp();
323 current_time_stamp.stamp();
326 double real_delta_time_sec = double(current_time_stamp.toUSecs() - last_time_stamp.toUSecs()) / 1000000.0;
327 last_time_stamp = current_time_stamp;
328 //fprintf(stdout,"\r%4.1lf ", 1/real_delta_time_sec );
331 // round the real time down to a multiple of 1/model-hz.
332 // this way all systems are updated the _same_ amount of dt.
333 static double reminder = 0.0;
334 static long global_multi_loop = 0;
335 real_delta_time_sec += reminder;
336 global_multi_loop = long(floor(real_delta_time_sec*model_hz));
337 global_multi_loop = SGMisc<long>::max(0, global_multi_loop);
338 reminder = real_delta_time_sec - double(global_multi_loop)/double(model_hz);
339 return double(global_multi_loop)/double(model_hz);
342 double ApplicationProperties::getDouble( const char * name, double def )
344 SGPropertyNode_ptr n = ApplicationProperties::Properties->getNode( name, false );
345 if( n == NULL ) return def;
346 return n->getDoubleValue();
349 SGPath ApplicationProperties::GetCwd()
352 char buf[512], *cwd = getcwd(buf, 511);
361 SGPath ApplicationProperties::GetRootPath( const char * sub )
365 SGPath subpath( sub );
367 // relative path to current working dir?
368 if (subpath.isRelative())
370 SGPath path = GetCwd();
376 if ( subpath.exists() )
383 // default: relative path to FGROOT
384 SGPath path( ApplicationProperties::root );
391 std::string ApplicationProperties::root = ".";
392 SGPropertyNode_ptr ApplicationProperties::Properties = new SGPropertyNode;
393 FGFontCache ApplicationProperties::fontCache;