From 59867130660c0f15eed82e1e9a0806ff2e2ca0fd Mon Sep 17 00:00:00 2001 From: fincs Date: Thu, 11 Jun 2020 03:02:24 +0200 Subject: [PATCH] apt: Refactor sleep/home state handling to be more intuitive, see details: - Added functions for checking APT state: - aptIsActive - aptShouldClose - aptShouldJumpToHome - aptCheckHomePressRejected (replaces aptIsHomePressed) - Added functions for handling incoming requests: - aptHandleSleep - aptHandleJumpToHome - Added aptJumpToHomeMenu (callable anytime) because why not - aptMainLoop is now aptHandleSleep/JumpToHome + return !aptShouldClose - APTHOOK_ONEXIT is now called during aptExit - Internal refactoring --- libctru/include/3ds/services/apt.h | 59 +++++---- libctru/source/services/apt.c | 185 +++++++++++++++++------------ 2 files changed, 143 insertions(+), 101 deletions(-) diff --git a/libctru/include/3ds/services/apt.h b/libctru/include/3ds/services/apt.h index 33cbde9..1d568ab 100644 --- a/libctru/include/3ds/services/apt.h +++ b/libctru/include/3ds/services/apt.h @@ -148,39 +148,52 @@ void aptExit(void); */ Result aptSendCommand(u32* aptcmdbuf); -/** - * @brief Gets whether to allow the system to enter sleep mode. - * @return Whether sleep mode is allowed. - */ +/// Returns true if the application is currently in the foreground. +bool aptIsActive(void); + +/// Returns true if the system has told the application to close. +bool aptShouldClose(void); + +/// Returns true if the system can enter sleep mode while the application is active. bool aptIsSleepAllowed(void); -/** - * @brief Sets whether to allow the system to enter sleep mode. - * @param allowed Whether to allow sleep mode. - */ +/// Configures whether the system can enter sleep mode while the application is active. void aptSetSleepAllowed(bool allowed); -/** - * @brief Gets whether to allow the system to go back to HOME menu. - * @return Whether going back to HOME menu is allowed. - */ +/// Handles incoming sleep mode requests. +void aptHandleSleep(void); + +/// Returns true if the user can press the HOME button to jump back to the HOME menu while the application is active. bool aptIsHomeAllowed(void); -/** - * @brief Sets whether to allow the system to go back to HOME menu. - * @param allowed Whether going back to HOME menu is allowed. - */ +/// Configures whether the user can press the HOME button to jump back to the HOME menu while the application is active. void aptSetHomeAllowed(bool allowed); -/** - * @brief Returns when the HOME button is pressed. - * @return Whether the HOME button is being pressed. - */ -bool aptIsHomePressed(void); +/// Returns true if the system requires the application to jump back to the HOME menu. +bool aptShouldJumpToHome(void); + +/// Returns true if there is an incoming HOME button press rejected by the policy set by \ref aptSetHomeAllowed (use this to show a "no HOME allowed" icon). +bool aptCheckHomePressRejected(void); + +/// \deprecated Alias for \ref aptCheckHomePressRejected. +static inline DEPRECATED bool aptIsHomePressed(void) +{ + return aptCheckHomePressRejected(); +} + +/// Jumps back to the HOME menu. +void aptJumpToHomeMenu(void); + +/// Handles incoming jump-to-HOME requests. +static inline void aptHandleJumpToHome(void) +{ + if (aptShouldJumpToHome()) + aptJumpToHomeMenu(); +} /** - * @brief Processes the current APT status. Generally used within a main loop. - * @return Whether the application should continue running. + * @brief Main function which handles sleep mode and HOME/power buttons - call this at the beginning of every frame. + * @return true if the application should keep running, false otherwise (see \ref aptShouldClose). */ bool aptMainLoop(void); diff --git a/libctru/source/services/apt.c b/libctru/source/services/apt.c index a1d4892..5dd8d0d 100644 --- a/libctru/source/services/apt.c +++ b/libctru/source/services/apt.c @@ -36,22 +36,33 @@ static void* aptMessageFuncData; enum { + // Current applet state FLAG_ACTIVE = BIT(0), - FLAG_ALLOWSLEEP = BIT(1), - FLAG_ORDERTOCLOSE = BIT(2), - FLAG_SHUTDOWN = BIT(3), - FLAG_POWERBUTTON = BIT(4), - FLAG_WKUPBYCANCEL = BIT(5), - FLAG_WANTSTOSLEEP = BIT(6), - FLAG_SLEEPING = BIT(7), - FLAG_SPURIOUS = BIT(30), - FLAG_EXITED = BIT(31), + FLAG_SLEEPING = BIT(1), + + // Sleep handling flags + FLAG_ALLOWSLEEP = BIT(2), + FLAG_SHOULDSLEEP = BIT(3), + + // Home button flags + FLAG_ALLOWHOME = BIT(4), + FLAG_SHOULDHOME = BIT(5), + FLAG_HOMEREJECTED = BIT(6), + + // Power button flags + FLAG_POWERBUTTON = BIT(7), + FLAG_SHUTDOWN = BIT(8), + + // Close handling flags + FLAG_ORDERTOCLOSE = BIT(9), + FLAG_CANCELLED = BIT(10), + + // Miscellaneous + FLAG_SPURIOUS = BIT(31), }; static u8 aptHomeButtonState; -static u8 aptRecentHomeButtonState; static u32 aptFlags; -static bool aptHomeAllowed = true; static u32 aptParameters[0x1000/4]; static u64 aptChainloadTid; static u8 aptChainloadMediatype; @@ -178,8 +189,6 @@ Result aptInit(void) if (AtomicPostIncrement(&aptRefCount)) return 0; - aptHomeAllowed = osGetSystemCoreVersion() == 2; - // Retrieve APT lock ret = APT_GetLockHandle(0x0, &aptLockHandle); if (R_FAILED(ret)) goto _fail; @@ -199,8 +208,12 @@ Result aptInit(void) aptEventHandlerThread = threadCreate(aptEventHandler, 0x0, APT_HANDLER_STACKSIZE, 0x31, -2, true); if (!aptEventHandlerThread) goto _fail3; - // Enable APT + // By default allow sleep mode and home button presses aptFlags = FLAG_ALLOWSLEEP; + if (osGetSystemCoreVersion() == 2) // ... except in safe mode, which doesn't have home menu running + aptFlags |= FLAG_ALLOWHOME; + + // Enable APT ret = APT_Enable(attr); if (R_FAILED(ret)) goto _fail3; @@ -228,6 +241,16 @@ _fail: return ret; } +bool aptIsActive(void) +{ + return (aptFlags & FLAG_ACTIVE) != 0; +} + +bool aptShouldClose(void) +{ + return (aptFlags & (FLAG_ORDERTOCLOSE|FLAG_CANCELLED)) != 0; +} + bool aptIsSleepAllowed(void) { return (aptFlags & FLAG_ALLOWSLEEP) != 0; @@ -250,17 +273,37 @@ void aptSetSleepAllowed(bool allowed) bool aptIsHomeAllowed(void) { - return aptHomeAllowed; + return (aptFlags & FLAG_ALLOWHOME) != 0; } void aptSetHomeAllowed(bool allowed) { - aptHomeAllowed = allowed; + if (allowed) + aptFlags |= FLAG_ALLOWHOME; + else + aptFlags &= ~FLAG_ALLOWHOME; } -bool aptIsHomePressed(void) +bool aptShouldJumpToHome(void) { - return aptRecentHomeButtonState; + return aptHomeButtonState || (aptFlags & (FLAG_SHOULDHOME|FLAG_POWERBUTTON)) != 0; +} + +bool aptCheckHomePressRejected(void) +{ + if (aptFlags & FLAG_HOMEREJECTED) + { + aptFlags &= ~FLAG_HOMEREJECTED; + return true; + } + return false; +} + +static void aptClearJumpToHome(void) +{ + aptHomeButtonState = 0; + APT_UnlockTransition(0x01); + APT_SleepIfShellClosed(); } void aptSetChainloader(u64 programID, u8 mediatype) @@ -283,10 +326,12 @@ void aptExit(void) if (!aptIsCrippled()) { - bool exited = (aptFlags & FLAG_EXITED) != 0; - if (exited || !aptIsReinit()) + bool closing = aptShouldClose(); + if (closing) + aptCallHook(APTHOOK_ONEXIT); + if (closing || !aptIsReinit()) { - if (!exited && aptIsChainload()) + if (!closing && aptIsChainload()) { // Check if Home Menu exists and has been launched bool hmRegistered; @@ -390,18 +435,24 @@ void aptEventHandler(void *arg) switch (signal) { case APTSIGNAL_HOMEBUTTON: - if (!aptHomeButtonState) aptHomeButtonState = 1; - break; case APTSIGNAL_HOMEBUTTON2: - if (!aptHomeButtonState) aptHomeButtonState = 2; + if (!aptIsActive()) + break; + else if (!aptIsHomeAllowed()) + { + aptFlags |= FLAG_HOMEREJECTED; + aptClearJumpToHome(); + } + else if (!aptHomeButtonState) + aptHomeButtonState = signal == APTSIGNAL_HOMEBUTTON ? 1 : 2; break; case APTSIGNAL_SLEEP_QUERY: { APT_QueryReply reply; - if (aptFlags & (FLAG_ORDERTOCLOSE|FLAG_WKUPBYCANCEL)) + if (aptShouldClose()) // Reject sleep if we are expected to close reply = APTREPLY_REJECT; - else if (aptFlags & FLAG_ACTIVE) + else if (aptIsActive()) // Accept sleep based on user setting if we are active reply = aptIsSleepAllowed() ? APTREPLY_ACCEPT : APTREPLY_REJECT; else @@ -414,24 +465,24 @@ void aptEventHandler(void *arg) break; } case APTSIGNAL_SLEEP_CANCEL: - if (aptFlags & FLAG_ACTIVE) - aptFlags &= ~FLAG_WANTSTOSLEEP; + if (aptIsActive()) + aptFlags &= ~FLAG_SHOULDSLEEP; break; case APTSIGNAL_SLEEP_ENTER: _aptDebug(10, aptFlags); - if (aptFlags & FLAG_ACTIVE) - aptFlags |= FLAG_WANTSTOSLEEP; + if (aptIsActive()) + aptFlags |= FLAG_SHOULDSLEEP; else // Since we are not active, this must be handled here. APT_ReplySleepNotificationComplete(envGetAptAppId()); break; case APTSIGNAL_SLEEP_WAKEUP: - if (!(aptFlags & FLAG_ACTIVE)) + if (!aptIsActive()) break; if (aptFlags & FLAG_SLEEPING) LightEvent_Signal(&aptSleepEvent); else - aptFlags &= ~FLAG_WANTSTOSLEEP; + aptFlags &= ~FLAG_SHOULDSLEEP; break; case APTSIGNAL_SHUTDOWN: aptFlags |= FLAG_ORDERTOCLOSE | FLAG_SHUTDOWN; @@ -504,7 +555,7 @@ APT_Command aptWaitForWakeUp(APT_Transition transition) } if (cmd == APTCMD_WAKEUP_CANCEL) - aptFlags |= FLAG_WKUPBYCANCEL; + aptFlags |= FLAG_CANCELLED; if (cmd != APTCMD_WAKEUP_JUMPTOHOME) { @@ -512,6 +563,7 @@ APT_Command aptWaitForWakeUp(APT_Transition transition) APT_SleepIfShellClosed(); } else { + aptFlags |= FLAG_SHOULDHOME; aptHomeButtonState = 1; APT_LockTransition(0x01, true); } @@ -519,11 +571,7 @@ APT_Command aptWaitForWakeUp(APT_Transition transition) if (transition == TR_JUMPTOMENU || transition == TR_LIBAPPLET || transition == TR_SYSAPPLET || transition == TR_APPJUMP) { if (cmd != APTCMD_WAKEUP_JUMPTOHOME) - { - aptHomeButtonState = 0; - APT_UnlockTransition(0x01); - APT_SleepIfShellClosed(); - } + aptClearJumpToHome(); } return cmd; @@ -637,12 +685,12 @@ static void aptScreenTransfer(NS_APPID appId, bool sysApplet) APT_SendCaptureBufferInfo(&capinfo); } -static void aptProcessJumpToMenu(void) +void aptJumpToHomeMenu(void) { bool sleep = aptIsSleepAllowed(); aptSetSleepAllowed(false); - aptFlags &= ~FLAG_SPURIOUS; // If we haven't received a spurious wakeup by now, we probably never will (see aptInit) + aptFlags &= ~(FLAG_SHOULDHOME|FLAG_SPURIOUS); // If we haven't received a spurious wakeup by now, we probably never will (see aptInit) APT_PrepareToJumpToHomeMenu(); aptCallHook(APTHOOK_ONSUSPEND); @@ -658,46 +706,27 @@ static void aptProcessJumpToMenu(void) aptSetSleepAllowed(sleep); } +void aptHandleSleep(void) +{ + if (!(aptFlags & FLAG_SHOULDSLEEP)) + return; + + aptFlags = (aptFlags &~ FLAG_SHOULDSLEEP) | FLAG_SLEEPING; + aptCallHook(APTHOOK_ONSLEEP); + APT_ReplySleepNotificationComplete(envGetAptAppId()); + LightEvent_Wait(&aptSleepEvent); + aptFlags &= ~FLAG_SLEEPING; + + if (aptIsActive()) + GSPGPU_SetLcdForceBlack(0); + aptCallHook(APTHOOK_ONWAKEUP); +} + bool aptMainLoop(void) { - if (aptIsCrippled()) return true; - if (aptFlags & FLAG_EXITED) return false; - - if (aptRecentHomeButtonState) aptRecentHomeButtonState = 0; - - if (aptFlags & FLAG_WANTSTOSLEEP) - { - aptFlags = (aptFlags &~ FLAG_WANTSTOSLEEP) | FLAG_SLEEPING; - aptCallHook(APTHOOK_ONSLEEP); - APT_ReplySleepNotificationComplete(envGetAptAppId()); - LightEvent_Wait(&aptSleepEvent); - aptFlags &= ~FLAG_SLEEPING; - - if (aptFlags & FLAG_ACTIVE) - GSPGPU_SetLcdForceBlack(0); - aptCallHook(APTHOOK_ONWAKEUP); - } - else if ((aptFlags & FLAG_POWERBUTTON) || aptHomeButtonState) - { - if (aptHomeAllowed || (aptFlags & FLAG_POWERBUTTON)) - aptProcessJumpToMenu(); - else - { - aptRecentHomeButtonState = aptHomeButtonState; - aptHomeButtonState = 0; - APT_UnlockTransition(0x01); - APT_SleepIfShellClosed(); - } - } - - if (aptFlags & (FLAG_ORDERTOCLOSE|FLAG_WKUPBYCANCEL)) - { - aptFlags |= FLAG_EXITED; - aptCallHook(APTHOOK_ONEXIT); - return false; - } - - return true; + aptHandleSleep(); + aptHandleJumpToHome(); + return !aptShouldClose(); } void aptHook(aptHookCookie* cookie, aptHookFn callback, void* param)