AuroraRuntime/Source/Console/ConsoleAsioStd/ConsoleAsioStd.cpp

196 lines
5.5 KiB
C++
Raw Normal View History

2021-06-27 21:25:29 +00:00
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: ConsoleAsioStd.cpp
Date: 2021-6-8
Author: Reece
***/
#include <RuntimeInternal.hpp>
#include "ConsoleAsioStd.hpp"
namespace Aurora::Console::ConsoleAsioStd
{
#if defined(AURORA_PLATFORM_WIN32) || defined(AURORA_PLATFORM_LINUX) || defined(AURORA_PLATFORM_APPLE)
#if defined(AURORA_PLATFORM_WIN32)
using StreamDescriptor_t = asio::windows::stream_handle;
#else
using StreamDescriptor_t = asio::posix::stream_descriptor;
#endif
static const AuMach kLineBufferMax = 2048;
static AuUInt8 gLineBuffer[kLineBufferMax] = { 0 };
static AuString gLineString = "";
static StreamDescriptor_t *gInputStream = {};
static StreamDescriptor_t *gOutputStream = {};
static asio::io_context gIOC;
void Init()
{
#if defined(AURORA_PLATFORM_WIN32)
if (GetConsoleWindow() == NULL)
{
if (!gRuntimeConfig.console.forceConsoleWindow)
{
return;
}
if (gRuntimeConfig.console.disableAll)
{
return;
}
auto ok = AllocConsole();
SysAssert(ok, "Request of a Win32 console yielded nada, forceConsole wasn't respected");
}
else
{
// no always means no under UNIX targets; but on windows, if you're compiling a console app, dont be surprised to find console output
// instead of attempting to detach from conhost, link as a WindowedApp and do not setup any windows, if you're looking for windowless
// ignore gRuntimeConfig.console.disableAll
}
DWORD dwMode;
bool ok;
auto hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
SysAssert(hOutput != INVALID_HANDLE_VALUE, "Couldn't open STDOUT");
ok = GetConsoleMode(hOutput, &dwMode);
SysAssert(ok, "Couldn't get console mode");
dwMode |= ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
ok = SetConsoleMode(hOutput, dwMode);
SysAssert(ok, "Couldn't set console mode");
auto fileHandle = CreateFileA("CONIN$",
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
NULL);
SysAssert(fileHandle != INVALID_HANDLE_VALUE, "Couldn't open CONIN");
gInputStream = _new StreamDescriptor_t(gIOC, fileHandle);
fileHandle = CreateFileA("CONOUT$",
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
NULL);
SysAssert(fileHandle != INVALID_HANDLE_VALUE, "Couldn't open CONOUT");
gOutputStream = _new StreamDescriptor_t(gIOC, fileHandle);
#else
// no always means no under UNIX targets
// we *know* stdin/out should be available under these standards
if (gRuntimeConfig.console.disableAll)
{
return;
}
gInputStream = _new StreamDescriptor_t(gIOC, STDIN_FILENO);
gOutputStream = _new StreamDescriptor_t(gIOC, STDOUT_FILENO);
#endif
SysAssert(gInputStream, "Couldn't allocate input stream handler");
SysAssert(gOutputStream, "Couldn't allocate output stream handler");
Aurora::Console::Hooks::AddHook([](const Aurora::Console::ConsoleMessage &string) -> void
{
#if defined(DEBUG) && defined(AURORA_PLATFORM_WIN32)
auto debugLine = string.ToSimplified() + "\r\n";
OutputDebugStringA(debugLine.c_str());
#endif
auto writeLine = string.ToConsole();
#if defined(AURORA_PLATFORM_WIN32)
writeLine += '\r';
#endif
writeLine += '\n';
asio::write(*gOutputStream, asio::buffer(writeLine));
});
}
static void ProcessLines()
{
AuMach index = 0;
while ((index = gLineString.find("\n")) != AuString::npos)
{
auto line = gLineString.substr(0, index);
gLineString = gLineString.substr(index + 1);
if (line[line.size() - 1] == '\r')
{
line.pop_back();
}
if (line.size())
{
Aurora::Console::Commands::DispatchCommand(line);
}
}
}
static void StdInHandler(const asio::error_code &code, std::size_t bytesCopied)
{
if (bytesCopied == 0)
{
return;
}
gLineString.append(reinterpret_cast<const char *>(gLineBuffer), bytesCopied);
ProcessLines();
}
void Pump()
{
if (!gInputStream) return;
gInputStream->async_read_some(asio::buffer(gLineBuffer, kLineBufferMax), StdInHandler);
gIOC.poll_one();
}
void Exit()
{
if (gInputStream)
{
delete gInputStream;
}
if (gOutputStream)
{
delete gOutputStream;
}
}
#else
void Pump()
{
}
void Init()
{
}
void Exit()
{
}
#endif
}