196 lines
5.5 KiB
C++
196 lines
5.5 KiB
C++
/***
|
|
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
|
|
} |