diff --git a/src/core/core.cpp b/src/core/core.cpp index 76bb2bae9d..92ba42fb9c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -163,6 +163,7 @@ struct System::Impl { gpu_core = VideoCore::CreateGPU(system); is_powered_on = true; + exit_lock = false; LOG_DEBUG(Core, "Initialized OK"); @@ -249,6 +250,7 @@ struct System::Impl { perf_stats->GetMeanFrametime()); is_powered_on = false; + exit_lock = false; // Shutdown emulation session renderer.reset(); @@ -333,6 +335,7 @@ struct System::Impl { std::unique_ptr interrupt_manager; CpuCoreManager cpu_core_manager; bool is_powered_on = false; + bool exit_lock = false; std::unique_ptr cheat_engine; std::unique_ptr memory_freezer; @@ -629,6 +632,14 @@ const Service::APM::Controller& System::GetAPMController() const { return impl->apm_controller; } +void System::SetExitLock(bool locked) { + impl->exit_lock = locked; +} + +bool System::GetExitLock() const { + return impl->exit_lock; +} + System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { return impl->Init(*this, emu_window); } diff --git a/src/core/core.h b/src/core/core.h index d2a3c82d8a..ff10ebe12b 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -326,6 +326,10 @@ public: const Service::APM::Controller& GetAPMController() const; + void SetExitLock(bool locked); + + bool GetExitLock() const; + private: System(); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3366fd8ce4..797c9a06fc 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -232,12 +232,12 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { IDebugFunctions::~IDebugFunctions() = default; -ISelfController::ISelfController(Core::System& system_, - std::shared_ptr nvflinger_) - : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger_)) { +ISelfController::ISelfController(Core::System& system, + std::shared_ptr nvflinger) + : ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Exit"}, + {0, &ISelfController::Exit, "Exit"}, {1, &ISelfController::LockExit, "LockExit"}, {2, &ISelfController::UnlockExit, "UnlockExit"}, {3, &ISelfController::EnterFatalSection, "EnterFatalSection"}, @@ -282,7 +282,7 @@ ISelfController::ISelfController(Core::System& system_, RegisterHandlers(functions); - auto& kernel = system_.Kernel(); + auto& kernel = system.Kernel(); launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, "ISelfController:LaunchableEvent"); @@ -298,15 +298,28 @@ ISelfController::ISelfController(Core::System& system_, ISelfController::~ISelfController() = default; +void ISelfController::Exit(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + system.Shutdown(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); + + system.SetExitLock(true); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); + + system.SetExitLock(false); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -550,6 +563,10 @@ void AppletMessageQueue::OperationModeChanged() { on_operation_mode_changed.writable->Signal(); } +void AppletMessageQueue::RequestExit() { + PushMessage(AppletMessage::ExitRequested); +} + ICommonStateGetter::ICommonStateGetter(Core::System& system, std::shared_ptr msg_queue) : ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) { diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 28f870302d..a3baeb6730 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -45,6 +45,7 @@ class AppletMessageQueue { public: enum class AppletMessage : u32 { NoMessage = 0, + ExitRequested = 4, FocusStateChanged = 15, OperationModeChanged = 30, PerformanceModeChanged = 31, @@ -59,6 +60,7 @@ public: AppletMessage PopMessage(); std::size_t GetMessageCount() const; void OperationModeChanged(); + void RequestExit(); private: std::queue messages; @@ -123,6 +125,7 @@ public: ~ISelfController() override; private: + void Exit(Kernel::HLERequestContext& ctx); void LockExit(Kernel::HLERequestContext& ctx); void UnlockExit(Kernel::HLERequestContext& ctx); void EnterFatalSection(Kernel::HLERequestContext& ctx); @@ -151,6 +154,8 @@ private: u32 idle_time_detection_extension = 0; u64 num_fatal_sections_entered = 0; bool is_auto_sleep_disabled = false; + + Core::System& system; }; class ICommonStateGetter final : public ServiceFramework { diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index 0e0d108586..2e3e45915b 100644 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h @@ -19,6 +19,8 @@ class NVFlinger; namespace AM { +class AppletMessageQueue; + class AppletAE final : public ServiceFramework { public: explicit AppletAE(std::shared_ptr nvflinger, diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index 99a65e7b50..758da792d9 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -19,6 +19,8 @@ class NVFlinger; namespace AM { +class AppletMessageQueue; + class AppletOE final : public ServiceFramework { public: explicit AppletOE(std::shared_ptr nvflinger, diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f147044d99..2d82df7394 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -22,6 +22,8 @@ #include "core/frontend/applets/general_frontend.h" #include "core/frontend/scope_acquire_window_context.h" #include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/applet_ae.h" +#include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/hid.h" @@ -83,6 +85,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/file_sys/submission_package.h" #include "core/frontend/applets/software_keyboard.h" #include "core/hle/kernel/process.h" +#include "core/hle/service/am/am.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/sm/sm.h" @@ -1674,6 +1677,11 @@ void GMainWindow::OnStartGame() { } void GMainWindow::OnPauseGame() { + Core::System& system{Core::System::GetInstance()}; + if (system.GetExitLock() && !ConfirmForceLockedExit()) { + return; + } + emu_thread->SetRunning(false); ui.action_Start->setEnabled(true); @@ -1685,6 +1693,11 @@ void GMainWindow::OnPauseGame() { } void GMainWindow::OnStopGame() { + Core::System& system{Core::System::GetInstance()}; + if (system.GetExitLock() && !ConfirmForceLockedExit()) { + return; + } + ShutdownGame(); } @@ -2182,13 +2195,41 @@ bool GMainWindow::ConfirmChangeGame() { if (emu_thread == nullptr) return true; - auto answer = QMessageBox::question( + const auto answer = QMessageBox::question( this, tr("yuzu"), tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); return answer != QMessageBox::No; } +bool GMainWindow::ConfirmForceLockedExit() { + if (emu_thread == nullptr) + return true; + + const auto answer = + QMessageBox::question(this, tr("yuzu"), + tr("The currently running application has requested yuzu to not " + "exit.\n\nWould you like to bypass this and exit anyway?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + return answer != QMessageBox::No; +} + +void GMainWindow::RequestGameExit() { + auto& sm{Core::System::GetInstance().ServiceManager()}; + auto applet_oe = sm.GetService("appletOE"); + auto applet_ae = sm.GetService("appletAE"); + bool has_signalled = false; + + if (applet_oe != nullptr) { + applet_oe->GetMessageQueue()->RequestExit(); + has_signalled = true; + } + + if (applet_ae != nullptr && !has_signalled) { + applet_ae->GetMessageQueue()->RequestExit(); + } +} + void GMainWindow::filterBarSetChecked(bool state) { ui.action_Show_Filter_Bar->setChecked(state); emit(OnToggleFilterBar()); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 7d16188cbc..e942d1248b 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -172,6 +172,8 @@ private: */ bool ConfirmClose(); bool ConfirmChangeGame(); + bool ConfirmForceLockedExit(); + void RequestGameExit(); void closeEvent(QCloseEvent* event) override; private slots: