#include "stdafx.h" #include "Console.h" #include "ConsoleStream.h" namespace System { // This holds the global std handle for the input stream __ConsoleInit Console::ConsoleInstance /*= __ConsoleInit()*/; enum class ControlKeyState { RightAltPressed = 0x0001, LeftAltPressed = 0x0002, RightCtrlPressed = 0x0004, LeftCtrlPressed = 0x0008, ShiftPressed = 0x0010, NumLockOn = 0x0020, ScrollLockOn = 0x0040, CapsLockOn = 0x0080, EnhancedKey = 0x0100 }; void Console::Beep() { ::Beep(800, 200); } void Console::Beep(uint32_t Frequency, uint32_t Duration) { ::Beep(Frequency, Duration); } void Console::Clear() { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFO cSBI{}; GetConsoleScreenBufferInfo(hStdOut, &cSBI); COORD cScreen{}; int conSize = (cSBI.dwSize.X * cSBI.dwSize.Y); DWORD nCellsWritten = 0; FillConsoleOutputCharacterA(hStdOut, ' ', conSize, cScreen, &nCellsWritten); nCellsWritten = 0; FillConsoleOutputAttribute(hStdOut, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED, conSize, cScreen, &nCellsWritten); SetConsoleCursorPosition(hStdOut, cScreen); } void Console::ClearLine() { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFO cSBI{}; GetConsoleScreenBufferInfo(hStdOut, &cSBI); cSBI.dwCursorPosition.X = 0; DWORD nCellsWritten = 0; FillConsoleOutputCharacterA(hStdOut, ' ', cSBI.dwSize.X, cSBI.dwCursorPosition, &nCellsWritten); SetConsoleCursorPosition(hStdOut, cSBI.dwCursorPosition); } void Console::ResetColor() { Console::SetColors(ConsoleColor::Gray, ConsoleColor::Black); } void Console::SetForegroundColor(ConsoleColor Color) { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFO cSBI{}; GetConsoleScreenBufferInfo(hStdOut, &cSBI); auto nColor = Console::ConsoleColorToColorAttribute(Color, false); int16_t Attrs = cSBI.wAttributes; Attrs &= ~((int16_t)Console::ForegroundMask); Attrs = (int16_t)(((uint32_t)(uint16_t)Attrs) | ((uint32_t)(uint16_t)nColor)); SetConsoleTextAttribute(hStdOut, Attrs); } void Console::SetBackgroundColor(ConsoleColor Color) { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFO cSBI{}; GetConsoleScreenBufferInfo(hStdOut, &cSBI); auto nColor = Console::ConsoleColorToColorAttribute(Color, true); int16_t Attrs = cSBI.wAttributes; Attrs &= ~((int16_t)Console::BackgroundMask); Attrs = (int16_t)(((uint32_t)(uint16_t)Attrs) | ((uint32_t)(uint16_t)nColor)); SetConsoleTextAttribute(hStdOut, Attrs); } void Console::SetColors(ConsoleColor Foreground, ConsoleColor Background) { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFO cSBI{}; GetConsoleScreenBufferInfo(hStdOut, &cSBI); auto nColor = Console::ConsoleColorToColorAttribute(Foreground, false); auto nColor2 = Console::ConsoleColorToColorAttribute(Background, true); int16_t Attrs = cSBI.wAttributes; Attrs &= ~((int16_t)Console::ForegroundMask); Attrs = (int16_t)(((uint32_t)(uint16_t)Attrs) | ((uint32_t)(uint16_t)nColor)); Attrs &= ~((int16_t)Console::BackgroundMask); Attrs = (int16_t)(((uint32_t)(uint16_t)Attrs) | ((uint32_t)(uint16_t)nColor2)); SetConsoleTextAttribute(hStdOut, Attrs); } void Console::SetBufferSize(uint32_t Width, uint32_t Height) { COORD cScreen; cScreen.X = (SHORT)Width; cScreen.Y = (SHORT)Height; SetConsoleScreenBufferSize(((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(), cScreen); } void Console::SetCursorPosition(uint32_t Left, uint32_t Right) { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); COORD cScreen; cScreen.X = (SHORT)Left; cScreen.Y = (SHORT)Right; SetConsoleCursorPosition(hStdOut, cScreen); } void Console::SetWindowSize(uint32_t Width, uint32_t Height) { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFO cSBI{}; GetConsoleScreenBufferInfo(hStdOut, &cSBI); COORD cScreen; cScreen.X = cSBI.dwSize.X; cScreen.Y = cSBI.dwSize.Y; bool nBufferResize = false; if (cSBI.dwSize.X < (SHORT)(cSBI.srWindow.Left + Width)) { cScreen.X = (SHORT)(cSBI.srWindow.Left + Width); nBufferResize = true; } if (cSBI.dwSize.Y < (SHORT)(cSBI.srWindow.Top + Height)) { cScreen.Y = (SHORT)(cSBI.srWindow.Top + Height); nBufferResize = true; } if (nBufferResize) SetConsoleScreenBufferSize(hStdOut, cScreen); SMALL_RECT srWindow = cSBI.srWindow; srWindow.Bottom = (SHORT)(srWindow.Top + Height - 1); srWindow.Right = (SHORT)(srWindow.Left + Width - 1); auto hResult = SetConsoleWindowInfo(hStdOut, true, &srWindow); if (!hResult) SetConsoleScreenBufferSize(hStdOut, cSBI.dwSize); } void Console::SetWindowPosition(uint32_t Left, uint32_t Top) { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFO cSBI{}; GetConsoleScreenBufferInfo(hStdOut, &cSBI); SMALL_RECT srWindow = cSBI.srWindow; srWindow.Bottom -= (SHORT)(srWindow.Top - Top); srWindow.Right -= (SHORT)(srWindow.Left - Left); srWindow.Left = (SHORT)Left; srWindow.Top = (SHORT)Top; SetConsoleWindowInfo(hStdOut, TRUE, &srWindow); } void Console::SetFontSize(uint32_t Width, uint32_t Height, uint32_t Weight) { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_FONT_INFOEX cFont; cFont.cbSize = sizeof(cFont); GetCurrentConsoleFontEx(hStdOut, false, &cFont); cFont.dwFontSize.X = Width; cFont.dwFontSize.Y = Height; cFont.FontWeight = Weight; SetCurrentConsoleFontEx(hStdOut, false, &cFont); } void Console::SetTitle(const String& Value) { SetConsoleTitleA((const char*)Value); } void Console::SetTitle(const char* Value) { SetConsoleTitleA(Value); } void Console::SetCursorVisible(bool Visible) { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_CURSOR_INFO cCI{}; GetConsoleCursorInfo(hStdOut, &cCI); cCI.bVisible = Visible; SetConsoleCursorInfo(hStdOut, &cCI); } void Console::MoveBufferArea(uint32_t SourceLeft, uint32_t SourceTop, uint32_t SourceWidth, uint32_t SourceHeight, uint32_t TargetLeft, uint32_t TargetTop, char SourceChar, ConsoleColor SourceForeColor, ConsoleColor SourceBackColor) { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFO cSBI{}; GetConsoleScreenBufferInfo(hStdOut, &cSBI); COORD BufferSize = cSBI.dwSize; COORD BufferCoord{}; SMALL_RECT ReadRegion{}; if (SourceWidth == 0 || SourceHeight == 0) return; auto CharBuffer = std::make_unique(SourceWidth * SourceHeight); BufferSize.X = (SHORT)SourceWidth; BufferSize.Y = (SHORT)SourceHeight; ReadRegion.Left = (SHORT)SourceLeft; ReadRegion.Right = (SHORT)(SourceLeft + SourceWidth - 1); ReadRegion.Top = (SHORT)SourceTop; ReadRegion.Bottom = (SHORT)(SourceTop + SourceHeight - 1); ReadConsoleOutput(hStdOut, CharBuffer.get(), BufferSize, BufferCoord, &ReadRegion); COLORREF NativeColor = (Console::ConsoleColorToColorAttribute(SourceBackColor, true) | Console::ConsoleColorToColorAttribute(SourceForeColor, false)); DWORD nWrite = 0; COORD WriteCoord{}; WriteCoord.X = (SHORT)SourceLeft; SHORT Attr = (SHORT)NativeColor; for (uint32_t i = SourceTop; i < (SourceTop + SourceHeight); i++) { WriteCoord.Y = (SHORT)i; FillConsoleOutputCharacterA(hStdOut, SourceChar, SourceWidth, WriteCoord, &nWrite); FillConsoleOutputAttribute(hStdOut, Attr, SourceWidth, WriteCoord, &nWrite); } SMALL_RECT WriteRegion{}; WriteRegion.Left = (SHORT)TargetLeft; WriteRegion.Right = (SHORT)(TargetLeft + SourceWidth); WriteRegion.Top = (SHORT)TargetTop; WriteRegion.Bottom = (SHORT)(TargetTop + SourceHeight); WriteConsoleOutput(hStdOut, CharBuffer.get(), BufferSize, BufferCoord, &WriteRegion); } void Console::SetMaximizeBoxVisible(bool Visible) { auto hConsole = GetConsoleWindow(); auto Style = GetWindowLong(hConsole, GWL_STYLE); if (Visible) Style |= WS_MAXIMIZEBOX; else Style &= ~WS_MAXIMIZEBOX; SetWindowLong(hConsole, GWL_STYLE, Style); } void Console::SetMinimizeBoxVisible(bool Visible) { auto hConsole = GetConsoleWindow(); auto Style = GetWindowLong(hConsole, GWL_STYLE); if (Visible) Style |= WS_MINIMIZEBOX; else Style &= ~WS_MINIMIZEBOX; SetWindowLong(hConsole, GWL_STYLE, Style); } void Console::SetWindowResizable(bool Resizable) { auto hConsole = GetConsoleWindow(); auto Style = GetWindowLong(hConsole, GWL_STYLE); if (Resizable) Style |= WS_SIZEBOX; else Style &= ~WS_SIZEBOX; SetWindowLong(hConsole, GWL_STYLE, Style); } void Console::CenterWindow() { auto hConsole = GetConsoleWindow(); auto hMonitor = MonitorFromWindow(hConsole, MONITOR_DEFAULTTONEAREST); if (hMonitor) { MONITORINFO Info; Info.cbSize = sizeof(Info); if (GetMonitorInfo(hMonitor, &Info)) { RECT cRect{}; GetWindowRect(hConsole, &cRect); auto Width = (cRect.right - cRect.left); auto Height = (cRect.bottom - cRect.top); auto X = (Info.rcWork.left + Info.rcWork.right) / 2 - Width / 2; auto Y = (Info.rcWork.top + Info.rcWork.bottom) / 2 - Height / 2; SetWindowPos(hConsole, NULL, X, Y, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE); } } } void Console::RemapConsoleColor(ConsoleColor Source, uint8_t R, uint8_t G, uint8_t B) { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFOEX cBuffer; cBuffer.cbSize = sizeof(cBuffer); GetConsoleScreenBufferInfoEx(hStdOut, &cBuffer); // GetConsoleScreenBufferInfoEx returns one short here, so we keep resizing each time... cBuffer.srWindow.Bottom++; cBuffer.srWindow.Right++; cBuffer.ColorTable[(int8_t)Source] = RGB(R, G, B); SetConsoleScreenBufferInfoEx(hStdOut, &cBuffer); } void Console::RemapAllConsoleColors(std::initializer_list Colors) { static_assert(sizeof(Colors) == 16, "You must specify all 16 colors"); auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFOEX cBuffer; cBuffer.cbSize = sizeof(cBuffer); GetConsoleScreenBufferInfoEx(hStdOut, &cBuffer); // GetConsoleScreenBufferInfoEx returns one short here, so we keep resizing each time... cBuffer.srWindow.Bottom++; cBuffer.srWindow.Right++; std::memcpy(cBuffer.ColorTable, Colors.begin(), sizeof(uint32_t) * 16); SetConsoleScreenBufferInfoEx(hStdOut, &cBuffer); } String Console::GetTitle() { char Buffer[2048]{}; GetConsoleTitleA(Buffer, 2048); // Honestly it would be too big at 256... return String(Buffer); } bool Console::GetCursorVisible() { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_CURSOR_INFO cCI{}; GetConsoleCursorInfo(hStdOut, &cCI); return cCI.bVisible; } ConsoleColor Console::GetForegroundColor() { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFO cSBI{}; GetConsoleScreenBufferInfo(hStdOut, &cSBI); return Console::ColorAttributeToConsoleColor((int16_t)(cSBI.wAttributes & Console::ForegroundMask)); } ConsoleColor Console::GetBackgroundColor() { auto hStdOut = ((IO::ConsoleStream*)ConsoleInstance.Out.GetBaseStream())->GetStreamHandle(); CONSOLE_SCREEN_BUFFER_INFO cSBI{}; GetConsoleScreenBufferInfo(hStdOut, &cSBI); return Console::ColorAttributeToConsoleColor((int16_t)(cSBI.wAttributes & Console::BackgroundMask)); } void Console::Header(const char* Heading, ConsoleColor Color) { Console::SetColors(Color, ConsoleColor::DarkGray); auto sLen = strlen(Heading); char HeaderBuffer[21]; std::memset(HeaderBuffer + 1, ' ', 19); HeaderBuffer[0] = '['; HeaderBuffer[20] = (char)0; HeaderBuffer[sLen + 1] = ']'; std::memcpy(HeaderBuffer + 1, Heading, sLen); ConsoleInstance.Out.Write((const char*)HeaderBuffer); Console::ResetColor(); ConsoleInstance.Out.Write(": "); } void Console::Progress(const char* Heading, ConsoleColor Color, uint32_t Width, uint32_t Progress) { Progress = min(Progress, 100); ConsoleInstance.Out.Write("\r"); Console::Header(Heading, Color); String Buffer(Width + 2, '\0', false); Buffer.Append("["); uint32_t Blocks = min((uint32_t)((Progress / 100.f) * Width), Width); for (uint32_t i = 0; i < Width; i++) { if (i < Blocks) Buffer.Append("="); else if (i == Blocks) Buffer.Append(">"); else Buffer.Append(" "); } Buffer.Append("]"); ConsoleInstance.Out.Write(Buffer); } int32_t Console::Read() { return ConsoleInstance.In.Read(); } ConsoleKeyInfo Console::ReadKey(bool Intercept) { INPUT_RECORD iRecord{}; DWORD rRead = 0; auto hStdIn = ((IO::ConsoleStream*)ConsoleInstance.In.GetBaseStream())->GetStreamHandle(); while (true) { ReadConsoleInputA(hStdIn, &iRecord, 1, &rRead); int16_t kCode = iRecord.Event.KeyEvent.wVirtualKeyCode; if (!IsKeyDownEvent(iRecord)) if (kCode != AltVKCode) continue; char Ch = (char)iRecord.Event.KeyEvent.uChar.AsciiChar; if (Ch == 0) if (IsModKey(iRecord)) continue; ConsoleKey key = (ConsoleKey)kCode; if (IsAltKeyDown(iRecord) && ((key >= ConsoleKey::NumPad0 && key <= ConsoleKey::NumPad9) || (key == ConsoleKey::Clear) || (key == ConsoleKey::Insert) || (key >= ConsoleKey::PageUp && key <= ConsoleKey::DownArrow))) continue; // We passed all checks break; } // Calculate key status auto State = iRecord.Event.KeyEvent.dwControlKeyState; bool Shift = (State & (uint32_t)ControlKeyState::ShiftPressed) != 0; bool Alt = (State & ((uint32_t)ControlKeyState::LeftAltPressed | (uint32_t)ControlKeyState::RightAltPressed)) != 0; bool Control = (State & ((uint32_t)ControlKeyState::LeftCtrlPressed | (uint32_t)ControlKeyState::RightCtrlPressed)) != 0; if (!Intercept) { char iBuffer[] = { iRecord.Event.KeyEvent.uChar.AsciiChar, 0 }; Console::Write(iBuffer); } return ConsoleKeyInfo((char)iRecord.Event.KeyEvent.uChar.AsciiChar, (ConsoleKey)iRecord.Event.KeyEvent.wVirtualKeyCode, Shift, Alt, Control); } String Console::ReadLine() { return ConsoleInstance.In.ReadLine(); } bool Console::IsKeyDownEvent(INPUT_RECORD iRecord) { return (iRecord.EventType == KEY_EVENT && iRecord.Event.KeyEvent.bKeyDown); } bool Console::IsModKey(INPUT_RECORD iRecord) { int16_t keyCode = iRecord.Event.KeyEvent.wVirtualKeyCode; return ((keyCode >= 0x10 && keyCode <= 0x12) || keyCode == 0x14 || keyCode == 0x90 || keyCode == 0x91); } bool Console::IsAltKeyDown(INPUT_RECORD iRecord) { return ((iRecord.Event.KeyEvent.dwControlKeyState) & ((uint32_t)ControlKeyState::LeftAltPressed | (uint32_t)ControlKeyState::RightAltPressed)) != 0; } ConsoleColor Console::ColorAttributeToConsoleColor(int16_t Attribute) { if ((Attribute & Console::BackgroundMask) != 0) Attribute = (int32_t)(((int32_t)Attribute) >> 4); return ConsoleColor(Attribute); } int16_t Console::ConsoleColorToColorAttribute(ConsoleColor Color, bool isBackground) { auto Result = (int16_t)Color; if (isBackground) Result = (int16_t)((int32_t)Result << 4); return Result; } }