mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-10 01:24:41 -06:00
coreinit+UI: Implement exit()
When coreinit exit() is hit, then Cemu will only close when the game was launched directly via command line parameters (-g or -t). Otherwise it returns to the game list. In the case where Cemu closes it will forward the coreinit exit status code as it's own exit status code. This is useful for cases where Cemu is used as a CLI tool for testing homebrew
This commit is contained in:
parent
a3d6395ed7
commit
53a74e44df
@ -443,6 +443,7 @@ namespace CafeSystem
|
|||||||
static SystemImplementation* s_implementation{nullptr};
|
static SystemImplementation* s_implementation{nullptr};
|
||||||
bool sLaunchModeIsStandalone = false;
|
bool sLaunchModeIsStandalone = false;
|
||||||
std::optional<std::vector<std::string>> s_overrideArgs;
|
std::optional<std::vector<std::string>> s_overrideArgs;
|
||||||
|
std::optional<sint32> s_foregroundReturnStatus;
|
||||||
|
|
||||||
bool sSystemRunning = false;
|
bool sSystemRunning = false;
|
||||||
TitleId sForegroundTitleId = 0;
|
TitleId sForegroundTitleId = 0;
|
||||||
@ -836,6 +837,7 @@ namespace CafeSystem
|
|||||||
{
|
{
|
||||||
CafeTitleList::WaitForMandatoryScan();
|
CafeTitleList::WaitForMandatoryScan();
|
||||||
sLaunchModeIsStandalone = false;
|
sLaunchModeIsStandalone = false;
|
||||||
|
s_foregroundReturnStatus = std::nullopt;
|
||||||
_pathToExecutable.clear();
|
_pathToExecutable.clear();
|
||||||
TitleIdParser tip(titleId);
|
TitleIdParser tip(titleId);
|
||||||
if (tip.GetType() == TitleIdParser::TITLE_TYPE::AOC || tip.GetType() == TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE)
|
if (tip.GetType() == TitleIdParser::TITLE_TYPE::AOC || tip.GetType() == TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE)
|
||||||
@ -1186,4 +1188,15 @@ namespace CafeSystem
|
|||||||
s_implementation->CafeRecreateCanvas();
|
s_implementation->CafeRecreateCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NotifyPPCProcessExit(sint32 status)
|
||||||
|
{
|
||||||
|
s_foregroundReturnStatus = status;
|
||||||
|
s_implementation->CafePPCProcessExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<sint32> GetForegroundTitleReturnStatus()
|
||||||
|
{
|
||||||
|
return s_foregroundReturnStatus;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ namespace CafeSystem
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void CafeRecreateCanvas() = 0;
|
virtual void CafeRecreateCanvas() = 0;
|
||||||
|
virtual void CafePPCProcessExit() = 0; // emulated process exited
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class PREPARE_STATUS_CODE
|
enum class PREPARE_STATUS_CODE
|
||||||
@ -43,6 +44,7 @@ namespace CafeSystem
|
|||||||
std::string GetForegroundTitleArgStr();
|
std::string GetForegroundTitleArgStr();
|
||||||
uint32 GetForegroundTitleOlvAccesskey();
|
uint32 GetForegroundTitleOlvAccesskey();
|
||||||
CosCapabilityBits GetForegroundTitleCosCapabilities(CosCapabilityGroup group);
|
CosCapabilityBits GetForegroundTitleCosCapabilities(CosCapabilityGroup group);
|
||||||
|
std::optional<sint32> GetForegroundTitleReturnStatus(); // valid once the foreground title exited gracefully via coreinit exit
|
||||||
|
|
||||||
void ShutdownTitle();
|
void ShutdownTitle();
|
||||||
|
|
||||||
@ -55,6 +57,8 @@ namespace CafeSystem
|
|||||||
uint32 GetRPXHashUpdated();
|
uint32 GetRPXHashUpdated();
|
||||||
|
|
||||||
void RequestRecreateCanvas();
|
void RequestRecreateCanvas();
|
||||||
|
void NotifyPPCProcessExit(sint32 status);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern RPLModule* applicationRPX;
|
extern RPLModule* applicationRPX;
|
||||||
|
|||||||
@ -238,15 +238,6 @@ namespace coreinit
|
|||||||
osLib_returnFromFunction(hCPU, 1);
|
osLib_returnFromFunction(hCPU, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void coreinit_exit(uint32 r)
|
|
||||||
{
|
|
||||||
cemuLog_log(LogType::Force, "The title terminated the process by calling coreinit.exit({})", (sint32)r);
|
|
||||||
DebugLogStackTrace(coreinit::OSGetCurrentThread(), coreinit::OSGetStackPointer());
|
|
||||||
cemu_assert_debug(false);
|
|
||||||
// never return
|
|
||||||
while (true) std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OSIsOffBoot()
|
bool OSIsOffBoot()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -289,7 +280,6 @@ namespace coreinit
|
|||||||
|
|
||||||
osLib_addFunction("coreinit", "ENVGetEnvironmentVariable", coreinitExport_ENVGetEnvironmentVariable);
|
osLib_addFunction("coreinit", "ENVGetEnvironmentVariable", coreinitExport_ENVGetEnvironmentVariable);
|
||||||
|
|
||||||
cafeExportRegisterFunc(coreinit_exit, "coreinit", "exit", LogType::CoreinitThread);
|
|
||||||
cafeExportRegister("coreinit", OSIsOffBoot, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSIsOffBoot, LogType::CoreinitThread);
|
||||||
cafeExportRegister("coreinit", OSGetBootPMFlags, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSGetBootPMFlags, LogType::CoreinitThread);
|
||||||
cafeExportRegister("coreinit", OSGetSystemMode, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSGetSystemMode, LogType::CoreinitThread);
|
||||||
|
|||||||
@ -15,13 +15,13 @@ void coreinitAsyncCallback_addWithLock(MPTR functionMPTR, uint32 numParameters,
|
|||||||
// coreinit shared memory
|
// coreinit shared memory
|
||||||
struct CoreinitSharedData
|
struct CoreinitSharedData
|
||||||
{
|
{
|
||||||
MEMPTR<void> MEMAllocFromDefaultHeap;
|
MEMPTR<void> MEMAllocFromDefaultHeap{nullptr};
|
||||||
MEMPTR<void> MEMAllocFromDefaultHeapEx;
|
MEMPTR<void> MEMAllocFromDefaultHeapEx{nullptr};
|
||||||
MEMPTR<void> MEMFreeToDefaultHeap;
|
MEMPTR<void> MEMFreeToDefaultHeap{nullptr};
|
||||||
MPTR __atexit_cleanup;
|
MEMPTR<void> __atexit_cleanup{nullptr};
|
||||||
MPTR __cpp_exception_init_ptr;
|
MEMPTR<void> __stdio_cleanup{nullptr};
|
||||||
MPTR __cpp_exception_cleanup_ptr;
|
MPTR __cpp_exception_init_ptr{0};
|
||||||
MPTR __stdio_cleanup;
|
MPTR __cpp_exception_cleanup_ptr{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CoreinitSharedData* gCoreinitData;
|
extern CoreinitSharedData* gCoreinitData;
|
||||||
|
|||||||
@ -693,7 +693,6 @@ namespace coreinit
|
|||||||
return s_sdkVersion;
|
return s_sdkVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
// move this to CafeSystem.cpp?
|
|
||||||
void OSLauncherThread(uint64 titleId)
|
void OSLauncherThread(uint64 titleId)
|
||||||
{
|
{
|
||||||
CafeSystem::ShutdownTitle();
|
CafeSystem::ShutdownTitle();
|
||||||
@ -702,6 +701,12 @@ namespace coreinit
|
|||||||
CafeSystem::LaunchForegroundTitle();
|
CafeSystem::LaunchForegroundTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OSShutdownThread(sint32 status)
|
||||||
|
{
|
||||||
|
CafeSystem::ShutdownTitle();
|
||||||
|
CafeSystem::NotifyPPCProcessExit(status);
|
||||||
|
}
|
||||||
|
|
||||||
uint32 __LaunchByTitleId(uint64 titleId, uint32 argc, MEMPTR<char>* argv)
|
uint32 __LaunchByTitleId(uint64 titleId, uint32 argc, MEMPTR<char>* argv)
|
||||||
{
|
{
|
||||||
// prepare argument buffer
|
// prepare argument buffer
|
||||||
@ -851,6 +856,26 @@ namespace coreinit
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __PPCExit(sint32 status)
|
||||||
|
{
|
||||||
|
// spawn shutdown thread (the current thread has to be destroyed as part of the shutdown process)
|
||||||
|
std::thread shutdownThread(OSShutdownThread, status);
|
||||||
|
shutdownThread.detach();
|
||||||
|
// suspend this thread
|
||||||
|
coreinit::OSSuspendThread(coreinit::OSGetCurrentThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
void coreinit_exit(sint32 status)
|
||||||
|
{
|
||||||
|
cemuLog_log(LogType::Force, "The title terminated the process by calling coreinit.exit({})", (sint32)status);
|
||||||
|
DebugLogStackTrace(coreinit::OSGetCurrentThread(), coreinit::OSGetStackPointer());
|
||||||
|
if (gCoreinitData->__atexit_cleanup)
|
||||||
|
PPCCoreCallback(gCoreinitData->__atexit_cleanup, status);
|
||||||
|
if (gCoreinitData->__stdio_cleanup)
|
||||||
|
PPCCoreCallback(gCoreinitData->__stdio_cleanup);
|
||||||
|
__PPCExit(status);
|
||||||
|
}
|
||||||
|
|
||||||
void miscInit()
|
void miscInit()
|
||||||
{
|
{
|
||||||
s_currentTitleId = CafeSystem::GetForegroundTitleId();
|
s_currentTitleId = CafeSystem::GetForegroundTitleId();
|
||||||
@ -886,6 +911,9 @@ namespace coreinit
|
|||||||
|
|
||||||
cafeExportRegister("coreinit", OSDriver_Register, LogType::Placeholder);
|
cafeExportRegister("coreinit", OSDriver_Register, LogType::Placeholder);
|
||||||
cafeExportRegister("coreinit", OSDriver_Deregister, LogType::Placeholder);
|
cafeExportRegister("coreinit", OSDriver_Deregister, LogType::Placeholder);
|
||||||
|
|
||||||
|
cafeExportRegisterFunc(coreinit_exit, "coreinit", "exit", LogType::CoreinitThread);
|
||||||
|
cafeExportRegister("coreinit", __PPCExit, LogType::CoreinitThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
#include "Cafe/TitleList/TitleList.h"
|
#include "Cafe/TitleList/TitleList.h"
|
||||||
#include "Cafe/TitleList/SaveList.h"
|
#include "Cafe/TitleList/SaveList.h"
|
||||||
|
#include "Cafe/CafeSystem.h"
|
||||||
|
|
||||||
wxIMPLEMENT_APP_NO_MAIN(CemuApp);
|
wxIMPLEMENT_APP_NO_MAIN(CemuApp);
|
||||||
|
|
||||||
@ -414,17 +415,19 @@ int CemuApp::OnExit()
|
|||||||
m_sdlEventPumpTimer = nullptr;
|
m_sdlEventPumpTimer = nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
wxApp::OnExit();
|
wxApp::OnExit();
|
||||||
wxTheClipboard->Flush();
|
wxTheClipboard->Flush();
|
||||||
InputManager::instance().Shutdown();
|
InputManager::instance().Shutdown();
|
||||||
|
int retValue = 0;
|
||||||
|
if (auto r = CafeSystem::GetForegroundTitleReturnStatus(); (LaunchSettings::GetLoadFile() || LaunchSettings::GetLoadTitleID()) && r)
|
||||||
|
retValue = *r;
|
||||||
#if BOOST_OS_MACOS
|
#if BOOST_OS_MACOS
|
||||||
SDLControllerProvider::ShutdownSDL();
|
SDLControllerProvider::ShutdownSDL();
|
||||||
#endif
|
#endif
|
||||||
#if BOOST_OS_WINDOWS
|
#if BOOST_OS_WINDOWS
|
||||||
ExitProcess(0);
|
ExitProcess(retValue);
|
||||||
#else
|
#else
|
||||||
_Exit(0);
|
_Exit(retValue);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -162,6 +162,7 @@ wxDEFINE_EVENT(wxEVT_SET_WINDOW_TITLE, wxCommandEvent);
|
|||||||
wxDEFINE_EVENT(wxEVT_REQUEST_GAMELIST_REFRESH, wxCommandEvent);
|
wxDEFINE_EVENT(wxEVT_REQUEST_GAMELIST_REFRESH, wxCommandEvent);
|
||||||
wxDEFINE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent);
|
wxDEFINE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent);
|
||||||
wxDEFINE_EVENT(wxEVT_REQUEST_RECREATE_CANVAS, wxCommandEvent);
|
wxDEFINE_EVENT(wxEVT_REQUEST_RECREATE_CANVAS, wxCommandEvent);
|
||||||
|
wxDEFINE_EVENT(wxEVT_REQUEST_GAME_EXIT, wxCommandEvent);
|
||||||
|
|
||||||
wxBEGIN_EVENT_TABLE(MainWindow, wxFrame)
|
wxBEGIN_EVENT_TABLE(MainWindow, wxFrame)
|
||||||
EVT_TIMER(MAINFRAME_ID_TIMER1, MainWindow::OnTimer)
|
EVT_TIMER(MAINFRAME_ID_TIMER1, MainWindow::OnTimer)
|
||||||
@ -243,6 +244,7 @@ EVT_COMMAND(wxID_ANY, wxEVT_ACCOUNTLIST_REFRESH, MainWindow::OnAccountListRefres
|
|||||||
EVT_COMMAND(wxID_ANY, wxEVT_SET_WINDOW_TITLE, MainWindow::OnSetWindowTitle)
|
EVT_COMMAND(wxID_ANY, wxEVT_SET_WINDOW_TITLE, MainWindow::OnSetWindowTitle)
|
||||||
|
|
||||||
EVT_COMMAND(wxID_ANY, wxEVT_REQUEST_RECREATE_CANVAS, MainWindow::OnRequestRecreateCanvas)
|
EVT_COMMAND(wxID_ANY, wxEVT_REQUEST_RECREATE_CANVAS, MainWindow::OnRequestRecreateCanvas)
|
||||||
|
EVT_COMMAND(wxID_ANY, wxEVT_REQUEST_GAME_EXIT, MainWindow::OnRequestGameExit)
|
||||||
|
|
||||||
wxEND_EVENT_TABLE()
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
@ -2469,6 +2471,27 @@ void MainWindow::CafeRecreateCanvas()
|
|||||||
sem.decrementWithWait();
|
sem.decrementWithWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::CafePPCProcessExit()
|
||||||
|
{
|
||||||
|
// this is called from the emulated PPC thread, so queue an event instead of handling it directly
|
||||||
|
wxQueueEvent(g_mainFrame, new wxCommandEvent(wxEVT_REQUEST_GAME_EXIT));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::OnRequestGameExit(wxCommandEvent& event)
|
||||||
|
{
|
||||||
|
// if the title was launched via the command line we close Cemu and use the foreground title's exit status as Cemu's process return code (via CemuApp:OnExit)
|
||||||
|
// this is useful for homebrew testing setups that use Cemu via CLI
|
||||||
|
// otherwise the title was launched from the game list, so we just stop it and return to the game list instead of closing Cemu
|
||||||
|
if (LaunchSettings::GetLoadFile() || LaunchSettings::GetLoadTitleID())
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EndEmulation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool MainWindow::FullscreenEnabled() const
|
bool MainWindow::FullscreenEnabled() const
|
||||||
{
|
{
|
||||||
return LaunchSettings::FullscreenEnabled().value_or(GetWxGUIConfig().fullscreen);
|
return LaunchSettings::FullscreenEnabled().value_or(GetWxGUIConfig().fullscreen);
|
||||||
|
|||||||
@ -157,8 +157,10 @@ private:
|
|||||||
|
|
||||||
// CafeSystem implementation
|
// CafeSystem implementation
|
||||||
void CafeRecreateCanvas() override;
|
void CafeRecreateCanvas() override;
|
||||||
|
void CafePPCProcessExit() override;
|
||||||
|
|
||||||
void OnRequestRecreateCanvas(wxCommandEvent& event);
|
void OnRequestRecreateCanvas(wxCommandEvent& event);
|
||||||
|
void OnRequestGameExit(wxCommandEvent& event);
|
||||||
|
|
||||||
wxRect GetDesktopRect();
|
wxRect GetDesktopRect();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user