catch_console_colour.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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. #if defined(__clang__)
  9. # pragma clang diagnostic push
  10. # pragma clang diagnostic ignored "-Wexit-time-destructors"
  11. #endif
  12. #include "catch_console_colour.h"
  13. #include "catch_enforce.h"
  14. #include "catch_errno_guard.h"
  15. #include "catch_interfaces_config.h"
  16. #include "catch_stream.h"
  17. #include "catch_context.h"
  18. #include "catch_platform.h"
  19. #include "catch_debugger.h"
  20. #include "catch_windows_h_proxy.h"
  21. #include <sstream>
  22. namespace Catch {
  23. namespace {
  24. struct IColourImpl {
  25. virtual ~IColourImpl() = default;
  26. virtual void use( Colour::Code _colourCode ) = 0;
  27. };
  28. struct NoColourImpl : IColourImpl {
  29. void use( Colour::Code ) override {}
  30. static IColourImpl* instance() {
  31. static NoColourImpl s_instance;
  32. return &s_instance;
  33. }
  34. };
  35. } // anon namespace
  36. } // namespace Catch
  37. #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
  38. # ifdef CATCH_PLATFORM_WINDOWS
  39. # define CATCH_CONFIG_COLOUR_WINDOWS
  40. # else
  41. # define CATCH_CONFIG_COLOUR_ANSI
  42. # endif
  43. #endif
  44. #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
  45. namespace Catch {
  46. namespace {
  47. class Win32ColourImpl : public IColourImpl {
  48. public:
  49. Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
  50. {
  51. CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
  52. GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
  53. originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
  54. originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
  55. }
  56. void use( Colour::Code _colourCode ) override {
  57. switch( _colourCode ) {
  58. case Colour::None: return setTextAttribute( originalForegroundAttributes );
  59. case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
  60. case Colour::Red: return setTextAttribute( FOREGROUND_RED );
  61. case Colour::Green: return setTextAttribute( FOREGROUND_GREEN );
  62. case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE );
  63. case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
  64. case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
  65. case Colour::Grey: return setTextAttribute( 0 );
  66. case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY );
  67. case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
  68. case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
  69. case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
  70. case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN );
  71. case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
  72. default:
  73. CATCH_ERROR( "Unknown colour requested" );
  74. }
  75. }
  76. private:
  77. void setTextAttribute( WORD _textAttribute ) {
  78. SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
  79. }
  80. HANDLE stdoutHandle;
  81. WORD originalForegroundAttributes;
  82. WORD originalBackgroundAttributes;
  83. };
  84. IColourImpl* platformColourInstance() {
  85. static Win32ColourImpl s_instance;
  86. IConfigPtr config = getCurrentContext().getConfig();
  87. UseColour::YesOrNo colourMode = config
  88. ? config->useColour()
  89. : UseColour::Auto;
  90. if( colourMode == UseColour::Auto )
  91. colourMode = UseColour::Yes;
  92. return colourMode == UseColour::Yes
  93. ? &s_instance
  94. : NoColourImpl::instance();
  95. }
  96. } // end anon namespace
  97. } // end namespace Catch
  98. #elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
  99. #include <unistd.h>
  100. namespace Catch {
  101. namespace {
  102. // use POSIX/ ANSI console terminal codes
  103. // Thanks to Adam Strzelecki for original contribution
  104. // (http://github.com/nanoant)
  105. // https://github.com/philsquared/Catch/pull/131
  106. class PosixColourImpl : public IColourImpl {
  107. public:
  108. void use( Colour::Code _colourCode ) override {
  109. switch( _colourCode ) {
  110. case Colour::None:
  111. case Colour::White: return setColour( "[0m" );
  112. case Colour::Red: return setColour( "[0;31m" );
  113. case Colour::Green: return setColour( "[0;32m" );
  114. case Colour::Blue: return setColour( "[0;34m" );
  115. case Colour::Cyan: return setColour( "[0;36m" );
  116. case Colour::Yellow: return setColour( "[0;33m" );
  117. case Colour::Grey: return setColour( "[1;30m" );
  118. case Colour::LightGrey: return setColour( "[0;37m" );
  119. case Colour::BrightRed: return setColour( "[1;31m" );
  120. case Colour::BrightGreen: return setColour( "[1;32m" );
  121. case Colour::BrightWhite: return setColour( "[1;37m" );
  122. case Colour::BrightYellow: return setColour( "[1;33m" );
  123. case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
  124. default: CATCH_INTERNAL_ERROR( "Unknown colour requested" );
  125. }
  126. }
  127. static IColourImpl* instance() {
  128. static PosixColourImpl s_instance;
  129. return &s_instance;
  130. }
  131. private:
  132. void setColour( const char* _escapeCode ) {
  133. getCurrentContext().getConfig()->stream()
  134. << '\033' << _escapeCode;
  135. }
  136. };
  137. bool useColourOnPlatform() {
  138. return
  139. #if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
  140. !isDebuggerActive() &&
  141. #endif
  142. #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
  143. isatty(STDOUT_FILENO)
  144. #else
  145. false
  146. #endif
  147. ;
  148. }
  149. IColourImpl* platformColourInstance() {
  150. ErrnoGuard guard;
  151. IConfigPtr config = getCurrentContext().getConfig();
  152. UseColour::YesOrNo colourMode = config
  153. ? config->useColour()
  154. : UseColour::Auto;
  155. if( colourMode == UseColour::Auto )
  156. colourMode = useColourOnPlatform()
  157. ? UseColour::Yes
  158. : UseColour::No;
  159. return colourMode == UseColour::Yes
  160. ? PosixColourImpl::instance()
  161. : NoColourImpl::instance();
  162. }
  163. } // end anon namespace
  164. } // end namespace Catch
  165. #else // not Windows or ANSI ///////////////////////////////////////////////
  166. namespace Catch {
  167. static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
  168. } // end namespace Catch
  169. #endif // Windows/ ANSI/ None
  170. namespace Catch {
  171. Colour::Colour( Code _colourCode ) { use( _colourCode ); }
  172. Colour::Colour( Colour&& other ) noexcept {
  173. m_moved = other.m_moved;
  174. other.m_moved = true;
  175. }
  176. Colour& Colour::operator=( Colour&& other ) noexcept {
  177. m_moved = other.m_moved;
  178. other.m_moved = true;
  179. return *this;
  180. }
  181. Colour::~Colour(){ if( !m_moved ) use( None ); }
  182. void Colour::use( Code _colourCode ) {
  183. static IColourImpl* impl = platformColourInstance();
  184. // Strictly speaking, this cannot possibly happen.
  185. // However, under some conditions it does happen (see #1626),
  186. // and this change is small enough that we can let practicality
  187. // triumph over purity in this case.
  188. if (impl != nullptr) {
  189. impl->use( _colourCode );
  190. }
  191. }
  192. std::ostream& operator << ( std::ostream& os, Colour const& ) {
  193. return os;
  194. }
  195. } // end namespace Catch
  196. #if defined(__clang__)
  197. # pragma clang diagnostic pop
  198. #endif