// Copyright 2013 Dolphin Emulator Project // Licensed under GPLv2 // Refer to the license.txt file included. #include #ifdef _WIN32 #include #include #endif #include "common/common.h" #include "common/log_manager.h" // Common #include "common/console_listener.h" // Common ConsoleListener::ConsoleListener() { #ifdef _WIN32 hConsole = nullptr; bUseColor = true; #else bUseColor = isatty(fileno(stdout)); #endif } ConsoleListener::~ConsoleListener() { Close(); } // 100, 100, "Dolphin Log Console" // Open console window - width and height is the size of console window // Name is the window title void ConsoleListener::Open(bool Hidden, int Width, int Height, const char *Title) { #ifdef _WIN32 if (!GetConsoleWindow()) { // Open the console window and create the window handle for GetStdHandle() AllocConsole(); // Hide if (Hidden) ShowWindow(GetConsoleWindow(), SW_HIDE); // Save the window handle that AllocConsole() created hConsole = GetStdHandle(STD_OUTPUT_HANDLE); // Set the console window title SetConsoleTitle(Common::UTF8ToTStr(Title).c_str()); // Set letter space LetterSpace(80, 4000); //MoveWindow(GetConsoleWindow(), 200,200, 800,800, true); } else { hConsole = GetStdHandle(STD_OUTPUT_HANDLE); } #endif } void ConsoleListener::UpdateHandle() { #ifdef _WIN32 hConsole = GetStdHandle(STD_OUTPUT_HANDLE); #endif } // Close the console window and close the eventual file handle void ConsoleListener::Close() { #ifdef _WIN32 if (hConsole == nullptr) return; FreeConsole(); hConsole = nullptr; #else fflush(nullptr); #endif } bool ConsoleListener::IsOpen() { #ifdef _WIN32 return (hConsole != nullptr); #else return true; #endif } /* LetterSpace: SetConsoleScreenBufferSize and SetConsoleWindowInfo are dependent on each other, that's the reason for the additional checks. */ void ConsoleListener::BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst) { #ifdef _WIN32 BOOL SB, SW; if (BufferFirst) { // Change screen buffer size COORD Co = {BufferWidth, BufferHeight}; SB = SetConsoleScreenBufferSize(hConsole, Co); // Change the screen buffer window size SMALL_RECT coo = {0,0,ScreenWidth, ScreenHeight}; // top, left, right, bottom SW = SetConsoleWindowInfo(hConsole, TRUE, &coo); } else { // Change the screen buffer window size SMALL_RECT coo = {0,0, ScreenWidth, ScreenHeight}; // top, left, right, bottom SW = SetConsoleWindowInfo(hConsole, TRUE, &coo); // Change screen buffer size COORD Co = {BufferWidth, BufferHeight}; SB = SetConsoleScreenBufferSize(hConsole, Co); } #endif } void ConsoleListener::LetterSpace(int Width, int Height) { #ifdef _WIN32 // Get console info CONSOLE_SCREEN_BUFFER_INFO ConInfo; GetConsoleScreenBufferInfo(hConsole, &ConInfo); // int OldBufferWidth = ConInfo.dwSize.X; int OldBufferHeight = ConInfo.dwSize.Y; int OldScreenWidth = (ConInfo.srWindow.Right - ConInfo.srWindow.Left); int OldScreenHeight = (ConInfo.srWindow.Bottom - ConInfo.srWindow.Top); // int NewBufferWidth = Width; int NewBufferHeight = Height; int NewScreenWidth = NewBufferWidth - 1; int NewScreenHeight = OldScreenHeight; // Width BufferWidthHeight(NewBufferWidth, OldBufferHeight, NewScreenWidth, OldScreenHeight, (NewBufferWidth > OldScreenWidth-1)); // Height BufferWidthHeight(NewBufferWidth, NewBufferHeight, NewScreenWidth, NewScreenHeight, (NewBufferHeight > OldScreenHeight-1)); // Resize the window too //MoveWindow(GetConsoleWindow(), 200,200, (Width*8 + 50),(NewScreenHeight*12 + 200), true); #endif } #ifdef _WIN32 COORD ConsoleListener::GetCoordinates(int BytesRead, int BufferWidth) { COORD Ret = {0, 0}; // Full rows int Step = (int)floor((float)BytesRead / (float)BufferWidth); Ret.Y += Step; // Partial row Ret.X = BytesRead - (BufferWidth * Step); return Ret; } #endif void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool Resize) { #ifdef _WIN32 // Check size if (Width < 8 || Height < 12) return; bool DBef = true; bool DAft = true; std::string SLog = ""; const HWND hWnd = GetConsoleWindow(); const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); // Get console info CONSOLE_SCREEN_BUFFER_INFO ConInfo; GetConsoleScreenBufferInfo(hConsole, &ConInfo); DWORD BufferSize = ConInfo.dwSize.X * ConInfo.dwSize.Y; // --------------------------------------------------------------------- // Save the current text // ------------------------ DWORD cCharsRead = 0; COORD coordScreen = { 0, 0 }; static const int MAX_BYTES = 1024 * 16; std::vector> Str; std::vector> Attr; // ReadConsoleOutputAttribute seems to have a limit at this level static const int ReadBufferSize = MAX_BYTES - 32; DWORD cAttrRead = ReadBufferSize; DWORD BytesRead = 0; while (BytesRead < BufferSize) { Str.resize(Str.size() + 1); if (!ReadConsoleOutputCharacter(hConsole, Str.back().data(), ReadBufferSize, coordScreen, &cCharsRead)) SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error"); Attr.resize(Attr.size() + 1); if (!ReadConsoleOutputAttribute(hConsole, Attr.back().data(), ReadBufferSize, coordScreen, &cAttrRead)) SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error"); // Break on error if (cAttrRead == 0) break; BytesRead += cAttrRead; coordScreen = GetCoordinates(BytesRead, ConInfo.dwSize.X); } // Letter space int LWidth = (int)(floor((float)Width / 8.0f) - 1.0f); int LHeight = (int)(floor((float)Height / 12.0f) - 1.0f); int LBufWidth = LWidth + 1; int LBufHeight = (int)floor((float)BufferSize / (float)LBufWidth); // Change screen buffer size LetterSpace(LBufWidth, LBufHeight); ClearScreen(true); coordScreen.Y = 0; coordScreen.X = 0; DWORD cCharsWritten = 0; int BytesWritten = 0; DWORD cAttrWritten = 0; for (size_t i = 0; i < Attr.size(); i++) { if (!WriteConsoleOutputCharacter(hConsole, Str[i].data(), ReadBufferSize, coordScreen, &cCharsWritten)) SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error"); if (!WriteConsoleOutputAttribute(hConsole, Attr[i].data(), ReadBufferSize, coordScreen, &cAttrWritten)) SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error"); BytesWritten += cAttrWritten; coordScreen = GetCoordinates(BytesWritten, LBufWidth); } const int OldCursor = ConInfo.dwCursorPosition.Y * ConInfo.dwSize.X + ConInfo.dwCursorPosition.X; COORD Coo = GetCoordinates(OldCursor, LBufWidth); SetConsoleCursorPosition(hConsole, Coo); if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str()); // Resize the window too if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true); #endif } void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text) { #if defined(_WIN32) WORD Color; switch (Level) { case OS_LEVEL: // light yellow Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; case NOTICE_LEVEL: // light green Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; case ERROR_LEVEL: // light red Color = FOREGROUND_RED | FOREGROUND_INTENSITY; break; case WARNING_LEVEL: // light purple Color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; case INFO_LEVEL: // cyan Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; case DEBUG_LEVEL: // gray Color = FOREGROUND_INTENSITY; break; default: // off-white Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; } SetConsoleTextAttribute(hConsole, Color); printf(Text); #else char ColorAttr[16] = ""; char ResetAttr[16] = ""; if (bUseColor) { strcpy(ResetAttr, "\033[0m"); switch (Level) { case NOTICE_LEVEL: // light green strcpy(ColorAttr, "\033[92m"); break; case ERROR_LEVEL: // light red strcpy(ColorAttr, "\033[91m"); break; case WARNING_LEVEL: // light yellow strcpy(ColorAttr, "\033[93m"); break; default: break; } } fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr); #endif } // Clear console screen void ConsoleListener::ClearScreen(bool Cursor) { #if defined(_WIN32) COORD coordScreen = { 0, 0 }; DWORD cCharsWritten; CONSOLE_SCREEN_BUFFER_INFO csbi; DWORD dwConSize; HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(hConsole, &csbi); dwConSize = csbi.dwSize.X * csbi.dwSize.Y; // Write space to the entire console FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten); GetConsoleScreenBufferInfo(hConsole, &csbi); FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten); // Reset cursor if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen); #endif }