1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     applet.cpp
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 48673 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "applet.h"
17 
18 namespace
19 {
RestoreGraphicSetting()20     void RestoreGraphicSetting()
21     {
22         // Recover the GPU register settings
23         // However, the following is the method for recovering the state set using DMPGL API.
24         // If using other libraries, it is necessary to reissue all of the register  setting commands with their own methods.
25         nngxUpdateState(NN_GX_STATE_ALL);
26 
27         // (Reference) Recovering settings with the GD library
28         // nn::gd::System::ForceDirty(nn::gd::MODULE_ALL);
29 
30         // (Reference) Recovering settings with the GR library
31         // The GR API only creates commands; it does not have a feature for recovering settings.
32         // When rendering next time, run all of the necessary commands.
33     }
34 }
35 
36 bool TransitionHandler::s_IsExitRequired = false;
37 volatile bool TransitionHandler::s_IsAfterWakeUp = false;
38 nn::os::LightEvent TransitionHandler::s_AwakeEvent;
39 nn::os::CriticalSection TransitionHandler::s_CriticalSection;
40 nn::os::CriticalSection TransitionHandler::s_CriticalSectionForSleep;
41 
42 TransitionHandler::TRANSITION_CALLBACK TransitionHandler::s_PrepareSleepCallback;
43 TransitionHandler::TRANSITION_CALLBACK TransitionHandler::s_AfterSleepCallback;
44 TransitionHandler::TRANSITION_CALLBACK TransitionHandler::s_PrepareHomeButtonCallback;
45 TransitionHandler::TRANSITION_CALLBACK TransitionHandler::s_AfterHomeButtonCallback;
46 TransitionHandler::TRANSITION_CALLBACK TransitionHandler::s_PreparePowerButtonCallback;
47 
Initialize()48 void TransitionHandler::Initialize()
49 {
50     // Set sleep-related callbacks
51     nn::applet::SetSleepQueryCallback(SleepQueryCallback, 0);
52     nn::applet::SetAwakeCallback(AwakeCallback, 0);
53 //     nn::applet::SetSleepCanceledCallback(NULL, 0); // Recommend not using SleepCanceledCallback
54 
55     s_AwakeEvent.Initialize(true);
56     s_CriticalSection.Initialize();
57     s_CriticalSectionForSleep.Initialize();
58 
59     // During wake-up, always set to Signal state
60     s_AwakeEvent.Signal();
61 
62     // Enable features related to the applet library.
63     // If you set 'false' for the argument, then sleep will be rejected automatically until nn::applet::EnableSleep() is called.
64     //
65     nn::applet::Enable(false);
66 
67     // The Initialize function for each of the gx, snd, and dsp libraries must be called after the applet::Enable function
68     // In particular, the nngxInitialize function, which initializes the GX library, should be called after determining whether to exit immediately following a call to the Enable function.
69 
70     // The application terminates here if the termination conditions are already established, such as by pressing the POWER Button while displaying the logo
71     if ( nn::applet::IsExpectedToCloseApplication() )
72     {
73         s_IsExitRequired = true;
74     }
75 }
76 
Finalize()77 void TransitionHandler::Finalize()
78 {
79     s_AwakeEvent.Finalize();
80     s_CriticalSection.Finalize();
81     s_CriticalSectionForSleep.Finalize();
82 
83     nn::applet::SetSleepQueryCallback(NULL, 0);
84     nn::applet::SetAwakeCallback(NULL, 0);
85 }
86 
EnableSleep()87 void TransitionHandler::EnableSleep()
88 {
89     // Enable responses to sleep requests.
90     // Check the system state. If the system has been closed, issue a sleep request.
91     nn::applet::EnableSleep(true);
92 }
93 
DisableSleep()94 void TransitionHandler::DisableSleep()
95 {
96     // Reject sleep.
97     // Return REJECT if a sleep request has come already.
98     nn::applet::DisableSleep(true);
99 }
100 
Process()101 void TransitionHandler::Process()
102 {
103     // When waking up from sleep, enable LCD after waiting for one-frame worth of rendering to complete
104     if ( TransitionHandler::s_IsAfterWakeUp )
105     {
106         nn::gx::StartLcdDisplay();
107         TransitionHandler::s_IsAfterWakeUp = false;
108     }
109 
110     // If the consistency of the GPU register settings is taken into account, the responses to various transitions and sleep requests should be done after rendering has ended.
111     //
112 
113     // Determination for System Sleep Mode
114     if ( nn::applet::IsExpectedToReplySleepQuery() )
115     {
116         // If you any reason REJECT is returned for SleepQuery, make the determination here.
117         // Return REJECT and exit from this function.
118 
119         // Do not enter sleep during file system processing
120         if ( TryLockForSleep() )
121         {
122             // Perform the Sleep Mode pre-processing here as required
123             if ( s_PrepareSleepCallback )
124             {
125                 s_PrepareSleepCallback();
126             }
127 
128             // Clears AwakeEvent immediately before permitting Sleep Mode
129             s_AwakeEvent.ClearSignal();
130 
131             // Permits Sleep Mode, and stops the main thread immediately
132             nn::applet::ReplySleepQuery(nn::applet::REPLY_ACCEPT);
133             s_AwakeEvent.Wait();
134 
135             // Perform the processing when recovering from Sleep Mode here as required
136             if ( s_AfterSleepCallback )
137             {
138                 s_AfterSleepCallback();
139             }
140 
141             UnlockForSleep();
142         }
143     }
144 
145     // Check termination request
146     if ( nn::applet::IsExpectedToCloseApplication() )
147     {
148         s_IsExitRequired = true;
149         return;
150     }
151 
152     // Performs application transition processing
153 
154     // HOME Button processing (the graphics library must already be initialized)
155     if ( nn::applet::IsExpectedToProcessHomeButton() )
156     {
157         // If exiting the block by determining with flags or other things here, the HOME Button prohibition interval can be implemented
158         // HOME Button flag must be deleted with nn::applet::ClearHomeButtonState function
159 
160         if ( TryLock() )
161         {
162             if ( s_PrepareHomeButtonCallback )
163             {
164                 s_PrepareHomeButtonCallback();
165             }
166 
167             nn::applet::ProcessHomeButtonAndWait();
168 
169             Unlock();
170 
171             // Caused by exiting from the HOME Menu or low batteries
172             // If it is determined to end the application, exit from the application main loop and go to termination processing
173             if ( nn::applet::IsExpectedToCloseApplication() )
174             {
175                 // No render rights are passed
176                 s_IsExitRequired = true;
177 
178                 if ( s_AfterHomeButtonCallback )
179                 {
180                     s_AfterHomeButtonCallback();
181                 }
182                 return;
183             }
184 
185             if ( s_AfterHomeButtonCallback )
186             {
187                 s_AfterHomeButtonCallback();
188             }
189 
190             // Restore graphics settings
191             RestoreGraphicSetting();
192         }
193     }
194 
195     // POWER Button processing (the graphics library must already be initialized)
196     if ( nn::applet::IsExpectedToProcessPowerButton() )
197     {
198         // Quickly handle the POWER Button, but as for the other tasks that should be done before the application ends, (such as dealing with save data) should be done after nn::applet::ProcessPowerButtonAndWait() returns.
199         //
200         //
201         if ( s_PreparePowerButtonCallback )
202         {
203             s_PreparePowerButtonCallback();
204         }
205 
206         nn::applet::ProcessPowerButtonAndWait();
207 
208         // If it is determined to end the application, exit from the application main loop and go to termination processing
209         if ( nn::applet::IsExpectedToCloseApplication() )
210         {
211             // No render rights are passed
212             s_IsExitRequired = true;
213             return;
214         }
215 
216         // Restore graphics settings
217         RestoreGraphicSetting();
218     }
219 }
220 
IsExitRequired()221 bool TransitionHandler::IsExitRequired()
222 {
223     return s_IsExitRequired;
224 }
225 
Lock()226 void TransitionHandler::Lock()
227 {
228     s_CriticalSection.Enter();
229 }
230 
TryLock()231 bool TransitionHandler::TryLock()
232 {
233     return s_CriticalSection.TryEnter();
234 }
235 
Unlock()236 void TransitionHandler::Unlock()
237 {
238     s_CriticalSection.Leave();
239 }
240 
LockForSleep()241 void TransitionHandler::LockForSleep()
242 {
243     s_CriticalSectionForSleep.Enter();
244 }
245 
TryLockForSleep()246 bool TransitionHandler::TryLockForSleep()
247 {
248     return s_CriticalSectionForSleep.TryEnter();
249 }
250 
UnlockForSleep()251 void TransitionHandler::UnlockForSleep()
252 {
253     s_CriticalSectionForSleep.Leave();
254 }
255 
256 /*------------------------------------------------------------------------*
257     Callback called when there is a sleep query
258  *------------------------------------------------------------------------*/
SleepQueryCallback(uptr arg)259 AppletQueryReply TransitionHandler::SleepQueryCallback( uptr arg )
260 {
261     NN_UNUSED_VAR(arg);
262 
263     if ( !nn::applet::IsActive() )
264     {
265         // When Inactive, the main thread is stopped on the applet::WaitForStarting function,
266         // and other threads also should be stopped. (Implementation-dependent on the application side.)
267         return nn::applet::REPLY_ACCEPT;
268     }
269     else
270     {
271         // The main thread is suspended here because it will sleep after the necessary processing completes
272         //
273         return nn::applet::REPLY_LATER;
274     }
275 }
276 
277 /*------------------------------------------------------------------------*
278     Callback called when recovering from sleep
279  *------------------------------------------------------------------------*/
AwakeCallback(uptr arg)280 void TransitionHandler::AwakeCallback( uptr arg )
281 {
282     NN_UNUSED_VAR(arg);
283     s_AwakeEvent.Signal();
284 
285     s_IsAfterWakeUp = true;
286 }
287