module std.dcl.console; import std.stdio; import std.string; debug = Off; //---------------------------------------------------------------------------- version (Windows) { import std.c.windows.windows; extern (Windows) static { // WinAPI Constants DWORD STD_INPUT_HANDLE = -10; DWORD STD_OUTPUT_HANDLE = -11; DWORD STD_ERROR_HANDLE = -12; // WinAPI Functions export { HANDLE GetStdHandle(DWORD nStdHandle); BOOL SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes); } } // extern (Windows) static } // version (Windows) //---------------------------------------------------------------------------- struct Console { static: // Singleton public: // Constants enum ANSIColor { Black, Red, Green, Yellow, Blue, Magenta, Cyan, White, DarkGray, BrightRed, BrightGreen, BrightYellow, BrightBlue, BrightMagenta, BrightCyan, BrightWhite, } const int Normal = 0x0000; const int Bright = 0x0008; version (Windows) { enum Win32Color { Black, Blue, Green, Yellow, Red, Cyan, Magenta, White, DarkGray, BrightBlue, BrightGreen, BrightYellow, BrightRed, BrightCyan, BrightMagenta, BrightWhite, } const Win32Color[16] ANSIToWin32ColorMap = [ Win32Color.Black, Win32Color.Red, Win32Color.Green, Win32Color.Yellow, Win32Color.Blue, Win32Color.Magenta, Win32Color.Cyan, Win32Color.White, Win32Color.DarkGray, Win32Color.BrightRed, Win32Color.BrightGreen, Win32Color.BrightYellow, Win32Color.BrightBlue, Win32Color.BrightMagenta, Win32Color.BrightCyan, Win32Color.BrightWhite, ]; public: // Interface void SetColor(Win32Color color) { debug (Debug) writef("color: " ~ toString(cast(int)(color))); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color); fflush(stdout); } void SetColor(Win32Color foreground, Win32Color background) { SetColor((foreground_ = foreground) | (background_ = background) << 4); } void Write(char[] text) { WriteImpl!(writef)(text); } void WriteLine(char[] text) { WriteImpl!(writefln)(text); } private: // Implementation template WriteImpl(alias writeFunc) { void WriteImpl(char[] text) { enum State { Start, Escape, CSI }; State state = State.Start; char[1024] buffer; int parameter; int[] paramList; int i = 0; foreach (char c; text) { switch (state) { case State.Start: if (c == '\x1B') { state = State.Escape; break; } buffer[i++] = c; if (i > 1000) { writef(buffer[0..i]); i = 0; } break; case State.Escape: if (c == '[') { state = State.CSI; } else { state = State.Start; buffer[i++] = '\x1B'; buffer[i++] = c; } break; case State.CSI: switch (c) { case ';': paramList ~= parameter; parameter = 0; break; case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': parameter = parameter * 10 + c - '0'; break; case 'm': writef(buffer[0..i]); i = 0; paramList ~= parameter; debug (Verbose) writef("Colors( "); foreach (int color; paramList) { debug (Verbose) writef(toString(color) ~ ' '); if (color == 0) { intensity_ = Normal; foreground_ = Win32Color.White; background_ = Win32Color.Black; } else if (color == 1) { intensity_ = Bright; } else if (color >= 30 && color < 38) foreground_ = ANSIToWin32ColorMap[(color - 30) | intensity_]; else if (color >= 40 && color < 48) background_ = ANSIToWin32ColorMap[color - 40]; } debug (Verbose) writef(") "); SetColor(foreground_, background_); state = State.Start; paramList.length = 0; parameter = 0; break; default: // Unsupported or malformed escape sequence state = State.Start; } } } writeFunc(buffer[0..i]); } } // template WriteImpl private: // Implementation int intensity_ = Normal; Win32Color foreground_ = Win32Color.White; Win32Color background_ = Win32Color.Black; } else // version (Windows) { public: // Interface void Write(char[] text) { writef(text); } void WriteLine(char[] text) { writefln(text); } } // version (Windows) } //---------------------------------------------------------------------------- unittest { for (int f = 30; f != 38; ++f) for (int b = 40; b != 48; ++b) Console.Write( "\x1B[" ~ toString(f) ~ ';' ~ toString(b) ~ 'm' ~ "Hello, world!" ); Console.WriteLine("\x1B[0;1m"); for (int f = 30; f != 38; ++f) for (int b = 40; b != 48; ++b) Console.Write( "\x1B[" ~ toString(f) ~ ';' ~ toString(b) ~ 'm' ~ "Hello, world!" ); Console.Write("\x1B[m"); }