diff --git a/CMakeLists.txt b/CMakeLists.txt index e5222c0f7..ca88d9b76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ PROJECT(OpenSceneGraph) SET(OPENSCENEGRAPH_MAJOR_VERSION 2) SET(OPENSCENEGRAPH_MINOR_VERSION 8) -SET(OPENSCENEGRAPH_PATCH_VERSION 4) +SET(OPENSCENEGRAPH_PATCH_VERSION 5) SET(OPENSCENEGRAPH_SOVERSION 65) # set to 0 when not a release candidate, non zero means that any generated @@ -245,6 +245,8 @@ ENDIF(WIN32) #luigi#ENDIF(UNIX) ######################################################################################################## +OPTION(OSG_NOTIFY_DISABLED "Set to ON to build OpenSceneGraph with the noitfy() disabled." OFF) + OPTION(OSG_USE_FLOAT_MATRIX "Set to ON to build OpenSceneGraph with float Matrix instead of double." OFF) MARK_AS_ADVANCED(OSG_USE_FLOAT_MATRIX) diff --git a/include/osg/Notify b/include/osg/Notify index df67b0eeb..b7ad579b4 100644 --- a/include/osg/Notify +++ b/include/osg/Notify @@ -11,10 +11,11 @@ * OpenSceneGraph Public License for more details. */ -#ifndef OSG_NOTIFY -#define OSG_NOTIFY 1 +#ifndef OSG_NOTIFY_H +#define OSG_NOTIFY_H 1 #include +#include // for NotifyHandler #include @@ -23,8 +24,8 @@ namespace osg { /** Range of notify levels from DEBUG_FP through to FATAL, ALWAYS * is reserved for forcing the absorption of all messages. The * keywords are also used verbatim when specified by the environmental - * variable OSGNOTIFYLEVEL. See documentation on osg::notify() for - * further details. + * variable OSGNOTIFYLEVEL or OSG_NOTIFY_LEVEL. + * See documentation on osg::notify() for further details. */ enum NotifySeverity { ALWAYS=0, @@ -37,38 +38,108 @@ enum NotifySeverity { }; /** set the notify level, overriding the default or the value set by - * the environmental variable OSGNOTIFYLEVEL. + * the environmental variable OSGNOTIFYLEVEL or OSG_NOTIFY_LEVEL. */ extern OSG_EXPORT void setNotifyLevel(NotifySeverity severity); /** get the notify level. */ extern OSG_EXPORT NotifySeverity getNotifyLevel(); -/** is notification enabled, given the current setNotifyLevel() setting? */ -extern OSG_EXPORT bool isNotifyEnabled(NotifySeverity severity); - /** initialize notify level. */ extern OSG_EXPORT bool initNotifyLevel(); +#ifdef OSG_NOTIFY_DISABLED + inline bool isNotifyEnabled(NotifySeverity) { return false; } +#else + /** is notification enabled, given the current setNotifyLevel() setting? */ + extern OSG_EXPORT bool isNotifyEnabled(NotifySeverity severity); +#endif + /** notify messaging function for providing fatal through to verbose * debugging messages. Level of messages sent to the console can * be controlled by setting the NotifyLevel either within your - * application or via the an environmental variable. For instance - * setenv OSGNOTIFYLEVEL DEBUG (for tsh), export OSGNOTIFYLEVEL=DEBUG - * (for bourne shell) or set OSGNOTIFYLEVEL=DEBUG (for Windows) all - * tell the osg to redirect all debugging and more important messages - * to the console (useful for debugging :-) setting ALWAYS will force + * application or via the an environmental variable i.e. + * - setenv OSGNOTIFYLEVEL DEBUG (for tsh) + * - export OSGNOTIFYLEVEL=DEBUG (for bourne shell) + * - set OSGNOTIFYLEVEL=DEBUG (for Windows) + * + * All tell the osg to redirect all debugging and more important messages + * to the notification stream (useful for debugging) setting ALWAYS will force * all messages to be absorbed, which might be appropriate for final * applications. Default NotifyLevel is NOTICE. Check the enum - * NotifySeverity for full range of possibilities. To use the notify + * #NotifySeverity for full range of possibilities. To use the notify * with your code simply use the notify function as a normal file - * stream (like cout) i.e osg::notify(osg::DEBUG) << "Hello Bugs!"< WARN). + * Notifications can be redirected to other sinks such as GUI widgets or + * windows debugger (WinDebugNotifyHandler) with custom handlers. + * Use setNotifyHandler to set custom handler. + * Note that osg notification API is not thread safe although notification + * handler is called from many threads. When incorporating handlers into GUI + * widgets you must take care of thread safety on your own. + * @see setNotifyHandler + */ +class OSG_EXPORT NotifyHandler : public osg::Referenced +{ +public: + virtual void notify(osg::NotifySeverity severity, const char *message) = 0; +}; + +/** Set notification handler, by default StandardNotifyHandler is used. + * @see NotifyHandler + */ +extern OSG_EXPORT void setNotifyHandler(NotifyHandler *handler); + +/** Get currrent notification handler. */ +extern OSG_EXPORT NotifyHandler *getNotifyHandler(); + +/** Redirects notification stream to stderr (severity <= WARN) or stdout (severity > WARN). + * The fputs() function is used to write messages to standard files. Note that + * std::out and std::cerr streams are not used. + * @see setNotifyHandler + */ +class OSG_EXPORT StandardNotifyHandler : public NotifyHandler +{ +public: + void notify(osg::NotifySeverity severity, const char *message); +}; + +#if defined(WIN32) && !defined(__CYGWIN__) + +/** Redirects notification stream to windows debugger with use of + * OuputDebugString functions. + * @see setNotifyHandler + */ +class OSG_EXPORT WinDebugNotifyHandler : public NotifyHandler +{ +public: + void notify(osg::NotifySeverity severity, const char *message); +}; + +#endif + } #endif diff --git a/include/osg/Version b/include/osg/Version index b01c8072b..8d611c304 100644 --- a/include/osg/Version +++ b/include/osg/Version @@ -21,8 +21,8 @@ extern "C" { #define OPENSCENEGRAPH_MAJOR_VERSION 2 #define OPENSCENEGRAPH_MINOR_VERSION 8 -#define OPENSCENEGRAPH_PATCH_VERSION 4 -#define OPENSCENEGRAPH_SOVERSION 65 +#define OPENSCENEGRAPH_PATCH_VERSION 5 +#define OPENSCENEGRAPH_SOVERSION 74 /** convinience macro that can be used to decide whether a feature is present or not i.e. * #if OSG_MIN_VERSION_REQUIRED(2,9,5) diff --git a/src/osg/Config.in b/src/osg/Config.in index 88aa642fa..6e9b248a4 100644 --- a/src/osg/Config.in +++ b/src/osg/Config.in @@ -23,6 +23,7 @@ #ifndef OSG_CONFIG #define OSG_CONFIG 1 +#cmakedefine OSG_NOTIFY_DISABLED #cmakedefine OSG_USE_FLOAT_MATRIX #cmakedefine OSG_USE_FLOAT_PLANE #cmakedefine OSG_USE_FLOAT_BOUNDINGSPHERE diff --git a/src/osg/Notify.cpp b/src/osg/Notify.cpp index 522b3db53..fda36c4ee 100644 --- a/src/osg/Notify.cpp +++ b/src/osg/Notify.cpp @@ -11,34 +11,148 @@ * OpenSceneGraph Public License for more details. */ #include +#include +#include #include #include +#include +#include #include -#include -using namespace std; +#include -osg::NotifySeverity g_NotifyLevel = osg::NOTICE; +namespace osg +{ + +class NullStreamBuffer : public std::streambuf +{ +private: + std::streamsize xsputn(const std::streambuf::char_type *str, std::streamsize n) + { + return n; + } +}; + +struct NullStream : public std::ostream +{ +public: + NullStream(): + std::ostream(new NullStreamBuffer) + { _buffer = dynamic_cast(rdbuf()); } + + ~NullStream() + { + rdbuf(0); + delete _buffer; + } + +protected: + NullStreamBuffer* _buffer; +}; + +/** Stream buffer calling notify handler when buffer is synchronized (usually on std::endl). + * Stream stores last notification severity to pass it to handler call. + */ +struct NotifyStreamBuffer : public std::stringbuf +{ + NotifyStreamBuffer() : _severity(osg::NOTICE) + { + } + + void setNotifyHandler(osg::NotifyHandler *handler) { _handler = handler; } + osg::NotifyHandler *getNotifyHandler() const { return _handler.get(); } + + /** Sets severity for next call of notify handler */ + void setCurrentSeverity(osg::NotifySeverity severity) { _severity = severity; } + osg::NotifySeverity getCurrentSeverity() const { return _severity; } + +private: + + int sync() + { + sputc(0); // string termination + if (_handler.valid()) + _handler->notify(_severity, pbase()); + pubseekpos(0, std::ios_base::out); // or str(std::string()) + return 0; + } + + osg::ref_ptr _handler; + osg::NotifySeverity _severity; +}; + +struct NotifyStream : public std::ostream +{ +public: + NotifyStream(): + std::ostream(new NotifyStreamBuffer) + { _buffer = dynamic_cast(rdbuf()); } + + void setCurrentSeverity(osg::NotifySeverity severity) + { + _buffer->setCurrentSeverity(severity); + } + + osg::NotifySeverity getCurrentSeverity() const + { + return _buffer->getCurrentSeverity(); + } + + ~NotifyStream() + { + rdbuf(0); + delete _buffer; + } + +protected: + NotifyStreamBuffer* _buffer; +}; + +} + +using namespace osg; + +static osg::ApplicationUsageProxy Notify_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE, "OSG_NOTIFY_LEVEL ", "FATAL | WARN | NOTICE | DEBUG_INFO | DEBUG_FP | DEBUG | INFO | ALWAYS"); + +static bool s_NeedNotifyInit = true; +static osg::NotifySeverity g_NotifyLevel = osg::NOTICE; +static osg::NullStream *g_NullStream; +static osg::NotifyStream *g_NotifyStream; void osg::setNotifyLevel(osg::NotifySeverity severity) { - osg::initNotifyLevel(); + if (s_NeedNotifyInit) osg::initNotifyLevel(); g_NotifyLevel = severity; } osg::NotifySeverity osg::getNotifyLevel() { - osg::initNotifyLevel(); + if (s_NeedNotifyInit) osg::initNotifyLevel(); return g_NotifyLevel; } +void osg::setNotifyHandler(osg::NotifyHandler *handler) +{ + osg::NotifyStreamBuffer *buffer = static_cast(g_NotifyStream->rdbuf()); + if (buffer) + buffer->setNotifyHandler(handler); +} + +osg::NotifyHandler* osg::getNotifyHandler() +{ + if (s_NeedNotifyInit) osg::initNotifyLevel(); + osg::NotifyStreamBuffer *buffer = static_cast(g_NotifyStream->rdbuf()); + return buffer ? buffer->getNotifyHandler() : 0; +} bool osg::initNotifyLevel() { - static bool s_NotifyInit = false; + static osg::NullStream s_NullStream; + static osg::NotifyStream s_NotifyStream; - if (s_NotifyInit) return true; + g_NullStream = &s_NullStream; + g_NotifyStream = &s_NotifyStream; // g_NotifyLevel // ============= @@ -72,56 +186,59 @@ bool osg::initNotifyLevel() } - s_NotifyInit = true; + // Setup standard notify handler + osg::NotifyStreamBuffer *buffer = dynamic_cast(g_NotifyStream->rdbuf()); + if (buffer && !buffer->getNotifyHandler()) + buffer->setNotifyHandler(new StandardNotifyHandler); + + s_NeedNotifyInit = false; return true; } +#ifndef OSG_NOTIFY_DISABLED bool osg::isNotifyEnabled( osg::NotifySeverity severity ) { + if (s_NeedNotifyInit) osg::initNotifyLevel(); return severity<=g_NotifyLevel; } - -class NullStreamBuffer : public std::streambuf -{ - private: - - virtual streamsize xsputn (const char_type*, streamsize n) - { - return n; - } -}; - -struct NullStream : public std::ostream -{ - NullStream(): - std::ostream(new NullStreamBuffer) {} - - virtual ~NullStream() - { - delete rdbuf(); - rdbuf(0); - } -}; +#endif std::ostream& osg::notify(const osg::NotifySeverity severity) { - // set up global notify null stream for inline notify - static NullStream s_NotifyNulStream; + if (s_NeedNotifyInit) osg::initNotifyLevel(); - static bool initialized = false; - if (!initialized) + if (osg::isNotifyEnabled(severity)) { - std::cerr<<""; // dummy op to force construction of cerr, before a reference is passed back to calling code. - std::cout<<""; // dummy op to force construction of cout, before a reference is passed back to calling code. - initialized = osg::initNotifyLevel(); + g_NotifyStream->setCurrentSeverity(severity); + return *g_NotifyStream; } - - if (severity<=g_NotifyLevel) - { - if (severity<=osg::WARN) return std::cerr; - else return std::cout; - } - return s_NotifyNulStream; + return *g_NullStream; } + +void osg::StandardNotifyHandler::notify(osg::NotifySeverity severity, const char *message) +{ +#if 1 + if (severity <= osg::WARN) + fputs(message, stderr); + else + fputs(message, stdout); +#else + fputs(message, stdout); +#endif +} + +#if defined(WIN32) && !defined(__CYGWIN__) + +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif +#include + +void osg::WinDebugNotifyHandler::notify(osg::NotifySeverity severity, const char *message) +{ + OutputDebugStringA(message); +} + +#endif