summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/core.cpp18
-rw-r--r--src/core/core.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp12
-rw-r--r--src/yuzu/bootmanager.cpp81
-rw-r--r--src/yuzu/bootmanager.h51
-rw-r--r--src/yuzu/main.cpp152
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu/util/overlay_dialog.cpp9
-rw-r--r--src/yuzu/util/overlay_dialog.h1
-rw-r--r--src/yuzu_cmd/CMakeLists.txt9
-rw-r--r--src/yuzu_cmd/yuzu.cpp7
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
556SystemResultStatus System::Run() { 550void System::Run() {
557 return impl->Run(); 551 impl->Run();
558} 552}
559 553
560SystemResultStatus System::Pause() { 554void System::Pause() {
561 return impl->Pause(); 555 impl->Pause();
562} 556}
563 557
564bool System::IsPaused() const { 558bool 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
47static Core::Frontend::WindowSystemType GetWindowSystemType(); 47static Core::Frontend::WindowSystemType GetWindowSystemType();
48 48
49EmuThread::EmuThread(Core::System& system_) : system{system_} {} 49EmuThread::EmuThread(Core::System& system) : m_system{system} {}
50 50
51EmuThread::~EmuThread() = default; 51EmuThread::~EmuThread() = default;
52 52
53void EmuThread::run() { 53void 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
49public: 49public:
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
95private: 94private:
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
104signals: 103signals:
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
1499void GMainWindow::HandleSigInterrupt(int sig) { 1499void 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
1551bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) { 1551bool 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
1782void GMainWindow::ShutdownGame() { 1788void 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
1822void 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
1828void GMainWindow::OnEmulationStopTimeExpired() {
1829 if (emu_thread) {
1806 emu_thread->ForceStop(); 1830 emu_thread->ForceStop();
1807 emu_thread->wait();
1808 } 1831 }
1832}
1833
1834void 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
1896void GMainWindow::ShutdownGame() {
1897 if (!emulation_running) {
1898 return;
1899 }
1900
1901 OnShutdownBegin();
1902 OnEmulationStopTimeExpired();
1903 OnEmulationStopped();
1856} 1904}
1857 1905
1858void GMainWindow::StoreRecentFile(const QString& filename) { 1906void 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
2967void GMainWindow::OnLoadComplete() { 3010void GMainWindow::OnLoadComplete() {
@@ -3904,79 +3947,6 @@ void GMainWindow::OnMouseActivity() {
3904 mouse_center_timer.stop(); 3947 mouse_center_timer.stop();
3905} 3948}
3906 3949
3907void 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
3980void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { 3950void 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
4219void GMainWindow::RequestGameExit() { 4185void 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;
29class GRenderWindow; 29class GRenderWindow;
30class LoadingScreen; 30class LoadingScreen;
31class MicroProfileDialog; 31class MicroProfileDialog;
32class OverlayDialog;
32class ProfilerWidget; 33class ProfilerWidget;
33class ControllerDialog; 34class ControllerDialog;
34class QLabel; 35class 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
340private: 344private:
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
264void 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)
50endif() 50endif()
51 51
52if(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()
59endif()
60
52if (MSVC) 61if (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
176int main(int argc, char** argv) { 176int 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();