diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-29 19:25:29 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-29 19:25:29 +0100 |
| commit | bc524d70253a4ab2fe40c3ca3e5666e267c0a4d1 (patch) | |
| tree | 1e629e7b46b1d9972a973bc93fd100bcebd395be /src/catch2/internal/catch_console_colour.cpp | |
| download | nihil-vendor/catch2.tar.gz nihil-vendor/catch2.tar.bz2 | |
import catch2 3.8.1vendor/catch2/3.8.1vendor/catch2
Diffstat (limited to 'src/catch2/internal/catch_console_colour.cpp')
| -rw-r--r-- | src/catch2/internal/catch_console_colour.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/src/catch2/internal/catch_console_colour.cpp b/src/catch2/internal/catch_console_colour.cpp new file mode 100644 index 0000000..142de9e --- /dev/null +++ b/src/catch2/internal/catch_console_colour.cpp @@ -0,0 +1,282 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + + +#include <catch2/internal/catch_console_colour.hpp> +#include <catch2/internal/catch_enforce.hpp> +#include <catch2/internal/catch_errno_guard.hpp> +#include <catch2/interfaces/catch_interfaces_config.hpp> +#include <catch2/internal/catch_istream.hpp> +#include <catch2/internal/catch_move_and_forward.hpp> +#include <catch2/internal/catch_context.hpp> +#include <catch2/internal/catch_platform.hpp> +#include <catch2/internal/catch_debugger.hpp> +#include <catch2/internal/catch_windows_h_proxy.hpp> +#include <catch2/internal/catch_compiler_capabilities.hpp> + +#include <cassert> +#include <ostream> +#include <utility> + +namespace Catch { + + ColourImpl::~ColourImpl() = default; + + ColourImpl::ColourGuard ColourImpl::guardColour( Colour::Code colourCode ) { + return ColourGuard(colourCode, this ); + } + + void ColourImpl::ColourGuard::engageImpl( std::ostream& stream ) { + assert( &stream == &m_colourImpl->m_stream->stream() && + "Engaging colour guard for different stream than used by the " + "parent colour implementation" ); + static_cast<void>( stream ); + + m_engaged = true; + m_colourImpl->use( m_code ); + } + + ColourImpl::ColourGuard::ColourGuard( Colour::Code code, + ColourImpl const* colour ): + m_colourImpl( colour ), m_code( code ) { + } + ColourImpl::ColourGuard::ColourGuard( ColourGuard&& rhs ) noexcept: + m_colourImpl( rhs.m_colourImpl ), + m_code( rhs.m_code ), + m_engaged( rhs.m_engaged ) { + rhs.m_engaged = false; + } + ColourImpl::ColourGuard& + ColourImpl::ColourGuard::operator=( ColourGuard&& rhs ) noexcept { + using std::swap; + swap( m_colourImpl, rhs.m_colourImpl ); + swap( m_code, rhs.m_code ); + swap( m_engaged, rhs.m_engaged ); + + return *this; + } + ColourImpl::ColourGuard::~ColourGuard() { + if ( m_engaged ) { + m_colourImpl->use( Colour::None ); + } + } + + ColourImpl::ColourGuard& + ColourImpl::ColourGuard::engage( std::ostream& stream ) & { + engageImpl( stream ); + return *this; + } + + ColourImpl::ColourGuard&& + ColourImpl::ColourGuard::engage( std::ostream& stream ) && { + engageImpl( stream ); + return CATCH_MOVE(*this); + } + + namespace { + //! A do-nothing implementation of colour, used as fallback for unknown + //! platforms, and when the user asks to deactivate all colours. + class NoColourImpl final : public ColourImpl { + public: + NoColourImpl( IStream* stream ): ColourImpl( stream ) {} + + private: + void use( Colour::Code ) const override {} + }; + } // namespace + + +} // namespace Catch + + +#if defined ( CATCH_CONFIG_COLOUR_WIN32 ) ///////////////////////////////////////// + +namespace Catch { +namespace { + + class Win32ColourImpl final : public ColourImpl { + public: + Win32ColourImpl(IStream* stream): + ColourImpl(stream) { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), + &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + 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(); + } + + private: + void use( Colour::Code _colourCode ) const override { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + + default: + CATCH_ERROR( "Unknown colour requested" ); + } + } + + void setTextAttribute( WORD _textAttribute ) const { + SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), + _textAttribute | + originalBackgroundAttributes ); + } + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + +} // end anon namespace +} // end namespace Catch + +#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 ) {} + + 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; + } + + 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; + }; + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0;34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + case Colour::BrightYellow: return setColour( "[1;33m" ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); + } + } + }; + +} // end anon namespace +} // end namespace Catch + +namespace Catch { + + Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode colourSelection, + IStream* stream ) { +#if defined( CATCH_CONFIG_COLOUR_WIN32 ) + if ( colourSelection == ColourMode::Win32 ) { + return Detail::make_unique<Win32ColourImpl>( stream ); + } +#endif + if ( colourSelection == ColourMode::ANSI ) { + return Detail::make_unique<ANSIColourImpl>( stream ); + } + if ( colourSelection == ColourMode::None ) { + return Detail::make_unique<NoColourImpl>( stream ); + } + + if ( colourSelection == ColourMode::PlatformDefault) { +#if defined( CATCH_CONFIG_COLOUR_WIN32 ) + if ( Win32ColourImpl::useImplementationForStream( *stream ) ) { + return Detail::make_unique<Win32ColourImpl>( stream ); + } +#endif + if ( ANSIColourImpl::useImplementationForStream( *stream ) ) { + return Detail::make_unique<ANSIColourImpl>( stream ); + } + return Detail::make_unique<NoColourImpl>( stream ); + } + + CATCH_ERROR( "Could not create colour impl for selection " << static_cast<int>(colourSelection) ); + } + + bool isColourImplAvailable( ColourMode colourSelection ) { + switch ( colourSelection ) { +#if defined( CATCH_CONFIG_COLOUR_WIN32 ) + case ColourMode::Win32: +#endif + case ColourMode::ANSI: + case ColourMode::None: + case ColourMode::PlatformDefault: + return true; + default: + return false; + } + } + + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + |
