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(); | 
