diff --git a/src/dusk/main.cpp b/src/dusk/main.cpp index 7d19325f8f..22cd5a9fc6 100644 --- a/src/dusk/main.cpp +++ b/src/dusk/main.cpp @@ -1,26 +1,126 @@ #if _WIN32 #define WINDOWS_LEAN_AND_MEAN #include +#include #endif #include +#include +#include +#include +#include +#include + int game_main(int argc, char* argv[]); -void WindowsSetupConsole(); +namespace { -int main(int argc, char* argv[]) { - WindowsSetupConsole(); +#if _WIN32 +bool ShouldShowWindowsConsole(int argc, char* argv[]) { + if (const auto* env = std::getenv("DUSK_CONSOLE")) { + if (env[0] != '\0' && env[0] != '0') { + return true; + } + } + + for (int i = 1; i < argc; ++i) { + if (std::string_view(argv[i]) == "--console") { + return true; + } + } + + return false; +} + +void SetupWindowsConsoleStreams() { + FILE* stream = nullptr; + freopen_s(&stream, "CONIN$", "r", stdin); + freopen_s(&stream, "CONOUT$", "w", stdout); + freopen_s(&stream, "CONOUT$", "w", stderr); +} + +void WindowsSetupConsole(bool showConsole) { + if (!showConsole) { + return; + } + + if (!AttachConsole(ATTACH_PARENT_PROCESS)) { + AllocConsole(); + } + + SetupWindowsConsoleStreams(); + SetConsoleOutputCP(CP_UTF8); + + if (const HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); + stdoutHandle != INVALID_HANDLE_VALUE && stdoutHandle != nullptr) { + DWORD consoleMode = 0; + if (GetConsoleMode(stdoutHandle, &consoleMode)) { + SetConsoleMode(stdoutHandle, + consoleMode | ENABLE_PROCESSED_OUTPUT + | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + } + } +} + +int DuskMain(int argc, char* argv[]) { + WindowsSetupConsole(ShouldShowWindowsConsole(argc, argv)); return game_main(argc, argv); } -void WindowsSetupConsole() { -#if _WIN32 - SetConsoleOutputCP(CP_UTF8); +std::vector WideArgsToUtf8(int argc, wchar_t** argv) { + std::vector utf8Args; + utf8Args.reserve(argc); - auto stdout = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD consoleMode; - GetConsoleMode(stdout, &consoleMode); - SetConsoleMode(stdout, consoleMode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + for (int i = 0; i < argc; ++i) { + const int requiredSize = + WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr); + if (requiredSize <= 0) { + utf8Args.emplace_back(); + continue; + } + + std::vector utf8Buffer(static_cast(requiredSize)); + WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, utf8Buffer.data(), requiredSize, nullptr, + nullptr); + utf8Args.emplace_back(utf8Buffer.data()); + } + + return utf8Args; +} + +int RunWindowsGuiEntryPoint() { + int argc = 0; + wchar_t** wideArgv = CommandLineToArgvW(GetCommandLineW(), &argc); + if (wideArgv == nullptr) { + return DuskMain(__argc, __argv); + } + + std::vector utf8Args = WideArgsToUtf8(argc, wideArgv); + LocalFree(wideArgv); + + std::vector argv; + argv.reserve(utf8Args.size()); + for (auto& arg : utf8Args) { + argv.push_back(arg.data()); + } + + return DuskMain(argc, argv.data()); +} +#else +int DuskMain(int argc, char* argv[]) { + return game_main(argc, argv); +} +#endif + +} // namespace + +int main(int argc, char* argv[]) { + return DuskMain(argc, argv); +} + +#if _WIN32 +int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) { + return RunWindowsGuiEntryPoint(); +} #endif -} \ No newline at end of file diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 7aaec4b005..c0a4320a54 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -418,6 +418,7 @@ int game_main(int argc, char* argv[]) { arg_options.add_options() ("l,log-level", "Log level from " + std::to_string(AuroraLogLevel::LOG_DEBUG) + " to " + std::to_string(AuroraLogLevel::LOG_FATAL), cxxopts::value()->default_value("0")) ("h,help", "Print usage") + ("console", "Show the Windows console window for logs", cxxopts::value()->default_value("false")->implicit_value("true")) ("dvd", "Path to DVD image file", cxxopts::value()) ("backend", "Graphics API backend to use (auto, d3d12, metal, vulkan, null)", cxxopts::value()) ("cvar", "Override configuration variables without modifying config", cxxopts::value>());