1 /** 2 * Authors: ponce 3 * Date: July 28, 2014 4 * License: Licensed under the MIT license. See LICENSE for more information 5 * Version: 1.0.2 6 */ 7 module colorize.winterm; 8 9 version(Windows) 10 { 11 import core.sys.windows.windows; 12 13 // Patch for DMD 2.065 compatibility 14 static if( __VERSION__ < 2066 ) private enum nogc = 1; 15 16 // This is a state machine to enable terminal colors on Windows. 17 // Parses and interpret ANSI/VT100 Terminal Control Escape Sequences. 18 // Only supports colour sequences, will output char incorrectly on invalid input. 19 struct WinTermEmulation 20 { 21 public: 22 @nogc void initialize() nothrow 23 { 24 // saves console attributes 25 _console = GetStdHandle(STD_OUTPUT_HANDLE); 26 _savedInitialColor = (0 != GetConsoleScreenBufferInfo(_console, &consoleInfo)); 27 _state = State.initial; 28 } 29 30 @nogc ~this() nothrow 31 { 32 // Restore initial text attributes on release 33 if (_savedInitialColor) 34 { 35 SetConsoleTextAttribute(_console, consoleInfo.wAttributes); 36 _savedInitialColor = false; 37 } 38 } 39 40 enum CharAction 41 { 42 write, 43 drop, 44 flush 45 } 46 47 // Eat one character and update color state accordingly. 48 // Returns what to do with the fed character. 49 @nogc CharAction feed(dchar d) nothrow 50 { 51 final switch(_state) with (State) 52 { 53 case initial: 54 if (d == '\x1B') 55 { 56 _state = escaped; 57 return CharAction.flush; 58 } 59 break; 60 61 case escaped: 62 if (d == '[') 63 { 64 _state = readingAttribute; 65 _parsedAttr = 0; 66 return CharAction.drop; 67 } 68 break; 69 70 71 case readingAttribute: 72 if (d >= '0' && d <= '9') 73 { 74 _parsedAttr = _parsedAttr * 10 + (d - '0'); 75 return CharAction.drop; 76 } 77 else if (d == ';') 78 { 79 executeAttribute(_parsedAttr); 80 _parsedAttr = 0; 81 return CharAction.drop; 82 } 83 else if (d == 'm') 84 { 85 executeAttribute(_parsedAttr); 86 _state = State.initial; 87 return CharAction.drop; 88 } 89 break; 90 } 91 return CharAction.write; 92 } 93 94 private: 95 HANDLE _console; 96 bool _savedInitialColor; 97 CONSOLE_SCREEN_BUFFER_INFO consoleInfo; 98 State _state; 99 WORD _currentAttr; 100 int _parsedAttr; 101 102 enum State 103 { 104 initial, 105 escaped, 106 readingAttribute 107 } 108 109 @nogc void setForegroundColor(WORD fgFlags) nothrow 110 { 111 _currentAttr = _currentAttr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY); 112 _currentAttr = _currentAttr | fgFlags; 113 SetConsoleTextAttribute(_console, _currentAttr); 114 } 115 116 @nogc void setBackgroundColor(WORD bgFlags) nothrow 117 { 118 _currentAttr = _currentAttr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY); 119 _currentAttr = _currentAttr | bgFlags; 120 SetConsoleTextAttribute(_console, _currentAttr); 121 } 122 123 @nogc void executeAttribute(int attr) nothrow 124 { 125 switch (attr) 126 { 127 case 0: 128 // reset all attributes 129 SetConsoleTextAttribute(_console, consoleInfo.wAttributes); 130 break; 131 132 default: 133 if ( (30 <= attr && attr <= 37) || (90 <= attr && attr <= 97) ) 134 { 135 WORD color = 0; 136 if (90 <= attr && attr <= 97) 137 { 138 color = FOREGROUND_INTENSITY; 139 attr -= 60; 140 } 141 attr -= 30; 142 color |= (attr & 1 ? FOREGROUND_RED : 0) | (attr & 2 ? FOREGROUND_GREEN : 0) | (attr & 4 ? FOREGROUND_BLUE : 0); 143 setForegroundColor(color); 144 } 145 146 if ( (40 <= attr && attr <= 47) || (100 <= attr && attr <= 107) ) 147 { 148 WORD color = 0; 149 if (100 <= attr && attr <= 107) 150 { 151 color = BACKGROUND_INTENSITY; 152 attr -= 60; 153 } 154 attr -= 40; 155 color |= (attr & 1 ? BACKGROUND_RED : 0) | (attr & 2 ? BACKGROUND_GREEN : 0) | (attr & 4 ? BACKGROUND_BLUE : 0); 156 setBackgroundColor(color); 157 } 158 } 159 } 160 } 161 }