diff --git a/troposphere/haze/include/haze/console_main_loop.hpp b/troposphere/haze/include/haze/console_main_loop.hpp index 359e9523f..b91281615 100644 --- a/troposphere/haze/include/haze/console_main_loop.hpp +++ b/troposphere/haze/include/haze/console_main_loop.hpp @@ -45,12 +45,16 @@ namespace haze { int idx; while (true) { + /* Wait for up to 1 frame delay time to be cancelled. */ Waiter cancel_waiter = waiterForUEvent(std::addressof(m_cancel_event)); Result rc = waitObjects(std::addressof(idx), std::addressof(cancel_waiter), 1, FrameDelayNs); + /* Finish if we were cancelled. */ if (R_SUCCEEDED(rc)) { break; } + + /* Otherwise, signal the console update event. */ if (svc::ResultTimedOut::Includes(rc)) { ueventSignal(std::addressof(m_event)); } @@ -154,13 +158,91 @@ namespace haze { /* If the plus button is held, request immediate exit. */ if (padGetButtonsDown(std::addressof(m_pad)) & HidNpadButton_Plus) { - m_reactor->RequestStop(); + m_reactor->SetResult(haze::ResultStopRequested()); } - /* Pump applet event loop. */ + /* Pump applet events, and check if exit was requested. */ if (!appletMainLoop()) { - m_reactor->RequestStop(); + m_reactor->SetResult(haze::ResultStopRequested()); } + + /* Check if focus was lost. */ + if (appletGetFocusState() == AppletFocusState_Background) { + m_reactor->SetResult(haze::ResultFocusLost()); + } + } + private: + static bool SuspendAndWaitForFocus() { + /* Enable suspension with resume notification. */ + appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleepNotify); + + /* Pump applet events. */ + while (appletMainLoop()) { + /* Check if focus was regained. */ + if (appletGetFocusState() != AppletFocusState_Background) { + return true; + } + } + + /* Exit was requested. */ + return false; + } + public: + static void RunApplication() { + /* Declare the object heap, to hold the database for an active session. */ + PtpObjectHeap ptp_object_heap; + + /* Declare the event reactor, and components which use it. */ + EventReactor event_reactor; + PtpResponder ptp_responder; + ConsoleMainLoop console_main_loop; + + /* Initialize the console.*/ + consoleInit(nullptr); + + while (true) { + /* Disable suspension. */ + appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend); + + /* Declare result from serving to use. */ + Result rc; + { + /* Ensure we don't go to sleep while transferring files. */ + appletSetAutoSleepDisabled(true); + + /* Clear the event reactor. */ + event_reactor.SetResult(ResultSuccess()); + + /* Configure the PTP responder and console main loop. */ + ptp_responder.Initialize(std::addressof(event_reactor), std::addressof(ptp_object_heap)); + console_main_loop.Initialize(std::addressof(event_reactor), std::addressof(ptp_object_heap)); + + /* Ensure we maintain a clean state on exit. */ + ON_SCOPE_EXIT { + /* Finalize the console main loop and PTP responder. */ + console_main_loop.Finalize(); + ptp_responder.Finalize(); + + /* Restore auto sleep setting. */ + appletSetAutoSleepDisabled(false); + }; + + /* Begin processing requests. */ + rc = ptp_responder.LoopProcess(); + } + + /* If focus was lost, try to pump the applet main loop until we receive focus again. */ + if (haze::ResultFocusLost::Includes(rc) && SuspendAndWaitForFocus()) { + continue; + } + + /* Otherwise, enable suspension and finish. */ + appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleep); + break; + } + + /* Finalize the console. */ + consoleExit(nullptr); } }; diff --git a/troposphere/haze/include/haze/event_reactor.hpp b/troposphere/haze/include/haze/event_reactor.hpp index 71d2c2123..fb87ff03c 100644 --- a/troposphere/haze/include/haze/event_reactor.hpp +++ b/troposphere/haze/include/haze/event_reactor.hpp @@ -30,15 +30,15 @@ namespace haze { EventConsumer *m_consumers[svc::ArgumentHandleCountMax]; Waiter m_waiters[svc::ArgumentHandleCountMax]; s32 m_num_wait_objects; - bool m_stop_requested; + Result m_result; public: - constexpr explicit EventReactor() : m_consumers(), m_waiters(), m_num_wait_objects(), m_stop_requested() { /* ... */ } + constexpr explicit EventReactor() : m_consumers(), m_waiters(), m_num_wait_objects(), m_result(ResultSuccess()) { /* ... */ } bool AddConsumer(EventConsumer *consumer, Waiter waiter); void RemoveConsumer(EventConsumer *consumer); public: - void RequestStop() { m_stop_requested = true; } - bool GetStopRequested() const { return m_stop_requested; } + void SetResult(Result r) { m_result = r; } + Result GetResult() const { return m_result; } public: template Result WaitFor(s32 *out_arg_waiter, Args &&... arg_waiters) { diff --git a/troposphere/haze/include/haze/file_system_proxy.hpp b/troposphere/haze/include/haze/file_system_proxy.hpp index 0497a4c8a..516ba1115 100644 --- a/troposphere/haze/include/haze/file_system_proxy.hpp +++ b/troposphere/haze/include/haze/file_system_proxy.hpp @@ -45,7 +45,7 @@ namespace haze { const Result rc = func(std::forward(args)...); /* If the event loop was stopped, return that here. */ - R_UNLESS(!m_reactor->GetStopRequested(), haze::ResultStopRequested()); + R_TRY(m_reactor->GetResult()); /* Otherwise, return the call result. */ R_RETURN(rc); diff --git a/troposphere/haze/include/haze/ptp_responder.hpp b/troposphere/haze/include/haze/ptp_responder.hpp index 163f7b4af..eac5aa330 100644 --- a/troposphere/haze/include/haze/ptp_responder.hpp +++ b/troposphere/haze/include/haze/ptp_responder.hpp @@ -40,9 +40,10 @@ namespace haze { Result Initialize(EventReactor *reactor, PtpObjectHeap *object_heap); void Finalize(); public: - Result HandleRequest(); + Result LoopProcess(); private: /* Request handling. */ + Result HandleRequest(); Result HandleRequestImpl(); Result HandleCommandRequest(PtpDataParser &dp); void ForceCloseSession(); diff --git a/troposphere/haze/include/haze/results.hpp b/troposphere/haze/include/haze/results.hpp index 7056e4c4d..fec0e84de 100644 --- a/troposphere/haze/include/haze/results.hpp +++ b/troposphere/haze/include/haze/results.hpp @@ -26,16 +26,16 @@ namespace haze { R_DEFINE_ERROR_RESULT(NotConfigured, 2); R_DEFINE_ERROR_RESULT(TransferFailed, 3); R_DEFINE_ERROR_RESULT(StopRequested, 4); - R_DEFINE_ERROR_RESULT(EndOfTransmission, 5); - R_DEFINE_ERROR_RESULT(UnknownPacketType, 6); - R_DEFINE_ERROR_RESULT(SessionNotOpen, 7); - R_DEFINE_ERROR_RESULT(OutOfMemory, 8); - R_DEFINE_ERROR_RESULT(InvalidObjectId, 9); - R_DEFINE_ERROR_RESULT(InvalidStorageId, 10); - R_DEFINE_ERROR_RESULT(OperationNotSupported, 11); - R_DEFINE_ERROR_RESULT(UnknownRequestType, 12); - R_DEFINE_ERROR_RESULT(UnknownPropertyCode, 13); - R_DEFINE_ERROR_RESULT(InvalidPropertyValue, 14); - R_DEFINE_ERROR_RESULT(GeneralFailure, 15); + R_DEFINE_ERROR_RESULT(FocusLost, 5); + R_DEFINE_ERROR_RESULT(EndOfTransmission, 6); + R_DEFINE_ERROR_RESULT(UnknownPacketType, 7); + R_DEFINE_ERROR_RESULT(SessionNotOpen, 8); + R_DEFINE_ERROR_RESULT(OutOfMemory, 9); + R_DEFINE_ERROR_RESULT(InvalidObjectId, 10); + R_DEFINE_ERROR_RESULT(InvalidStorageId, 11); + R_DEFINE_ERROR_RESULT(OperationNotSupported, 12); + R_DEFINE_ERROR_RESULT(UnknownRequestType, 13); + R_DEFINE_ERROR_RESULT(UnknownPropertyCode, 14); + R_DEFINE_ERROR_RESULT(InvalidPropertyValue, 15); } diff --git a/troposphere/haze/source/event_reactor.cpp b/troposphere/haze/source/event_reactor.cpp index c570a4ca1..0b58eeae0 100644 --- a/troposphere/haze/source/event_reactor.cpp +++ b/troposphere/haze/source/event_reactor.cpp @@ -53,7 +53,7 @@ namespace haze { HAZE_ASSERT(m_num_wait_objects + num_arg_waiters <= svc::ArgumentHandleCountMax); while (true) { - R_UNLESS(!m_stop_requested, haze::ResultStopRequested()); + R_TRY(m_result); /* Insert waiters from argument list. */ for (s32 i = 0; i < num_arg_waiters; i++) { diff --git a/troposphere/haze/source/main.cpp b/troposphere/haze/source/main.cpp index 7d3ff810d..f2eaf732c 100644 --- a/troposphere/haze/source/main.cpp +++ b/troposphere/haze/source/main.cpp @@ -17,38 +17,8 @@ #include int main(int argc, char **argv) { - /* Declare the object heap, to hold the database for an active session. */ - haze::PtpObjectHeap ptp_object_heap; - - /* Declare the event reactor, and components which use it. */ - haze::EventReactor event_reactor; - haze::PtpResponder ptp_responder; - haze::ConsoleMainLoop console_main_loop; - - /* Initialize the console.*/ - consoleInit(nullptr); - - /* Ensure we don't go to sleep while transferring files. */ - appletSetAutoSleepDisabled(true); - - /* Configure the PTP responder and console main loop. */ - ptp_responder.Initialize(std::addressof(event_reactor), std::addressof(ptp_object_heap)); - console_main_loop.Initialize(std::addressof(event_reactor), std::addressof(ptp_object_heap)); - - /* Process events until the user requests exit. */ - while (!event_reactor.GetStopRequested()) { - ptp_responder.HandleRequest(); - } - - /* Finalize the console main loop and PTP responder. */ - console_main_loop.Finalize(); - ptp_responder.Finalize(); - - /* Restore auto sleep setting. */ - appletSetAutoSleepDisabled(false); - - /* Finalize the console. */ - consoleExit(nullptr); + /* Run the application. */ + haze::ConsoleMainLoop::RunApplication(); /* Return to the loader. */ return 0; diff --git a/troposphere/haze/source/ptp_responder.cpp b/troposphere/haze/source/ptp_responder.cpp index 9e7336cb7..b800af792 100644 --- a/troposphere/haze/source/ptp_responder.cpp +++ b/troposphere/haze/source/ptp_responder.cpp @@ -191,6 +191,25 @@ namespace haze { m_fs.Finalize(); } + Result PtpResponder::LoopProcess() { + while (true) { + /* Try to handle a request. */ + R_TRY_CATCH(this->HandleRequest()) { + R_CATCH(haze::ResultStopRequested, haze::ResultFocusLost) { + /* If we encountered a stop condition, we're done.*/ + R_THROW(R_CURRENT_RESULT); + } + R_CATCH_ALL() { + /* On other failures, try to handle another request. */ + continue; + } + } R_END_TRY_CATCH; + + /* Otherwise, handle the next request. */ + /* ... */ + } + } + Result PtpResponder::HandleRequest() { ON_RESULT_FAILURE { /* For general failure modes, the failure is unrecoverable. Close the session. */ @@ -223,9 +242,6 @@ namespace haze { /* Errors from fs are typically recoverable. */ R_TRY(this->WriteResponse(PtpResponseCode_GeneralError)); } - R_CATCH_ALL() { - R_THROW(haze::ResultGeneralFailure()); - } } R_END_TRY_CATCH; R_SUCCEED();