EHS/src/io/Console.cpp
Karutoh bcd71cf2b5
All checks were successful
Build & Release / Windows-AMD64-Build (push) Successful in 1m8s
Build & Release / Linux-AMD64-Build (push) Successful in 1m30s
Build & Release / Linux-AARCH64-Build (push) Successful in 3m21s
Adjusted workflow.
2024-02-05 22:25:30 -08:00

799 lines
22 KiB
C++

#include "ehs/io/Console.h"
#include "ehs/Log.h"
#include "ehs/io/File.h"
#if defined(EHS_OS_LINUX)
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#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("Error", 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("Error", 2, "Failed to wait for console input.");
//if (!SetConsoleActiveScreenBuffer(hdlOut))
// EHS_LOG_INT("Error", 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("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + ".");
#elif defined(EHS_OS_LINUX)
int code = close(hdlOut);
if (code == -1)
EHS_LOG_INT("Error", 0, "Failed to free the console output with error #" + Str_8::FromNum(errno) + ".");
code = close(hdlIn);
if (code == -1)
EHS_LOG_INT("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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("Error", 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<Str_32> 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<Byte> data = cmdFile.ReadArray(bufferSize);
cmdFile.Release();
Vector<Str_32> 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<Str_16> 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<Byte> data = cmdFile.ReadArray(bufferSize);
cmdFile.Release();
Vector<Str_16> 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<Str_8> 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<Byte> data = cmdFile.ReadArray(bufferSize);
cmdFile.Release();
Vector<Str_8> 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("Error", 0, "Failed to retrieve native handle with error #" + Str_8::FromNum(GetLastError()) + ".");
return hdl;
#else
return nullptr;
#endif
}
*/
}