mirror of
https://github.com/geode-sdk/geode.git
synced 2025-02-17 00:30:26 -05:00
Merge branch 'main' of https://github.com/geode-sdk/geode into main
This commit is contained in:
commit
b0e5588419
9 changed files with 246 additions and 162 deletions
|
@ -109,7 +109,8 @@ namespace geode {
|
|||
private:
|
||||
class Impl;
|
||||
std::shared_ptr<Nest::Impl> m_impl;
|
||||
friend class Logger;
|
||||
friend GEODE_DLL std::shared_ptr<Nest> saveNest();
|
||||
friend GEODE_DLL void loadNest(std::shared_ptr<Nest> const& nest);
|
||||
public:
|
||||
explicit Nest(std::shared_ptr<Nest::Impl> impl);
|
||||
};
|
||||
|
|
|
@ -19,15 +19,6 @@ using namespace geode::prelude;
|
|||
#include "load.hpp"
|
||||
|
||||
$execute {
|
||||
listenForSettingChanges("show-platform-console", +[](bool value) {
|
||||
if (value) {
|
||||
console::open();
|
||||
}
|
||||
else {
|
||||
console::close();
|
||||
}
|
||||
});
|
||||
|
||||
ipc::listen("ipc-test", [](ipc::IPCEvent* event) -> matjson::Value {
|
||||
return "Hello from Geode!";
|
||||
});
|
||||
|
@ -95,7 +86,12 @@ void tryShowForwardCompat() {
|
|||
|
||||
int geodeEntry(void* platformData) {
|
||||
thread::setName("Main");
|
||||
|
||||
log::Logger::get()->setup();
|
||||
console::setup();
|
||||
if (LoaderImpl::get()->isForwardCompatMode()) {
|
||||
console::openIfClosed();
|
||||
}
|
||||
|
||||
std::string forwardCompatSuffix;
|
||||
if (LoaderImpl::get()->isForwardCompatMode())
|
||||
|
@ -132,10 +128,9 @@ int geodeEntry(void* platformData) {
|
|||
tryShowForwardCompat();
|
||||
|
||||
// open console
|
||||
if (LoaderImpl::get()->isForwardCompatMode() ||
|
||||
if (!LoaderImpl::get()->isForwardCompatMode() &&
|
||||
Mod::get()->getSettingValue<bool>("show-platform-console")) {
|
||||
log::debug("Opening console");
|
||||
console::open();
|
||||
console::openIfClosed();
|
||||
}
|
||||
|
||||
// set up loader, load mods, etc.
|
||||
|
|
|
@ -712,7 +712,6 @@ std::vector<LoadProblem> Loader::Impl::getProblems() const {
|
|||
}
|
||||
|
||||
void Loader::Impl::forceReset() {
|
||||
console::close();
|
||||
for (auto& [_, mod] : m_mods) {
|
||||
delete mod;
|
||||
}
|
||||
|
|
|
@ -91,23 +91,32 @@ std::string cocos2d::format_as(cocos2d::ccColor4B const& col) {
|
|||
|
||||
// Log
|
||||
|
||||
inline static thread_local int32_t s_nestLevel = 0;
|
||||
inline static thread_local int32_t s_nestCountOffset = 0;
|
||||
|
||||
void log::vlogImpl(Severity sev, Mod* mod, fmt::string_view format, fmt::format_args args) {
|
||||
Logger::get()->push(
|
||||
sev,
|
||||
mod,
|
||||
fmt::vformat(format, args)
|
||||
);
|
||||
if (!mod->isLoggingEnabled()) return;
|
||||
|
||||
auto nestCount = s_nestLevel * 2;
|
||||
if (nestCount != 0) {
|
||||
nestCount += s_nestCountOffset;
|
||||
}
|
||||
|
||||
Logger::get()->push(sev, thread::getName(), mod->getName(), nestCount,
|
||||
fmt::vformat(format, args));
|
||||
}
|
||||
|
||||
|
||||
Log::Log(Severity sev, std::string&& thread, Mod* mod, std::string&& content) :
|
||||
m_sender(mod),
|
||||
Log::Log(Severity sev, std::string&& thread, std::string&& source, int32_t nestCount,
|
||||
std::string&& content) :
|
||||
m_time(log_clock::now()),
|
||||
m_severity(sev),
|
||||
m_thread(thread),
|
||||
m_source(source),
|
||||
m_nestCount(nestCount),
|
||||
m_content(content) {}
|
||||
|
||||
Log::~Log() {}
|
||||
Log::~Log() = default;
|
||||
|
||||
auto convertTime(auto timePoint) {
|
||||
// std::chrono::current_zone() isnt available on clang (android),
|
||||
|
@ -117,12 +126,8 @@ auto convertTime(auto timePoint) {
|
|||
return fmt::localtime(timeEpoch);
|
||||
}
|
||||
|
||||
std::string Log::toString(bool logTime, int32_t nestCount) const {
|
||||
std::string res;
|
||||
|
||||
if (logTime) {
|
||||
res += fmt::format("{:%H:%M:%S}", convertTime(m_time));
|
||||
}
|
||||
std::string Log::toString() const {
|
||||
std::string res = fmt::format("{:%H:%M:%S}", convertTime(m_time));
|
||||
|
||||
switch (m_severity.m_value) {
|
||||
case Severity::Debug:
|
||||
|
@ -142,33 +147,49 @@ std::string Log::toString(bool logTime, int32_t nestCount) const {
|
|||
break;
|
||||
}
|
||||
|
||||
auto senderName = m_sender ? m_sender->getName() : "Geode?";
|
||||
auto threadName = m_thread;
|
||||
auto nestCount = m_nestCount;
|
||||
auto source = m_source;
|
||||
auto thread = m_thread;
|
||||
|
||||
if (nestCount != 0) {
|
||||
nestCount -= static_cast<int32_t>(senderName.size() + threadName.size());
|
||||
nestCount -= static_cast<int32_t>(source.size() + thread.size());
|
||||
}
|
||||
|
||||
if (nestCount < 0) {
|
||||
auto initSenderLength = static_cast<int32_t>(senderName.size());
|
||||
auto initThreadLength = static_cast<int32_t>(threadName.size());
|
||||
auto initSourceLength = static_cast<int32_t>(source.size());
|
||||
auto initThreadLength = static_cast<int32_t>(thread.size());
|
||||
|
||||
auto needsCollapse = -nestCount;
|
||||
|
||||
auto senderCollapse = needsCollapse / 2;
|
||||
auto senderLength = std::max(initSenderLength - senderCollapse, 2);
|
||||
senderCollapse = initSenderLength - senderLength;
|
||||
if (initThreadLength == 0) {
|
||||
auto sourceCollapse = needsCollapse;
|
||||
auto sourceLength = std::max(initSourceLength - sourceCollapse, 2);
|
||||
if (sourceLength < source.size())
|
||||
source = fmt::format("{}>", source.substr(0, sourceLength - 1));
|
||||
}
|
||||
else {
|
||||
auto sourceCollapse = needsCollapse / 2;
|
||||
auto sourceLength = std::max(initSourceLength - sourceCollapse, 2);
|
||||
sourceCollapse = initSourceLength - sourceLength;
|
||||
|
||||
auto threadCollapse = needsCollapse - senderCollapse;
|
||||
auto threadLength = std::max(initThreadLength - threadCollapse, 2);
|
||||
auto threadCollapse = needsCollapse - sourceCollapse;
|
||||
auto threadLength = std::max(initThreadLength - threadCollapse, 2);
|
||||
threadCollapse = initThreadLength - threadLength;
|
||||
|
||||
if (senderLength < senderName.size())
|
||||
senderName = fmt::format("{}>", senderName.substr(0, senderLength - 1));
|
||||
if (threadLength < threadName.size())
|
||||
threadName = fmt::format("{}>", threadName.substr(0, threadLength - 1));
|
||||
sourceCollapse = needsCollapse - threadCollapse;
|
||||
sourceLength = std::max(initSourceLength - sourceCollapse, 2);
|
||||
|
||||
if (sourceLength < source.size())
|
||||
source = fmt::format("{}>", source.substr(0, sourceLength - 1));
|
||||
if (threadLength < thread.size())
|
||||
thread = fmt::format("{}>", thread.substr(0, threadLength - 1));
|
||||
}
|
||||
}
|
||||
|
||||
res += fmt::format(" [{}] [{}]: ", threadName, senderName);
|
||||
if (thread.empty())
|
||||
res += fmt::format(" [{}]: ", source);
|
||||
else
|
||||
res += fmt::format(" [{}] [{}]: ", thread, source);
|
||||
|
||||
for (int32_t i = 0; i < nestCount; i++) {
|
||||
res += " ";
|
||||
|
@ -179,22 +200,10 @@ std::string Log::toString(bool logTime, int32_t nestCount) const {
|
|||
return res;
|
||||
}
|
||||
|
||||
log_clock::time_point Log::getTime() const {
|
||||
return m_time;
|
||||
}
|
||||
|
||||
Mod* Log::getSender() const {
|
||||
return m_sender;
|
||||
}
|
||||
|
||||
Severity Log::getSeverity() const {
|
||||
return m_severity;
|
||||
}
|
||||
|
||||
std::string_view Log::getContent() const {
|
||||
return m_content;
|
||||
}
|
||||
|
||||
// Logger
|
||||
|
||||
Logger* Logger::get() {
|
||||
|
@ -207,49 +216,26 @@ void Logger::setup() {
|
|||
}
|
||||
|
||||
std::mutex g_logMutex;
|
||||
void Logger::push(Severity sev, Mod* mod, std::string&& content) {
|
||||
if (!mod->isLoggingEnabled()) return;
|
||||
|
||||
Log* log = nullptr;
|
||||
void Logger::push(Severity sev, std::string&& thread, std::string&& source, int32_t nestCount,
|
||||
std::string&& content) {
|
||||
Log* log;
|
||||
{
|
||||
std::lock_guard g(g_logMutex);
|
||||
log = &m_logs.emplace_back(sev, thread::getName(), mod, std::move(content));
|
||||
log = &m_logs.emplace_back(sev, std::move(thread), std::move(source), nestCount,
|
||||
std::move(content));
|
||||
}
|
||||
|
||||
auto nestCount = s_nestLevel * 2;
|
||||
if (nestCount != 0) {
|
||||
nestCount += s_nestCountOffset;
|
||||
auto const logStr = log->toString();
|
||||
{
|
||||
std::lock_guard g(g_logMutex);
|
||||
console::log(logStr, log->getSeverity());
|
||||
m_logStream << logStr << std::endl;
|
||||
}
|
||||
|
||||
auto const logStr = log->toString(true, nestCount);
|
||||
|
||||
console::log(logStr, log->getSeverity());
|
||||
m_logStream << logStr << std::endl;
|
||||
}
|
||||
|
||||
void Logger::pushNest() {
|
||||
if (s_nestLevel == 0)
|
||||
s_nestCountOffset = static_cast<int32_t>(Mod::get()->getName().size() + thread::getName().size());
|
||||
s_nestLevel++;
|
||||
}
|
||||
|
||||
void Logger::popNest() {
|
||||
s_nestLevel--;
|
||||
}
|
||||
|
||||
Nest::Nest(std::shared_ptr<Nest::Impl> impl) : m_impl(std::move(impl)) { }
|
||||
Nest::Impl::Impl(int32_t nestLevel, int32_t nestCountOffset) :
|
||||
m_nestLevel(nestLevel), m_nestCountOffset(nestCountOffset) { }
|
||||
|
||||
std::shared_ptr<Nest> Logger::saveNest() {
|
||||
return std::make_shared<Nest>(std::make_shared<Nest::Impl>(s_nestLevel, s_nestCountOffset));
|
||||
}
|
||||
|
||||
void Logger::loadNest(std::shared_ptr<Nest> const& nest) {
|
||||
s_nestLevel = nest->m_impl->m_nestLevel;
|
||||
s_nestCountOffset = nest->m_impl->m_nestCountOffset;
|
||||
}
|
||||
|
||||
std::vector<Log> const& Logger::list() {
|
||||
return m_logs;
|
||||
}
|
||||
|
@ -266,17 +252,20 @@ std::string geode::log::generateLogName() {
|
|||
}
|
||||
|
||||
void log::pushNest() {
|
||||
Logger::pushNest();
|
||||
if (s_nestLevel == 0)
|
||||
s_nestCountOffset = static_cast<int32_t>(Mod::get()->getName().size() + thread::getName().size());
|
||||
s_nestLevel++;
|
||||
}
|
||||
|
||||
void log::popNest() {
|
||||
Logger::popNest();
|
||||
s_nestLevel--;
|
||||
}
|
||||
|
||||
std::shared_ptr<Nest> log::saveNest() {
|
||||
return Logger::saveNest();
|
||||
return std::make_shared<Nest>(std::make_shared<Nest::Impl>(s_nestLevel, s_nestCountOffset));
|
||||
}
|
||||
|
||||
void log::loadNest(std::shared_ptr<Nest> const& nest) {
|
||||
Logger::loadNest(nest);
|
||||
s_nestLevel = nest->m_impl->m_nestLevel;
|
||||
s_nestCountOffset = nest->m_impl->m_nestCountOffset;
|
||||
}
|
||||
|
|
|
@ -9,46 +9,36 @@
|
|||
|
||||
namespace geode::log {
|
||||
class Log final {
|
||||
Mod* m_sender;
|
||||
log_clock::time_point m_time;
|
||||
Severity m_severity;
|
||||
std::string m_thread;
|
||||
std::string m_source;
|
||||
int32_t m_nestCount;
|
||||
std::string m_content;
|
||||
|
||||
public:
|
||||
~Log();
|
||||
Log(Severity sev, std::string&& thread, Mod* mod, std::string&& content);
|
||||
Log(Severity sev, std::string&& thread, std::string&& source, int32_t nestCount,
|
||||
std::string&& content);
|
||||
|
||||
std::string toString(bool logTime = true, int32_t nestCount = 0) const;
|
||||
[[nodiscard]] std::string toString() const;
|
||||
|
||||
std::string_view getContent() const;
|
||||
log_clock::time_point getTime() const;
|
||||
Mod* getSender() const;
|
||||
Severity getSeverity() const;
|
||||
[[nodiscard]] Severity getSeverity() const;
|
||||
};
|
||||
|
||||
class Logger {
|
||||
private:
|
||||
std::vector<Log> m_logs;
|
||||
std::ofstream m_logStream;
|
||||
inline static thread_local int32_t s_nestLevel;
|
||||
inline static thread_local int32_t s_nestCountOffset;
|
||||
|
||||
Logger() {}
|
||||
Logger() = default;
|
||||
public:
|
||||
static Logger* get();
|
||||
|
||||
void setup();
|
||||
|
||||
void push(Severity sev, Mod* mod, std::string&& content);
|
||||
// why would you need this lol
|
||||
// void pop(Log* log);
|
||||
|
||||
static void pushNest();
|
||||
static void popNest();
|
||||
|
||||
[[nodiscard]] static std::shared_ptr<Nest> saveNest();
|
||||
static void loadNest(std::shared_ptr<Nest> const& nest);
|
||||
void push(Severity sev, std::string&& thread, std::string&& source, int32_t nestCount,
|
||||
std::string&& content);
|
||||
|
||||
std::vector<Log> const& list();
|
||||
void clear();
|
||||
|
|
|
@ -3,8 +3,15 @@
|
|||
#include <string>
|
||||
|
||||
namespace geode::console {
|
||||
void open();
|
||||
void close();
|
||||
// intended for setting up an already attached console
|
||||
// for example, if the game was launched with a debugger, it'd already have a console attached
|
||||
// so we can setup that console regardless of the setting
|
||||
void setup();
|
||||
// if the setting is on, we call tryOpenIfClosed, and if there's no console attached yet
|
||||
// (e.g. from a debugger, see above), this function should create a new console
|
||||
// and attach it (perhaps, by calling setup again, see windows impl for an example)
|
||||
void openIfClosed();
|
||||
|
||||
void log(std::string const& msg, Severity severity);
|
||||
void messageBox(char const* title, std::string const& info, Severity severity = Severity::Error);
|
||||
}
|
||||
|
|
|
@ -17,13 +17,8 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
void console::open() {
|
||||
return;
|
||||
}
|
||||
|
||||
void console::close() {
|
||||
return;
|
||||
}
|
||||
void console::setup() { }
|
||||
void console::openIfClosed() { }
|
||||
|
||||
void console::log(std::string const& msg, Severity severity) {
|
||||
__android_log_print(
|
||||
|
|
|
@ -46,7 +46,8 @@ void console::log(std::string const& msg, Severity severity) {
|
|||
}
|
||||
|
||||
|
||||
void console::open() {
|
||||
void console::setup() { }
|
||||
void console::openIfClosed() {
|
||||
if (s_isOpen) return;
|
||||
|
||||
std::string outFile = "/tmp/command_output_XXXXXX";
|
||||
|
@ -87,20 +88,10 @@ void console::open() {
|
|||
s_isOpen = true;
|
||||
|
||||
for (auto const& log : log::Logger::get()->list()) {
|
||||
console::log(log.toString(true), log.getSeverity());
|
||||
console::log(log.toString(), log.getSeverity());
|
||||
}
|
||||
}
|
||||
|
||||
void console::close() {
|
||||
if (s_isOpen) {
|
||||
::close(s_platformData.logFd);
|
||||
unlink(s_platformData.logFile.c_str());
|
||||
unlink(s_platformData.scriptFile.c_str());
|
||||
}
|
||||
|
||||
s_isOpen = false;
|
||||
}
|
||||
|
||||
CFDataRef msgPortCallback(CFMessagePortRef port, SInt32 messageID, CFDataRef data, void* info) {
|
||||
if (!CFDataGetLength(data)) return NULL;
|
||||
|
||||
|
|
|
@ -1,54 +1,171 @@
|
|||
#include <loader/console.hpp>
|
||||
#include <loader/LogImpl.hpp>
|
||||
#include <iostream>
|
||||
#include <io.h>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
bool s_isOpen = false;
|
||||
bool s_hasAnsiColorSupport = false;
|
||||
HANDLE s_outHandle = nullptr;
|
||||
bool s_useEscapeCodes = false;
|
||||
|
||||
void console::open() {
|
||||
if (s_isOpen) return;
|
||||
if (AllocConsole() == 0) return;
|
||||
void setupConsole(bool forceUseEscapeCodes = false) {
|
||||
SetConsoleCP(CP_UTF8);
|
||||
// redirect console output
|
||||
freopen_s(reinterpret_cast<FILE**>(stdout), "CONOUT$", "w", stdout);
|
||||
freopen_s(reinterpret_cast<FILE**>(stdin), "CONIN$", "r", stdin);
|
||||
|
||||
// Set output mode to handle ansi color sequences
|
||||
auto handleStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
// set output mode to handle ansi color sequences
|
||||
DWORD consoleMode = 0;
|
||||
if (GetConsoleMode(handleStdout, &consoleMode)) {
|
||||
consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
if (SetConsoleMode(handleStdout, consoleMode)) {
|
||||
s_hasAnsiColorSupport = true;
|
||||
s_useEscapeCodes = forceUseEscapeCodes || GetConsoleMode(s_outHandle, &consoleMode) &&
|
||||
SetConsoleMode(s_outHandle, consoleMode | ENABLE_PROCESSED_OUTPUT |
|
||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
|
||||
if (s_useEscapeCodes && !forceUseEscapeCodes) {
|
||||
// test if the console *actually* supports escape codes (thanks wine)
|
||||
s_useEscapeCodes = false;
|
||||
DWORD written;
|
||||
CONSOLE_SCREEN_BUFFER_INFO preInfo;
|
||||
CONSOLE_SCREEN_BUFFER_INFO postInfo;
|
||||
if (GetConsoleScreenBufferInfo(s_outHandle, &preInfo) &&
|
||||
WriteConsoleA(s_outHandle, "\x1b[0m", 4, &written, nullptr) &&
|
||||
GetConsoleScreenBufferInfo(s_outHandle, &postInfo)) {
|
||||
s_useEscapeCodes = preInfo.dwCursorPosition.X == postInfo.dwCursorPosition.X &&
|
||||
preInfo.dwCursorPosition.Y == postInfo.dwCursorPosition.Y;
|
||||
SetConsoleCursorPosition(s_outHandle, preInfo.dwCursorPosition);
|
||||
}
|
||||
}
|
||||
|
||||
s_isOpen = true;
|
||||
|
||||
for (auto const& log : log::Logger::get()->list()) {
|
||||
console::log(log.toString(true), log.getSeverity());
|
||||
console::log(log.toString(), log.getSeverity());
|
||||
}
|
||||
}
|
||||
|
||||
void console::close() {
|
||||
if (!s_isOpen) return;
|
||||
struct stdData {
|
||||
OVERLAPPED m_overlap{};
|
||||
std::string const& m_name;
|
||||
const Severity m_sev;
|
||||
std::string& m_cur;
|
||||
char* m_buf;
|
||||
stdData(std::string const& name, const Severity sev, std::string& cur, char* buf) :
|
||||
m_name(name), m_sev(sev), m_cur(cur), m_buf(buf) { }
|
||||
};
|
||||
void WINAPI CompletedReadRoutine(DWORD error, DWORD read, LPOVERLAPPED overlap) {
|
||||
auto* o = reinterpret_cast<stdData*>(overlap);
|
||||
for (auto i = 0; i < read && !error; i++) {
|
||||
if (o->m_buf[i] != '\n') {
|
||||
if (o->m_buf[i] != '\r')
|
||||
o->m_cur += o->m_buf[i];
|
||||
continue;
|
||||
}
|
||||
log::Logger::get()->push(o->m_sev, "", std::string(o->m_name), 0, std::string(o->m_cur));
|
||||
o->m_cur.clear();
|
||||
}
|
||||
delete o;
|
||||
}
|
||||
|
||||
fclose(stdin);
|
||||
fclose(stdout);
|
||||
FreeConsole();
|
||||
bool redirectStd(FILE* which, std::string const& name, const Severity sev) {
|
||||
auto pipeName = fmt::format(R"(\\.\pipe\geode-{}-{})", name, GetCurrentProcessId());
|
||||
auto pipe = CreateNamedPipeA(
|
||||
pipeName.c_str(),
|
||||
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_REJECT_REMOTE_CLIENTS,
|
||||
1, 0, 1024, 0, nullptr
|
||||
);
|
||||
if (!pipe) {
|
||||
log::warn("Failed to create pipe, {} will be unavailable", name);
|
||||
return false;
|
||||
}
|
||||
std::thread([pipe, name, sev]() {
|
||||
thread::setName(fmt::format("{} Read Thread", name));
|
||||
auto event = CreateEventA(nullptr, false, false, nullptr);
|
||||
std::string cur;
|
||||
while (true) {
|
||||
char buf[1024];
|
||||
auto* data = new stdData(name, sev, cur, buf);
|
||||
data->m_overlap.hEvent = event;
|
||||
ReadFileEx(pipe, buf, 1024, &data->m_overlap, &CompletedReadRoutine);
|
||||
auto res = WaitForSingleObjectEx(event, INFINITE, true);
|
||||
if (!res)
|
||||
continue;
|
||||
}
|
||||
}).detach();
|
||||
FILE* yum;
|
||||
if (freopen_s(&yum, pipeName.c_str(), "w", which)) {
|
||||
log::warn("Failed to reopen file, {} will be unavailable", name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
s_isOpen = false;
|
||||
void console::setup() {
|
||||
// if the game launched from a console or with a console already attached,
|
||||
// this is where we find that out and save its handle
|
||||
s_outHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (!s_outHandle && AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
s_outHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
}
|
||||
|
||||
if (s_outHandle) {
|
||||
std::string path;
|
||||
DWORD dummy;
|
||||
// use GetConsoleMode to check if the handle is a console
|
||||
if (!GetConsoleMode(s_outHandle, &dummy)) {
|
||||
// explicitly ignore some stupid handles
|
||||
char buf[MAX_PATH + 1];
|
||||
auto count = GetFinalPathNameByHandleA(s_outHandle, buf, MAX_PATH + 1,
|
||||
FILE_NAME_OPENED | VOLUME_NAME_NT);
|
||||
if (count != 0) {
|
||||
path = std::string(buf, count - 1);
|
||||
}
|
||||
|
||||
// count == 0 => not a console and not a file, assume it's closed
|
||||
// wine does something weird with /dev/null? not sure tbh but it's definitely up to no good
|
||||
if (count == 0 || path.ends_with("\\dev\\null")) {
|
||||
s_outHandle = nullptr;
|
||||
CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
CloseHandle(GetStdHandle(STD_INPUT_HANDLE));
|
||||
CloseHandle(GetStdHandle(STD_ERROR_HANDLE));
|
||||
FreeConsole();
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, nullptr);
|
||||
SetStdHandle(STD_INPUT_HANDLE, nullptr);
|
||||
SetStdHandle(STD_ERROR_HANDLE, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// clion console supports escape codes but we can't query that because it's a named pipe
|
||||
setupConsole(string::contains(path, "cidr-"));
|
||||
}
|
||||
|
||||
auto oldStdout = _dup(_fileno(stdout));
|
||||
|
||||
redirectStd(stdout, "stdout", Severity::Info);
|
||||
redirectStd(stderr, "stderr", Severity::Debug);
|
||||
|
||||
// re-open the file from the handle we just stole..
|
||||
if (oldStdout >= 0) {
|
||||
_fdopen(oldStdout, "w");
|
||||
s_outHandle = reinterpret_cast<HANDLE>(_get_osfhandle(oldStdout));
|
||||
}
|
||||
}
|
||||
|
||||
void console::openIfClosed() {
|
||||
if (s_outHandle)
|
||||
return;
|
||||
AllocConsole();
|
||||
s_outHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
// reopen conin$/conout$ if they're closed
|
||||
if (!s_outHandle) {
|
||||
s_outHandle = CreateFileA("CONOUT$", GENERIC_WRITE, 0, nullptr, 0, 0, nullptr);
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, s_outHandle);
|
||||
SetStdHandle(STD_INPUT_HANDLE, CreateFileA("CONIN$", GENERIC_READ, 0, nullptr, 0, 0, nullptr));
|
||||
SetStdHandle(STD_ERROR_HANDLE, s_outHandle);
|
||||
}
|
||||
setupConsole();
|
||||
}
|
||||
|
||||
void console::log(std::string const& msg, Severity severity) {
|
||||
if (!s_isOpen)
|
||||
if (!s_outHandle)
|
||||
return;
|
||||
DWORD written;
|
||||
|
||||
if (!s_hasAnsiColorSupport) {
|
||||
std::cout << msg << "\n" << std::flush;
|
||||
if (!s_useEscapeCodes || msg.size() <= 14) {
|
||||
WriteFile(s_outHandle, (msg + "\n").c_str(), msg.size() + 1, &written, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -78,11 +195,11 @@ void console::log(std::string const& msg, Severity severity) {
|
|||
auto const colorStr = fmt::format("\x1b[38;5;{}m", color);
|
||||
auto const color2Str = color2 == -1 ? "\x1b[0m" : fmt::format("\x1b[38;5;{}m", color2);
|
||||
auto const newMsg = fmt::format(
|
||||
"{}{}{}{}\x1b[0m",
|
||||
"{}{}{}{}\x1b[0m\n",
|
||||
colorStr, msg.substr(0, 14), color2Str, msg.substr(14)
|
||||
);
|
||||
|
||||
std::cout << newMsg << "\n" << std::flush;
|
||||
WriteFile(s_outHandle, newMsg.c_str(), newMsg.size(), &written, nullptr);
|
||||
}
|
||||
|
||||
void console::messageBox(char const* title, std::string const& info, Severity severity) {
|
||||
|
|
Loading…
Reference in a new issue