#include "ehs/io/Console.h" #include "ehs/Log.h" #include "ehs/io/File.h" #if defined(EHS_OS_LINUX) #include #include #include #endif namespace ehs { ConsoleHdl Console::hdlOut = 0; ConsoleHdl Console::hdlIn = 0; #if defined(EHS_OS_WINDOWS) bool Console::isConsole = true; #endif void Console::Attach() { #if defined(EHS_OS_WINDOWS) if (!AttachConsole(ATTACH_PARENT_PROCESS)) { DWORD code = GetLastError(); if (code == ERROR_INVALID_HANDLE) return; } hdlIn = GetStdHandle(STD_INPUT_HANDLE); hdlOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD mode = 0; if (!GetConsoleMode(hdlOut, &mode)) { if (GetLastError() == ERROR_INVALID_HANDLE) isConsole = false; } else { //DWORD code = WaitForSingleObject(hdlIn, 15000); //if (code == WAIT_FAILED || code == WAIT_TIMEOUT || code == WAIT_ABANDONED) // EHS_LOG_INT(LogType::ERR, 0, "Failed to wait for console input."); isConsole = true; } #elif defined(EHS_OS_LINUX) hdlOut = open("/dev/stdout", O_WRONLY | O_SYNC); hdlIn = open("/dev/stdin", O_RDONLY); #else return; #endif } bool Console::Create() { #if defined(EHS_OS_WINDOWS) if (!AllocConsole()) return false; hdlIn = GetStdHandle(STD_INPUT_HANDLE); hdlOut = GetStdHandle(STD_OUTPUT_HANDLE); if (WaitForSingleObject(hdlIn, EHS_INFINITE) == WAIT_FAILED) EHS_LOG_INT(LogType::ERR, 2, "Failed to wait for console input."); //if (!SetConsoleActiveScreenBuffer(hdlOut)) // EHS_LOG_INT(LogType::ERR, 3, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); isConsole = true; #endif return true; } void Console::Free() { #if defined(EHS_OS_WINDOWS) if (!FreeConsole()) EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); #elif defined(EHS_OS_LINUX) int code = close(hdlOut); if (code == -1) EHS_LOG_INT(LogType::ERR, 0, "Failed to free the console output with error #" + Str_8::FromNum(errno) + "."); code = close(hdlIn); if (code == -1) EHS_LOG_INT(LogType::ERR, 0, "Failed to free the console input with error #" + Str_8::FromNum(errno) + "."); #endif hdlOut = 0; hdlIn = 0; } bool Console::CanRead() { return hdlIn; } bool Console::CanWrite() { return hdlOut; } void Console::Write_32(const Str_32& str, const bool newLine) { #if defined(EHS_OS_WINDOWS) if (isConsole) { Str_16 r = UTF::To_16(str); if (newLine) r += L"\r\n"; DWORD offset = 0; do { DWORD written = 0; if (!WriteConsoleW(hdlOut, &r[offset], (DWORD)r.Size() - offset, &written, nullptr)) EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); offset += written; } while (offset < r.Size()); } else { Str_8 r = UTF::To_8(str); if (newLine) r += "\r\n"; DWORD offset = 0; do { DWORD written = 0; if (!WriteFile(hdlOut, &((Char_8*)r)[offset], (DWORD)r.Size(true) - offset, &written, nullptr)) EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); offset += written; } while (offset < r.Size(true)); } #elif defined(EHS_OS_LINUX) Str_32 result = str; if (newLine) result += U"\n"; UInt_64 offset = 0; do { ssize_t written = write(hdlOut, result, result.Size(true)); if (written == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to write to console with error #" + Str_8::FromNum(errno) + "."); return; } offset += written; } while (offset < result.Size(true)); #else return; #endif } void Console::Write_16(const Str_16& str, const bool newLine) { #if defined(EHS_OS_WINDOWS) if (isConsole) { Str_16 r = str; if (newLine) r += L"\r\n"; DWORD offset = 0; do { DWORD written = 0; if (!WriteConsoleW(hdlOut, &r[offset], (DWORD)r.Size() - offset, &written, nullptr)) EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); offset += written; } while (offset < r.Size()); } else { Str_8 r = UTF::To_8(str); if (newLine) r += "\r\n"; DWORD offset = 0; do { DWORD written = 0; if (!WriteFile(hdlOut, &((Char_8*)r)[offset], (DWORD)r.Size(true) - offset, &written, nullptr)) EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); offset += written; } while (offset < r.Size(true)); } #elif defined(EHS_OS_LINUX) Str_16 result = str; if (newLine) result += L"\n"; UInt_64 offset = 0; do { ssize_t written = write(hdlOut, result, result.Size(true)); if (written == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to write to console with error #" + Str_8::FromNum(errno) + "."); return; } offset += written; } while (offset < result.Size(true)); #endif } void Console::Write_8(const Str_8& str, const bool newLine) { #if defined(EHS_OS_WINDOWS) if (isConsole) { Str_16 r = UTF::To_16(str); if (newLine) r += L"\r\n"; DWORD offset = 0; do { DWORD written = 0; if (!WriteConsoleW(hdlOut, &r[offset], (DWORD)r.Size() - offset, &written, nullptr)) EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); offset += written; } while (offset < r.Size()); } else { Str_8 r = str; if (newLine) r += "\r\n"; DWORD offset = 0; do { DWORD written = 0; if (!WriteFile(hdlOut, &r[offset], (DWORD)r.Size() - offset, &written, nullptr)) EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); offset += written; } while (offset < r.Size()); } #elif defined(EHS_OS_LINUX) Str_8 result = str; if (newLine) result += "\n"; UInt_64 offset = 0; do { ssize_t written = write(hdlOut, result, result.Size()); if (written == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to write to console with error #" + Str_8::FromNum(errno) + "."); return; } offset += written; } while (offset < result.Size()); #endif } Str_32 Console::Read_32(const UInt_64 bufferSize) { if (!hdlIn) return U""; #if defined(EHS_OS_WINDOWS) if (isConsole) { Str_16 result; DWORD offset = 0; do { result.Resize(result.Size() + bufferSize); DWORD read = 0; if (!ReadConsoleW(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) { EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); return U""; } offset += read; } while (result[offset - 1] != L'\n'); if (offset >= 2 && result[offset - 2] == L'\r' && result[offset - 1] == L'\n') result.Resize(offset - 2); else result.Resize(offset - 1); return UTF::To_32(result); } else { Str_8 result; DWORD offset = 0; do { result.Resize(result.Size() + bufferSize); DWORD read = 0; if (!ReadFile(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) { EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); return U""; } offset += read; } while (result[offset - 1] != '\n'); if (offset >= 2 && result[offset - 2] == '\r' && result[offset - 1] == '\n') result.Resize(offset - 2); else result.Resize(offset - 1); return UTF::To_32(result); } #elif defined(EHS_OS_LINUX) Str_32 result; Str_32 input(bufferSize); ssize_t read = 0; do { read = ::read(hdlIn, input, bufferSize); if (read == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to read from console with error #" + Str_8::FromNum(errno) + "."); return result; } result.Push(input, read); } while (input[read - 1] != U'\n'); return result; #else return {}; #endif } Str_16 Console::Read_16(const UInt_64 bufferSize) { if (!hdlIn) return L""; #if defined(EHS_OS_WINDOWS) if (isConsole) { Str_16 result; DWORD offset = 0; do { result.Resize(result.Size() + bufferSize); DWORD read = 0; if (!ReadConsoleW(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) { EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); return L""; } offset += read; } while (result[offset - 1] != L'\n'); if (offset >= 2 && result[offset - 2] == L'\r' && result[offset - 1] == L'\n') result.Resize(offset - 2); else result.Resize(offset - 1); return result; } else { Str_8 result; DWORD offset = 0; do { result.Resize(result.Size() + bufferSize); DWORD read = 0; if (!ReadFile(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) { EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); return L""; } offset += read; } while (result[offset - 1] != '\n'); if (offset >= 2 && result[offset - 2] == '\r' && result[offset - 1] == '\n') result.Resize(offset - 2); else result.Resize(offset - 1); return UTF::To_16(result); } #elif defined(EHS_OS_LINUX) Str_16 result; Str_16 input(bufferSize); ssize_t read = 0; do { read = ::read(hdlIn, input, bufferSize); if (read == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to read from console with error #" + Str_8::FromNum(errno) + "."); return result; } result.Push(input, read); } while (input[read - 1] != L'\n'); return result; #else return {}; #endif } Str_8 Console::Read_8(const UInt_64 bufferSize) { if (!hdlIn) return ""; #if defined(EHS_OS_WINDOWS) if (isConsole) { Str_16 result; DWORD offset = 0; do { result.Resize(result.Size() + bufferSize); DWORD read = 0; if (!ReadConsoleW(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) { EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); return ""; } offset += read; } while (result[offset - 1] != L'\n'); if (offset >= 2 && result[offset - 2] == L'\r' && result[offset - 1] == L'\n') result.Resize(offset - 2); else result.Resize(offset - 1); return UTF::To_8(result); } else { Str_8 result; DWORD offset = 0; do { result.Resize(result.Size() + bufferSize); DWORD read = 0; if (!ReadFile(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) { EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); return ""; } offset += read; } while (result[offset - 1] != '\n'); if (offset >= 2 && result[offset - 2] == '\r' && result[offset - 1] == '\n') result.Resize(offset - 2); else result.Resize(offset - 1); return result; } #elif defined(EHS_OS_LINUX) Str_8 result; Str_8 input(bufferSize); ssize_t read = 0; do { read = ::read(hdlIn, input, bufferSize); if (read == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to read from console with error #" + Str_8::FromNum(errno) + "."); return result; } result.Push(input, read); } while (input[read - 1] != '\n'); return result.Sub(0, result.Size() - 1); #else return {}; #endif } void Console::Clear() { #if defined(EHS_OS_WINDOWS) CONSOLE_SCREEN_BUFFER_INFO info = {}; if (!GetConsoleScreenBufferInfo(hdlOut, &info)) return; DWORD size = info.dwSize.X * info.dwSize.Y; DWORD written = 0; if (!FillConsoleOutputCharacterW(hdlOut, L' ', size, {0, 0}, &written)) return; if (!GetConsoleScreenBufferInfo(hdlOut, &info)) return; if (!FillConsoleOutputAttribute(hdlOut, info.wAttributes, size, {0, 0}, &written)) return; SetConsoleCursorPosition(hdlOut, {0, 0}); #elif defined(EHS_OS_LINUX) const Char_8 code[] = "\033[2J\033[1;1H"; UInt_64 offset = 0; do { ssize_t written = write(hdlOut, code, sizeof(code)); if (written == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to clear console with error #" + Str_8::FromNum(errno) + "."); return; } offset += written; } while (offset < sizeof(code)); #endif } void Console::SetTitle_32(const Str_32& title) { #if defined(EHS_OS_WINDOWS) if (!SetConsoleTitleW(UTF::To_16(title))) EHS_LOG_INT(LogType::ERR, 0, "Failed to set console title with error #" + Str_8::FromNum(GetLastError()) + "."); #elif defined(EHS_OS_LINUX) Str_32 code = U"\033]0;" + title + U"\007"; UInt_64 offset = 0; do { ssize_t written = write(hdlOut, code, code.Size(true)); if (written == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to set console title with error #" + Str_8::FromNum(errno) + "."); return; } offset += written; } while (offset < code.Size(true)); #endif } void Console::SetTitle_16(const Str_16& title) { #if defined(EHS_OS_WINDOWS) if (!SetConsoleTitleW(title)) EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); #elif defined(EHS_OS_LINUX) Str_16 code = L"\033]0;" + title + L"\007"; UInt_64 offset = 0; do { ssize_t written = write(hdlOut, code, code.Size(true)); if (written == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to set console title with error #" + Str_8::FromNum(errno) + "."); return; } offset += written; } while (offset < code.Size(true)); #endif } void Console::SetTitle_8(const Str_8& title) { #if defined(EHS_OS_WINDOWS) if (!SetConsoleTitleW(UTF::To_16(title))) EHS_LOG_INT(LogType::ERR, 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); #elif defined(EHS_OS_LINUX) Str_8 code = "\033]0;" + title + "\007"; UInt_64 offset = 0; do { ssize_t written = write(hdlOut, code, code.Size()); if (written == -1) { EHS_LOG_INT(LogType::ERR, 0, "Failed to set console title with error #" + Str_8::FromNum(errno) + "."); return; } offset += written; } while (offset < code.Size()); #endif } Str_32 Console::GetTitle_32() { #if defined(EHS_OS_WINDOWS) Str_16 title(EHS_MAX_PATH); DWORD size = GetConsoleTitleW(&title[0], EHS_MAX_PATH); title.Resize(size); return UTF::To_32(title); #else return {}; #endif } Str_16 Console::GetTitle_16() { #if defined(EHS_OS_WINDOWS) Str_16 title(EHS_MAX_PATH); DWORD size = GetConsoleTitleW(&title[0], EHS_MAX_PATH); title.Resize(size); return title; #else return {}; #endif } Str_8 Console::GetTitle_8() { #if defined(EHS_OS_WINDOWS) Str_16 title(EHS_MAX_PATH); DWORD size = GetConsoleTitleW(&title[0], EHS_MAX_PATH); title.Resize(size); return UTF::To_8(title); #else return {}; #endif } Vector Console::GetArgs_32(const UInt_64 bufferSize) { #if defined(EHS_OS_WINDOWS) return UTF::To_32(GetCommandLineW()).Split(U" "); #elif defined(EHS_OS_LINUX) File cmdFile("/proc/self/cmdline", Mode::READ, Disposition::OPEN); Array data = cmdFile.ReadArray(bufferSize); cmdFile.Release(); Vector args; UInt_64 o = 0; for (UInt_64 i = 0; i < data.Size() - 1; i += sizeof(Char_8)) { if (data[i] != '\0') continue; args.Push(UTF::To_32((Char_8*)&data[o], i - o)); o = i + sizeof(Char_8); } if (o < data.Size()) args.Push(UTF::To_32((Char_8*)&data[o], data.Size() - o - 1)); return args; #else return {}; #endif } Vector Console::GetArgs_16(const UInt_64 bufferSize) { #if defined(EHS_OS_WINDOWS) return Str_16(GetCommandLineW()).Split(L" "); #elif defined(EHS_OS_LINUX) File cmdFile("/proc/self/cmdline", Mode::READ, Disposition::OPEN); Array data = cmdFile.ReadArray(bufferSize); cmdFile.Release(); Vector args; UInt_64 o = 0; for (UInt_64 i = 0; i < data.Size() - 1; i += sizeof(Char_8)) { if (data[i] != '\0') continue; args.Push(UTF::To_16((Char_8*)&data[o], i - o)); o = i + sizeof(Char_8); } if (o < data.Size()) args.Push(UTF::To_16((Char_8*)&data[o], data.Size() - o - 1)); return args; #else return {}; #endif } Vector Console::GetArgs_8(const UInt_64 bufferSize) { #if defined(EHS_OS_WINDOWS) return UTF::To_8(GetCommandLineW()).Split(" "); #elif defined(EHS_OS_LINUX) File cmdFile("/proc/self/cmdline", Mode::READ, Disposition::OPEN); Array data = cmdFile.ReadArray(bufferSize); cmdFile.Release(); Vector args; UInt_64 o = 0; for (UInt_64 i = 0; i < data.Size() - 1; i += sizeof(Char_8)) { if (data[i] != '\0') continue; args.Push(Str_8((Char_8*)&data[o], i - o)); o = i + sizeof(Char_8); } if (o < data.Size()) args.Push(Str_8((Char_8*)&data[o], data.Size() - o - 1)); return args; #else return {}; #endif } /* void* Console::GetHandle() { #if defined(EHS_OS_WINDOWS) void* hdl = FindWindowW(nullptr, GetTitle_16()); if (hdl == nullptr) EHS_LOG_INT(LogType::ERR, 0, "Failed to retrieve native handle with error #" + Str_8::FromNum(GetLastError()) + "."); return hdl; #else return nullptr; #endif } */ }