Merge branch 'next' into durk-atc
This commit is contained in:
commit
b5025ccf5d
8 changed files with 330 additions and 298 deletions
|
@ -65,7 +65,7 @@
|
||||||
/>
|
/>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCLinkerTool"
|
Name="VCLinkerTool"
|
||||||
AdditionalDependencies="fltkd.lib ul_d.lib comctl32.lib wsock32.lib zlibd.lib sg_d.lib"
|
AdditionalDependencies="fltkd.lib comctl32.lib wsock32.lib zlibd.lib"
|
||||||
LinkIncremental="2"
|
LinkIncremental="2"
|
||||||
AdditionalLibraryDirectories="..\..\..\..\3rdParty\lib"
|
AdditionalLibraryDirectories="..\..\..\..\3rdParty\lib"
|
||||||
GenerateDebugInformation="true"
|
GenerateDebugInformation="true"
|
||||||
|
@ -145,7 +145,7 @@
|
||||||
/>
|
/>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCLinkerTool"
|
Name="VCLinkerTool"
|
||||||
AdditionalDependencies="fltkd.lib ul_d.lib comctl32.lib wsock32.lib zlibd.lib sg_d.lib"
|
AdditionalDependencies="fltkd.lib comctl32.lib wsock32.lib zlibd.lib"
|
||||||
LinkIncremental="2"
|
LinkIncremental="2"
|
||||||
AdditionalLibraryDirectories="..\..\..\..\3rdParty.x64\lib"
|
AdditionalLibraryDirectories="..\..\..\..\3rdParty.x64\lib"
|
||||||
GenerateDebugInformation="true"
|
GenerateDebugInformation="true"
|
||||||
|
@ -226,7 +226,7 @@
|
||||||
/>
|
/>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCLinkerTool"
|
Name="VCLinkerTool"
|
||||||
AdditionalDependencies="fltk.lib ul.lib sg.lib comctl32.lib wsock32.lib zlib.lib"
|
AdditionalDependencies="fltk.lib comctl32.lib wsock32.lib zlib.lib"
|
||||||
LinkIncremental="1"
|
LinkIncremental="1"
|
||||||
AdditionalLibraryDirectories="..\..\..\..\3rdParty\lib"
|
AdditionalLibraryDirectories="..\..\..\..\3rdParty\lib"
|
||||||
GenerateDebugInformation="false"
|
GenerateDebugInformation="false"
|
||||||
|
@ -309,7 +309,7 @@
|
||||||
/>
|
/>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCLinkerTool"
|
Name="VCLinkerTool"
|
||||||
AdditionalDependencies="fltk.lib ul.lib sg.lib comctl32.lib wsock32.lib zlib.lib"
|
AdditionalDependencies="fltk.lib comctl32.lib wsock32.lib zlib.lib"
|
||||||
LinkIncremental="1"
|
LinkIncremental="1"
|
||||||
AdditionalLibraryDirectories="..\..\..\..\3rdParty.x64\lib"
|
AdditionalLibraryDirectories="..\..\..\..\3rdParty.x64\lib"
|
||||||
GenerateDebugInformation="false"
|
GenerateDebugInformation="false"
|
||||||
|
|
|
@ -219,6 +219,10 @@ FGEnvironmentMgr::bind ()
|
||||||
&SGSky::get_3dCloudVisRange,
|
&SGSky::get_3dCloudVisRange,
|
||||||
&SGSky::set_3dCloudVisRange);
|
&SGSky::set_3dCloudVisRange);
|
||||||
|
|
||||||
|
_tiedProperties.Tie("clouds3d-wrap", thesky,
|
||||||
|
&SGSky::get_3dCloudWrap,
|
||||||
|
&SGSky::set_3dCloudWrap);
|
||||||
|
|
||||||
// _tiedProperties.Tie("lightning-enable", &sgEnviro,
|
// _tiedProperties.Tie("lightning-enable", &sgEnviro,
|
||||||
// &SGEnviro::get_lightning_enable_state,
|
// &SGEnviro::get_lightning_enable_state,
|
||||||
// &SGEnviro::set_lightning_enable_state);
|
// &SGEnviro::set_lightning_enable_state);
|
||||||
|
|
|
@ -31,14 +31,16 @@
|
||||||
|
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
#include <simgear/misc/strutils.hxx>
|
#include <simgear/misc/strutils.hxx>
|
||||||
#include <simgear/props/tiedpropertylist.hxx>
|
#include <simgear/props/tiedpropertylist.hxx>
|
||||||
|
#include <simgear/io/HTTPClient.hxx>
|
||||||
|
#include <simgear/io/HTTPRequest.hxx>
|
||||||
|
#include <simgear/timing/sg_time.hxx>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#if defined(ENABLE_THREADS)
|
|
||||||
#include <OpenThreads/Thread>
|
|
||||||
#include <simgear/threads/SGQueue.hxx>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using simgear::PropertyList;
|
using simgear::PropertyList;
|
||||||
|
|
||||||
|
@ -110,6 +112,7 @@ protected:
|
||||||
simgear::TiedPropertyList _tiedProperties;
|
simgear::TiedPropertyList _tiedProperties;
|
||||||
typedef std::vector<LiveMetarProperties_ptr> MetarPropertiesList;
|
typedef std::vector<LiveMetarProperties_ptr> MetarPropertiesList;
|
||||||
MetarPropertiesList _metarProperties;
|
MetarPropertiesList _metarProperties;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------------- */
|
||||||
|
@ -195,86 +198,95 @@ public:
|
||||||
virtual void update (bool first, double delta_time_sec);
|
virtual void update (bool first, double delta_time_sec);
|
||||||
virtual void shutdown ();
|
virtual void shutdown ();
|
||||||
|
|
||||||
class MetarLoadRequest {
|
/**
|
||||||
public:
|
* callback from MetarGetRequest when a download succeeds
|
||||||
MetarLoadRequest( const string & stationId ) :
|
*/
|
||||||
_stationId(stationId),
|
void gotMetar(const string& stationId, const string& metar);
|
||||||
_proxyHost(fgGetNode("/sim/presets/proxy/host", true)->getStringValue()),
|
|
||||||
_proxyPort(fgGetNode("/sim/presets/proxy/port", true)->getStringValue()),
|
|
||||||
_proxyAuth(fgGetNode("/sim/presets/proxy/authentication", true)->getStringValue())
|
|
||||||
{}
|
|
||||||
MetarLoadRequest( const MetarLoadRequest & other ) :
|
|
||||||
_stationId(other._stationId),
|
|
||||||
_proxyHost(other._proxyAuth),
|
|
||||||
_proxyPort(other._proxyPort),
|
|
||||||
_proxyAuth(other._proxyAuth)
|
|
||||||
{}
|
|
||||||
string _stationId;
|
|
||||||
string _proxyHost;
|
|
||||||
string _proxyPort;
|
|
||||||
string _proxyAuth;
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
class MetarLoadResponse {
|
|
||||||
public:
|
|
||||||
MetarLoadResponse( const string & stationId, const string metar ) {
|
|
||||||
_stationId = stationId;
|
|
||||||
_metar = metar;
|
|
||||||
}
|
|
||||||
MetarLoadResponse( const MetarLoadResponse & other ) {
|
|
||||||
_stationId = other._stationId;
|
|
||||||
_metar = other._metar;
|
|
||||||
}
|
|
||||||
string _stationId;
|
|
||||||
string _metar;
|
|
||||||
};
|
|
||||||
private:
|
private:
|
||||||
double _positionTimeToLive;
|
double _positionTimeToLive;
|
||||||
double _requestTimer;
|
double _requestTimer;
|
||||||
|
|
||||||
#if defined(ENABLE_THREADS)
|
simgear::HTTP::Client _http;
|
||||||
class MetarLoadThread : public OpenThreads::Thread {
|
|
||||||
public:
|
|
||||||
MetarLoadThread( long maxAge );
|
|
||||||
virtual ~MetarLoadThread( ) { stop(); }
|
|
||||||
void requestMetar( const MetarLoadRequest & metarRequest, bool background = true );
|
|
||||||
bool hasMetar() { return _responseQueue.size() > 0; }
|
|
||||||
MetarLoadResponse getMetar() { return _responseQueue.pop(); }
|
|
||||||
virtual void run();
|
|
||||||
void stop();
|
|
||||||
private:
|
|
||||||
void fetch( const MetarLoadRequest & );
|
|
||||||
long _maxAge;
|
|
||||||
long _minRequestInterval;
|
|
||||||
volatile bool _stop;
|
|
||||||
SGBlockingQueue <MetarLoadRequest> _requestQueue;
|
|
||||||
SGBlockingQueue <MetarLoadResponse> _responseQueue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MetarLoadThread * _metarLoadThread;
|
class MetarGetRequest : public simgear::HTTP::Request
|
||||||
#endif
|
{
|
||||||
|
public:
|
||||||
|
MetarGetRequest(NoaaMetarRealWxController* con, const string& s) :
|
||||||
|
Request(""),
|
||||||
|
stationId(s),
|
||||||
|
fromProxy(false),
|
||||||
|
wxController(con)
|
||||||
|
{
|
||||||
|
setUrl("http://weather.noaa.gov/pub/data/observations/metar/stations/" + stationId + ".TXT");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual string_list requestHeaders() const
|
||||||
|
{
|
||||||
|
string_list r;
|
||||||
|
r.push_back("X-Time");
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual string header(const string& name) const
|
||||||
|
{
|
||||||
|
if (name == "X-Time") {
|
||||||
|
char buf[16];
|
||||||
|
snprintf(buf, 16, "%ld", globals->get_time_params()->get_cur_time());
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Request::header(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void responseHeader(const string& key, const string& value)
|
||||||
|
{
|
||||||
|
if (key == "x-metarproxy") {
|
||||||
|
fromProxy = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void gotBodyData(const char* s, int n)
|
||||||
|
{
|
||||||
|
metar += string(s, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void responseComplete()
|
||||||
|
{
|
||||||
|
if (responseCode() == 200) {
|
||||||
|
wxController->gotMetar(stationId, metar);
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "metar download failed:" << url() << ": reason:" << responseReason());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fromMetarProxy() const
|
||||||
|
{ return fromProxy; }
|
||||||
|
private:
|
||||||
|
string stationId;
|
||||||
|
string metar;
|
||||||
|
bool fromProxy;
|
||||||
|
NoaaMetarRealWxController* wxController;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NoaaMetarRealWxController::NoaaMetarRealWxController( SGPropertyNode_ptr rootNode ) :
|
NoaaMetarRealWxController::NoaaMetarRealWxController( SGPropertyNode_ptr rootNode ) :
|
||||||
BasicRealWxController(rootNode),
|
BasicRealWxController(rootNode),
|
||||||
_positionTimeToLive(0.0),
|
_positionTimeToLive(0.0),
|
||||||
_requestTimer(0.0)
|
_requestTimer(0.0)
|
||||||
{
|
{
|
||||||
#if defined(ENABLE_THREADS)
|
string proxyHost(fgGetString("/sim/presets/proxy/host"));
|
||||||
_metarLoadThread = new MetarLoadThread(getMetarMaxAgeMin());
|
int proxyPort(fgGetInt("/sim/presets/proxy/port"));
|
||||||
_metarLoadThread->start();
|
string proxyAuth(fgGetString("/sim/presets/proxy/auth"));
|
||||||
#endif
|
|
||||||
|
if (!proxyHost.empty()) {
|
||||||
|
_http.setProxy(proxyHost, proxyPort, proxyAuth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoaaMetarRealWxController::shutdown()
|
void NoaaMetarRealWxController::shutdown()
|
||||||
{
|
{
|
||||||
#if defined(ENABLE_THREADS)
|
|
||||||
if( _metarLoadThread ) {
|
|
||||||
delete _metarLoadThread;
|
|
||||||
_metarLoadThread = NULL;
|
|
||||||
}
|
|
||||||
#endif // ENABLE_THREADS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NoaaMetarRealWxController::~NoaaMetarRealWxController()
|
NoaaMetarRealWxController::~NoaaMetarRealWxController()
|
||||||
|
@ -283,6 +295,8 @@ NoaaMetarRealWxController::~NoaaMetarRealWxController()
|
||||||
|
|
||||||
void NoaaMetarRealWxController::update( bool first, double dt )
|
void NoaaMetarRealWxController::update( bool first, double dt )
|
||||||
{
|
{
|
||||||
|
_http.update();
|
||||||
|
|
||||||
_positionTimeToLive -= dt;
|
_positionTimeToLive -= dt;
|
||||||
_requestTimer -= dt;
|
_requestTimer -= dt;
|
||||||
|
|
||||||
|
@ -316,141 +330,30 @@ void NoaaMetarRealWxController::update( bool first, double dt )
|
||||||
if( _requestTimer <= 0.0 ) {
|
if( _requestTimer <= 0.0 ) {
|
||||||
_requestTimer = 10.0;
|
_requestTimer = 10.0;
|
||||||
|
|
||||||
for( MetarPropertiesList::iterator it = _metarProperties.begin();
|
BOOST_FOREACH(LiveMetarProperties* p, _metarProperties) {
|
||||||
it != _metarProperties.end(); it++ ) {
|
if( p->getTimeToLive() > 0.0 ) continue;
|
||||||
|
const std::string & stationId = p->getStationId();
|
||||||
if( (*it)->getTimeToLive() > 0.0 ) continue;
|
|
||||||
const std::string & stationId = (*it)->getStationId();
|
|
||||||
if( stationId.empty() ) continue;
|
if( stationId.empty() ) continue;
|
||||||
|
|
||||||
SG_LOG(SG_ALL, SG_INFO,
|
SG_LOG(SG_ALL, SG_INFO,
|
||||||
"NoaaMetarRealWxController::update(): spawning load request for station-id '" << stationId << "'" );
|
"NoaaMetarRealWxController::update(): spawning load request for station-id '" << stationId << "'" );
|
||||||
|
|
||||||
MetarLoadRequest request( stationId );
|
_http.makeRequest(new MetarGetRequest(this, stationId));
|
||||||
// load the metar for the nearest airport in the foreground if the fdm is uninitialized
|
} // of MetarProperties iteration
|
||||||
// to make sure a metar is received
|
|
||||||
// before the automatic runway selection code runs. All subsequent calls
|
|
||||||
// run in the background
|
|
||||||
bool background = fgGetBool("/sim/fdm-initialized", false ) || it != _metarProperties.begin();
|
|
||||||
_metarLoadThread->requestMetar( request, background );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pick all the received responses from the result queue and update the associated
|
void NoaaMetarRealWxController::gotMetar(const string& stationId, const string& metar)
|
||||||
// property tree
|
{
|
||||||
while( _metarLoadThread->hasMetar() ) {
|
SG_LOG( SG_ALL, SG_INFO, "NoaaMetarRwalWxController::update() received METAR for " << stationId << ": " << metar );
|
||||||
MetarLoadResponse metar = _metarLoadThread->getMetar();
|
BOOST_FOREACH(LiveMetarProperties* p, _metarProperties) {
|
||||||
SG_LOG( SG_ALL, SG_INFO, "NoaaMetarRwalWxController::update() received METAR for " << metar._stationId << ": " << metar._metar );
|
if (p->getStationId() != stationId)
|
||||||
for( MetarPropertiesList::iterator it = _metarProperties.begin();
|
|
||||||
it != _metarProperties.end(); it++ ) {
|
|
||||||
if( (*it)->getStationId() != metar._stationId )
|
|
||||||
continue;
|
continue;
|
||||||
(*it)->setTimeToLive(900);
|
|
||||||
(*it)->setMetar( metar._metar );
|
p->setTimeToLive(900);
|
||||||
|
p->setMetar( metar );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
#if defined(ENABLE_THREADS)
|
|
||||||
NoaaMetarRealWxController::MetarLoadThread::MetarLoadThread( long maxAge ) :
|
|
||||||
_maxAge(maxAge),
|
|
||||||
_minRequestInterval(2000),
|
|
||||||
_stop(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void NoaaMetarRealWxController::MetarLoadThread::requestMetar( const MetarLoadRequest & metarRequest, bool background )
|
|
||||||
{
|
|
||||||
if( background ) {
|
|
||||||
if( _requestQueue.size() > 10 ) {
|
|
||||||
SG_LOG(SG_ALL,SG_ALERT,
|
|
||||||
"NoaaMetarRealWxController::MetarLoadThread::requestMetar() more than 10 outstanding METAR requests, dropping "
|
|
||||||
<< metarRequest._stationId );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_requestQueue.push( metarRequest );
|
|
||||||
} else {
|
|
||||||
fetch( metarRequest );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NoaaMetarRealWxController::MetarLoadThread::stop()
|
|
||||||
{
|
|
||||||
// set stop flag and wake up the thread with an empty request
|
|
||||||
_stop = true;
|
|
||||||
MetarLoadRequest request("");
|
|
||||||
requestMetar(request);
|
|
||||||
join();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NoaaMetarRealWxController::MetarLoadThread::run()
|
|
||||||
{
|
|
||||||
SGTimeStamp lastRun = SGTimeStamp::fromSec(0);
|
|
||||||
for( ;; ) {
|
|
||||||
SGTimeStamp dt = SGTimeStamp::now() - lastRun;
|
|
||||||
|
|
||||||
long delayMs = _minRequestInterval - dt.getSeconds() * 1000;
|
|
||||||
while (( delayMs > 0 ) && !_stop)
|
|
||||||
{
|
|
||||||
// sleep no more than 3 seconds at a time, otherwise shutdown response is too slow
|
|
||||||
long sleepMs = (delayMs>3000) ? 3000 : delayMs;
|
|
||||||
microSleep( sleepMs * 1000 );
|
|
||||||
delayMs -= sleepMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_stop)
|
|
||||||
break;
|
|
||||||
|
|
||||||
lastRun = SGTimeStamp::now();
|
|
||||||
|
|
||||||
const MetarLoadRequest request = _requestQueue.pop();
|
|
||||||
|
|
||||||
if (( request._stationId.size() == 0 ) || _stop)
|
|
||||||
break;
|
|
||||||
|
|
||||||
fetch( request );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NoaaMetarRealWxController::MetarLoadThread::fetch( const MetarLoadRequest & request )
|
|
||||||
{
|
|
||||||
SGSharedPtr<FGMetar> result = NULL;
|
|
||||||
|
|
||||||
try {
|
|
||||||
result = new FGMetar( request._stationId, request._proxyHost, request._proxyPort, request._proxyAuth );
|
|
||||||
_minRequestInterval = 2000;
|
|
||||||
} catch (const sg_io_exception& e) {
|
|
||||||
SG_LOG( SG_GENERAL, SG_WARN, "NoaaMetarRealWxController::fetchMetar(): can't get METAR for "
|
|
||||||
<< request._stationId << ":" << e.getFormattedMessage().c_str() );
|
|
||||||
_minRequestInterval += _minRequestInterval/2;
|
|
||||||
if( _minRequestInterval > 30000 )
|
|
||||||
_minRequestInterval = 30000;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string reply = result->getData();
|
|
||||||
std::replace(reply.begin(), reply.end(), '\n', ' ');
|
|
||||||
string metar = simgear::strutils::strip( reply );
|
|
||||||
|
|
||||||
if( metar.empty() ) {
|
|
||||||
SG_LOG( SG_GENERAL, SG_WARN, "NoaaMetarRealWxController::fetchMetar(): dropping empty METAR for "
|
|
||||||
<< request._stationId );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( _maxAge && result->getAge_min() > _maxAge ) {
|
|
||||||
SG_LOG( SG_GENERAL, SG_ALERT, "NoaaMetarRealWxController::fetchMetar(): dropping outdated METAR "
|
|
||||||
<< metar );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetarLoadResponse response( request._stationId, metar );
|
|
||||||
_responseQueue.push( response );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,20 @@ FGEventHandler::FGEventHandler() :
|
||||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Home] = '7';
|
numlockKeyMap[GUIEventAdapter::KEY_KP_Home] = '7';
|
||||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Up] = '8';
|
numlockKeyMap[GUIEventAdapter::KEY_KP_Up] = '8';
|
||||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Page_Up] = '9';
|
numlockKeyMap[GUIEventAdapter::KEY_KP_Page_Up] = '9';
|
||||||
|
numlockKeyMap[GUIEventAdapter::KEY_KP_Delete] = '.';
|
||||||
|
|
||||||
|
// mapping when NumLock is off
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Insert] = PU_KEY_INSERT;
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_End] = PU_KEY_END;
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Down] = PU_KEY_DOWN;
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Page_Down] = PU_KEY_PAGE_DOWN;
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Left] = PU_KEY_LEFT;
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Begin] = '5';
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Right] = PU_KEY_RIGHT;
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Home] = PU_KEY_HOME;
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Up] = PU_KEY_UP;
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Page_Up] = PU_KEY_PAGE_UP;
|
||||||
|
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Delete] = 127;
|
||||||
|
|
||||||
for (int i = 0; i < 128; i++)
|
for (int i = 0; i < 128; i++)
|
||||||
release_keys[i] = i;
|
release_keys[i] = i;
|
||||||
|
@ -259,7 +273,6 @@ void FGEventHandler::handleKey(const osgGA::GUIEventAdapter& ea, int& key,
|
||||||
case GUIEventAdapter::KEY_F10: key = PU_KEY_F10; break;
|
case GUIEventAdapter::KEY_F10: key = PU_KEY_F10; break;
|
||||||
case GUIEventAdapter::KEY_F11: key = PU_KEY_F11; break;
|
case GUIEventAdapter::KEY_F11: key = PU_KEY_F11; break;
|
||||||
case GUIEventAdapter::KEY_F12: key = PU_KEY_F12; break;
|
case GUIEventAdapter::KEY_F12: key = PU_KEY_F12; break;
|
||||||
case GUIEventAdapter::KEY_KP_Delete: key = '.'; break;
|
|
||||||
case GUIEventAdapter::KEY_KP_Enter: key = '\r'; break;
|
case GUIEventAdapter::KEY_KP_Enter: key = '\r'; break;
|
||||||
case GUIEventAdapter::KEY_KP_Add: key = '+'; break;
|
case GUIEventAdapter::KEY_KP_Add: key = '+'; break;
|
||||||
case GUIEventAdapter::KEY_KP_Divide: key = '/'; break;
|
case GUIEventAdapter::KEY_KP_Divide: key = '/'; break;
|
||||||
|
@ -268,18 +281,30 @@ void FGEventHandler::handleKey(const osgGA::GUIEventAdapter& ea, int& key,
|
||||||
}
|
}
|
||||||
osgGA::GUIEventAdapter::EventType eventType = ea.getEventType();
|
osgGA::GUIEventAdapter::EventType eventType = ea.getEventType();
|
||||||
|
|
||||||
std::map<int, int>::iterator numPadIter = numlockKeyMap.find(key);
|
|
||||||
|
|
||||||
if (numPadIter != numlockKeyMap.end()) {
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
// Num Lock is always true on Mac
|
// Num Lock is always true on Mac
|
||||||
|
std::map<int, int>::iterator numPadIter = numlockKeyMap.find(key);
|
||||||
|
if (numPadIter != numlockKeyMap.end()) {
|
||||||
key = numPadIter->second;
|
key = numPadIter->second;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
if (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_NUM_LOCK) {
|
if (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_NUM_LOCK)
|
||||||
|
{
|
||||||
|
// NumLock on: map to numeric keys
|
||||||
|
std::map<int, int>::iterator numPadIter = numlockKeyMap.find(key);
|
||||||
|
if (numPadIter != numlockKeyMap.end()) {
|
||||||
key = numPadIter->second;
|
key = numPadIter->second;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// NumLock off: map to PU arrow keys
|
||||||
|
std::map<int, int>::iterator numPadIter = noNumlockKeyMap.find(key);
|
||||||
|
if (numPadIter != noNumlockKeyMap.end()) {
|
||||||
|
key = numPadIter->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
modifiers = osgToFGModifiers(ea.getModKeyMask());
|
modifiers = osgToFGModifiers(ea.getModKeyMask());
|
||||||
currentModifiers = modifiers;
|
currentModifiers = modifiers;
|
||||||
|
|
|
@ -113,6 +113,7 @@ protected:
|
||||||
int statsType;
|
int statsType;
|
||||||
int currentModifiers;
|
int currentModifiers;
|
||||||
std::map<int, int> numlockKeyMap;
|
std::map<int, int> numlockKeyMap;
|
||||||
|
std::map<int, int> noNumlockKeyMap;
|
||||||
void handleKey(const osgGA::GUIEventAdapter& ea, int& key, int& modifiers);
|
void handleKey(const osgGA::GUIEventAdapter& ea, int& key, int& modifiers);
|
||||||
bool resizable;
|
bool resizable;
|
||||||
bool mouseWarped;
|
bool mouseWarped;
|
||||||
|
|
|
@ -28,8 +28,12 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <osg/Camera>
|
||||||
|
#include <osg/Transform>
|
||||||
|
#include <osg/MatrixTransform>
|
||||||
|
#include <osg/PositionAttitudeTransform>
|
||||||
|
#include <osg/CameraView>
|
||||||
#include <osgViewer/Viewer>
|
#include <osgViewer/Viewer>
|
||||||
#include <osgUtil/IntersectVisitor>
|
|
||||||
|
|
||||||
#include <simgear/constants.h>
|
#include <simgear/constants.h>
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
@ -38,6 +42,8 @@
|
||||||
#include <simgear/scene/util/SGNodeMasks.hxx>
|
#include <simgear/scene/util/SGNodeMasks.hxx>
|
||||||
#include <simgear/scene/util/SGSceneUserData.hxx>
|
#include <simgear/scene/util/SGSceneUserData.hxx>
|
||||||
#include <simgear/scene/model/CheckSceneryVisitor.hxx>
|
#include <simgear/scene/model/CheckSceneryVisitor.hxx>
|
||||||
|
#include <simgear/scene/bvh/BVHNode.hxx>
|
||||||
|
#include <simgear/scene/bvh/BVHLineSegmentVisitor.hxx>
|
||||||
|
|
||||||
#include <Main/renderer.hxx>
|
#include <Main/renderer.hxx>
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
|
@ -70,6 +76,138 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FGSceneryIntersect : public osg::NodeVisitor {
|
||||||
|
public:
|
||||||
|
FGSceneryIntersect(const SGLineSegmentd& lineSegment,
|
||||||
|
const osg::Node* skipNode) :
|
||||||
|
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
|
||||||
|
_lineSegment(lineSegment),
|
||||||
|
_skipNode(skipNode),
|
||||||
|
_material(0),
|
||||||
|
_haveHit(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool getHaveHit() const
|
||||||
|
{ return _haveHit; }
|
||||||
|
const SGLineSegmentd& getLineSegment() const
|
||||||
|
{ return _lineSegment; }
|
||||||
|
const SGMaterial* getMaterial() const
|
||||||
|
{ return _material; }
|
||||||
|
|
||||||
|
virtual void apply(osg::Node& node)
|
||||||
|
{
|
||||||
|
if (&node == _skipNode)
|
||||||
|
return;
|
||||||
|
if (!testBoundingSphere(node.getBound()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
addBoundingVolume(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void apply(osg::Group& group)
|
||||||
|
{
|
||||||
|
if (&group == _skipNode)
|
||||||
|
return;
|
||||||
|
if (!testBoundingSphere(group.getBound()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
traverse(group);
|
||||||
|
addBoundingVolume(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void apply(osg::Transform& transform)
|
||||||
|
{ handleTransform(transform); }
|
||||||
|
virtual void apply(osg::Camera& camera)
|
||||||
|
{
|
||||||
|
if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
|
||||||
|
return;
|
||||||
|
handleTransform(camera);
|
||||||
|
}
|
||||||
|
virtual void apply(osg::CameraView& transform)
|
||||||
|
{ handleTransform(transform); }
|
||||||
|
virtual void apply(osg::MatrixTransform& transform)
|
||||||
|
{ handleTransform(transform); }
|
||||||
|
virtual void apply(osg::PositionAttitudeTransform& transform)
|
||||||
|
{ handleTransform(transform); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleTransform(osg::Transform& transform)
|
||||||
|
{
|
||||||
|
if (&transform == _skipNode)
|
||||||
|
return;
|
||||||
|
// Hmm, may be this needs to be refined somehow ...
|
||||||
|
if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!testBoundingSphere(transform.getBound()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
osg::Matrix inverseMatrix;
|
||||||
|
if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
|
||||||
|
return;
|
||||||
|
osg::Matrix matrix;
|
||||||
|
if (!transform.computeLocalToWorldMatrix(matrix, this))
|
||||||
|
return;
|
||||||
|
|
||||||
|
SGLineSegmentd lineSegment = _lineSegment;
|
||||||
|
bool haveHit = _haveHit;
|
||||||
|
const SGMaterial* material = _material;
|
||||||
|
|
||||||
|
_haveHit = false;
|
||||||
|
_lineSegment = lineSegment.transform(SGMatrixd(inverseMatrix.ptr()));
|
||||||
|
|
||||||
|
addBoundingVolume(transform);
|
||||||
|
traverse(transform);
|
||||||
|
|
||||||
|
if (_haveHit) {
|
||||||
|
_lineSegment = _lineSegment.transform(SGMatrixd(matrix.ptr()));
|
||||||
|
} else {
|
||||||
|
_lineSegment = lineSegment;
|
||||||
|
_material = material;
|
||||||
|
_haveHit = haveHit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
|
||||||
|
{
|
||||||
|
SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
|
||||||
|
if (!userData)
|
||||||
|
return 0;
|
||||||
|
return userData->getBVHNode();
|
||||||
|
}
|
||||||
|
void addBoundingVolume(osg::Node& node)
|
||||||
|
{
|
||||||
|
simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
|
||||||
|
if (!bvNode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find ground intersection on the bvh nodes
|
||||||
|
simgear::BVHLineSegmentVisitor lineSegmentVisitor(_lineSegment,
|
||||||
|
0/*startTime*/);
|
||||||
|
bvNode->accept(lineSegmentVisitor);
|
||||||
|
if (!lineSegmentVisitor.empty()) {
|
||||||
|
_lineSegment = lineSegmentVisitor.getLineSegment();
|
||||||
|
_material = lineSegmentVisitor.getMaterial();
|
||||||
|
_haveHit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool testBoundingSphere(const osg::BoundingSphere& bound) const
|
||||||
|
{
|
||||||
|
if (!bound.valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SGSphered sphere(toVec3d(toSG(bound._center)), bound._radius);
|
||||||
|
return intersects(_lineSegment, sphere);
|
||||||
|
}
|
||||||
|
|
||||||
|
SGLineSegmentd _lineSegment;
|
||||||
|
const osg::Node* _skipNode;
|
||||||
|
|
||||||
|
const SGMaterial* _material;
|
||||||
|
bool _haveHit;
|
||||||
|
};
|
||||||
|
|
||||||
// Scenery Management system
|
// Scenery Management system
|
||||||
FGScenery::FGScenery()
|
FGScenery::FGScenery()
|
||||||
{
|
{
|
||||||
|
@ -139,42 +277,19 @@ FGScenery::get_elevation_m(const SGGeod& geod, double& alt,
|
||||||
geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000));
|
geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000));
|
||||||
SGVec3d end = SGVec3d::fromGeod(geodEnd);
|
SGVec3d end = SGVec3d::fromGeod(geodEnd);
|
||||||
|
|
||||||
osgUtil::IntersectVisitor intersectVisitor;
|
FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
|
||||||
intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
|
intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
|
||||||
osg::ref_ptr<osg::LineSegment> lineSegment;
|
|
||||||
lineSegment = new osg::LineSegment(toOsg(start), toOsg(end));
|
|
||||||
intersectVisitor.addLineSegment(lineSegment.get());
|
|
||||||
get_scene_graph()->accept(intersectVisitor);
|
get_scene_graph()->accept(intersectVisitor);
|
||||||
bool hits = false;
|
|
||||||
if (intersectVisitor.hits()) {
|
|
||||||
int nHits = intersectVisitor.getNumHits(lineSegment.get());
|
|
||||||
alt = -SGLimitsd::max();
|
|
||||||
for (int i = 0; i < nHits; ++i) {
|
|
||||||
const osgUtil::Hit& hit
|
|
||||||
= intersectVisitor.getHitList(lineSegment.get())[i];
|
|
||||||
if (butNotFrom &&
|
|
||||||
std::find(hit.getNodePath().begin(), hit.getNodePath().end(),
|
|
||||||
butNotFrom) != hit.getNodePath().end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// We might need the double variant of the intersection point.
|
if (!intersectVisitor.getHaveHit())
|
||||||
// Thus we cannot use the float variant delivered by
|
return false;
|
||||||
// hit.getWorldIntersectPoint() but we have to redo that with osg::Vec3d.
|
|
||||||
osg::Vec3d point = hit.getLocalIntersectPoint();
|
geodEnd = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd());
|
||||||
if (hit.getMatrix())
|
alt = geodEnd.getElevationM();
|
||||||
point = point*(*hit.getMatrix());
|
|
||||||
SGGeod geod = SGGeod::fromCart(toSG(point));
|
|
||||||
double elevation = geod.getElevationM();
|
|
||||||
if (alt < elevation) {
|
|
||||||
alt = elevation;
|
|
||||||
hits = true;
|
|
||||||
if (material)
|
if (material)
|
||||||
*material = SGMaterialLib::findMaterial(hit.getGeode());
|
*material = intersectVisitor.getMaterial();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hits;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -191,39 +306,15 @@ FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
|
||||||
SGVec3d start = pos;
|
SGVec3d start = pos;
|
||||||
SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ???
|
SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ???
|
||||||
|
|
||||||
osgUtil::IntersectVisitor intersectVisitor;
|
FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
|
||||||
intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
|
intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
|
||||||
osg::ref_ptr<osg::LineSegment> lineSegment;
|
|
||||||
lineSegment = new osg::LineSegment(toOsg(start), toOsg(end));
|
|
||||||
intersectVisitor.addLineSegment(lineSegment.get());
|
|
||||||
get_scene_graph()->accept(intersectVisitor);
|
get_scene_graph()->accept(intersectVisitor);
|
||||||
bool hits = false;
|
|
||||||
if (intersectVisitor.hits()) {
|
|
||||||
int nHits = intersectVisitor.getNumHits(lineSegment.get());
|
|
||||||
double dist = SGLimitsd::max();
|
|
||||||
for (int i = 0; i < nHits; ++i) {
|
|
||||||
const osgUtil::Hit& hit
|
|
||||||
= intersectVisitor.getHitList(lineSegment.get())[i];
|
|
||||||
if (butNotFrom &&
|
|
||||||
std::find(hit.getNodePath().begin(), hit.getNodePath().end(),
|
|
||||||
butNotFrom) != hit.getNodePath().end())
|
|
||||||
continue;
|
|
||||||
// We might need the double variant of the intersection point.
|
|
||||||
// Thus we cannot use the float variant delivered by
|
|
||||||
// hit.getWorldIntersectPoint() but we have to redo that with osg::Vec3d.
|
|
||||||
osg::Vec3d point = hit.getLocalIntersectPoint();
|
|
||||||
if (hit.getMatrix())
|
|
||||||
point = point*(*hit.getMatrix());
|
|
||||||
double newdist = length(start - toSG(point));
|
|
||||||
if (newdist < dist) {
|
|
||||||
dist = newdist;
|
|
||||||
nearestHit = toSG(point);
|
|
||||||
hits = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hits;
|
if (!intersectVisitor.getHaveHit())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
nearestHit = intersectVisitor.getLineSegment().getEnd();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FGScenery::scenery_available(const SGGeod& position, double range_m)
|
bool FGScenery::scenery_available(const SGGeod& position, double range_m)
|
||||||
|
|
|
@ -24,14 +24,12 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <direct.h>
|
# include <direct.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <FL/Fl_File_Chooser.H>
|
#include <FL/Fl_File_Chooser.H>
|
||||||
#include <plib/ul.h>
|
|
||||||
|
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
|
||||||
|
@ -142,11 +140,12 @@ void FGAdminUI::update_install_box() {
|
||||||
install_box->clear();
|
install_box->clear();
|
||||||
|
|
||||||
if ( source.length() ) {
|
if ( source.length() ) {
|
||||||
ulDir *dir = ulOpenDir( source.c_str() ) ;
|
struct dirent **list;
|
||||||
ulDirEnt *ent;
|
int nb = fl_filename_list( source.c_str(), &list );
|
||||||
while ( dir != 0 && ( ent = ulReadDir( dir ) ) ) {
|
for ( int i = 0; i < nb; ++i ) {
|
||||||
// find base name of archive file
|
// find base name of archive file
|
||||||
char base[FL_PATH_MAX];
|
char base[FL_PATH_MAX];
|
||||||
|
dirent *ent = list[i];
|
||||||
strncpy( base, ent->d_name, FL_PATH_MAX );
|
strncpy( base, ent->d_name, FL_PATH_MAX );
|
||||||
const char *p = fl_filename_ext( base );
|
const char *p = fl_filename_ext( base );
|
||||||
int offset, expected_length = 0;
|
int offset, expected_length = 0;
|
||||||
|
@ -186,9 +185,10 @@ void FGAdminUI::update_install_box() {
|
||||||
// cout << install.str() << " exists." << endl;
|
// cout << install.str() << " exists." << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
free( ent );
|
||||||
}
|
}
|
||||||
|
free( list );
|
||||||
|
|
||||||
ulCloseDir( dir );
|
|
||||||
for ( set<string>::iterator it = file_list.begin(); it != file_list.end(); ++it ) {
|
for ( set<string>::iterator it = file_list.begin(); it != file_list.end(); ++it ) {
|
||||||
install_box->add( it->c_str() );
|
install_box->add( it->c_str() );
|
||||||
}
|
}
|
||||||
|
@ -217,16 +217,18 @@ void FGAdminUI::update_remove_box() {
|
||||||
set<string> dir_list;
|
set<string> dir_list;
|
||||||
for ( int i = 0; i < 2; i++ ) {
|
for ( int i = 0; i < 2; i++ ) {
|
||||||
if ( !path[i].empty() ) {
|
if ( !path[i].empty() ) {
|
||||||
ulDir *dir = ulOpenDir( path[i].c_str() ) ;
|
dirent **list;
|
||||||
ulDirEnt *ent;
|
int nb = fl_filename_list( path[i].c_str(), &list );
|
||||||
while ( dir != 0 && ( ent = ulReadDir( dir ) ) ) {
|
for ( int i = 0; i < nb; ++i ) {
|
||||||
|
dirent *ent = list[i];
|
||||||
if ( strlen(ent->d_name) == 7 &&
|
if ( strlen(ent->d_name) == 7 &&
|
||||||
( ent->d_name[0] == 'e' || ent->d_name[0] == 'w' ) &&
|
( ent->d_name[0] == 'e' || ent->d_name[0] == 'w' ) &&
|
||||||
( ent->d_name[4] == 'n' || ent->d_name[4] == 's' ) ) {
|
( ent->d_name[4] == 'n' || ent->d_name[4] == 's' ) ) {
|
||||||
dir_list.insert( ent->d_name );
|
dir_list.insert( ent->d_name );
|
||||||
}
|
}
|
||||||
|
free( ent );
|
||||||
}
|
}
|
||||||
ulCloseDir( dir );
|
free( list );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +256,7 @@ void FGAdminUI::install_selected() {
|
||||||
SGPath file( source );
|
SGPath file( source );
|
||||||
file.append( f );
|
file.append( f );
|
||||||
struct stat info;
|
struct stat info;
|
||||||
stat( file.str().c_str(), &info );
|
fl_stat( file.str().c_str(), &info );
|
||||||
float old_max = progress->maximum();
|
float old_max = progress->maximum();
|
||||||
progress->maximum( info.st_size );
|
progress->maximum( info.st_size );
|
||||||
progress_label = "Installing ";
|
progress_label = "Installing ";
|
||||||
|
@ -279,23 +281,25 @@ void FGAdminUI::install_selected() {
|
||||||
|
|
||||||
static unsigned long count_dir( const char *dir_name, bool top = true ) {
|
static unsigned long count_dir( const char *dir_name, bool top = true ) {
|
||||||
unsigned long cnt = 0L;
|
unsigned long cnt = 0L;
|
||||||
ulDir *dir = ulOpenDir( dir_name ) ;
|
dirent **list;
|
||||||
if ( dir ) {
|
int nb = fl_filename_list( dir_name, &list );
|
||||||
ulDirEnt *ent;
|
if ( nb != 0 ) {
|
||||||
while ( ent = ulReadDir( dir ) ) {
|
for ( int i = 0; i < nb; ++i ) {
|
||||||
|
dirent *ent = list[i];
|
||||||
if ( strcmp( ent->d_name, "." ) == 0 ) {
|
if ( strcmp( ent->d_name, "." ) == 0 ) {
|
||||||
// ignore "."
|
// ignore "."
|
||||||
} else if ( strcmp( ent->d_name, ".." ) == 0 ) {
|
} else if ( strcmp( ent->d_name, ".." ) == 0 ) {
|
||||||
// ignore ".."
|
// ignore ".."
|
||||||
} else if ( ent->d_isdir ) {
|
} else if ( fl_filename_isdir( ent->d_name ) ) {
|
||||||
SGPath child( dir_name );
|
SGPath child( dir_name );
|
||||||
child.append( ent->d_name );
|
child.append( ent->d_name );
|
||||||
cnt += count_dir( child.c_str(), false );
|
cnt += count_dir( child.c_str(), false );
|
||||||
} else {
|
} else {
|
||||||
cnt += 1;
|
cnt += 1;
|
||||||
}
|
}
|
||||||
|
free( ent );
|
||||||
}
|
}
|
||||||
ulCloseDir( dir );
|
free( list );
|
||||||
} else if ( top ) {
|
} else if ( top ) {
|
||||||
string base = dir_name;
|
string base = dir_name;
|
||||||
size_t pos = base.rfind('/');
|
size_t pos = base.rfind('/');
|
||||||
|
@ -310,15 +314,16 @@ static unsigned long count_dir( const char *dir_name, bool top = true ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_dir( const char *dir_name, void (*step)(void*,int), void *data, bool top = true ) {
|
static void remove_dir( const char *dir_name, void (*step)(void*,int), void *data, bool top = true ) {
|
||||||
ulDir *dir = ulOpenDir( dir_name ) ;
|
dirent **list;
|
||||||
if ( dir ) {
|
int nb = fl_filename_list( dir_name, &list );
|
||||||
ulDirEnt *ent;
|
if ( nb != 0 ) {
|
||||||
while ( ent = ulReadDir( dir ) ) {
|
for ( int i = 0; i < nb; ++i ) {
|
||||||
|
dirent *ent = list[i];
|
||||||
if ( strcmp( ent->d_name, "." ) == 0 ) {
|
if ( strcmp( ent->d_name, "." ) == 0 ) {
|
||||||
// ignore "."
|
// ignore "."
|
||||||
} else if ( strcmp( ent->d_name, ".." ) == 0 ) {
|
} else if ( strcmp( ent->d_name, ".." ) == 0 ) {
|
||||||
// ignore ".."
|
// ignore ".."
|
||||||
} else if ( ent->d_isdir ) {
|
} else if ( fl_filename_isdir( ent->d_name ) ) {
|
||||||
SGPath child( dir_name );
|
SGPath child( dir_name );
|
||||||
child.append( ent->d_name );
|
child.append( ent->d_name );
|
||||||
remove_dir( child.c_str(), step, data, false );
|
remove_dir( child.c_str(), step, data, false );
|
||||||
|
@ -328,8 +333,9 @@ static void remove_dir( const char *dir_name, void (*step)(void*,int), void *dat
|
||||||
unlink( child.c_str() );
|
unlink( child.c_str() );
|
||||||
if (step) step( data, 1 );
|
if (step) step( data, 1 );
|
||||||
}
|
}
|
||||||
|
free( ent );
|
||||||
}
|
}
|
||||||
ulCloseDir( dir );
|
free( list );
|
||||||
rmdir( dir_name );
|
rmdir( dir_name );
|
||||||
} else if ( top ) {
|
} else if ( top ) {
|
||||||
string base = dir_name;
|
string base = dir_name;
|
||||||
|
|
|
@ -441,6 +441,8 @@ sub serve()
|
||||||
$metar .= sprintf "$icao %02d%02d%02dZ ", $day, $hour, $min;
|
$metar .= sprintf "$icao %02d%02d%02dZ ", $day, $hour, $min;
|
||||||
$metar .= $last_metar{$addr} || $METAR_DEFAULT;
|
$metar .= $last_metar{$addr} || $METAR_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print $client "HTTP/1.0 200 OK\015\012";
|
||||||
print $client "Content-Type: text/plain\015\012"
|
print $client "Content-Type: text/plain\015\012"
|
||||||
. "X-MetarProxy: nasse Maus\015\012"
|
. "X-MetarProxy: nasse Maus\015\012"
|
||||||
. "\015\012"
|
. "\015\012"
|
||||||
|
|
Loading…
Reference in a new issue