diff --git a/include/osg/Notify b/include/osg/Notify index df67b0eeb..46fca135d 100644 --- a/include/osg/Notify +++ b/include/osg/Notify @@ -15,6 +15,7 @@ #define OSG_NOTIFY 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,7 +38,7 @@ 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); @@ -53,22 +54,79 @@ extern OSG_EXPORT bool initNotifyLevel(); /** 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/src/osg/Notify.cpp b/src/osg/Notify.cpp index d25574f8c..16cab8945 100644 --- a/src/osg/Notify.cpp +++ b/src/osg/Notify.cpp @@ -12,16 +12,95 @@ */ #include #include +#include #include #include +#include +#include #include -#include -using namespace std; +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(&_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(&_buffer) {} + + void setCurrentSeverity(osg::NotifySeverity severity) + { + _buffer.setCurrentSeverity(severity); + } + + osg::NotifySeverity getCurrentSeverity() const + { + return _buffer.getCurrentSeverity(); + } + +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"); osg::NotifySeverity g_NotifyLevel = osg::NOTICE; +osg::NullStream g_NullStream; +osg::NotifyStream g_NotifyStream; void osg::setNotifyLevel(osg::NotifySeverity severity) { @@ -36,6 +115,19 @@ osg::NotifySeverity osg::getNotifyLevel() return g_NotifyLevel; } +void osg::setNotifyHandler(osg::NotifyHandler *handler) +{ + osg::NotifyStreamBuffer *buffer = static_cast(g_NotifyStream.rdbuf()); + if (buffer) + buffer->setNotifyHandler(handler); +} + +osg::NotifyHandler *getNotifyHandler() +{ + osg::initNotifyLevel(); + osg::NotifyStreamBuffer *buffer = static_cast(g_NotifyStream.rdbuf()); + return buffer ? buffer->getNotifyHandler() : 0; +} bool osg::initNotifyLevel() { @@ -75,6 +167,11 @@ bool osg::initNotifyLevel() } + // Setup standard notify handler + osg::NotifyStreamBuffer *buffer = dynamic_cast(g_NotifyStream.rdbuf()); + if (buffer && !buffer->getNotifyHandler()) + buffer->setNotifyHandler(new StandardNotifyHandler); + s_NotifyInit = true; return true; @@ -86,45 +183,38 @@ bool osg::isNotifyEnabled( osg::NotifySeverity severity ) 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); - } -}; - std::ostream& osg::notify(const osg::NotifySeverity severity) { - // set up global notify null stream for inline notify - static NullStream s_NotifyNulStream; - static bool initialized = false; if (!initialized) { - 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(); } if (severity<=g_NotifyLevel) { - if (severity<=osg::WARN) return std::cerr; - else return std::cout; + g_NotifyStream.setCurrentSeverity(severity); + return g_NotifyStream; } - return s_NotifyNulStream; + return g_NullStream; } + +void osg::StandardNotifyHandler::notify(osg::NotifySeverity severity, const char *message) +{ + if (severity <= osg::WARN) + fputs(message, stderr); + else + fputs(message, stdout); +} + +#if defined(WIN32) && !defined(__CYGWIN__) + +#define WIN32_LEAN_AND_MEAN +#include + +void osg::WinDebugNotifyHandler::notify(osg::NotifySeverity severity, const char *message) +{ + OutputDebugStringA(message); +} + +#endif