diff --git a/Source/Core/Core/HW/ProcessorInterface.cpp b/Source/Core/Core/HW/ProcessorInterface.cpp index 04fec4cd07..a24f5529b6 100644 --- a/Source/Core/Core/HW/ProcessorInterface.cpp +++ b/Source/Core/Core/HW/ProcessorInterface.cpp @@ -37,6 +37,9 @@ static void ToggleResetButtonCallback(u64 userdata, s64 cyclesLate); static CoreTiming::EventType* iosNotifyResetButton; static void IOSNotifyResetButtonCallback(u64 userdata, s64 cyclesLate); +static CoreTiming::EventType* iosNotifyPowerButton; +static void IOSNotifyPowerButtonCallback(u64 userdata, s64 cyclesLate); + // Let the PPC know that an external exception is set/cleared void UpdateException(); @@ -75,6 +78,8 @@ void Init() toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback); iosNotifyResetButton = CoreTiming::RegisterEvent("IOSNotifyResetButton", IOSNotifyResetButtonCallback); + iosNotifyPowerButton = + CoreTiming::RegisterEvent("IOSNotifyPowerButton", IOSNotifyPowerButtonCallback); } void RegisterMMIO(MMIO::Mapping* mmio, u32 base) @@ -214,6 +219,17 @@ static void IOSNotifyResetButtonCallback(u64 userdata, s64 cyclesLate) } } +static void IOSNotifyPowerButtonCallback(u64 userdata, s64 cyclesLate) +{ + if (SConfig::GetInstance().bWii) + { + std::shared_ptr stm = + WII_IPC_HLE_Interface::GetDeviceByName("/dev/stm/eventhook"); + if (stm) + std::static_pointer_cast(stm)->PowerButton(); + } +} + void ResetButton_Tap() { CoreTiming::ScheduleEvent(0, toggleResetButton, true, CoreTiming::FromThread::ANY); @@ -222,4 +238,9 @@ void ResetButton_Tap() CoreTiming::FromThread::ANY); } +void PowerButton_Tap() +{ + CoreTiming::ScheduleEvent(0, iosNotifyPowerButton, 0, CoreTiming::FromThread::ANY); +} + } // namespace ProcessorInterface diff --git a/Source/Core/Core/HW/ProcessorInterface.h b/Source/Core/Core/HW/ProcessorInterface.h index 5b9310e9fb..3f42105358 100644 --- a/Source/Core/Core/HW/ProcessorInterface.h +++ b/Source/Core/Core/HW/ProcessorInterface.h @@ -76,5 +76,6 @@ void SetInterrupt(u32 _causemask, bool _bSet = true); // Thread-safe func which sets and clears reset button state automagically void ResetButton_Tap(); +void PowerButton_Tap(); } // namespace ProcessorInterface diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_stm.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_stm.cpp index 38612a4929..0bd3f8f257 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_stm.cpp +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_stm.cpp @@ -4,6 +4,12 @@ #include "Core/IPC_HLE/WII_IPC_HLE_Device_stm.h" +namespace Core +{ +void QueueHostJob(std::function job, bool run_during_stop); +void Stop(); +} + static u32 s_event_hook_address = 0; IPCCommandResult CWII_IPC_HLE_Device_stm_immediate::Open(u32 command_address, u32 mode) @@ -38,6 +44,12 @@ IPCCommandResult CWII_IPC_HLE_Device_stm_immediate::IOCtl(u32 command_address) switch (parameter) { + case IOCTL_STM_IDLE: + case IOCTL_STM_SHUTDOWN: + NOTICE_LOG(WII_IPC_STM, "IOCTL_STM_IDLE or IOCTL_STM_SHUTDOWN received, shutting down"); + Core::QueueHostJob(&Core::Stop, false); + break; + case IOCTL_STM_RELEASE_EH: if (s_event_hook_address == 0) { @@ -123,7 +135,7 @@ IPCCommandResult CWII_IPC_HLE_Device_stm_eventhook::IOCtl(u32 command_address) return GetNoReply(); } -void CWII_IPC_HLE_Device_stm_eventhook::ResetButton() const +void CWII_IPC_HLE_Device_stm_eventhook::TriggerEvent(const u32 event) const { if (!m_Active || s_event_hook_address == 0) { @@ -133,7 +145,7 @@ void CWII_IPC_HLE_Device_stm_eventhook::ResetButton() const // The reset button returns STM_EVENT_RESET. u32 buffer_out = Memory::Read_U32(s_event_hook_address + 0x18); - Memory::Write_U32(STM_EVENT_RESET, buffer_out); + Memory::Write_U32(event, buffer_out); // Fill in command buffer. Memory::Write_U32(FS_SUCCESS, s_event_hook_address + 4); @@ -144,3 +156,14 @@ void CWII_IPC_HLE_Device_stm_eventhook::ResetButton() const WII_IPC_HLE_Interface::EnqueueReply(s_event_hook_address); s_event_hook_address = 0; } + +void CWII_IPC_HLE_Device_stm_eventhook::ResetButton() const +{ + // The reset button returns STM_EVENT_RESET. + TriggerEvent(STM_EVENT_RESET); +} + +void CWII_IPC_HLE_Device_stm_eventhook::PowerButton() const +{ + TriggerEvent(STM_EVENT_POWER); +} diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_stm.h b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_stm.h index a84837bb29..56adc6a929 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_stm.h +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_stm.h @@ -60,4 +60,8 @@ public: IPCCommandResult IOCtl(u32 command_address) override; void ResetButton() const; + void PowerButton() const; + +private: + void TriggerEvent(u32 event) const; }; diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h index 43a5a3745a..1e7c5eec0a 100644 --- a/Source/Core/DolphinWX/Frame.h +++ b/Source/Core/DolphinWX/Frame.h @@ -158,6 +158,7 @@ private: bool m_bGameLoading = false; bool m_bClosing = false; bool m_confirmStop = false; + bool m_tried_graceful_shutdown = false; int m_saveSlot = 1; std::vector drives; diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 2b690c0e5a..e20cc15411 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -1155,9 +1155,12 @@ void CFrame::DoStop() Core::SetState(Core::CORE_PAUSE); } - wxMessageDialog m_StopDlg(this, _("Do you want to stop the current emulation?"), - _("Please confirm..."), - wxYES_NO | wxSTAY_ON_TOP | wxICON_EXCLAMATION, wxDefaultPosition); + wxMessageDialog m_StopDlg( + this, !m_tried_graceful_shutdown ? _("Do you want to stop the current emulation?") : + _("A shutdown is already in progress. Unsaved data " + "may be lost if you stop the current emulation " + "before it completes. Force stop?"), + _("Please confirm..."), wxYES_NO | wxSTAY_ON_TOP | wxICON_EXCLAMATION, wxDefaultPosition); HotkeyManagerEmu::Enable(false); int Ret = m_StopDlg.ShowModal(); @@ -1172,6 +1175,16 @@ void CFrame::DoStop() } } + if (SConfig::GetInstance().bWii && !m_tried_graceful_shutdown) + { + Core::DisplayMessage("Shutting down", 30000); + Core::SetState(Core::CORE_RUN); + ProcessorInterface::PowerButton_Tap(); + m_confirmStop = false; + m_tried_graceful_shutdown = true; + return; + } + if (UseDebugger && g_pCodeWindow) { if (g_pCodeWindow->m_WatchWindow) @@ -1207,6 +1220,7 @@ void CFrame::DoStop() void CFrame::OnStopped() { m_confirmStop = false; + m_tried_graceful_shutdown = false; #if defined(HAVE_X11) && HAVE_X11 if (SConfig::GetInstance().bDisableScreenSaver)