From 0e16419f0db66e950661673b1a0ad337a8cdbd4d Mon Sep 17 00:00:00 2001 From: ehofman Date: Fri, 6 Jan 2006 09:50:58 +0000 Subject: [PATCH] Stuart Buchanan: - Provide a Nasal interface to display simple text messages on the screen like the ATC display. In fact, I copied the code from the ATCDisplay.cxx and simply shifted it further down the screen. Erik: TODO: Integrate the two pieces of code. --- src/Main/fg_init.cxx | 9 ++ src/Main/globals.hxx | 7 + src/Main/renderer.cxx | 4 + src/Scripting/Makefile.am | 6 +- src/Scripting/NasalDisplay.cxx | 239 +++++++++++++++++++++++++++++++++ src/Scripting/NasalDisplay.hxx | 93 +++++++++++++ src/Scripting/NasalSys.cxx | 18 +++ src/Scripting/NasalSys.hxx | 7 +- 8 files changed, 379 insertions(+), 4 deletions(-) create mode 100644 src/Scripting/NasalDisplay.cxx create mode 100644 src/Scripting/NasalDisplay.hxx diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 834c2dc6c..4b09eb83a 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -102,6 +102,7 @@ #include #include #include +#include #include #include #include @@ -1758,6 +1759,14 @@ bool fgInitSubsystems() { globals->get_io()->bind(); + //////////////////////////////////////////////////////////////////// + // Initialise Nasal display system + //////////////////////////////////////////////////////////////////// + + SG_LOG(SG_GENERAL, SG_INFO, " Nasal Display"); + globals->set_Nasal_display(new FGNasalDisplay); + globals->get_Nasal_display()->init(); + //////////////////////////////////////////////////////////////////// // Add a new 2D panel. //////////////////////////////////////////////////////////////////// diff --git a/src/Main/globals.hxx b/src/Main/globals.hxx index c91df3c36..e59a6b79e 100644 --- a/src/Main/globals.hxx +++ b/src/Main/globals.hxx @@ -73,6 +73,7 @@ class FGControls; class FGFlightPlanDispatcher; class FGIO; class FGNavList; +class FGNasalDisplay; class FGTACANList; class FGFixList; class FGLight; @@ -209,6 +210,9 @@ private: FGNavList *carrierlist; FGTACANList *channellist; FGFixList *fixlist; + + // Scripting display + FGNasalDisplay * Nasal_display; #ifdef FG_MPLAYER_AS @@ -358,6 +362,9 @@ public: inline void set_tile_mgr ( FGTileMgr *t ) { tile_mgr = t; } inline FGIO* get_io() const { return io; } + + inline FGNasalDisplay *get_Nasal_display() const { return Nasal_display; } + inline void set_Nasal_display( FGNasalDisplay *d ) {Nasal_display = d; } inline FGNavList *get_navlist() const { return navlist; } inline void set_navlist( FGNavList *n ) { navlist = n; } diff --git a/src/Main/renderer.cxx b/src/Main/renderer.cxx index 0e25a7169..40487bc82 100644 --- a/src/Main/renderer.cxx +++ b/src/Main/renderer.cxx @@ -66,6 +66,7 @@ #include #include #include +#include #include #include @@ -746,6 +747,9 @@ FGRenderer::update( bool refresh_camera_settings ) { if((fgGetBool("/sim/atc/enabled")) || (fgGetBool("/sim/ai-traffic/enabled"))) globals->get_ATC_display()->update(delta_time_sec); + // Update any messages from the Nasal System + globals->get_Nasal_display()->update(delta_time_sec); + // update the panel subsystem if ( globals->get_current_panel() != NULL ) { globals->get_current_panel()->update(delta_time_sec); diff --git a/src/Scripting/Makefile.am b/src/Scripting/Makefile.am index 468e969d7..63b1944ef 100644 --- a/src/Scripting/Makefile.am +++ b/src/Scripting/Makefile.am @@ -1,6 +1,8 @@ noinst_LIBRARIES = libScripting.a -libScripting_a_SOURCES = NasalSys.cxx NasalSys.hxx nasal-props.cxx -# libScripting_a_SOURCES = scriptmgr.cxx scriptmgr.hxx NasalSys.cxx NasalSys.hxx +libScripting_a_SOURCES = \ + NasalSys.cxx NasalSys.hxx \ + NasalDisplay.cxx NasalDisplay.hxx \ + nasal-props.cxx INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/Scripting/NasalDisplay.cxx b/src/Scripting/NasalDisplay.cxx new file mode 100644 index 000000000..aa5994450 --- /dev/null +++ b/src/Scripting/NasalDisplay.cxx @@ -0,0 +1,239 @@ +// NasalDisplay.cxx - routines to display Nasal output +// +// Written by David Luff, started October 2001. +// +// Copyright (C) 2001 David C Luff - david.luff@nottingham.ac.uk +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_WINDOWS_H +# include +#endif + +#include + +#include SG_GLU_H + +#include + +#include +#include
+#include + +#include "NasalDisplay.hxx" + + +// Constructor +FGNasalDisplay::FGNasalDisplay() { + rep_msg = false; + change_msg_flag = false; + dsp_offset1 = 0.0; + dsp_offset2 = 0.0; +} + + +// Destructor +FGNasalDisplay::~FGNasalDisplay() { +} + +void FGNasalDisplay::init() { +} + +void FGNasalDisplay::bind() { +} + +void FGNasalDisplay::unbind() { +} + +// update - this actually draws the visuals and should be called from the main Flightgear rendering loop. +void FGNasalDisplay::update(double dt) { + + // These strings are used for temporary storage of the transmission string in order + // that the string we view only changes when the next repetition starts scrolling + // even though the master string (rep_msg_str) may change at any time. + static string msg1 = ""; + static string msg2 = ""; + + if( rep_msg || msgList.size() ) { + SGPropertyNode *xsize_node = fgGetNode("/sim/startup/xsize"); + SGPropertyNode *ysize_node = fgGetNode("/sim/startup/ysize"); + int iwidth = xsize_node->getIntValue(); + int iheight = ysize_node->getIntValue(); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D( 0, iwidth, 0, iheight ); + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + glLoadIdentity(); + + glDisable( GL_DEPTH_TEST ); + glDisable( GL_LIGHTING ); + + glColor3f( 0.9, 0.4, 0.2 ); + + float fps = general.get_frame_rate(); + + if(rep_msg) { + //cout << "dsp_offset1 = " << dsp_offset1 << " dsp_offset2 = " << dsp_offset2 << endl; + if(dsp_offset1 == 0) { + msg1 = rep_msg_str; + } + if(dsp_offset2 == 0) { + msg2 = rep_msg_str; + } + // Check for the situation where one offset is negative and the message is changed + if(change_msg_flag) { + if(dsp_offset1 < 0) { + msg1 = rep_msg_str; + } else if(dsp_offset2 < 0) { + msg2 = rep_msg_str; + } + change_msg_flag = false; + } + + // guiFnt.drawString( rep_msg_str.c_str(), + // int(iwidth - guiFnt.getStringWidth(buf) - 10 - (int)dsp_offset), + // (iheight - 20) ); + guiFnt.drawString( msg1.c_str(), + int(iwidth - 10 - dsp_offset1), + (iheight - 20) ); + guiFnt.drawString( msg2.c_str(), + int(iwidth - 10 - dsp_offset2), + (iheight - 20) ); + + // Try to scroll at a frame rate independent speed + // 40 pixels/second looks about right for now + if(dsp_offset1 >= dsp_offset2) { + dsp_offset1+=(40.0/fps); + dsp_offset2 = dsp_offset1 - (rep_msg_str.size() * 10) - 100; + if(dsp_offset1 > (iwidth + (rep_msg_str.size() * 10))) + dsp_offset1 = 0; + } else { + dsp_offset2+=(40.0/fps); + dsp_offset1 = dsp_offset2 - (rep_msg_str.size() * 10) - 100; + if(dsp_offset2 > (iwidth + (rep_msg_str.size() * 10))) + dsp_offset2 = 0; + } + + } + + if(msgList.size()) { + //cout << "Attempting to render single message\n"; + // We have at least one non-repeating message to process + if(fgGetBool("/ATC/display/scroll-single-messages")) { // Scroll single messages across the screen. + msgList_itr = msgList.begin(); + int i = 0; + while(msgList_itr != msgList.end()) { + nasalMessage m = *msgList_itr; + //cout << "m.counter = " << m.counter << '\n'; + if(m.dsp_offset > (iwidth + (m.msg.size() * 10))) { + //cout << "Stopping single message\n"; + msgList_itr = msgList.erase(msgList_itr); + } else if(m.counter > m.start_count) { + //cout << "Drawing single message\n"; + guiFnt.drawString( m.msg.c_str(), + int(iwidth - 10 - m.dsp_offset), + (iheight - 80) ); + m.counter += dt; + m.dsp_offset += (80.0/fps); + msgList[i] = m; + ++msgList_itr; + ++i; + } else { + //cout << "Not yet started single message\n"; + m.counter += dt; + msgList[i] = m; + ++msgList_itr; + ++i; + } + } + } else { // Display single messages for a short period of time. + msgList_itr = msgList.begin(); + int i = 0; + while(msgList_itr != msgList.end()) { + nasalMessage m = *msgList_itr; + //cout << "m.counter = " << m.counter << '\n'; + if(m.counter > m.stop_count) { + //cout << "Stopping single message\n"; + msgList_itr = msgList.erase(msgList_itr); + } else if(m.counter > m.start_count) { + int pin = (((int)m.msg.size() * 8) >= iwidth ? 5 : (iwidth - (m.msg.size() * 8))/2); + //cout << m.msg << '\n'; + //cout << "pin = " << pin << ", iwidth = " << iwidth << ", msg.size = " << m.msg.size() << '\n'; + guiFnt.drawString( m.msg.c_str(), pin, (iheight - 80) ); + m.counter += dt; + msgList[i] = m; + ++msgList_itr; + ++i; + } else { + m.counter += dt; + msgList[i] = m; + ++msgList_itr; + ++i; + } + } + } + } + glEnable( GL_DEPTH_TEST ); + glEnable( GL_LIGHTING ); + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + glMatrixMode( GL_MODELVIEW ); + glPopMatrix(); + } +} + +void FGNasalDisplay::RegisterSingleMessage(const string& msg, double delay) { + //cout << msg << '\n'; + nasalMessage m; + m.msg = msg; + m.repeating = false; + m.counter = 0.0; + m.start_count = delay; + m.stop_count = m.start_count + 5.0; // Display for 5ish seconds for now - this might have to change eg. be related to length of message in future + //cout << "m.stop_count = " << m.stop_count << '\n'; + m.id = 0; + m.dsp_offset = 0.0; + + msgList.push_back(m); + //cout << "Single message registered\n"; +} + +void FGNasalDisplay::RegisterRepeatingMessage(const string& msg) { + rep_msg = true; + rep_msg_str = msg; + return; +} + +void FGNasalDisplay::ChangeRepeatingMessage(const string& newmsg) { + rep_msg_str = newmsg; + change_msg_flag = true; + return; +} + +void FGNasalDisplay::CancelRepeatingMessage() { + rep_msg = false; + rep_msg_str = ""; + dsp_offset1 = 0; + dsp_offset2 = 0; + return; +} + diff --git a/src/Scripting/NasalDisplay.hxx b/src/Scripting/NasalDisplay.hxx new file mode 100644 index 000000000..4d2d88341 --- /dev/null +++ b/src/Scripting/NasalDisplay.hxx @@ -0,0 +1,93 @@ +// NasalDisplay.hxx - class to manage the graphical display of Nasal messages. +// - mainly copied from ATCDisplay.hxx +// +// Written by David Luff, started October 2001. +// +// Copyright (C) 2001 David C Luff - david.luff@nottingham.ac.uk +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef _FG_NASAL_DISPLAY_HXX +#define _FG_NASAL_DISPLAY_HXX + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include + +SG_USING_STD(vector); +SG_USING_STD(string); + +struct nasalMessage { + string msg; + bool repeating; + double counter; // count of how many seconds since the message was registered + double start_count; // value of counter at which display should start (seconds) + double stop_count; // value of counter at which display should stop (seconds) + int id; + double dsp_offset; +}; + +// ASSUMPTION - with two radios the list won't be long so we don't need to map the id's +typedef vector nasalMessageList; +typedef nasalMessageList::iterator nasalMessageListIterator; + +class FGNasalDisplay : public SGSubsystem +{ + +private: + bool rep_msg; // Flag to indicate there is a repeating transmission to display + bool change_msg_flag; // Flag to indicate that the repeating message has changed + double dsp_offset1; // Used to set the correct position of scrolling display + double dsp_offset2; + string rep_msg_str; // The repeating transmission to play + nasalMessageList msgList; + nasalMessageListIterator msgList_itr; + +public: + FGNasalDisplay(); + ~FGNasalDisplay(); + + void init(); + + void bind(); + + void unbind(); + + // Display any registered messages + void update(double dt); + + // Register a single message for display after a delay of delay seconds + // Will automatically stop displaying after a suitable interval. + void RegisterSingleMessage(const string& msg, double delay = 0.0); + + // For now we will assume only one repeating message at once + // This is not really robust + + // Register a continuously repeating message + void RegisterRepeatingMessage(const string& msg); + + // Change a repeating message - assume that the message changes after the string has finished for now + void ChangeRepeatingMessage(const string& newmsg); + + // Cancel the current repeating message + void CancelRepeatingMessage(); +}; + +#endif // _FG_ATC_DISPLAY_HXX diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 53b9f8357..4c8eddbb1 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -16,6 +16,7 @@ #include
#include "NasalSys.hxx" +#include "NasalDisplay.hxx" // Read and return file contents in a single buffer. Note use of // stat() to get the file size. This is a win32 function, believe it @@ -287,6 +288,17 @@ static naRef f_rand(naContext c, naRef me, int argc, naRef* args) return naNum(sg_random()); } +// Wrapper function for screenPrint +static naRef f_screenPrint(naContext c, naRef me, int argc, naRef* args) +{ + if(argc != 1 || !naIsString(args[0])) + naRuntimeError(c, "bad arguments to screenPrint()"); + naRef lmsg = args[0]; + FGNasalSys* nasal = (FGNasalSys*)globals->get_subsystem("nasal"); + nasal->screenPrint(naStr_data(lmsg)); + return naNil(); +} + // Table of extension functions. Terminate with zeros. static struct { char* name; naCFunction func; } funcs[] = { { "getprop", f_getprop }, @@ -298,6 +310,7 @@ static struct { char* name; naCFunction func; } funcs[] = { { "_cmdarg", f_cmdarg }, { "_interpolate", f_interpolate }, { "rand", f_rand }, + { "screenPrint", f_screenPrint }, { 0, 0 } }; @@ -582,3 +595,8 @@ void FGNasalSys::setListener(int argc, naRef* args) node->addChangeListener(new FGNasalListener(handler, this, gcSave(handler))); } +// functions providing access to the NasalDisplay - used to display text directly on the screen +void FGNasalSys::screenPrint(const char* src) +{ + globals->get_Nasal_display()->RegisterSingleMessage(src, 0); +} diff --git a/src/Scripting/NasalSys.hxx b/src/Scripting/NasalSys.hxx index f3e756b59..1a1423bc6 100644 --- a/src/Scripting/NasalSys.hxx +++ b/src/Scripting/NasalSys.hxx @@ -47,7 +47,9 @@ public: void createModule(const char* moduleName, const char* fileName, const char* src, int len); - + + void screenPrint(const char* src); + private: friend class FGNasalScript; friend class FGNasalListener; @@ -96,7 +98,8 @@ public: ~FGNasalScript() { _nas->gcRelease(_gcKey); } bool call() { - naCall(_nas->_context, _code, 0, 0, naNil(), naNil()); + naRef n = naNil(); + naCall(_nas->_context, _code, 0, &n, naNil(), naNil()); return naGetError(_nas->_context) == 0; } -- 2.39.5