diff --git a/Source/Core/Common/SysConf.cpp b/Source/Core/Common/SysConf.cpp index d4c58a3def..ec0bad5b9b 100644 --- a/Source/Core/Common/SysConf.cpp +++ b/Source/Core/Common/SysConf.cpp @@ -314,7 +314,7 @@ void SysConf::GenerateSysConf() // IPL.IDL current_offset += create_item(items[23], Type_SmallArray, "IPL.IDL", 1, current_offset); - items[23].data[0] = 0x01; + items[23].data[0] = 0x00; // IPL.EULA current_offset += create_item(items[24], Type_Bool, "IPL.EULA", 1, current_offset); diff --git a/Source/Core/Core/BootManager.cpp b/Source/Core/Core/BootManager.cpp index f7651b5bce..6370bbf9d1 100644 --- a/Source/Core/Core/BootManager.cpp +++ b/Source/Core/Core/BootManager.cpp @@ -392,6 +392,15 @@ bool BootCore(const std::string& _rFilename) SConfig::GetInstance().m_SYSCONF->SetData("IPL.PGS", StartUp.bProgressive); SConfig::GetInstance().m_SYSCONF->SetData("IPL.E60", StartUp.bPAL60); + if (StartUp.bWii) + { + // Disable WiiConnect24's standby mode. If it is enabled, it prevents us from receiving + // shutdown commands in the State Transition Manager (STM). + // TODO: remove this if and once Dolphin supports WC24 standby mode. + SConfig::GetInstance().m_SYSCONF->SetData("IPL.IDL", 0x00); + NOTICE_LOG(BOOT, "Disabling WC24 'standby' (shutdown to idle) to avoid hanging on shutdown"); + } + // Run the game // Init the core if (!Core::Init()) diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 5989d47399..ef5c3e9d1e 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -143,6 +143,7 @@ set(SRCS ActionReplay.cpp IPC_HLE/WII_Socket.cpp IPC_HLE/WII_IPC_HLE_Device_net.cpp IPC_HLE/WII_IPC_HLE_Device_net_ssl.cpp + IPC_HLE/WII_IPC_HLE_Device_stm.cpp IPC_HLE/WII_IPC_HLE_Device_sdio_slot0.cpp IPC_HLE/WII_IPC_HLE_Device_usb.cpp IPC_HLE/WII_IPC_HLE_Device_usb_kbd.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index e89a1d6a41..f0a54a74f3 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -183,6 +183,7 @@ + @@ -386,8 +387,8 @@ - + @@ -480,4 +481,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index bc12b068ac..082b302901 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -585,6 +585,9 @@ IPC HLE %28IOS/Starlet%29\SDIO - SD Card + + IPC HLE %28IOS/Starlet%29 + IPC HLE %28IOS/Starlet%29\USB @@ -1247,4 +1250,4 @@ - \ No newline at end of file + 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 new file mode 100644 index 0000000000..0bd3f8f257 --- /dev/null +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_stm.cpp @@ -0,0 +1,169 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#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) +{ + INFO_LOG(WII_IPC_STM, "STM immediate: Open"); + Memory::Write_U32(GetDeviceID(), command_address + 4); + m_Active = true; + return GetDefaultReply(); +} + +IPCCommandResult CWII_IPC_HLE_Device_stm_immediate::Close(u32 command_address, bool force) +{ + INFO_LOG(WII_IPC_STM, "STM immediate: Close"); + if (!force) + Memory::Write_U32(0, command_address + 4); + m_Active = false; + return GetDefaultReply(); +} + +IPCCommandResult CWII_IPC_HLE_Device_stm_immediate::IOCtl(u32 command_address) +{ + u32 parameter = Memory::Read_U32(command_address + 0x0C); + u32 buffer_in = Memory::Read_U32(command_address + 0x10); + u32 buffer_in_size = Memory::Read_U32(command_address + 0x14); + u32 buffer_out = Memory::Read_U32(command_address + 0x18); + u32 buffer_out_size = Memory::Read_U32(command_address + 0x1C); + + // Prepare the out buffer(s) with zeroes as a safety precaution + // to avoid returning bad values + Memory::Memset(buffer_out, 0, buffer_out_size); + u32 return_value = 0; + + 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) + { + return_value = FS_ENOENT; + break; + } + Memory::Write_U32(0, Memory::Read_U32(s_event_hook_address + 0x18)); + Memory::Write_U32(FS_SUCCESS, s_event_hook_address + 4); + Memory::Write_U32(IPC_REP_ASYNC, s_event_hook_address); + Memory::Write_U32(IPC_CMD_IOCTL, s_event_hook_address + 8); + WII_IPC_HLE_Interface::EnqueueReply(s_event_hook_address); + s_event_hook_address = 0; + break; + + case IOCTL_STM_HOTRESET: + INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str()); + INFO_LOG(WII_IPC_STM, " IOCTL_STM_HOTRESET"); + break; + + case IOCTL_STM_VIDIMMING: // (Input: 20 bytes, Output: 20 bytes) + INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str()); + INFO_LOG(WII_IPC_STM, " IOCTL_STM_VIDIMMING"); + // DumpCommands(buffer_in, buffer_in_size / 4, LogTypes::WII_IPC_STM); + // Memory::Write_U32(1, buffer_out); + // return_value = 1; + break; + + case IOCTL_STM_LEDMODE: // (Input: 20 bytes, Output: 20 bytes) + INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str()); + INFO_LOG(WII_IPC_STM, " IOCTL_STM_LEDMODE"); + break; + + default: + { + _dbg_assert_msg_(WII_IPC_STM, 0, "CWII_IPC_HLE_Device_stm_immediate: 0x%x", parameter); + + INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str()); + DEBUG_LOG(WII_IPC_STM, " parameter: 0x%x", parameter); + DEBUG_LOG(WII_IPC_STM, " InBuffer: 0x%08x", buffer_in); + DEBUG_LOG(WII_IPC_STM, " InBufferSize: 0x%08x", buffer_in_size); + DEBUG_LOG(WII_IPC_STM, " OutBuffer: 0x%08x", buffer_out); + DEBUG_LOG(WII_IPC_STM, " OutBufferSize: 0x%08x", buffer_out_size); + } + break; + } + + // Write return value to the IPC call + Memory::Write_U32(return_value, command_address + 0x4); + return GetDefaultReply(); +} + +IPCCommandResult CWII_IPC_HLE_Device_stm_eventhook::Open(u32 command_address, u32 mode) +{ + Memory::Write_U32(GetDeviceID(), command_address + 4); + m_Active = true; + return GetDefaultReply(); +} + +IPCCommandResult CWII_IPC_HLE_Device_stm_eventhook::Close(u32 command_address, bool force) +{ + s_event_hook_address = 0; + + INFO_LOG(WII_IPC_STM, "STM eventhook: Close"); + if (!force) + Memory::Write_U32(0, command_address + 4); + m_Active = false; + return GetDefaultReply(); +} + +IPCCommandResult CWII_IPC_HLE_Device_stm_eventhook::IOCtl(u32 command_address) +{ + u32 parameter = Memory::Read_U32(command_address + 0x0C); + if (parameter != IOCTL_STM_EVENTHOOK) + { + ERROR_LOG(WII_IPC_STM, "Bad IOCtl in CWII_IPC_HLE_Device_stm_eventhook"); + Memory::Write_U32(FS_EINVAL, command_address + 4); + return GetDefaultReply(); + } + + // IOCTL_STM_EVENTHOOK waits until the reset button or power button + // is pressed. + s_event_hook_address = command_address; + return GetNoReply(); +} + +void CWII_IPC_HLE_Device_stm_eventhook::TriggerEvent(const u32 event) const +{ + if (!m_Active || s_event_hook_address == 0) + { + // If the device isn't open, ignore the button press. + return; + } + + // The reset button returns STM_EVENT_RESET. + u32 buffer_out = Memory::Read_U32(s_event_hook_address + 0x18); + Memory::Write_U32(event, buffer_out); + + // Fill in command buffer. + Memory::Write_U32(FS_SUCCESS, s_event_hook_address + 4); + Memory::Write_U32(IPC_REP_ASYNC, s_event_hook_address); + Memory::Write_U32(IPC_CMD_IOCTL, s_event_hook_address + 8); + + // Generate a reply to the IPC command. + 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 b8c78b2776..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 @@ -4,7 +4,6 @@ #pragma once -#include #include "Core/IPC_HLE/WII_IPC_HLE_Device.h" enum @@ -32,155 +31,37 @@ enum }; // The /dev/stm/immediate -class CWII_IPC_HLE_Device_stm_immediate : public IWII_IPC_HLE_Device +class CWII_IPC_HLE_Device_stm_immediate final : public IWII_IPC_HLE_Device { public: - CWII_IPC_HLE_Device_stm_immediate(u32 _DeviceID, const std::string& _rDeviceName) - : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName) + CWII_IPC_HLE_Device_stm_immediate(u32 device_id, const std::string& device_name) + : IWII_IPC_HLE_Device(device_id, device_name) { } - virtual ~CWII_IPC_HLE_Device_stm_immediate() {} - IPCCommandResult Open(u32 _CommandAddress, u32 _Mode) override - { - INFO_LOG(WII_IPC_STM, "STM immediate: Open"); - Memory::Write_U32(GetDeviceID(), _CommandAddress + 4); - m_Active = true; - return GetDefaultReply(); - } - - IPCCommandResult Close(u32 _CommandAddress, bool _bForce) override - { - INFO_LOG(WII_IPC_STM, "STM immediate: Close"); - if (!_bForce) - Memory::Write_U32(0, _CommandAddress + 4); - m_Active = false; - return GetDefaultReply(); - } - - IPCCommandResult IOCtl(u32 _CommandAddress) override - { - u32 Parameter = Memory::Read_U32(_CommandAddress + 0x0C); - u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); - u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); - u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); - u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C); - - // Prepare the out buffer(s) with zeroes as a safety precaution - // to avoid returning bad values - Memory::Memset(BufferOut, 0, BufferOutSize); - u32 ReturnValue = 0; - - switch (Parameter) - { - case IOCTL_STM_RELEASE_EH: - INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str()); - INFO_LOG(WII_IPC_STM, " IOCTL_STM_RELEASE_EH"); - break; - - case IOCTL_STM_HOTRESET: - INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str()); - INFO_LOG(WII_IPC_STM, " IOCTL_STM_HOTRESET"); - break; - - case IOCTL_STM_VIDIMMING: // (Input: 20 bytes, Output: 20 bytes) - INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str()); - INFO_LOG(WII_IPC_STM, " IOCTL_STM_VIDIMMING"); - // DumpCommands(BufferIn, BufferInSize / 4, LogTypes::WII_IPC_STM); - // Memory::Write_U32(1, BufferOut); - // ReturnValue = 1; - break; - - case IOCTL_STM_LEDMODE: // (Input: 20 bytes, Output: 20 bytes) - INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str()); - INFO_LOG(WII_IPC_STM, " IOCTL_STM_LEDMODE"); - break; - - default: - { - _dbg_assert_msg_(WII_IPC_STM, 0, "CWII_IPC_HLE_Device_stm_immediate: 0x%x", Parameter); - - INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str()); - DEBUG_LOG(WII_IPC_STM, " Parameter: 0x%x", Parameter); - DEBUG_LOG(WII_IPC_STM, " InBuffer: 0x%08x", BufferIn); - DEBUG_LOG(WII_IPC_STM, " InBufferSize: 0x%08x", BufferInSize); - DEBUG_LOG(WII_IPC_STM, " OutBuffer: 0x%08x", BufferOut); - DEBUG_LOG(WII_IPC_STM, " OutBufferSize: 0x%08x", BufferOutSize); - } - break; - } - - // Write return value to the IPC call - Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); - return GetDefaultReply(); - } + ~CWII_IPC_HLE_Device_stm_immediate() override = default; + IPCCommandResult Open(u32 command_address, u32 mode) override; + IPCCommandResult Close(u32 command_address, bool force) override; + IPCCommandResult IOCtl(u32 command_address) override; }; // The /dev/stm/eventhook -class CWII_IPC_HLE_Device_stm_eventhook : public IWII_IPC_HLE_Device +class CWII_IPC_HLE_Device_stm_eventhook final : public IWII_IPC_HLE_Device { public: - CWII_IPC_HLE_Device_stm_eventhook(u32 _DeviceID, const std::string& _rDeviceName) - : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName), m_EventHookAddress(0) + CWII_IPC_HLE_Device_stm_eventhook(u32 device_id, const std::string& device_name) + : IWII_IPC_HLE_Device(device_id, device_name) { } - virtual ~CWII_IPC_HLE_Device_stm_eventhook() {} - IPCCommandResult Open(u32 _CommandAddress, u32 _Mode) override - { - Memory::Write_U32(GetDeviceID(), _CommandAddress + 4); - m_Active = true; - return GetDefaultReply(); - } + ~CWII_IPC_HLE_Device_stm_eventhook() override = default; + IPCCommandResult Open(u32 command_address, u32 mode) override; + IPCCommandResult Close(u32 command_address, bool force) override; + IPCCommandResult IOCtl(u32 command_address) override; - IPCCommandResult Close(u32 _CommandAddress, bool _bForce) override - { - m_EventHookAddress = 0; + void ResetButton() const; + void PowerButton() const; - INFO_LOG(WII_IPC_STM, "STM eventhook: Close"); - if (!_bForce) - Memory::Write_U32(0, _CommandAddress + 4); - m_Active = false; - return GetDefaultReply(); - } - - IPCCommandResult IOCtl(u32 _CommandAddress) override - { - u32 Parameter = Memory::Read_U32(_CommandAddress + 0x0C); - if (Parameter != IOCTL_STM_EVENTHOOK) - { - ERROR_LOG(WII_IPC_STM, "Bad IOCtl in CWII_IPC_HLE_Device_stm_eventhook"); - Memory::Write_U32(FS_EINVAL, _CommandAddress + 4); - return GetDefaultReply(); - } - - // IOCTL_STM_EVENTHOOK waits until the reset button or power button - // is pressed. - m_EventHookAddress = _CommandAddress; - return GetNoReply(); - } - - void ResetButton() - { - if (!m_Active || m_EventHookAddress == 0) - { - // If the device isn't open, ignore the button press. - return; - } - - // The reset button returns STM_EVENT_RESET. - u32 BufferOut = Memory::Read_U32(m_EventHookAddress + 0x18); - Memory::Write_U32(STM_EVENT_RESET, BufferOut); - - // Fill in command buffer. - Memory::Write_U32(FS_SUCCESS, m_EventHookAddress + 4); - Memory::Write_U32(IPC_REP_ASYNC, m_EventHookAddress); - Memory::Write_U32(IPC_CMD_IOCTL, m_EventHookAddress + 8); - - // Generate a reply to the IPC command. - WII_IPC_HLE_Interface::EnqueueReply(m_EventHookAddress); - } - - // STATE_TO_SAVE - u32 m_EventHookAddress; +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 51675ae115..d253446161 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -1148,9 +1148,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(); @@ -1165,6 +1168,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) @@ -1200,6 +1213,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) diff --git a/Source/Core/DolphinWX/MainNoGUI.cpp b/Source/Core/DolphinWX/MainNoGUI.cpp index 1d06ff559a..c954539e79 100644 --- a/Source/Core/DolphinWX/MainNoGUI.cpp +++ b/Source/Core/DolphinWX/MainNoGUI.cpp @@ -34,6 +34,8 @@ static bool rendererHasFocus = true; static bool rendererIsFullscreen = false; static Common::Flag s_running{true}; +static Common::Flag s_shutdown_requested{false}; +static Common::Flag s_tried_graceful_shutdown{false}; static void signal_handler(int) { @@ -41,7 +43,12 @@ static void signal_handler(int) if (write(STDERR_FILENO, message, sizeof(message)) < 0) { } - s_running.Clear(); + s_shutdown_requested.Set(); +} + +namespace ProcessorInterface +{ +void PowerButton_Tap(); } class Platform @@ -222,6 +229,19 @@ class PlatformX11 : public Platform // The actual loop while (s_running.IsSet()) { + if (s_shutdown_requested.TestAndClear()) + { + if (!s_tried_graceful_shutdown.IsSet() && SConfig::GetInstance().bWii) + { + ProcessorInterface::PowerButton_Tap(); + s_tried_graceful_shutdown.Set(); + } + else + { + s_running.Clear(); + } + } + XEvent event; KeySym key; for (int num_events = XPending(dpy); num_events > 0; num_events--) @@ -286,7 +306,7 @@ class PlatformX11 : public Platform break; case ClientMessage: if ((unsigned long)event.xclient.data.l[0] == XInternAtom(dpy, "WM_DELETE_WINDOW", False)) - s_running.Clear(); + s_shutdown_requested.Set(); break; } } @@ -375,6 +395,7 @@ int main(int argc, char* argv[]) UICommon::SetUserDirectory(""); // Auto-detect user folder UICommon::Init(); + Core::SetOnStoppedCallback([]() { s_running.Clear(); }); platform->Init(); // Shut down cleanly on SIGINT and SIGTERM