diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
m--------- | externals/Vulkan-Headers | 0 | ||||
-rw-r--r-- | src/core/core.cpp | 18 | ||||
-rw-r--r-- | src/core/core.h | 4 | ||||
-rw-r--r-- | src/video_core/vulkan_common/vulkan_wrapper.cpp | 12 | ||||
-rw-r--r-- | src/yuzu/bootmanager.cpp | 81 | ||||
-rw-r--r-- | src/yuzu/bootmanager.h | 51 | ||||
-rw-r--r-- | src/yuzu/main.cpp | 152 | ||||
-rw-r--r-- | src/yuzu/main.h | 8 | ||||
-rw-r--r-- | src/yuzu/util/overlay_dialog.cpp | 9 | ||||
-rw-r--r-- | src/yuzu/util/overlay_dialog.h | 1 | ||||
-rw-r--r-- | src/yuzu_cmd/CMakeLists.txt | 9 | ||||
-rw-r--r-- | src/yuzu_cmd/yuzu.cpp | 7 |
13 files changed, 166 insertions, 188 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 47eddf99e..f71a8b3e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,7 +208,7 @@ find_package(libusb 1.0.24) find_package(lz4 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED) find_package(Opus 1.3) -find_package(Vulkan 1.3.213) +find_package(Vulkan 1.3.238) find_package(ZLIB 1.2 REQUIRED) find_package(zstd 1.5 REQUIRED) diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers -Subproject 2826791bed6a793f164bf534cd859968f13df8a +Subproject 00671c64ba5c488ade22ad572a0ef81d5e64c80 diff --git a/src/core/core.cpp b/src/core/core.cpp index a738f221f..47292cd78 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -183,26 +183,20 @@ struct System::Impl { Initialize(system); } - SystemResultStatus Run() { + void Run() { std::unique_lock<std::mutex> lk(suspend_guard); - status = SystemResultStatus::Success; kernel.Suspend(false); core_timing.SyncPause(false); is_paused.store(false, std::memory_order_relaxed); - - return status; } - SystemResultStatus Pause() { + void Pause() { std::unique_lock<std::mutex> lk(suspend_guard); - status = SystemResultStatus::Success; core_timing.SyncPause(true); kernel.Suspend(true); is_paused.store(true, std::memory_order_relaxed); - - return status; } bool IsPaused() const { @@ -553,12 +547,12 @@ void System::Initialize() { impl->Initialize(*this); } -SystemResultStatus System::Run() { - return impl->Run(); +void System::Run() { + impl->Run(); } -SystemResultStatus System::Pause() { - return impl->Pause(); +void System::Pause() { + impl->Pause(); } bool System::IsPaused() const { diff --git a/src/core/core.h b/src/core/core.h index 4ebedffd9..fb5cda2f5 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -152,13 +152,13 @@ public: * Run the OS and Application * This function will start emulation and run the relevant devices */ - [[nodiscard]] SystemResultStatus Run(); + void Run(); /** * Pause the OS and Application * This function will pause emulation and stop the relevant devices */ - [[nodiscard]] SystemResultStatus Pause(); + void Pause(); /// Check if the core is currently paused. [[nodiscard]] bool IsPaused() const; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 483b534a0..7dca7341c 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -314,6 +314,18 @@ const char* ToString(VkResult result) noexcept { return "VK_ERROR_VALIDATION_FAILED_EXT"; case VkResult::VK_ERROR_INVALID_SHADER_NV: return "VK_ERROR_INVALID_SHADER_NV"; + case VkResult::VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR: + return "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR"; + case VkResult::VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR: + return "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR"; + case VkResult::VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR: + return "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR"; + case VkResult::VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR: + return "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR"; + case VkResult::VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR: + return "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR"; + case VkResult::VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR: + return "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR"; case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; case VkResult::VK_ERROR_FRAGMENTATION_EXT: diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 1368b20d5..3d560f303 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -46,30 +46,28 @@ static Core::Frontend::WindowSystemType GetWindowSystemType(); -EmuThread::EmuThread(Core::System& system_) : system{system_} {} +EmuThread::EmuThread(Core::System& system) : m_system{system} {} EmuThread::~EmuThread() = default; void EmuThread::run() { - std::string name = "EmuControlThread"; - MicroProfileOnThreadCreate(name.c_str()); - Common::SetCurrentThreadName(name.c_str()); + const char* name = "EmuControlThread"; + MicroProfileOnThreadCreate(name); + Common::SetCurrentThreadName(name); - auto& gpu = system.GPU(); - auto stop_token = stop_source.get_token(); - bool debugger_should_start = system.DebuggerEnabled(); + auto& gpu = m_system.GPU(); + auto stop_token = m_stop_source.get_token(); - system.RegisterHostThread(); + m_system.RegisterHostThread(); // Main process has been loaded. Make the context current to this thread and begin GPU and CPU // execution. gpu.ObtainContext(); emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); - if (Settings::values.use_disk_shader_cache.GetValue()) { - system.Renderer().ReadRasterizer()->LoadDiskResources( - system.GetCurrentProcessProgramID(), stop_token, + m_system.Renderer().ReadRasterizer()->LoadDiskResources( + m_system.GetCurrentProcessProgramID(), stop_token, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { emit LoadProgress(stage, value, total); }); @@ -79,57 +77,34 @@ void EmuThread::run() { gpu.ReleaseContext(); gpu.Start(); - system.GetCpuManager().OnGpuReady(); + m_system.GetCpuManager().OnGpuReady(); - system.RegisterExitCallback([this]() { - stop_source.request_stop(); - SetRunning(false); - }); + if (m_system.DebuggerEnabled()) { + m_system.InitializeDebugger(); + } - // Holds whether the cpu was running during the last iteration, - // so that the DebugModeLeft signal can be emitted before the - // next execution step - bool was_active = false; while (!stop_token.stop_requested()) { - if (running) { - if (was_active) { - emit DebugModeLeft(); - } + std::unique_lock lk{m_should_run_mutex}; + if (m_should_run) { + m_system.Run(); + m_is_running.store(true); + m_is_running.notify_all(); - running_guard = true; - Core::SystemResultStatus result = system.Run(); - if (result != Core::SystemResultStatus::Success) { - running_guard = false; - this->SetRunning(false); - emit ErrorThrown(result, system.GetStatusDetails()); - } - - if (debugger_should_start) { - system.InitializeDebugger(); - debugger_should_start = false; - } - - running_wait.Wait(); - result = system.Pause(); - if (result != Core::SystemResultStatus::Success) { - running_guard = false; - this->SetRunning(false); - emit ErrorThrown(result, system.GetStatusDetails()); - } - running_guard = false; - - if (!stop_token.stop_requested()) { - was_active = true; - emit DebugModeEntered(); - } + Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; }); } else { - std::unique_lock lock{running_mutex}; - Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); }); + m_system.Pause(); + m_is_running.store(false); + m_is_running.notify_all(); + + emit DebugModeEntered(); + Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; }); + emit DebugModeLeft(); } } // Shutdown the main emulated process - system.ShutdownMainProcess(); + m_system.DetachDebugger(); + m_system.ShutdownMainProcess(); #if MICROPROFILE_ENABLED MicroProfileOnThreadExit(); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index b24141fd9..eca16b313 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -47,7 +47,7 @@ class EmuThread final : public QThread { Q_OBJECT public: - explicit EmuThread(Core::System& system_); + explicit EmuThread(Core::System& system); ~EmuThread() override; /** @@ -57,30 +57,30 @@ public: void run() override; /** - * Sets whether the emulation thread is running or not - * @param running_ Boolean value, set the emulation thread to running if true - * @note This function is thread-safe + * Sets whether the emulation thread should run or not + * @param should_run Boolean value, set the emulation thread to running if true */ - void SetRunning(bool running_) { - std::unique_lock lock{running_mutex}; - running = running_; - lock.unlock(); - running_cv.notify_all(); - if (!running) { - running_wait.Set(); - /// Wait until effectively paused - while (running_guard) - ; + void SetRunning(bool should_run) { + // TODO: Prevent other threads from modifying the state until we finish. + { + // Notify the running thread to change state. + std::unique_lock run_lk{m_should_run_mutex}; + m_should_run = should_run; + m_should_run_cv.notify_one(); + } + + // Wait until paused, if pausing. + if (!should_run) { + m_is_running.wait(true); } } /** * Check if the emulation thread is running or not * @return True if the emulation thread is running, otherwise false - * @note This function is thread-safe */ bool IsRunning() const { - return running; + return m_is_running.load() || m_should_run; } /** @@ -88,18 +88,17 @@ public: */ void ForceStop() { LOG_WARNING(Frontend, "Force stopping EmuThread"); - stop_source.request_stop(); - SetRunning(false); + m_stop_source.request_stop(); } private: - bool running = false; - std::stop_source stop_source; - std::mutex running_mutex; - std::condition_variable_any running_cv; - Common::Event running_wait{}; - std::atomic_bool running_guard{false}; - Core::System& system; + Core::System& m_system; + + std::stop_source m_stop_source; + std::mutex m_should_run_mutex; + std::condition_variable_any m_should_run_cv; + std::atomic<bool> m_is_running{false}; + bool m_should_run{true}; signals: /** @@ -120,8 +119,6 @@ signals: */ void DebugModeLeft(); - void ErrorThrown(Core::SystemResultStatus, std::string); - void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index fe140dce0..6121711e0 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1498,7 +1498,7 @@ void GMainWindow::SetupSigInterrupts() { void GMainWindow::HandleSigInterrupt(int sig) { if (sig == SIGINT) { - exit(1); + _exit(1); } // Calling into Qt directly from a signal handler is not safe, @@ -1550,8 +1550,9 @@ void GMainWindow::AllowOSSleep() { bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) { // Shutdown previous session if the emu thread is still active... - if (emu_thread != nullptr) + if (emu_thread != nullptr) { ShutdownGame(); + } if (!render_window->InitRenderTarget()) { return false; @@ -1710,6 +1711,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t system->RegisterExecuteProgramCallback( [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); + system->RegisterExitCallback([this] { + emu_thread->ForceStop(); + render_window->Exit(); + }); + connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views @@ -1779,7 +1785,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t OnStartGame(); } -void GMainWindow::ShutdownGame() { +void GMainWindow::OnShutdownBegin() { if (!emulation_running) { return; } @@ -1794,20 +1800,48 @@ void GMainWindow::ShutdownGame() { Settings::values.use_speed_limit.SetValue(true); system->SetShuttingDown(true); - system->DetachDebugger(); discord_rpc->Pause(); RequestGameExit(); + emu_thread->disconnect(); + emu_thread->SetRunning(true); emit EmulationStopping(); - // Wait for emulation thread to complete and delete it - if (!emu_thread->wait(5000)) { + shutdown_timer.setSingleShot(true); + shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000); + connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); + connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); + + // Disable everything to prevent anything from being triggered here + ui->action_Pause->setEnabled(false); + ui->action_Restart->setEnabled(false); + ui->action_Stop->setEnabled(false); +} + +void GMainWindow::OnShutdownBeginDialog() { + shutdown_dialog = new OverlayDialog(this, *system, QString{}, tr("Closing software..."), + QString{}, QString{}, Qt::AlignHCenter | Qt::AlignVCenter); + shutdown_dialog->open(); +} + +void GMainWindow::OnEmulationStopTimeExpired() { + if (emu_thread) { emu_thread->ForceStop(); - emu_thread->wait(); } +} + +void GMainWindow::OnEmulationStopped() { + shutdown_timer.stop(); + emu_thread->disconnect(); + emu_thread->wait(); emu_thread = nullptr; + if (shutdown_dialog) { + shutdown_dialog->deleteLater(); + shutdown_dialog = nullptr; + } + emulation_running = false; discord_rpc->Update(); @@ -1853,6 +1887,20 @@ void GMainWindow::ShutdownGame() { // When closing the game, destroy the GLWindow to clear the context after the game is closed render_window->ReleaseRenderTarget(); + + Settings::RestoreGlobalState(system->IsPoweredOn()); + system->HIDCore().ReloadInputDevices(); + UpdateStatusButtons(); +} + +void GMainWindow::ShutdownGame() { + if (!emulation_running) { + return; + } + + OnShutdownBegin(); + OnEmulationStopTimeExpired(); + OnEmulationStopped(); } void GMainWindow::StoreRecentFile(const QString& filename) { @@ -2919,8 +2967,6 @@ void GMainWindow::OnStartGame() { emu_thread->SetRunning(true); - connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); - UpdateMenuState(); OnTasStateChanged(); @@ -2957,11 +3003,8 @@ void GMainWindow::OnStopGame() { return; } - ShutdownGame(); - - Settings::RestoreGlobalState(system->IsPoweredOn()); - system->HIDCore().ReloadInputDevices(); - UpdateStatusButtons(); + OnShutdownBegin(); + OnShutdownBeginDialog(); } void GMainWindow::OnLoadComplete() { @@ -3904,79 +3947,6 @@ void GMainWindow::OnMouseActivity() { mouse_center_timer.stop(); } -void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) { - QMessageBox::StandardButton answer; - QString status_message; - const QString common_message = - tr("The game you are trying to load requires additional files from your Switch to be " - "dumped " - "before playing.<br/><br/>For more information on dumping these files, please see the " - "following wiki page: <a " - "href='https://yuzu-emu.org/wiki/" - "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " - "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to " - "quit " - "back to the game list? Continuing emulation may result in crashes, corrupted save " - "data, or other bugs."); - switch (result) { - case Core::SystemResultStatus::ErrorSystemFiles: { - QString message; - if (details.empty()) { - message = - tr("yuzu was unable to locate a Switch system archive. %1").arg(common_message); - } else { - message = tr("yuzu was unable to locate a Switch system archive: %1. %2") - .arg(QString::fromStdString(details), common_message); - } - - answer = QMessageBox::question(this, tr("System Archive Not Found"), message, - QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - status_message = tr("System Archive Missing"); - break; - } - - case Core::SystemResultStatus::ErrorSharedFont: { - const QString message = - tr("yuzu was unable to locate the Switch shared fonts. %1").arg(common_message); - answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message, - QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - status_message = tr("Shared Font Missing"); - break; - } - - default: - answer = QMessageBox::question( - this, tr("Fatal Error"), - tr("yuzu has encountered a fatal error, please see the log for more details. " - "For more information on accessing the log, please see the following page: " - "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How " - "to " - "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game " - "list? " - "Continuing emulation may result in crashes, corrupted save data, or other " - "bugs."), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - status_message = tr("Fatal Error encountered"); - break; - } - - if (answer == QMessageBox::Yes) { - if (emu_thread) { - ShutdownGame(); - - Settings::RestoreGlobalState(system->IsPoweredOn()); - system->HIDCore().ReloadInputDevices(); - UpdateStatusButtons(); - } - } else { - // Only show the message if the game is still running. - if (emu_thread) { - emu_thread->SetRunning(true); - message_label->setText(status_message); - } - } -} - void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { if (behavior == ReinitializeKeyBehavior::Warning) { const auto res = QMessageBox::information( @@ -4121,10 +4091,6 @@ void GMainWindow::closeEvent(QCloseEvent* event) { // Shutdown session if the emu thread is active... if (emu_thread != nullptr) { ShutdownGame(); - - Settings::RestoreGlobalState(system->IsPoweredOn()); - system->HIDCore().ReloadInputDevices(); - UpdateStatusButtons(); } render_window->close(); @@ -4217,6 +4183,10 @@ bool GMainWindow::ConfirmForceLockedExit() { } void GMainWindow::RequestGameExit() { + if (!system->IsPoweredOn()) { + return; + } + auto& sm{system->ServiceManager()}; auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE"); auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1047ba276..95220b063 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -29,6 +29,7 @@ class GImageInfo; class GRenderWindow; class LoadingScreen; class MicroProfileDialog; +class OverlayDialog; class ProfilerWidget; class ControllerDialog; class QLabel; @@ -332,10 +333,13 @@ private slots: void ResetWindowSize900(); void ResetWindowSize1080(); void OnCaptureScreenshot(); - void OnCoreError(Core::SystemResultStatus, std::string); void OnReinitializeKeys(ReinitializeKeyBehavior behavior); void OnLanguageChanged(const QString& locale); void OnMouseActivity(); + void OnShutdownBegin(); + void OnShutdownBeginDialog(); + void OnEmulationStopped(); + void OnEmulationStopTimeExpired(); private: QString GetGameListErrorRemoving(InstalledEntryType type) const; @@ -385,6 +389,8 @@ private: GRenderWindow* render_window; GameList* game_list; LoadingScreen* loading_screen; + QTimer shutdown_timer; + OverlayDialog* shutdown_dialog{}; GameListPlaceholder* game_list_placeholder; diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp index 3fa3d0afb..796f5bf41 100644 --- a/src/yuzu/util/overlay_dialog.cpp +++ b/src/yuzu/util/overlay_dialog.cpp @@ -3,6 +3,7 @@ #include <QKeyEvent> #include <QScreen> +#include <QWindow> #include "core/core.h" #include "core/hid/hid_types.h" @@ -162,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() { const auto height = static_cast<float>(parentWidget()->height()); // High DPI - const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f; + const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f; const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; const auto body_text_font_size = @@ -259,3 +260,9 @@ void OverlayDialog::InputThread() { std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } + +void OverlayDialog::keyPressEvent(QKeyEvent* e) { + if (!ui->buttonsDialog->isHidden() || e->key() != Qt::Key_Escape) { + QDialog::keyPressEvent(e); + } +} diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h index 39c44393c..872283d61 100644 --- a/src/yuzu/util/overlay_dialog.h +++ b/src/yuzu/util/overlay_dialog.h @@ -94,6 +94,7 @@ private: /// The thread where input is being polled and processed. void InputThread(); + void keyPressEvent(QKeyEvent* e) override; std::unique_ptr<Ui::OverlayDialog> ui; diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index f6eeb9d8d..61b6cc4e0 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -49,6 +49,15 @@ if(UNIX AND NOT APPLE) install(TARGETS yuzu-cmd) endif() +if(WIN32) + # compile as a win32 gui application instead of a console application + if(MSVC) + set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") + elseif(MINGW) + set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows") + endif() +endif() + if (MSVC) include(CopyYuzuSDLDeps) copy_yuzu_SDL_deps(yuzu-cmd) diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index a80649703..91133569d 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -174,6 +174,13 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { /// Application entry point int main(int argc, char** argv) { +#ifdef _WIN32 + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + freopen("CONOUT$", "wb", stdout); + freopen("CONOUT$", "wb", stderr); + } +#endif + Common::Log::Initialize(); Common::Log::SetColorConsoleBackendEnabled(true); Common::Log::Start(); |