diff --git a/src/catch2/internal/catch_console_colour.cpp b/src/catch2/internal/catch_console_colour.cpp index 142de9e65e..878407bfcc 100644 --- a/src/catch2/internal/catch_console_colour.cpp +++ b/src/catch2/internal/catch_console_colour.cpp @@ -97,7 +97,6 @@ namespace Catch { } // namespace Catch - #if defined ( CATCH_CONFIG_COLOUR_WIN32 ) ///////////////////////////////////////// namespace Catch { @@ -115,10 +114,13 @@ namespace { } static bool useImplementationForStream(IStream const& stream) { - // Win32 text colour APIs can only be used on console streams - // We cannot check that the output hasn't been redirected, - // so we just check that the original stream is console stream. - return stream.isConsole(); + OSVERSIONINFOA versionInfo; + // Use as fallback for Windows versions <10.0 only. + // Newer Windows versions have full support for ANSI color codes. + if ( GetVersionExA( &versionInfo ) && versionInfo.dwMajorVersion < 10 ) { + return stream.isConsole(); + } + return false; } private: @@ -160,48 +162,23 @@ namespace { #endif // Windows/ ANSI/ None - -#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) || defined( __GLIBC__ ) -# define CATCH_INTERNAL_HAS_ISATTY -# include -#endif - namespace Catch { namespace { class ANSIColourImpl final : public ColourImpl { public: - ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} + ANSIColourImpl( IStream* stream ): ColourImpl( stream ) { + } static bool useImplementationForStream(IStream const& stream) { - // This is kinda messy due to trying to support a bunch of - // different platforms at once. - // The basic idea is that if we are asked to do autodetection (as - // opposed to being told to use posixy colours outright), then we - // only want to use the colours if we are writing to console. - // However, console might be redirected, so we make an attempt at - // checking for that on platforms where we know how to do that. - bool useColour = stream.isConsole(); -#if defined( CATCH_INTERNAL_HAS_ISATTY ) && \ - !( defined( __DJGPP__ ) && defined( __STRICT_ANSI__ ) ) - ErrnoGuard _; // for isatty - useColour = useColour && isatty( STDOUT_FILENO ); -# endif -# if defined( CATCH_PLATFORM_MAC ) || defined( CATCH_PLATFORM_IPHONE ) - useColour = useColour && !isDebuggerActive(); -# endif - - return useColour; + return stream.isConsole(); } private: void use( Colour::Code _colourCode ) const override { auto setColour = [&out = m_stream->stream()]( char const* escapeCode ) { - // The escape sequence must be flushed to console, otherwise - // if stdin and stderr are intermixed, we'd get accidentally - // coloured output. - out << '\033' << escapeCode << std::flush; + out << '\033' << escapeCode; }; switch( _colourCode ) { case Colour::None: diff --git a/src/catch2/internal/catch_istream.cpp b/src/catch2/internal/catch_istream.cpp index 717da0e78c..e2eeb8b738 100644 --- a/src/catch2/internal/catch_istream.cpp +++ b/src/catch2/internal/catch_istream.cpp @@ -6,15 +6,25 @@ // SPDX-License-Identifier: BSL-1.0 -#include -#include #include -#include +#include +#include +#include #include +#include +#include #include #include +#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) || \ + defined( __GLIBC__ ) +# define CATCH_INTERNAL_HAS_ISATTY +# include +#elif defined( CATCH_PLATFORM_WINDOWS ) +# include +#endif + namespace Catch { Catch::IStream::~IStream() = default; @@ -86,29 +96,78 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// +#if defined( CATCH_PLATFORM_WINDOWS ) + bool enableVirtualTerminalSupport( DWORD stdHandle ) { + HANDLE outputHandle = GetStdHandle( stdHandle ); + DWORD mode = 0; + const DWORD requiredMode = ENABLE_PROCESSED_OUTPUT | + ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if ( GetConsoleMode( outputHandle, &mode ) && + ( mode & requiredMode ) == requiredMode ) { + // VT100 style sequence processing has to be enabled by + // explicit opt-in. + const DWORD newMode = mode | requiredMode; + if( SetConsoleMode( outputHandle, newMode ) ) + { + return true; + } + // Restore fail-safe state. + SetConsoleMode( outputHandle, mode ); + } + return false; + } +#endif + class CoutStream final : public IStream { std::ostream m_os; + bool m_isatty; public: // Store the streambuf from cout up-front because // cout may get redirected when running tests - CoutStream() : m_os( Catch::cout().rdbuf() ) {} + CoutStream() : m_os( Catch::cout().rdbuf() ) { + m_isatty = true; +#if defined( CATCH_INTERNAL_HAS_ISATTY ) && \ + !( defined( __DJGPP__ ) && defined( __STRICT_ANSI__ ) ) + ErrnoGuard _; // for isatty + m_isatty = m_isatty && isatty( STDOUT_FILENO ); +#elif defined( CATCH_PLATFORM_WINDOWS ) + m_isatty = m_isatty && _isatty( _fileno( stdout ) ); + m_isatty = m_isatty && enableVirtualTerminalSupport( STD_OUTPUT_HANDLE ); +#endif +#if defined( CATCH_PLATFORM_MAC ) || defined( CATCH_PLATFORM_IPHONE ) + m_isatty = m_isatty && !isDebuggerActive(); +#endif + } public: // IStream std::ostream& stream() override { return m_os; } - bool isConsole() const override { return true; } + bool isConsole() const override { return m_isatty; } }; class CerrStream : public IStream { std::ostream m_os; - + bool m_isatty; public: // Store the streambuf from cerr up-front because // cout may get redirected when running tests - CerrStream(): m_os( Catch::cerr().rdbuf() ) {} + CerrStream(): m_os( Catch::cerr().rdbuf() ) { + m_isatty = true; +#if defined( CATCH_INTERNAL_HAS_ISATTY ) && \ + !( defined( __DJGPP__ ) && defined( __STRICT_ANSI__ ) ) + ErrnoGuard _; // for isatty + m_isatty = m_isatty && isatty( STDERR_FILENO ); +#elif defined( CATCH_PLATFORM_WINDOWS ) + m_isatty = m_isatty && _isatty( _fileno( stderr ) ); + m_isatty = m_isatty && enableVirtualTerminalSupport( STD_ERROR_HANDLE ); +#endif +#if defined( CATCH_PLATFORM_MAC ) || defined( CATCH_PLATFORM_IPHONE ) + m_isatty = m_isatty && !isDebuggerActive(); +#endif + } public: // IStream std::ostream& stream() override { return m_os; } - bool isConsole() const override { return true; } + bool isConsole() const override { return m_isatty; } }; ///////////////////////////////////////////////////////////////////////////