1 // Copyright (c) 2012- PPSSPP Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
20 #include "base/NativeApp.h"
21 #include "base/display.h"
22 #include "base/mutex.h"
23 #include "base/timeutil.h"
24 #include "input/input_state.h"
25 #include "profiler/profiler.h"
27 #include "Core/Core.h"
28 #include "Core/Config.h"
29 #include "Core/MemMap.h"
30 #include "Core/SaveState.h"
31 #include "Core/System.h"
32 #include "Core/MIPS/MIPS.h"
36 #include "Windows/OpenGLBase.h"
37 #include "Windows/D3D9Base.h"
39 #include "Windows/InputDevice.h"
44 #include "Core/Debugger/Breakpoints.h"
46 // Time until we stop considering the core active without user input.
47 // Should this be configurable? 2 hours currently.
48 static const double ACTIVITY_IDLE_TIMEOUT
= 2.0 * 3600.0;
50 static event m_hStepEvent
;
51 static recursive_mutex m_hStepMutex
;
52 static event m_hInactiveEvent
;
53 static recursive_mutex m_hInactiveMutex
;
54 static bool singleStepPending
= false;
55 static std::set
<Core_ShutdownFunc
> shutdownFuncs
;
56 static bool windowHidden
= false;
57 static double lastActivity
= 0.0;
58 static double lastKeepAwake
= 0.0;
60 #if defined (_WIN32) && !defined (__LIBRETRO__)
61 InputState input_state
;
63 extern InputState input_state
;
66 void Core_NotifyWindowHidden(bool hidden
) {
67 windowHidden
= hidden
;
68 // TODO: Wait until we can react?
71 void Core_NotifyActivity() {
72 lastActivity
= time_now_d();
75 void Core_ListenShutdown(Core_ShutdownFunc func
) {
76 shutdownFuncs
.insert(func
);
79 void Core_NotifyShutdown() {
80 for (auto it
= shutdownFuncs
.begin(); it
!= shutdownFuncs
.end(); ++it
) {
85 void Core_ErrorPause() {
86 Core_UpdateState(CORE_ERROR
);
89 void Core_Halt(const char *msg
) {
90 Core_EnableStepping(true);
91 ERROR_LOG(CPU
, "CPU HALTED : %s",msg
);
96 Core_UpdateState(CORE_POWERDOWN
);
97 Core_NotifyShutdown();
98 m_hStepEvent
.notify_one();
101 bool Core_IsStepping() {
102 return coreState
== CORE_STEPPING
|| coreState
== CORE_POWERDOWN
;
105 bool Core_IsActive() {
106 return coreState
== CORE_RUNNING
|| coreState
== CORE_NEXTFRAME
|| coreStatePending
;
109 bool Core_IsInactive() {
110 return coreState
!= CORE_RUNNING
&& coreState
!= CORE_NEXTFRAME
&& !coreStatePending
;
113 void Core_WaitInactive() {
114 while (Core_IsActive()) {
115 m_hInactiveEvent
.wait(m_hInactiveMutex
);
119 void Core_WaitInactive(int milliseconds
) {
120 if (Core_IsActive()) {
121 m_hInactiveEvent
.wait_for(m_hInactiveMutex
, milliseconds
);
125 bool UpdateScreenScale(int width
, int height
, bool smallWindow
) {
128 #if defined(__SYMBIAN32__)
130 #elif defined(_WIN32)
135 pixel_in_dps
= 1.0f
/ g_dpi_scale
;
137 int new_dp_xres
= width
* g_dpi_scale
;
138 int new_dp_yres
= height
* g_dpi_scale
;
140 bool dp_changed
= new_dp_xres
!= dp_xres
|| new_dp_yres
!= dp_yres
;
141 bool px_changed
= pixel_xres
!= width
|| pixel_yres
!= height
;
143 if (dp_changed
|| px_changed
) {
144 dp_xres
= new_dp_xres
;
145 dp_yres
= new_dp_yres
;
155 void UpdateRunLoop() {
156 if (windowHidden
&& g_Config
.bPauseWhenMinimized
) {
160 NativeUpdate(input_state
);
163 lock_guard
guard(input_state
.lock
);
164 EndInputState(&input_state
);
167 if (GetUIState() != UISTATE_EXIT
) {
172 #if defined(USING_WIN_UI)
174 void GPU_SwapBuffers() {
175 switch (g_Config
.iGPUBackend
) {
176 case GPU_BACKEND_OPENGL
:
179 case GPU_BACKEND_DIRECT3D9
:
187 void Core_RunLoop() {
188 while ((GetUIState() != UISTATE_INGAME
|| !PSP_IsInited()) && GetUIState() != UISTATE_EXIT
) {
190 #if defined(USING_WIN_UI)
191 double startTime
= time_now_d();
194 // Simple throttling to not burn the GPU in the menu.
196 double diffTime
= time_now_d() - startTime
;
197 int sleepTime
= (int)(1000.0 / 60.0) - (int)(diffTime
* 1000.0);
208 while (!coreState
&& GetUIState() == UISTATE_INGAME
) {
211 #if defined(USING_WIN_UI)
212 if (!windowHidden
&& !Core_IsStepping()) {
215 // Keep the system awake for longer than normal for cutscenes and the like.
216 const double now
= time_now_d();
217 if (now
< lastActivity
+ ACTIVITY_IDLE_TIMEOUT
) {
218 // Only resetting it ever prime number seconds in case the call is expensive.
219 // Using a prime number to ensure there's no interaction with other periodic events.
220 if (now
- lastKeepAwake
> 89.0 || now
< lastKeepAwake
) {
221 SetThreadExecutionState(ES_SYSTEM_REQUIRED
| ES_DISPLAY_REQUIRED
);
230 void Core_DoSingleStep() {
231 singleStepPending
= true;
232 m_hStepEvent
.notify_one();
235 void Core_UpdateSingleStep() {
236 m_hStepEvent
.notify_one();
239 void Core_SingleStep() {
240 currentMIPS
->SingleStep();
243 static inline void CoreStateProcessed() {
244 if (coreStatePending
) {
245 coreStatePending
= false;
246 m_hInactiveEvent
.notify_one();
250 // Some platforms, like Android, do not call this function but handle things on their own.
254 host
->UpdateDisassembly();
256 #if !defined(USING_QT_UI) || defined(MOBILE_DEVICE)
261 if (GetUIState() != UISTATE_INGAME
) {
262 CoreStateProcessed();
263 if (GetUIState() == UISTATE_EXIT
) {
267 #if defined(USING_QT_UI) && !defined(MOBILE_DEVICE)
277 // enter a fast runloop
281 // We should never get here on Android.
283 singleStepPending
= false;
284 CoreStateProcessed();
286 // Check if there's any pending savestate actions.
287 SaveState::Process();
288 if (coreState
== CORE_POWERDOWN
) {
292 // wait for step command..
293 #if defined(USING_QT_UI) || defined(_DEBUG)
294 host
->UpdateDisassembly();
295 host
->UpdateMemView();
296 host
->SendCoreWait(true);
299 m_hStepEvent
.wait(m_hStepMutex
);
301 #if defined(USING_QT_UI) || defined(_DEBUG)
302 host
->SendCoreWait(false);
304 #if defined(USING_QT_UI) && !defined(MOBILE_DEVICE)
305 if (coreState
!= CORE_STEPPING
)
308 // No step pending? Let's go back to the wait.
309 if (!singleStepPending
|| coreState
!= CORE_STEPPING
) {
310 if (coreState
== CORE_POWERDOWN
) {
317 // update disasm dialog
318 #if defined(USING_QT_UI) || defined(_DEBUG)
319 host
->UpdateDisassembly();
320 host
->UpdateMemView();
328 CoreStateProcessed();
340 void Core_EnableStepping(bool step
) {
344 host
->SetDebugMode(true);
346 m_hStepEvent
.reset();
347 Core_UpdateState(CORE_STEPPING
);
350 host
->SetDebugMode(false);
352 coreState
= CORE_RUNNING
;
353 coreStatePending
= false;
354 m_hStepEvent
.notify_one();