module std.dcl.console; import std.stdio; import std.string; version (Windows) { extern (Windows) static { import std.c.windows.windows; // 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) } // version(Windows) struct Console { public: // Constants enum ANSIColor { Black, Red, Green, Yellow, Blue, Magenta, Cyan, White, DarkGray, BrightRed, BrightGreen, BrightYellow, BrightBlue, BrightMagenta, BrightCyan, BrightWhite, } static const int Normal = 0x0000; static const int Bright = 0x0008; version (Windows) static { enum Win32Color { Black, Blue, Green, Yellow, Red, Cyan, Magenta, White, DarkGray, BrightBlue, BrightGreen, BrightYellow, BrightRed, BrightCyan, BrightMagenta, BrightWhite, } 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) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color); } void SetColor(Win32Color foreground, Win32Color background) { SetColor((foreground_ = foreground) | (background_ = background) << 4); } void WriteLine(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; foreach (int color; paramList) { 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]; } SetColor(foreground_, background_); state = State.Start; break; default: // Unsupported or malformed escape sequence state = State.Start; } } } writefln(buffer[0..i]); } private: // Implementation int intensity_ = Normal; Win32Color foreground_ = Win32Color.White; Win32Color background_ = Win32Color.Black; } else // version(Windows) { void WriteLine(char[] text) { writefln(text); } } // version(Windows) } unittest { for (int i = 30; i != 38; ++i) Console.WriteLine("\x1B[" ~ toString(i) ~ "mHello, world!\x1B[m"); for (int i = 40; i != 48; ++i) Console.WriteLine("\x1B[" ~ toString(i) ~ "mHello, world!\x1B[m"); Console.WriteLine("\x1B[0;1m"); for (int i = 30; i != 38; ++i) Console.WriteLine("\x1B[" ~ toString(i) ~ "mHello, world!\x1B[m"); Console.WriteLine("\x1B[mClear"); }