Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 11 additions & 34 deletions src/catch2/internal/catch_console_colour.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ namespace Catch {

} // namespace Catch


#if defined ( CATCH_CONFIG_COLOUR_WIN32 ) /////////////////////////////////////////

namespace Catch {
Expand All @@ -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:
Expand Down Expand Up @@ -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 <unistd.h>
#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:
Expand Down
75 changes: 67 additions & 8 deletions src/catch2/internal/catch_istream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@

// SPDX-License-Identifier: BSL-1.0

#include <catch2/internal/catch_istream.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_debug_console.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_errno_guard.hpp>
#include <catch2/internal/catch_istream.hpp>
#include <catch2/internal/catch_stdstreams.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_windows_h_proxy.hpp>

#include <cstdio>
#include <fstream>

#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) || \
defined( __GLIBC__ )
# define CATCH_INTERNAL_HAS_ISATTY
# include <unistd.h>
#elif defined( CATCH_PLATFORM_WINDOWS )
# include <io.h>
#endif

namespace Catch {

Catch::IStream::~IStream() = default;
Expand Down Expand Up @@ -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; }
};

///////////////////////////////////////////////////////////////////////////
Expand Down