]> git.mxchange.org Git - flightgear.git/blob - utils/fgpanel/FGPanelApplication.cxx
NavDisplay: fix many bugs relating to stretched and heading-rotated symbols.
[flightgear.git] / utils / fgpanel / FGPanelApplication.cxx
1 //
2 //  Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
3 //
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.
8 // 
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.
13 //
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.
17 //
18
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #ifdef HAVE_WINDOWS_H
24 #include <windows.h>
25 #include <direct.h>
26 #endif
27
28 #include "FGGLApplication.hxx"
29 #include "FGPanelApplication.hxx"
30 #if defined (SG_MAC)
31 #include <OpenGL/gl.h>
32 #include <GLUT/glut.h>
33 #else
34 #include <GL/gl.h>
35 #include <GL/glut.h>
36 #endif
37
38 #include <simgear/math/SGMisc.hxx>
39 #include <simgear/misc/sg_path.hxx>
40 #include <simgear/props/props_io.hxx>
41 #include <simgear/structure/exception.hxx>
42 #include <simgear/misc/ResourceManager.hxx>
43
44 #include <iostream>
45
46 #include "panel_io.hxx"
47 #include "ApplicationProperties.hxx"
48
49 using namespace std;
50
51 inline static string ParseArgs( int argc, char ** argv, const string & token )
52 {
53   for( int i = 0; i < argc; i++ ) {
54     string arg = argv[i];
55     if( arg.find( token ) == 0 )
56       return arg.substr( token.length() );
57   }
58   return "";
59 }
60
61 inline static string ParseArgs( int argc, char ** argv, const char * token )
62 {
63   string s = token;
64   return ParseArgs( argc, argv, s );
65 }
66
67 #include "FGPNGTextureLoader.hxx"
68 #include "FGRGBTextureLoader.hxx"
69 static FGPNGTextureLoader pngTextureLoader;
70 static FGRGBTextureLoader rgbTextureLoader;
71
72 FGPanelApplication::FGPanelApplication( int argc, char ** argv ) :
73   FGGLApplication( "FlightGear Panel", argc, argv )
74 {
75   sglog().setLogLevels( SG_ALL, SG_WARN );
76   FGCroppedTexture::registerTextureLoader( "png", &pngTextureLoader );
77   FGCroppedTexture::registerTextureLoader( "rgb", &rgbTextureLoader );
78
79   string panelFilename;
80   string fgRoot;
81
82   for( int i = 1; i < argc; i++ ) {
83     panelFilename = ParseArgs( argc, argv, "--panel=" );
84     fgRoot        = ParseArgs( argc, argv, "--fg-root=" );
85   }
86
87   if( fgRoot.length() > 0 )
88     ApplicationProperties::root = fgRoot;
89
90   simgear::ResourceManager::instance()->addBasePath(ApplicationProperties::root);
91
92   if( panelFilename.length() == 0 ) {
93     cerr << "Need a panel filename. Use --panel=path_to_filename" << endl; 
94     throw exception();
95   }
96
97   // see if we got a valid fgdata path
98   SGPath BaseCheck(ApplicationProperties::root);
99   BaseCheck.append("version");
100   if (!BaseCheck.exists())
101   {
102       cerr << "Missing base package. Use --fg-root=path_to_fgdata" << endl; 
103       throw exception();
104   }
105
106   try {
107     SGPath tpath = ApplicationProperties::GetRootPath( panelFilename.c_str() );
108     readProperties( tpath.str(), ApplicationProperties::Properties );
109   }
110   catch( sg_io_exception & e ) {
111     cerr << e.getFormattedMessage() << endl;
112     throw;
113   }
114
115   for( int i = 1; i < argc; i++ ) {
116     string arg = argv[i];
117     if( arg.find( "--prop:" ) == 0 ) {
118       string s2 = arg.substr( 7 );
119       string::size_type p = s2.find( "=" );
120       if( p != string::npos ) {
121         string propertyName = s2.substr( 0, p );
122         string propertyValue = s2.substr( p+1 );
123         ApplicationProperties::Properties->getNode( propertyName.c_str(), true )->setValue( propertyValue.c_str() );
124       }
125     }
126   }
127
128   SGPropertyNode_ptr n;
129   if( (n = ApplicationProperties::Properties->getNode( "panel" )) != NULL )
130     panel = FGReadablePanel::read( n );
131
132   protocol = new FGPanelProtocol( ApplicationProperties::Properties->getNode( "communication", true ) );
133   protocol->init();
134 }
135
136 FGPanelApplication::~FGPanelApplication()
137 {
138 }
139
140 void FGPanelApplication::Run()
141 {
142   int mode = GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE;
143   int w = panel == NULL ? 0 : panel->getWidth();
144   int h = panel == NULL ? 0 : panel->getHeight();
145   if( w == 0 && h == 0 ) {
146     w = 1024;
147     h = 768;
148   } else if( w == 0 ) {
149     w = h / 0.75;
150   } else if( h == 0 ) { 
151     h = w * 0.75;
152   }
153   
154   bool gameMode = ApplicationProperties::Properties->getNode( "game-mode", true )->getBoolValue();
155   FGGLApplication::Run( mode, gameMode, w, h );
156 }
157
158 void FGPanelApplication::Init()
159 {
160   glAlphaFunc(GL_GREATER, 0.1);
161   glutSetCursor( GLUT_CURSOR_NONE );
162   ApplicationProperties::fontCache.initializeFonts();
163 }
164
165 void FGPanelApplication::Reshape( int width, int height )
166 {
167   this->width = width;
168   this->height = height;
169   glViewport(0, 0, (GLsizei) width, (GLsizei) height);
170 }
171
172 void FGPanelApplication::Idle()
173 {
174   double d = glutGet(GLUT_ELAPSED_TIME);
175
176   double dt = Sleep();
177   if( dt == 0 )
178     return;
179
180   if( panel != NULL )
181     panel->update( dt );
182
183   glutSwapBuffers();
184
185   if( protocol != NULL )
186     protocol->update( dt );
187
188   static double dsum = 0.0;
189   static unsigned cnt = 0;
190   dsum += glutGet(GLUT_ELAPSED_TIME)-d;
191   cnt++;
192   if( dsum > 1000.0 ) {
193     ApplicationProperties::Properties->getNode( "/sim/frame-rate", true )->setDoubleValue(cnt*1000.0/dsum );
194     dsum = 0.0;
195     cnt = 0;
196   }
197 }
198
199 void FGPanelApplication::Key( unsigned char key, int x, int y )
200 {
201   switch( key ) {
202     case 0x1b:
203       exit(0);
204       break;
205   }
206 }
207
208 double FGPanelApplication::Sleep()
209 {
210   SGTimeStamp current_time_stamp;
211   static SGTimeStamp last_time_stamp;
212
213   if ( last_time_stamp.get_seconds() == 0 )
214     last_time_stamp.stamp();
215
216   double model_hz = 60;
217   double throttle_hz = ApplicationProperties::getDouble("/sim/frame-rate-throttle-hz", 0.0);
218   if ( throttle_hz > 0.0 ) {
219     // optionally throttle the frame rate (to get consistent frame
220     // rates or reduce cpu usage.
221
222     double frame_us = 1.0e6 / throttle_hz;
223
224     // sleep based timing loop.
225     //
226     // Calling sleep, even usleep() on linux is less accurate than
227     // we like, but it does free up the cpu for other tasks during
228     // the sleep so it is desirable.  Because of the way sleep()
229     // is implemented in consumer operating systems like windows
230     // and linux, you almost always sleep a little longer than the
231     // requested amount.
232     //
233     // To combat the problem of sleeping too long, we calculate the
234     // desired wait time and shorten it by 2000us (2ms) to avoid
235     // [hopefully] over-sleep'ing.  The 2ms value was arrived at
236     // via experimentation.  We follow this up at the end with a
237     // simple busy-wait loop to get the final pause timing exactly
238     // right.
239     //
240     // Assuming we don't oversleep by more than 2000us, this
241     // should be a reasonable compromise between sleep based
242     // waiting, and busy waiting.
243
244     // sleep() will always overshoot by a bit so undersleep by
245     // 2000us in the hopes of never oversleeping.
246     frame_us -= 2000.0;
247     if ( frame_us < 0.0 ) {
248       frame_us = 0.0;
249     }
250     current_time_stamp.stamp();
251
252     /* Convert to ms */
253     double elapsed_us = (current_time_stamp - last_time_stamp).toUSecs();
254     if ( elapsed_us < frame_us ) {
255       double requested_us = frame_us - elapsed_us;
256 #ifdef _WIN32
257       ::Sleep ((int)(requested_us / 1000.0)) ;
258 #else
259       usleep ( (useconds_t)(requested_us ) ) ;
260 #endif
261     }
262     // busy wait timing loop.
263     //
264     // This yields the most accurate timing.  If the previous
265     // usleep() call is omitted this will peg the cpu
266     // (which is just fine if FG is the only app you care about.)
267     current_time_stamp.stamp();
268     SGTimeStamp next_time_stamp = last_time_stamp;
269     next_time_stamp += SGTimeStamp::fromSec(1e-6*frame_us);
270     while ( current_time_stamp < next_time_stamp ) {
271       current_time_stamp.stamp();
272     }
273
274   } else {
275     current_time_stamp.stamp();
276   }
277
278   double real_delta_time_sec = double(current_time_stamp.toUSecs() - last_time_stamp.toUSecs()) / 1000000.0;
279   last_time_stamp = current_time_stamp;
280 //fprintf(stdout,"\r%4.1lf ", 1/real_delta_time_sec );
281 //fflush(stdout);
282
283   // round the real time down to a multiple of 1/model-hz.
284   // this way all systems are updated the _same_ amount of dt.
285   static double reminder = 0.0;
286   static long global_multi_loop = 0;
287   real_delta_time_sec += reminder;
288   global_multi_loop = long(floor(real_delta_time_sec*model_hz));
289   global_multi_loop = SGMisc<long>::max(0, global_multi_loop);
290   reminder = real_delta_time_sec - double(global_multi_loop)/double(model_hz);
291   return double(global_multi_loop)/double(model_hz);
292 }
293
294 double ApplicationProperties::getDouble( const char * name, double def )
295 {
296   SGPropertyNode_ptr n = ApplicationProperties::Properties->getNode( name, false );
297   if( n == NULL ) return def;
298   return n->getDoubleValue();
299 }
300
301 SGPath ApplicationProperties::GetCwd()
302 {
303   SGPath path(".");
304   char buf[512], *cwd = getcwd(buf, 511);
305   buf[511] = '\0';
306   if (cwd)
307   {
308     path = cwd;
309   }
310   return path;
311 }
312
313 SGPath ApplicationProperties::GetRootPath( const char * sub )
314 {
315   if( sub != NULL )
316   {
317     SGPath subpath( sub );
318
319     // relative path to current working dir?
320     if (subpath.isRelative())
321     {
322       SGPath path = GetCwd();
323       path.append( sub );
324       if (path.exists())
325         return path;
326     }
327     else
328     if ( subpath.exists() )
329     {
330       // absolute path
331       return subpath;
332     }
333   }
334
335   // default: relative path to FGROOT
336   SGPath path( ApplicationProperties::root );
337   if( sub != NULL )
338     path.append( sub );
339
340   return path;
341 }
342
343 std::string ApplicationProperties::root = ".";
344 SGPropertyNode_ptr ApplicationProperties::Properties = new SGPropertyNode;
345 FGFontCache ApplicationProperties::fontCache;