]> git.mxchange.org Git - flightgear.git/blob - utils/fgpanel/FGPanelApplication.cxx
JsonUriHandler: allow POST updates without child
[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 #ifdef __APPLE__
29 #  include <CoreFoundation/CoreFoundation.h>
30 #endif
31
32 #include "FGGLApplication.hxx"
33 #include "FGPanelApplication.hxx"
34 #if defined (SG_MAC)
35 #include <OpenGL/gl.h>
36 #include <GLUT/glut.h>
37 #else
38 #include <GL/gl.h>
39 #include <GL/glut.h>
40 #endif
41
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>
47
48 #include <iostream>
49
50 #include "panel_io.hxx"
51 #include "ApplicationProperties.hxx"
52
53 using namespace std;
54
55 inline static string ParseArgs( int argc, char ** argv, const string & token )
56 {
57   for( int i = 0; i < argc; i++ ) {
58     string arg = argv[i];
59     if( arg.find( token ) == 0 )
60       return arg.substr( token.length() );
61   }
62   return "";
63 }
64
65 inline static string ParseArgs( int argc, char ** argv, const char * token )
66 {
67   string s = token;
68   return ParseArgs( argc, argv, s );
69 }
70
71
72 // define default location of fgdata (use the same as for fgfs)
73 #if defined(__CYGWIN__)
74 inline static string platformDefaultRoot()
75 {
76   return "../data";
77 }
78
79 #elif defined(_WIN32)
80 inline static string platformDefaultRoot()
81 {
82   return "..\\data";
83 }
84 #elif defined(__APPLE__)
85 inline static string platformDefaultRoot()
86 {
87   /*
88    The following code looks for the base package inside the application
89    bundle, in the standard Contents/Resources location.
90    */
91   CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
92
93   // look for a 'data' subdir
94   CFURLRef dataDir = CFURLCreateCopyAppendingPathComponent(NULL, resourcesUrl, CFSTR("data"), true);
95
96   // now convert down to a path, and the a c-string
97   CFStringRef path = CFURLCopyFileSystemPath(dataDir, kCFURLPOSIXPathStyle);
98   string root = CFStringGetCStringPtr(path, CFStringGetSystemEncoding());
99
100   CFRelease(resourcesUrl);
101   CFRelease(dataDir);
102   CFRelease(path);
103
104   return root;
105 }
106 #else
107 inline static string platformDefaultRoot()
108 {
109   return PKGLIBDIR;
110 }
111 #endif
112
113 #include "FGPNGTextureLoader.hxx"
114 #include "FGRGBTextureLoader.hxx"
115 static FGPNGTextureLoader pngTextureLoader;
116 static FGRGBTextureLoader rgbTextureLoader;
117
118 FGPanelApplication::FGPanelApplication( int argc, char ** argv ) :
119   FGGLApplication( "FlightGear Panel", argc, argv )
120 {
121   sglog().setLogLevels( SG_ALL, SG_WARN );
122   FGCroppedTexture::registerTextureLoader( "png", &pngTextureLoader );
123   FGCroppedTexture::registerTextureLoader( "rgb", &rgbTextureLoader );
124
125   ApplicationProperties::root = platformDefaultRoot();
126
127   string panelFilename;
128   string fgRoot;
129
130   for( int i = 1; i < argc; i++ ) {
131     panelFilename = ParseArgs( argc, argv, "--panel=" );
132     fgRoot        = ParseArgs( argc, argv, "--fg-root=" );
133   }
134
135   if( fgRoot.length() > 0 )
136     ApplicationProperties::root = fgRoot;
137
138   simgear::ResourceManager::instance()->addBasePath(ApplicationProperties::root);
139
140   if( panelFilename.length() == 0 ) {
141     cerr << "Need a panel filename. Use --panel=path_to_filename" << endl; 
142     throw exception();
143   }
144
145   // see if we got a valid fgdata path
146   SGPath BaseCheck(ApplicationProperties::root);
147   BaseCheck.append("version");
148   if (!BaseCheck.exists())
149   {
150       cerr << "Missing base package. Use --fg-root=path_to_fgdata" << endl; 
151       throw exception();
152   }
153
154   try {
155     SGPath tpath = ApplicationProperties::GetRootPath( panelFilename.c_str() );
156     readProperties( tpath.str(), ApplicationProperties::Properties );
157   }
158   catch( sg_io_exception & e ) {
159     cerr << e.getFormattedMessage() << endl;
160     throw;
161   }
162
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() );
172       }
173     }
174   }
175
176   SGPropertyNode_ptr n;
177   if( (n = ApplicationProperties::Properties->getNode( "panel" )) != NULL )
178     panel = FGReadablePanel::read( n );
179
180   protocol = new FGPanelProtocol( ApplicationProperties::Properties->getNode( "communication", true ) );
181   protocol->init();
182 }
183
184 FGPanelApplication::~FGPanelApplication()
185 {
186 }
187
188 void FGPanelApplication::Run()
189 {
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 ) {
194     w = 1024;
195     h = 768;
196   } else if( w == 0 ) {
197     w = h / 0.75;
198   } else if( h == 0 ) { 
199     h = w * 0.75;
200   }
201   
202   bool gameMode = ApplicationProperties::Properties->getNode( "game-mode", true )->getBoolValue();
203   FGGLApplication::Run( mode, gameMode, w, h );
204 }
205
206 void FGPanelApplication::Init()
207 {
208   glAlphaFunc(GL_GREATER, 0.1);
209   glutSetCursor( GLUT_CURSOR_NONE );
210   ApplicationProperties::fontCache.initializeFonts();
211 }
212
213 void FGPanelApplication::Reshape( int width, int height )
214 {
215   this->width = width;
216   this->height = height;
217   glViewport(0, 0, (GLsizei) width, (GLsizei) height);
218 }
219
220 void FGPanelApplication::Idle()
221 {
222   double d = glutGet(GLUT_ELAPSED_TIME);
223
224   double dt = Sleep();
225   if( dt == 0 )
226     return;
227
228   if( panel != NULL )
229     panel->update( dt );
230
231   glutSwapBuffers();
232
233   if( protocol != NULL )
234     protocol->update( dt );
235
236   static double dsum = 0.0;
237   static unsigned cnt = 0;
238   dsum += glutGet(GLUT_ELAPSED_TIME)-d;
239   cnt++;
240   if( dsum > 1000.0 ) {
241     ApplicationProperties::Properties->getNode( "/sim/frame-rate", true )->setDoubleValue(cnt*1000.0/dsum );
242     dsum = 0.0;
243     cnt = 0;
244   }
245 }
246
247 void FGPanelApplication::Key( unsigned char key, int x, int y )
248 {
249   switch( key ) {
250     case 0x1b:
251       exit(0);
252       break;
253   }
254 }
255
256 double FGPanelApplication::Sleep()
257 {
258   SGTimeStamp current_time_stamp;
259   static SGTimeStamp last_time_stamp;
260
261   if ( last_time_stamp.get_seconds() == 0 )
262     last_time_stamp.stamp();
263
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.
269
270     double frame_us = 1.0e6 / throttle_hz;
271
272     // sleep based timing loop.
273     //
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
279     // requested amount.
280     //
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
286     // right.
287     //
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.
291
292     // sleep() will always overshoot by a bit so undersleep by
293     // 2000us in the hopes of never oversleeping.
294     frame_us -= 2000.0;
295     if ( frame_us < 0.0 ) {
296       frame_us = 0.0;
297     }
298     current_time_stamp.stamp();
299
300     /* Convert to ms */
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;
304 #ifdef _WIN32
305       ::Sleep ((int)(requested_us / 1000.0)) ;
306 #else
307       usleep ( (useconds_t)(requested_us ) ) ;
308 #endif
309     }
310     // busy wait timing loop.
311     //
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();
320     }
321
322   } else {
323     current_time_stamp.stamp();
324   }
325
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 );
329 //fflush(stdout);
330
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);
340 }
341
342 double ApplicationProperties::getDouble( const char * name, double def )
343 {
344   SGPropertyNode_ptr n = ApplicationProperties::Properties->getNode( name, false );
345   if( n == NULL ) return def;
346   return n->getDoubleValue();
347 }
348
349 SGPath ApplicationProperties::GetCwd()
350 {
351   SGPath path(".");
352   char buf[512], *cwd = getcwd(buf, 511);
353   buf[511] = '\0';
354   if (cwd)
355   {
356     path = cwd;
357   }
358   return path;
359 }
360
361 SGPath ApplicationProperties::GetRootPath( const char * sub )
362 {
363   if( sub != NULL )
364   {
365     SGPath subpath( sub );
366
367     // relative path to current working dir?
368     if (subpath.isRelative())
369     {
370       SGPath path = GetCwd();
371       path.append( sub );
372       if (path.exists())
373         return path;
374     }
375     else
376     if ( subpath.exists() )
377     {
378       // absolute path
379       return subpath;
380     }
381   }
382
383   // default: relative path to FGROOT
384   SGPath path( ApplicationProperties::root );
385   if( sub != NULL )
386     path.append( sub );
387
388   return path;
389 }
390
391 std::string ApplicationProperties::root = ".";
392 SGPropertyNode_ptr ApplicationProperties::Properties = new SGPropertyNode;
393 FGFontCache ApplicationProperties::fontCache;