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 #ifndef TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
9 #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
10 
11 #include "catch_console_colour.hpp"
12 #include "catch_errno_guard.hpp"
13 
14 namespace Catch {
15     namespace {
16 
17         struct IColourImpl {
~IColourImplCatch::__anon81dd07840111::IColourImpl18             virtual ~IColourImpl() {}
19             virtual void use( Colour::Code _colourCode ) = 0;
20         };
21 
22         struct NoColourImpl : IColourImpl {
useCatch::__anon81dd07840111::NoColourImpl23             void use( Colour::Code ) {}
24 
instanceCatch::__anon81dd07840111::NoColourImpl25             static IColourImpl* instance() {
26                 static NoColourImpl s_instance;
27                 return &s_instance;
28             }
29         };
30 
31     } // anon namespace
32 } // namespace Catch
33 
34 #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
35 #   ifdef CATCH_PLATFORM_WINDOWS
36 #       define CATCH_CONFIG_COLOUR_WINDOWS
37 #   else
38 #       define CATCH_CONFIG_COLOUR_ANSI
39 #   endif
40 #endif
41 
42 
43 #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
44 
45 #include "catch_windows_h_proxy.h"
46 
47 namespace Catch {
48 namespace {
49 
50     class Win32ColourImpl : public IColourImpl {
51     public:
Win32ColourImpl()52         Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
53         {
54             CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
55             GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
56             originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
57             originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
58         }
59 
use(Colour::Code _colourCode)60         virtual void use( Colour::Code _colourCode ) {
61             switch( _colourCode ) {
62                 case Colour::None:      return setTextAttribute( originalForegroundAttributes );
63                 case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
64                 case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
65                 case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
66                 case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
67                 case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
68                 case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
69                 case Colour::Grey:      return setTextAttribute( 0 );
70 
71                 case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
72                 case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
73                 case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
74                 case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
75 
76                 case Colour::Bright: throw std::logic_error( "not a colour" );
77             }
78         }
79 
80     private:
setTextAttribute(WORD _textAttribute)81         void setTextAttribute( WORD _textAttribute ) {
82             SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
83         }
84         HANDLE stdoutHandle;
85         WORD originalForegroundAttributes;
86         WORD originalBackgroundAttributes;
87     };
88 
platformColourInstance()89     IColourImpl* platformColourInstance() {
90         static Win32ColourImpl s_instance;
91 
92         Ptr<IConfig const> config = getCurrentContext().getConfig();
93         UseColour::YesOrNo colourMode = config
94             ? config->useColour()
95             : UseColour::Auto;
96         if( colourMode == UseColour::Auto )
97             colourMode = !isDebuggerActive()
98                 ? UseColour::Yes
99                 : UseColour::No;
100         return colourMode == UseColour::Yes
101             ? &s_instance
102             : NoColourImpl::instance();
103     }
104 
105 } // end anon namespace
106 } // end namespace Catch
107 
108 #elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
109 
110 #include <unistd.h>
111 
112 namespace Catch {
113 namespace {
114 
115     // use POSIX/ ANSI console terminal codes
116     // Thanks to Adam Strzelecki for original contribution
117     // (http://github.com/nanoant)
118     // https://github.com/philsquared/Catch/pull/131
119     class PosixColourImpl : public IColourImpl {
120     public:
use(Colour::Code _colourCode)121         virtual void use( Colour::Code _colourCode ) {
122             switch( _colourCode ) {
123                 case Colour::None:
124                 case Colour::White:     return setColour( "[0m" );
125                 case Colour::Red:       return setColour( "[0;31m" );
126                 case Colour::Green:     return setColour( "[0;32m" );
127                 case Colour::Blue:      return setColour( "[0;34m" );
128                 case Colour::Cyan:      return setColour( "[0;36m" );
129                 case Colour::Yellow:    return setColour( "[0;33m" );
130                 case Colour::Grey:      return setColour( "[1;30m" );
131 
132                 case Colour::LightGrey:     return setColour( "[0;37m" );
133                 case Colour::BrightRed:     return setColour( "[1;31m" );
134                 case Colour::BrightGreen:   return setColour( "[1;32m" );
135                 case Colour::BrightWhite:   return setColour( "[1;37m" );
136 
137                 case Colour::Bright: throw std::logic_error( "not a colour" );
138             }
139         }
instance()140         static IColourImpl* instance() {
141             static PosixColourImpl s_instance;
142             return &s_instance;
143         }
144 
145     private:
setColour(const char * _escapeCode)146         void setColour( const char* _escapeCode ) {
147             Catch::cout() << '\033' << _escapeCode;
148         }
149     };
150 
platformColourInstance()151     IColourImpl* platformColourInstance() {
152         ErrnoGuard guard;
153         Ptr<IConfig const> config = getCurrentContext().getConfig();
154         UseColour::YesOrNo colourMode = config
155             ? config->useColour()
156             : UseColour::Auto;
157         if( colourMode == UseColour::Auto )
158             colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) )
159                 ? UseColour::Yes
160                 : UseColour::No;
161         return colourMode == UseColour::Yes
162             ? PosixColourImpl::instance()
163             : NoColourImpl::instance();
164     }
165 
166 } // end anon namespace
167 } // end namespace Catch
168 
169 #else  // not Windows or ANSI ///////////////////////////////////////////////
170 
171 namespace Catch {
172 
platformColourInstance()173     static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
174 
175 } // end namespace Catch
176 
177 #endif // Windows/ ANSI/ None
178 
179 namespace Catch {
180 
Colour(Code _colourCode)181     Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
Colour(Colour const & _other)182     Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
~Colour()183     Colour::~Colour(){ if( !m_moved ) use( None ); }
184 
use(Code _colourCode)185     void Colour::use( Code _colourCode ) {
186         static IColourImpl* impl = platformColourInstance();
187         impl->use( _colourCode );
188     }
189 
190 } // end namespace Catch
191 
192 #endif // TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
193