1 /* 2 * Created by Phil on 25/2/2012. 3 * Copyright 2012 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 */ 8 9 10 #if defined(__clang__) 11 # pragma clang diagnostic push 12 # pragma clang diagnostic ignored "-Wexit-time-destructors" 13 #endif 14 15 16 #include "catch_console_colour.h" 17 #include "catch_enforce.h" 18 #include "catch_errno_guard.h" 19 #include "catch_interfaces_config.h" 20 #include "catch_stream.h" 21 #include "catch_context.h" 22 #include "catch_platform.h" 23 #include "catch_debugger.h" 24 #include "catch_windows_h_proxy.h" 25 26 #include <sstream> 27 28 namespace Catch { 29 namespace { 30 31 struct IColourImpl { 32 virtual ~IColourImpl() = default; 33 virtual void use( Colour::Code _colourCode ) = 0; 34 }; 35 36 struct NoColourImpl : IColourImpl { useCatch::__anonf96e100d0111::NoColourImpl37 void use( Colour::Code ) {} 38 instanceCatch::__anonf96e100d0111::NoColourImpl39 static IColourImpl* instance() { 40 static NoColourImpl s_instance; 41 return &s_instance; 42 } 43 }; 44 45 } // anon namespace 46 } // namespace Catch 47 48 #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) 49 # ifdef CATCH_PLATFORM_WINDOWS 50 # define CATCH_CONFIG_COLOUR_WINDOWS 51 # else 52 # define CATCH_CONFIG_COLOUR_ANSI 53 # endif 54 #endif 55 56 57 #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// 58 59 namespace Catch { 60 namespace { 61 62 class Win32ColourImpl : public IColourImpl { 63 public: Win32ColourImpl()64 Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) 65 { 66 CONSOLE_SCREEN_BUFFER_INFO csbiInfo; 67 GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); 68 originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); 69 originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); 70 } 71 use(Colour::Code _colourCode)72 virtual void use( Colour::Code _colourCode ) override { 73 switch( _colourCode ) { 74 case Colour::None: return setTextAttribute( originalForegroundAttributes ); 75 case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); 76 case Colour::Red: return setTextAttribute( FOREGROUND_RED ); 77 case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); 78 case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); 79 case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); 80 case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); 81 case Colour::Grey: return setTextAttribute( 0 ); 82 83 case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); 84 case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); 85 case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); 86 case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); 87 case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); 88 89 case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); 90 91 default: 92 CATCH_ERROR( "Unknown colour requested" ); 93 } 94 } 95 96 private: setTextAttribute(WORD _textAttribute)97 void setTextAttribute( WORD _textAttribute ) { 98 SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); 99 } 100 HANDLE stdoutHandle; 101 WORD originalForegroundAttributes; 102 WORD originalBackgroundAttributes; 103 }; 104 platformColourInstance()105 IColourImpl* platformColourInstance() { 106 static Win32ColourImpl s_instance; 107 108 IConfigPtr config = getCurrentContext().getConfig(); 109 UseColour::YesOrNo colourMode = config 110 ? config->useColour() 111 : UseColour::Auto; 112 if( colourMode == UseColour::Auto ) 113 colourMode = UseColour::Yes; 114 return colourMode == UseColour::Yes 115 ? &s_instance 116 : NoColourImpl::instance(); 117 } 118 119 } // end anon namespace 120 } // end namespace Catch 121 122 #elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// 123 124 #include <unistd.h> 125 126 namespace Catch { 127 namespace { 128 129 // use POSIX/ ANSI console terminal codes 130 // Thanks to Adam Strzelecki for original contribution 131 // (http://github.com/nanoant) 132 // https://github.com/philsquared/Catch/pull/131 133 class PosixColourImpl : public IColourImpl { 134 public: use(Colour::Code _colourCode)135 virtual void use( Colour::Code _colourCode ) override { 136 switch( _colourCode ) { 137 case Colour::None: 138 case Colour::White: return setColour( "[0m" ); 139 case Colour::Red: return setColour( "[0;31m" ); 140 case Colour::Green: return setColour( "[0;32m" ); 141 case Colour::Blue: return setColour( "[0;34m" ); 142 case Colour::Cyan: return setColour( "[0;36m" ); 143 case Colour::Yellow: return setColour( "[0;33m" ); 144 case Colour::Grey: return setColour( "[1;30m" ); 145 146 case Colour::LightGrey: return setColour( "[0;37m" ); 147 case Colour::BrightRed: return setColour( "[1;31m" ); 148 case Colour::BrightGreen: return setColour( "[1;32m" ); 149 case Colour::BrightWhite: return setColour( "[1;37m" ); 150 case Colour::BrightYellow: return setColour( "[1;33m" ); 151 152 case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); 153 default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); 154 } 155 } instance()156 static IColourImpl* instance() { 157 static PosixColourImpl s_instance; 158 return &s_instance; 159 } 160 161 private: setColour(const char * _escapeCode)162 void setColour( const char* _escapeCode ) { 163 getCurrentContext().getConfig()->stream() 164 << '\033' << _escapeCode; 165 } 166 }; 167 useColourOnPlatform()168 bool useColourOnPlatform() { 169 return 170 #ifdef CATCH_PLATFORM_MAC 171 !isDebuggerActive() && 172 #endif 173 #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) 174 isatty(STDOUT_FILENO) 175 #else 176 false 177 #endif 178 ; 179 } platformColourInstance()180 IColourImpl* platformColourInstance() { 181 ErrnoGuard guard; 182 IConfigPtr config = getCurrentContext().getConfig(); 183 UseColour::YesOrNo colourMode = config 184 ? config->useColour() 185 : UseColour::Auto; 186 if( colourMode == UseColour::Auto ) 187 colourMode = useColourOnPlatform() 188 ? UseColour::Yes 189 : UseColour::No; 190 return colourMode == UseColour::Yes 191 ? PosixColourImpl::instance() 192 : NoColourImpl::instance(); 193 } 194 195 } // end anon namespace 196 } // end namespace Catch 197 198 #else // not Windows or ANSI /////////////////////////////////////////////// 199 200 namespace Catch { 201 platformColourInstance()202 static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } 203 204 } // end namespace Catch 205 206 #endif // Windows/ ANSI/ None 207 208 namespace Catch { 209 Colour(Code _colourCode)210 Colour::Colour( Code _colourCode ) { use( _colourCode ); } Colour(Colour && rhs)211 Colour::Colour( Colour&& rhs ) noexcept { 212 m_moved = rhs.m_moved; 213 rhs.m_moved = true; 214 } operator =(Colour && rhs)215 Colour& Colour::operator=( Colour&& rhs ) noexcept { 216 m_moved = rhs.m_moved; 217 rhs.m_moved = true; 218 return *this; 219 } 220 ~Colour()221 Colour::~Colour(){ if( !m_moved ) use( None ); } 222 use(Code _colourCode)223 void Colour::use( Code _colourCode ) { 224 static IColourImpl* impl = platformColourInstance(); 225 impl->use( _colourCode ); 226 } 227 operator <<(std::ostream & os,Colour const &)228 std::ostream& operator << ( std::ostream& os, Colour const& ) { 229 return os; 230 } 231 232 } // end namespace Catch 233 234 #if defined(__clang__) 235 # pragma clang diagnostic pop 236 #endif 237 238