Shut down Wii software gracefully
This adds support for triggering the power event (in the STM), so that stopping emulation first triggers a shutdown event, which notably gives emulated software time to save game data (issue 8979) and clean up SYSCONF (to disconnect Wiimotes and update their state in the SYSCONF). On the first press, the stop button/hotkey/whatever will trigger a STM power event. On a second try, we will forcefully stop emulation, just like how it was working before.
This commit is contained in:
parent
ae723f5251
commit
9b72b5f144
|
@ -37,6 +37,9 @@ static void ToggleResetButtonCallback(u64 userdata, s64 cyclesLate);
|
||||||
static CoreTiming::EventType* iosNotifyResetButton;
|
static CoreTiming::EventType* iosNotifyResetButton;
|
||||||
static void IOSNotifyResetButtonCallback(u64 userdata, s64 cyclesLate);
|
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
|
// Let the PPC know that an external exception is set/cleared
|
||||||
void UpdateException();
|
void UpdateException();
|
||||||
|
|
||||||
|
@ -75,6 +78,8 @@ void Init()
|
||||||
toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
|
toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
|
||||||
iosNotifyResetButton =
|
iosNotifyResetButton =
|
||||||
CoreTiming::RegisterEvent("IOSNotifyResetButton", IOSNotifyResetButtonCallback);
|
CoreTiming::RegisterEvent("IOSNotifyResetButton", IOSNotifyResetButtonCallback);
|
||||||
|
iosNotifyPowerButton =
|
||||||
|
CoreTiming::RegisterEvent("IOSNotifyPowerButton", IOSNotifyPowerButtonCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
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<IWII_IPC_HLE_Device> stm =
|
||||||
|
WII_IPC_HLE_Interface::GetDeviceByName("/dev/stm/eventhook");
|
||||||
|
if (stm)
|
||||||
|
std::static_pointer_cast<CWII_IPC_HLE_Device_stm_eventhook>(stm)->PowerButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ResetButton_Tap()
|
void ResetButton_Tap()
|
||||||
{
|
{
|
||||||
CoreTiming::ScheduleEvent(0, toggleResetButton, true, CoreTiming::FromThread::ANY);
|
CoreTiming::ScheduleEvent(0, toggleResetButton, true, CoreTiming::FromThread::ANY);
|
||||||
|
@ -222,4 +238,9 @@ void ResetButton_Tap()
|
||||||
CoreTiming::FromThread::ANY);
|
CoreTiming::FromThread::ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PowerButton_Tap()
|
||||||
|
{
|
||||||
|
CoreTiming::ScheduleEvent(0, iosNotifyPowerButton, 0, CoreTiming::FromThread::ANY);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ProcessorInterface
|
} // namespace ProcessorInterface
|
||||||
|
|
|
@ -76,5 +76,6 @@ void SetInterrupt(u32 _causemask, bool _bSet = true);
|
||||||
|
|
||||||
// Thread-safe func which sets and clears reset button state automagically
|
// Thread-safe func which sets and clears reset button state automagically
|
||||||
void ResetButton_Tap();
|
void ResetButton_Tap();
|
||||||
|
void PowerButton_Tap();
|
||||||
|
|
||||||
} // namespace ProcessorInterface
|
} // namespace ProcessorInterface
|
||||||
|
|
|
@ -4,6 +4,12 @@
|
||||||
|
|
||||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_stm.h"
|
#include "Core/IPC_HLE/WII_IPC_HLE_Device_stm.h"
|
||||||
|
|
||||||
|
namespace Core
|
||||||
|
{
|
||||||
|
void QueueHostJob(std::function<void()> job, bool run_during_stop);
|
||||||
|
void Stop();
|
||||||
|
}
|
||||||
|
|
||||||
static u32 s_event_hook_address = 0;
|
static u32 s_event_hook_address = 0;
|
||||||
|
|
||||||
IPCCommandResult CWII_IPC_HLE_Device_stm_immediate::Open(u32 command_address, u32 mode)
|
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)
|
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:
|
case IOCTL_STM_RELEASE_EH:
|
||||||
if (s_event_hook_address == 0)
|
if (s_event_hook_address == 0)
|
||||||
{
|
{
|
||||||
|
@ -123,7 +135,7 @@ IPCCommandResult CWII_IPC_HLE_Device_stm_eventhook::IOCtl(u32 command_address)
|
||||||
return GetNoReply();
|
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)
|
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.
|
// The reset button returns STM_EVENT_RESET.
|
||||||
u32 buffer_out = Memory::Read_U32(s_event_hook_address + 0x18);
|
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.
|
// Fill in command buffer.
|
||||||
Memory::Write_U32(FS_SUCCESS, s_event_hook_address + 4);
|
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);
|
WII_IPC_HLE_Interface::EnqueueReply(s_event_hook_address);
|
||||||
s_event_hook_address = 0;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -60,4 +60,8 @@ public:
|
||||||
IPCCommandResult IOCtl(u32 command_address) override;
|
IPCCommandResult IOCtl(u32 command_address) override;
|
||||||
|
|
||||||
void ResetButton() const;
|
void ResetButton() const;
|
||||||
|
void PowerButton() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void TriggerEvent(u32 event) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -158,6 +158,7 @@ private:
|
||||||
bool m_bGameLoading = false;
|
bool m_bGameLoading = false;
|
||||||
bool m_bClosing = false;
|
bool m_bClosing = false;
|
||||||
bool m_confirmStop = false;
|
bool m_confirmStop = false;
|
||||||
|
bool m_tried_graceful_shutdown = false;
|
||||||
int m_saveSlot = 1;
|
int m_saveSlot = 1;
|
||||||
|
|
||||||
std::vector<std::string> drives;
|
std::vector<std::string> drives;
|
||||||
|
|
|
@ -1155,9 +1155,12 @@ void CFrame::DoStop()
|
||||||
Core::SetState(Core::CORE_PAUSE);
|
Core::SetState(Core::CORE_PAUSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
wxMessageDialog m_StopDlg(this, _("Do you want to stop the current emulation?"),
|
wxMessageDialog m_StopDlg(
|
||||||
_("Please confirm..."),
|
this, !m_tried_graceful_shutdown ? _("Do you want to stop the current emulation?") :
|
||||||
wxYES_NO | wxSTAY_ON_TOP | wxICON_EXCLAMATION, wxDefaultPosition);
|
_("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);
|
HotkeyManagerEmu::Enable(false);
|
||||||
int Ret = m_StopDlg.ShowModal();
|
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 (UseDebugger && g_pCodeWindow)
|
||||||
{
|
{
|
||||||
if (g_pCodeWindow->m_WatchWindow)
|
if (g_pCodeWindow->m_WatchWindow)
|
||||||
|
@ -1207,6 +1220,7 @@ void CFrame::DoStop()
|
||||||
void CFrame::OnStopped()
|
void CFrame::OnStopped()
|
||||||
{
|
{
|
||||||
m_confirmStop = false;
|
m_confirmStop = false;
|
||||||
|
m_tried_graceful_shutdown = false;
|
||||||
|
|
||||||
#if defined(HAVE_X11) && HAVE_X11
|
#if defined(HAVE_X11) && HAVE_X11
|
||||||
if (SConfig::GetInstance().bDisableScreenSaver)
|
if (SConfig::GetInstance().bDisableScreenSaver)
|
||||||
|
|
Loading…
Reference in New Issue