diff options
Diffstat (limited to 'src')
| -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 |
11 files changed, 165 insertions, 187 deletions
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 { | |||
| 183 | Initialize(system); | 183 | Initialize(system); |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | SystemResultStatus Run() { | 186 | void Run() { |
| 187 | std::unique_lock<std::mutex> lk(suspend_guard); | 187 | std::unique_lock<std::mutex> lk(suspend_guard); |
| 188 | status = SystemResultStatus::Success; | ||
| 189 | 188 | ||
| 190 | kernel.Suspend(false); | 189 | kernel.Suspend(false); |
| 191 | core_timing.SyncPause(false); | 190 | core_timing.SyncPause(false); |
| 192 | is_paused.store(false, std::memory_order_relaxed); | 191 | is_paused.store(false, std::memory_order_relaxed); |
| 193 | |||
| 194 | return status; | ||
| 195 | } | 192 | } |
| 196 | 193 | ||
| 197 | SystemResultStatus Pause() { | 194 | void Pause() { |
| 198 | std::unique_lock<std::mutex> lk(suspend_guard); | 195 | std::unique_lock<std::mutex> lk(suspend_guard); |
| 199 | status = SystemResultStatus::Success; | ||
| 200 | 196 | ||
| 201 | core_timing.SyncPause(true); | 197 | core_timing.SyncPause(true); |
| 202 | kernel.Suspend(true); | 198 | kernel.Suspend(true); |
| 203 | is_paused.store(true, std::memory_order_relaxed); | 199 | is_paused.store(true, std::memory_order_relaxed); |
| 204 | |||
| 205 | return status; | ||
| 206 | } | 200 | } |
| 207 | 201 | ||
| 208 | bool IsPaused() const { | 202 | bool IsPaused() const { |
| @@ -553,12 +547,12 @@ void System::Initialize() { | |||
| 553 | impl->Initialize(*this); | 547 | impl->Initialize(*this); |
| 554 | } | 548 | } |
| 555 | 549 | ||
| 556 | SystemResultStatus System::Run() { | 550 | void System::Run() { |
| 557 | return impl->Run(); | 551 | impl->Run(); |
| 558 | } | 552 | } |
| 559 | 553 | ||
| 560 | SystemResultStatus System::Pause() { | 554 | void System::Pause() { |
| 561 | return impl->Pause(); | 555 | impl->Pause(); |
| 562 | } | 556 | } |
| 563 | 557 | ||
| 564 | bool System::IsPaused() const { | 558 | 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: | |||
| 152 | * Run the OS and Application | 152 | * Run the OS and Application |
| 153 | * This function will start emulation and run the relevant devices | 153 | * This function will start emulation and run the relevant devices |
| 154 | */ | 154 | */ |
| 155 | [[nodiscard]] SystemResultStatus Run(); | 155 | void Run(); |
| 156 | 156 | ||
| 157 | /** | 157 | /** |
| 158 | * Pause the OS and Application | 158 | * Pause the OS and Application |
| 159 | * This function will pause emulation and stop the relevant devices | 159 | * This function will pause emulation and stop the relevant devices |
| 160 | */ | 160 | */ |
| 161 | [[nodiscard]] SystemResultStatus Pause(); | 161 | void Pause(); |
| 162 | 162 | ||
| 163 | /// Check if the core is currently paused. | 163 | /// Check if the core is currently paused. |
| 164 | [[nodiscard]] bool IsPaused() const; | 164 | [[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 { | |||
| 314 | return "VK_ERROR_VALIDATION_FAILED_EXT"; | 314 | return "VK_ERROR_VALIDATION_FAILED_EXT"; |
| 315 | case VkResult::VK_ERROR_INVALID_SHADER_NV: | 315 | case VkResult::VK_ERROR_INVALID_SHADER_NV: |
| 316 | return "VK_ERROR_INVALID_SHADER_NV"; | 316 | return "VK_ERROR_INVALID_SHADER_NV"; |
| 317 | case VkResult::VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR: | ||
| 318 | return "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR"; | ||
| 319 | case VkResult::VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR: | ||
| 320 | return "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR"; | ||
| 321 | case VkResult::VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR: | ||
| 322 | return "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR"; | ||
| 323 | case VkResult::VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR: | ||
| 324 | return "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR"; | ||
| 325 | case VkResult::VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR: | ||
| 326 | return "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR"; | ||
| 327 | case VkResult::VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR: | ||
| 328 | return "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR"; | ||
| 317 | case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: | 329 | case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: |
| 318 | return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; | 330 | return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; |
| 319 | case VkResult::VK_ERROR_FRAGMENTATION_EXT: | 331 | 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 @@ | |||
| 46 | 46 | ||
| 47 | static Core::Frontend::WindowSystemType GetWindowSystemType(); | 47 | static Core::Frontend::WindowSystemType GetWindowSystemType(); |
| 48 | 48 | ||
| 49 | EmuThread::EmuThread(Core::System& system_) : system{system_} {} | 49 | EmuThread::EmuThread(Core::System& system) : m_system{system} {} |
| 50 | 50 | ||
| 51 | EmuThread::~EmuThread() = default; | 51 | EmuThread::~EmuThread() = default; |
| 52 | 52 | ||
| 53 | void EmuThread::run() { | 53 | void EmuThread::run() { |
| 54 | std::string name = "EmuControlThread"; | 54 | const char* name = "EmuControlThread"; |
| 55 | MicroProfileOnThreadCreate(name.c_str()); | 55 | MicroProfileOnThreadCreate(name); |
| 56 | Common::SetCurrentThreadName(name.c_str()); | 56 | Common::SetCurrentThreadName(name); |
| 57 | 57 | ||
| 58 | auto& gpu = system.GPU(); | 58 | auto& gpu = m_system.GPU(); |
| 59 | auto stop_token = stop_source.get_token(); | 59 | auto stop_token = m_stop_source.get_token(); |
| 60 | bool debugger_should_start = system.DebuggerEnabled(); | ||
| 61 | 60 | ||
| 62 | system.RegisterHostThread(); | 61 | m_system.RegisterHostThread(); |
| 63 | 62 | ||
| 64 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU | 63 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU |
| 65 | // execution. | 64 | // execution. |
| 66 | gpu.ObtainContext(); | 65 | gpu.ObtainContext(); |
| 67 | 66 | ||
| 68 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 67 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 69 | |||
| 70 | if (Settings::values.use_disk_shader_cache.GetValue()) { | 68 | if (Settings::values.use_disk_shader_cache.GetValue()) { |
| 71 | system.Renderer().ReadRasterizer()->LoadDiskResources( | 69 | m_system.Renderer().ReadRasterizer()->LoadDiskResources( |
| 72 | system.GetCurrentProcessProgramID(), stop_token, | 70 | m_system.GetCurrentProcessProgramID(), stop_token, |
| 73 | [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { | 71 | [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { |
| 74 | emit LoadProgress(stage, value, total); | 72 | emit LoadProgress(stage, value, total); |
| 75 | }); | 73 | }); |
| @@ -79,57 +77,34 @@ void EmuThread::run() { | |||
| 79 | gpu.ReleaseContext(); | 77 | gpu.ReleaseContext(); |
| 80 | gpu.Start(); | 78 | gpu.Start(); |
| 81 | 79 | ||
| 82 | system.GetCpuManager().OnGpuReady(); | 80 | m_system.GetCpuManager().OnGpuReady(); |
| 83 | 81 | ||
| 84 | system.RegisterExitCallback([this]() { | 82 | if (m_system.DebuggerEnabled()) { |
| 85 | stop_source.request_stop(); | 83 | m_system.InitializeDebugger(); |
| 86 | SetRunning(false); | 84 | } |
| 87 | }); | ||
| 88 | 85 | ||
| 89 | // Holds whether the cpu was running during the last iteration, | ||
| 90 | // so that the DebugModeLeft signal can be emitted before the | ||
| 91 | // next execution step | ||
| 92 | bool was_active = false; | ||
| 93 | while (!stop_token.stop_requested()) { | 86 | while (!stop_token.stop_requested()) { |
| 94 | if (running) { | 87 | std::unique_lock lk{m_should_run_mutex}; |
| 95 | if (was_active) { | 88 | if (m_should_run) { |
| 96 | emit DebugModeLeft(); | 89 | m_system.Run(); |
| 97 | } | 90 | m_is_running.store(true); |
| 91 | m_is_running.notify_all(); | ||
| 98 | 92 | ||
| 99 | running_guard = true; | 93 | Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; }); |
| 100 | Core::SystemResultStatus result = system.Run(); | ||
| 101 | if (result != Core::SystemResultStatus::Success) { | ||
| 102 | running_guard = false; | ||
| 103 | this->SetRunning(false); | ||
| 104 | emit ErrorThrown(result, system.GetStatusDetails()); | ||
| 105 | } | ||
| 106 | |||
| 107 | if (debugger_should_start) { | ||
| 108 | system.InitializeDebugger(); | ||
| 109 | debugger_should_start = false; | ||
| 110 | } | ||
| 111 | |||
| 112 | running_wait.Wait(); | ||
| 113 | result = system.Pause(); | ||
| 114 | if (result != Core::SystemResultStatus::Success) { | ||
| 115 | running_guard = false; | ||
| 116 | this->SetRunning(false); | ||
| 117 | emit ErrorThrown(result, system.GetStatusDetails()); | ||
| 118 | } | ||
| 119 | running_guard = false; | ||
| 120 | |||
| 121 | if (!stop_token.stop_requested()) { | ||
| 122 | was_active = true; | ||
| 123 | emit DebugModeEntered(); | ||
| 124 | } | ||
| 125 | } else { | 94 | } else { |
| 126 | std::unique_lock lock{running_mutex}; | 95 | m_system.Pause(); |
| 127 | Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); }); | 96 | m_is_running.store(false); |
| 97 | m_is_running.notify_all(); | ||
| 98 | |||
| 99 | emit DebugModeEntered(); | ||
| 100 | Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; }); | ||
| 101 | emit DebugModeLeft(); | ||
| 128 | } | 102 | } |
| 129 | } | 103 | } |
| 130 | 104 | ||
| 131 | // Shutdown the main emulated process | 105 | // Shutdown the main emulated process |
| 132 | system.ShutdownMainProcess(); | 106 | m_system.DetachDebugger(); |
| 107 | m_system.ShutdownMainProcess(); | ||
| 133 | 108 | ||
| 134 | #if MICROPROFILE_ENABLED | 109 | #if MICROPROFILE_ENABLED |
| 135 | MicroProfileOnThreadExit(); | 110 | 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 { | |||
| 47 | Q_OBJECT | 47 | Q_OBJECT |
| 48 | 48 | ||
| 49 | public: | 49 | public: |
| 50 | explicit EmuThread(Core::System& system_); | 50 | explicit EmuThread(Core::System& system); |
| 51 | ~EmuThread() override; | 51 | ~EmuThread() override; |
| 52 | 52 | ||
| 53 | /** | 53 | /** |
| @@ -57,30 +57,30 @@ public: | |||
| 57 | void run() override; | 57 | void run() override; |
| 58 | 58 | ||
| 59 | /** | 59 | /** |
| 60 | * Sets whether the emulation thread is running or not | 60 | * Sets whether the emulation thread should run or not |
| 61 | * @param running_ Boolean value, set the emulation thread to running if true | 61 | * @param should_run Boolean value, set the emulation thread to running if true |
| 62 | * @note This function is thread-safe | ||
| 63 | */ | 62 | */ |
| 64 | void SetRunning(bool running_) { | 63 | void SetRunning(bool should_run) { |
| 65 | std::unique_lock lock{running_mutex}; | 64 | // TODO: Prevent other threads from modifying the state until we finish. |
| 66 | running = running_; | 65 | { |
| 67 | lock.unlock(); | 66 | // Notify the running thread to change state. |
| 68 | running_cv.notify_all(); | 67 | std::unique_lock run_lk{m_should_run_mutex}; |
| 69 | if (!running) { | 68 | m_should_run = should_run; |
| 70 | running_wait.Set(); | 69 | m_should_run_cv.notify_one(); |
| 71 | /// Wait until effectively paused | 70 | } |
| 72 | while (running_guard) | 71 | |
| 73 | ; | 72 | // Wait until paused, if pausing. |
| 73 | if (!should_run) { | ||
| 74 | m_is_running.wait(true); | ||
| 74 | } | 75 | } |
| 75 | } | 76 | } |
| 76 | 77 | ||
| 77 | /** | 78 | /** |
| 78 | * Check if the emulation thread is running or not | 79 | * Check if the emulation thread is running or not |
| 79 | * @return True if the emulation thread is running, otherwise false | 80 | * @return True if the emulation thread is running, otherwise false |
| 80 | * @note This function is thread-safe | ||
| 81 | */ | 81 | */ |
| 82 | bool IsRunning() const { | 82 | bool IsRunning() const { |
| 83 | return running; | 83 | return m_is_running.load() || m_should_run; |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | /** | 86 | /** |
| @@ -88,18 +88,17 @@ public: | |||
| 88 | */ | 88 | */ |
| 89 | void ForceStop() { | 89 | void ForceStop() { |
| 90 | LOG_WARNING(Frontend, "Force stopping EmuThread"); | 90 | LOG_WARNING(Frontend, "Force stopping EmuThread"); |
| 91 | stop_source.request_stop(); | 91 | m_stop_source.request_stop(); |
| 92 | SetRunning(false); | ||
| 93 | } | 92 | } |
| 94 | 93 | ||
| 95 | private: | 94 | private: |
| 96 | bool running = false; | 95 | Core::System& m_system; |
| 97 | std::stop_source stop_source; | 96 | |
| 98 | std::mutex running_mutex; | 97 | std::stop_source m_stop_source; |
| 99 | std::condition_variable_any running_cv; | 98 | std::mutex m_should_run_mutex; |
| 100 | Common::Event running_wait{}; | 99 | std::condition_variable_any m_should_run_cv; |
| 101 | std::atomic_bool running_guard{false}; | 100 | std::atomic<bool> m_is_running{false}; |
| 102 | Core::System& system; | 101 | bool m_should_run{true}; |
| 103 | 102 | ||
| 104 | signals: | 103 | signals: |
| 105 | /** | 104 | /** |
| @@ -120,8 +119,6 @@ signals: | |||
| 120 | */ | 119 | */ |
| 121 | void DebugModeLeft(); | 120 | void DebugModeLeft(); |
| 122 | 121 | ||
| 123 | void ErrorThrown(Core::SystemResultStatus, std::string); | ||
| 124 | |||
| 125 | void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); | 122 | void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); |
| 126 | }; | 123 | }; |
| 127 | 124 | ||
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() { | |||
| 1498 | 1498 | ||
| 1499 | void GMainWindow::HandleSigInterrupt(int sig) { | 1499 | void GMainWindow::HandleSigInterrupt(int sig) { |
| 1500 | if (sig == SIGINT) { | 1500 | if (sig == SIGINT) { |
| 1501 | exit(1); | 1501 | _exit(1); |
| 1502 | } | 1502 | } |
| 1503 | 1503 | ||
| 1504 | // Calling into Qt directly from a signal handler is not safe, | 1504 | // Calling into Qt directly from a signal handler is not safe, |
| @@ -1550,8 +1550,9 @@ void GMainWindow::AllowOSSleep() { | |||
| 1550 | 1550 | ||
| 1551 | bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) { | 1551 | bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) { |
| 1552 | // Shutdown previous session if the emu thread is still active... | 1552 | // Shutdown previous session if the emu thread is still active... |
| 1553 | if (emu_thread != nullptr) | 1553 | if (emu_thread != nullptr) { |
| 1554 | ShutdownGame(); | 1554 | ShutdownGame(); |
| 1555 | } | ||
| 1555 | 1556 | ||
| 1556 | if (!render_window->InitRenderTarget()) { | 1557 | if (!render_window->InitRenderTarget()) { |
| 1557 | return false; | 1558 | return false; |
| @@ -1710,6 +1711,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t | |||
| 1710 | system->RegisterExecuteProgramCallback( | 1711 | system->RegisterExecuteProgramCallback( |
| 1711 | [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); | 1712 | [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); |
| 1712 | 1713 | ||
| 1714 | system->RegisterExitCallback([this] { | ||
| 1715 | emu_thread->ForceStop(); | ||
| 1716 | render_window->Exit(); | ||
| 1717 | }); | ||
| 1718 | |||
| 1713 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | 1719 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |
| 1714 | connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); | 1720 | connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); |
| 1715 | // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views | 1721 | // 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 | |||
| 1779 | OnStartGame(); | 1785 | OnStartGame(); |
| 1780 | } | 1786 | } |
| 1781 | 1787 | ||
| 1782 | void GMainWindow::ShutdownGame() { | 1788 | void GMainWindow::OnShutdownBegin() { |
| 1783 | if (!emulation_running) { | 1789 | if (!emulation_running) { |
| 1784 | return; | 1790 | return; |
| 1785 | } | 1791 | } |
| @@ -1794,20 +1800,48 @@ void GMainWindow::ShutdownGame() { | |||
| 1794 | Settings::values.use_speed_limit.SetValue(true); | 1800 | Settings::values.use_speed_limit.SetValue(true); |
| 1795 | 1801 | ||
| 1796 | system->SetShuttingDown(true); | 1802 | system->SetShuttingDown(true); |
| 1797 | system->DetachDebugger(); | ||
| 1798 | discord_rpc->Pause(); | 1803 | discord_rpc->Pause(); |
| 1799 | 1804 | ||
| 1800 | RequestGameExit(); | 1805 | RequestGameExit(); |
| 1806 | emu_thread->disconnect(); | ||
| 1807 | emu_thread->SetRunning(true); | ||
| 1801 | 1808 | ||
| 1802 | emit EmulationStopping(); | 1809 | emit EmulationStopping(); |
| 1803 | 1810 | ||
| 1804 | // Wait for emulation thread to complete and delete it | 1811 | shutdown_timer.setSingleShot(true); |
| 1805 | if (!emu_thread->wait(5000)) { | 1812 | shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000); |
| 1813 | connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); | ||
| 1814 | connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); | ||
| 1815 | |||
| 1816 | // Disable everything to prevent anything from being triggered here | ||
| 1817 | ui->action_Pause->setEnabled(false); | ||
| 1818 | ui->action_Restart->setEnabled(false); | ||
| 1819 | ui->action_Stop->setEnabled(false); | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | void GMainWindow::OnShutdownBeginDialog() { | ||
| 1823 | shutdown_dialog = new OverlayDialog(this, *system, QString{}, tr("Closing software..."), | ||
| 1824 | QString{}, QString{}, Qt::AlignHCenter | Qt::AlignVCenter); | ||
| 1825 | shutdown_dialog->open(); | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | void GMainWindow::OnEmulationStopTimeExpired() { | ||
| 1829 | if (emu_thread) { | ||
| 1806 | emu_thread->ForceStop(); | 1830 | emu_thread->ForceStop(); |
| 1807 | emu_thread->wait(); | ||
| 1808 | } | 1831 | } |
| 1832 | } | ||
| 1833 | |||
| 1834 | void GMainWindow::OnEmulationStopped() { | ||
| 1835 | shutdown_timer.stop(); | ||
| 1836 | emu_thread->disconnect(); | ||
| 1837 | emu_thread->wait(); | ||
| 1809 | emu_thread = nullptr; | 1838 | emu_thread = nullptr; |
| 1810 | 1839 | ||
| 1840 | if (shutdown_dialog) { | ||
| 1841 | shutdown_dialog->deleteLater(); | ||
| 1842 | shutdown_dialog = nullptr; | ||
| 1843 | } | ||
| 1844 | |||
| 1811 | emulation_running = false; | 1845 | emulation_running = false; |
| 1812 | 1846 | ||
| 1813 | discord_rpc->Update(); | 1847 | discord_rpc->Update(); |
| @@ -1853,6 +1887,20 @@ void GMainWindow::ShutdownGame() { | |||
| 1853 | 1887 | ||
| 1854 | // When closing the game, destroy the GLWindow to clear the context after the game is closed | 1888 | // When closing the game, destroy the GLWindow to clear the context after the game is closed |
| 1855 | render_window->ReleaseRenderTarget(); | 1889 | render_window->ReleaseRenderTarget(); |
| 1890 | |||
| 1891 | Settings::RestoreGlobalState(system->IsPoweredOn()); | ||
| 1892 | system->HIDCore().ReloadInputDevices(); | ||
| 1893 | UpdateStatusButtons(); | ||
| 1894 | } | ||
| 1895 | |||
| 1896 | void GMainWindow::ShutdownGame() { | ||
| 1897 | if (!emulation_running) { | ||
| 1898 | return; | ||
| 1899 | } | ||
| 1900 | |||
| 1901 | OnShutdownBegin(); | ||
| 1902 | OnEmulationStopTimeExpired(); | ||
| 1903 | OnEmulationStopped(); | ||
| 1856 | } | 1904 | } |
| 1857 | 1905 | ||
| 1858 | void GMainWindow::StoreRecentFile(const QString& filename) { | 1906 | void GMainWindow::StoreRecentFile(const QString& filename) { |
| @@ -2919,8 +2967,6 @@ void GMainWindow::OnStartGame() { | |||
| 2919 | 2967 | ||
| 2920 | emu_thread->SetRunning(true); | 2968 | emu_thread->SetRunning(true); |
| 2921 | 2969 | ||
| 2922 | connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); | ||
| 2923 | |||
| 2924 | UpdateMenuState(); | 2970 | UpdateMenuState(); |
| 2925 | OnTasStateChanged(); | 2971 | OnTasStateChanged(); |
| 2926 | 2972 | ||
| @@ -2957,11 +3003,8 @@ void GMainWindow::OnStopGame() { | |||
| 2957 | return; | 3003 | return; |
| 2958 | } | 3004 | } |
| 2959 | 3005 | ||
| 2960 | ShutdownGame(); | 3006 | OnShutdownBegin(); |
| 2961 | 3007 | OnShutdownBeginDialog(); | |
| 2962 | Settings::RestoreGlobalState(system->IsPoweredOn()); | ||
| 2963 | system->HIDCore().ReloadInputDevices(); | ||
| 2964 | UpdateStatusButtons(); | ||
| 2965 | } | 3008 | } |
| 2966 | 3009 | ||
| 2967 | void GMainWindow::OnLoadComplete() { | 3010 | void GMainWindow::OnLoadComplete() { |
| @@ -3904,79 +3947,6 @@ void GMainWindow::OnMouseActivity() { | |||
| 3904 | mouse_center_timer.stop(); | 3947 | mouse_center_timer.stop(); |
| 3905 | } | 3948 | } |
| 3906 | 3949 | ||
| 3907 | void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) { | ||
| 3908 | QMessageBox::StandardButton answer; | ||
| 3909 | QString status_message; | ||
| 3910 | const QString common_message = | ||
| 3911 | tr("The game you are trying to load requires additional files from your Switch to be " | ||
| 3912 | "dumped " | ||
| 3913 | "before playing.<br/><br/>For more information on dumping these files, please see the " | ||
| 3914 | "following wiki page: <a " | ||
| 3915 | "href='https://yuzu-emu.org/wiki/" | ||
| 3916 | "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " | ||
| 3917 | "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to " | ||
| 3918 | "quit " | ||
| 3919 | "back to the game list? Continuing emulation may result in crashes, corrupted save " | ||
| 3920 | "data, or other bugs."); | ||
| 3921 | switch (result) { | ||
| 3922 | case Core::SystemResultStatus::ErrorSystemFiles: { | ||
| 3923 | QString message; | ||
| 3924 | if (details.empty()) { | ||
| 3925 | message = | ||
| 3926 | tr("yuzu was unable to locate a Switch system archive. %1").arg(common_message); | ||
| 3927 | } else { | ||
| 3928 | message = tr("yuzu was unable to locate a Switch system archive: %1. %2") | ||
| 3929 | .arg(QString::fromStdString(details), common_message); | ||
| 3930 | } | ||
| 3931 | |||
| 3932 | answer = QMessageBox::question(this, tr("System Archive Not Found"), message, | ||
| 3933 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | ||
| 3934 | status_message = tr("System Archive Missing"); | ||
| 3935 | break; | ||
| 3936 | } | ||
| 3937 | |||
| 3938 | case Core::SystemResultStatus::ErrorSharedFont: { | ||
| 3939 | const QString message = | ||
| 3940 | tr("yuzu was unable to locate the Switch shared fonts. %1").arg(common_message); | ||
| 3941 | answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message, | ||
| 3942 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | ||
| 3943 | status_message = tr("Shared Font Missing"); | ||
| 3944 | break; | ||
| 3945 | } | ||
| 3946 | |||
| 3947 | default: | ||
| 3948 | answer = QMessageBox::question( | ||
| 3949 | this, tr("Fatal Error"), | ||
| 3950 | tr("yuzu has encountered a fatal error, please see the log for more details. " | ||
| 3951 | "For more information on accessing the log, please see the following page: " | ||
| 3952 | "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How " | ||
| 3953 | "to " | ||
| 3954 | "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game " | ||
| 3955 | "list? " | ||
| 3956 | "Continuing emulation may result in crashes, corrupted save data, or other " | ||
| 3957 | "bugs."), | ||
| 3958 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | ||
| 3959 | status_message = tr("Fatal Error encountered"); | ||
| 3960 | break; | ||
| 3961 | } | ||
| 3962 | |||
| 3963 | if (answer == QMessageBox::Yes) { | ||
| 3964 | if (emu_thread) { | ||
| 3965 | ShutdownGame(); | ||
| 3966 | |||
| 3967 | Settings::RestoreGlobalState(system->IsPoweredOn()); | ||
| 3968 | system->HIDCore().ReloadInputDevices(); | ||
| 3969 | UpdateStatusButtons(); | ||
| 3970 | } | ||
| 3971 | } else { | ||
| 3972 | // Only show the message if the game is still running. | ||
| 3973 | if (emu_thread) { | ||
| 3974 | emu_thread->SetRunning(true); | ||
| 3975 | message_label->setText(status_message); | ||
| 3976 | } | ||
| 3977 | } | ||
| 3978 | } | ||
| 3979 | |||
| 3980 | void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | 3950 | void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { |
| 3981 | if (behavior == ReinitializeKeyBehavior::Warning) { | 3951 | if (behavior == ReinitializeKeyBehavior::Warning) { |
| 3982 | const auto res = QMessageBox::information( | 3952 | const auto res = QMessageBox::information( |
| @@ -4121,10 +4091,6 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 4121 | // Shutdown session if the emu thread is active... | 4091 | // Shutdown session if the emu thread is active... |
| 4122 | if (emu_thread != nullptr) { | 4092 | if (emu_thread != nullptr) { |
| 4123 | ShutdownGame(); | 4093 | ShutdownGame(); |
| 4124 | |||
| 4125 | Settings::RestoreGlobalState(system->IsPoweredOn()); | ||
| 4126 | system->HIDCore().ReloadInputDevices(); | ||
| 4127 | UpdateStatusButtons(); | ||
| 4128 | } | 4094 | } |
| 4129 | 4095 | ||
| 4130 | render_window->close(); | 4096 | render_window->close(); |
| @@ -4217,6 +4183,10 @@ bool GMainWindow::ConfirmForceLockedExit() { | |||
| 4217 | } | 4183 | } |
| 4218 | 4184 | ||
| 4219 | void GMainWindow::RequestGameExit() { | 4185 | void GMainWindow::RequestGameExit() { |
| 4186 | if (!system->IsPoweredOn()) { | ||
| 4187 | return; | ||
| 4188 | } | ||
| 4189 | |||
| 4220 | auto& sm{system->ServiceManager()}; | 4190 | auto& sm{system->ServiceManager()}; |
| 4221 | auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE"); | 4191 | auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE"); |
| 4222 | auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); | 4192 | 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; | |||
| 29 | class GRenderWindow; | 29 | class GRenderWindow; |
| 30 | class LoadingScreen; | 30 | class LoadingScreen; |
| 31 | class MicroProfileDialog; | 31 | class MicroProfileDialog; |
| 32 | class OverlayDialog; | ||
| 32 | class ProfilerWidget; | 33 | class ProfilerWidget; |
| 33 | class ControllerDialog; | 34 | class ControllerDialog; |
| 34 | class QLabel; | 35 | class QLabel; |
| @@ -332,10 +333,13 @@ private slots: | |||
| 332 | void ResetWindowSize900(); | 333 | void ResetWindowSize900(); |
| 333 | void ResetWindowSize1080(); | 334 | void ResetWindowSize1080(); |
| 334 | void OnCaptureScreenshot(); | 335 | void OnCaptureScreenshot(); |
| 335 | void OnCoreError(Core::SystemResultStatus, std::string); | ||
| 336 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 336 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| 337 | void OnLanguageChanged(const QString& locale); | 337 | void OnLanguageChanged(const QString& locale); |
| 338 | void OnMouseActivity(); | 338 | void OnMouseActivity(); |
| 339 | void OnShutdownBegin(); | ||
| 340 | void OnShutdownBeginDialog(); | ||
| 341 | void OnEmulationStopped(); | ||
| 342 | void OnEmulationStopTimeExpired(); | ||
| 339 | 343 | ||
| 340 | private: | 344 | private: |
| 341 | QString GetGameListErrorRemoving(InstalledEntryType type) const; | 345 | QString GetGameListErrorRemoving(InstalledEntryType type) const; |
| @@ -385,6 +389,8 @@ private: | |||
| 385 | GRenderWindow* render_window; | 389 | GRenderWindow* render_window; |
| 386 | GameList* game_list; | 390 | GameList* game_list; |
| 387 | LoadingScreen* loading_screen; | 391 | LoadingScreen* loading_screen; |
| 392 | QTimer shutdown_timer; | ||
| 393 | OverlayDialog* shutdown_dialog{}; | ||
| 388 | 394 | ||
| 389 | GameListPlaceholder* game_list_placeholder; | 395 | GameListPlaceholder* game_list_placeholder; |
| 390 | 396 | ||
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 @@ | |||
| 3 | 3 | ||
| 4 | #include <QKeyEvent> | 4 | #include <QKeyEvent> |
| 5 | #include <QScreen> | 5 | #include <QScreen> |
| 6 | #include <QWindow> | ||
| 6 | 7 | ||
| 7 | #include "core/core.h" | 8 | #include "core/core.h" |
| 8 | #include "core/hid/hid_types.h" | 9 | #include "core/hid/hid_types.h" |
| @@ -162,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() { | |||
| 162 | const auto height = static_cast<float>(parentWidget()->height()); | 163 | const auto height = static_cast<float>(parentWidget()->height()); |
| 163 | 164 | ||
| 164 | // High DPI | 165 | // High DPI |
| 165 | const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f; | 166 | const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f; |
| 166 | 167 | ||
| 167 | const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; | 168 | const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; |
| 168 | const auto body_text_font_size = | 169 | const auto body_text_font_size = |
| @@ -259,3 +260,9 @@ void OverlayDialog::InputThread() { | |||
| 259 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); | 260 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
| 260 | } | 261 | } |
| 261 | } | 262 | } |
| 263 | |||
| 264 | void OverlayDialog::keyPressEvent(QKeyEvent* e) { | ||
| 265 | if (!ui->buttonsDialog->isHidden() || e->key() != Qt::Key_Escape) { | ||
| 266 | QDialog::keyPressEvent(e); | ||
| 267 | } | ||
| 268 | } | ||
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: | |||
| 94 | 94 | ||
| 95 | /// The thread where input is being polled and processed. | 95 | /// The thread where input is being polled and processed. |
| 96 | void InputThread(); | 96 | void InputThread(); |
| 97 | void keyPressEvent(QKeyEvent* e) override; | ||
| 97 | 98 | ||
| 98 | std::unique_ptr<Ui::OverlayDialog> ui; | 99 | std::unique_ptr<Ui::OverlayDialog> ui; |
| 99 | 100 | ||
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) | |||
| 49 | install(TARGETS yuzu-cmd) | 49 | install(TARGETS yuzu-cmd) |
| 50 | endif() | 50 | endif() |
| 51 | 51 | ||
| 52 | if(WIN32) | ||
| 53 | # compile as a win32 gui application instead of a console application | ||
| 54 | if(MSVC) | ||
| 55 | set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") | ||
| 56 | elseif(MINGW) | ||
| 57 | set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows") | ||
| 58 | endif() | ||
| 59 | endif() | ||
| 60 | |||
| 52 | if (MSVC) | 61 | if (MSVC) |
| 53 | include(CopyYuzuSDLDeps) | 62 | include(CopyYuzuSDLDeps) |
| 54 | copy_yuzu_SDL_deps(yuzu-cmd) | 63 | 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) { | |||
| 174 | 174 | ||
| 175 | /// Application entry point | 175 | /// Application entry point |
| 176 | int main(int argc, char** argv) { | 176 | int main(int argc, char** argv) { |
| 177 | #ifdef _WIN32 | ||
| 178 | if (AttachConsole(ATTACH_PARENT_PROCESS)) { | ||
| 179 | freopen("CONOUT$", "wb", stdout); | ||
| 180 | freopen("CONOUT$", "wb", stderr); | ||
| 181 | } | ||
| 182 | #endif | ||
| 183 | |||
| 177 | Common::Log::Initialize(); | 184 | Common::Log::Initialize(); |
| 178 | Common::Log::SetColorConsoleBackendEnabled(true); | 185 | Common::Log::SetColorConsoleBackendEnabled(true); |
| 179 | Common::Log::Start(); | 186 | Common::Log::Start(); |