2012-11-01 15:19:01 +00:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2012-11-04 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2017-02-24 19:50:27 +00:00
# include "ppsspp_config.h"
2014-12-22 02:50:07 +00:00
# include <algorithm>
2021-07-08 19:30:23 +00:00
# include <functional>
using namespace std : : placeholders ;
2015-02-01 17:04:06 +00:00
2020-10-04 21:24:14 +00:00
# include "Common/Render/TextureAtlas.h"
# include "Common/GPU/OpenGL/GLFeatures.h"
2020-10-04 22:05:28 +00:00
# include "Common/Render/Text/draw_text.h"
2024-02-02 08:02:40 +00:00
# include "Common/File/FileUtil.h"
2023-01-27 08:19:29 +00:00
# include "Common/Battery/Battery.h"
2012-11-01 15:19:01 +00:00
2020-10-04 18:48:47 +00:00
# include "Common/UI/Root.h"
# include "Common/UI/UI.h"
# include "Common/UI/Context.h"
# include "Common/UI/Tween.h"
# include "Common/UI/View.h"
2022-11-21 19:15:22 +00:00
# include "Common/UI/AsyncImageFileView.h"
2022-11-17 11:19:17 +00:00
# include "Common/VR/PPSSPPVR.h"
2012-11-01 15:19:01 +00:00
2020-10-01 11:05:04 +00:00
# include "Common/Data/Text/I18n.h"
# include "Common/Input/InputState.h"
2020-08-15 13:22:44 +00:00
# include "Common/Log.h"
2020-10-04 08:30:18 +00:00
# include "Common/System/Display.h"
# include "Common/System/System.h"
# include "Common/System/NativeApp.h"
2023-03-22 21:17:53 +00:00
# include "Common/System/Request.h"
2023-06-30 15:15:49 +00:00
# include "Common/System/OSD.h"
2020-10-04 08:30:18 +00:00
# include "Common/Profiler/Profiler.h"
# include "Common/Math/curves.h"
2020-08-15 18:53:08 +00:00
# include "Common/TimeUtil.h"
2013-07-01 04:43:01 +00:00
2016-09-02 00:09:56 +00:00
# ifndef MOBILE_DEVICE
2016-08-27 18:38:05 +00:00
# include "Core/AVIDump.h"
2016-09-02 00:09:56 +00:00
# endif
2013-03-29 19:51:14 +00:00
# include "Core/Config.h"
2018-06-17 01:42:31 +00:00
# include "Core/ConfigValues.h"
2013-03-29 19:51:14 +00:00
# include "Core/CoreTiming.h"
# include "Core/CoreParameter.h"
# include "Core/Core.h"
2020-10-03 22:25:21 +00:00
# include "Core/KeyMap.h"
2020-12-19 19:26:02 +00:00
# include "Core/MemFault.h"
2014-02-09 22:04:16 +00:00
# include "Core/Reporting.h"
2013-03-29 19:51:14 +00:00
# include "Core/System.h"
2024-02-02 08:02:40 +00:00
# include "Core/FileSystems/VirtualDiscFileSystem.h"
2013-03-29 19:51:14 +00:00
# include "GPU/GPUState.h"
# include "GPU/GPUInterface.h"
2020-08-03 21:17:22 +00:00
# include "GPU/Common/FramebufferManagerCommon.h"
2018-02-05 15:21:39 +00:00
# if !PPSSPP_PLATFORM(UWP)
2018-01-17 12:59:32 +00:00
# include "GPU/Vulkan/DebugVisVulkan.h"
2018-02-05 15:21:39 +00:00
# endif
2024-07-25 08:08:56 +00:00
# include "Core/MIPS/MIPS.h"
2013-03-29 19:51:14 +00:00
# include "Core/HLE/sceCtrl.h"
2015-10-28 20:20:20 +00:00
# include "Core/HLE/sceSas.h"
2013-03-29 19:51:14 +00:00
# include "Core/Debugger/SymbolMap.h"
2023-07-02 10:00:13 +00:00
# include "Core/RetroAchievements.h"
2013-10-30 17:16:27 +00:00
# include "Core/SaveState.h"
2015-01-29 11:55:49 +00:00
# include "Core/HLE/__sceAudio.h"
2016-10-21 10:35:54 +00:00
# include "Core/HLE/proAdhoc.h"
2022-01-30 17:41:08 +00:00
# include "Core/HW/Display.h"
2012-11-01 15:19:01 +00:00
2016-09-04 15:42:20 +00:00
# include "UI/BackgroundAudio.h"
2013-12-05 13:14:15 +00:00
# include "UI/OnScreenDisplay.h"
2013-04-07 20:43:23 +00:00
# include "UI/GamepadEmu.h"
2014-12-22 09:48:17 +00:00
# include "UI/PauseScreen.h"
2013-06-10 20:06:51 +00:00
# include "UI/MainScreen.h"
2013-04-07 20:43:23 +00:00
# include "UI/EmuScreen.h"
2013-09-07 18:54:11 +00:00
# include "UI/DevScreens.h"
2013-04-13 19:24:07 +00:00
# include "UI/GameInfoCache.h"
2013-07-15 15:41:24 +00:00
# include "UI/MiscScreens.h"
2013-09-07 15:29:44 +00:00
# include "UI/ControlMappingScreen.h"
2015-12-21 05:32:05 +00:00
# include "UI/DisplayLayoutScreen.h"
2013-09-07 15:29:44 +00:00
# include "UI/GameSettingsScreen.h"
2015-10-14 15:45:21 +00:00
# include "UI/ProfilerDraw.h"
2018-08-12 22:08:56 +00:00
# include "UI/DiscordIntegration.h"
2016-10-21 10:35:54 +00:00
# include "UI/ChatScreen.h"
2023-08-02 12:28:52 +00:00
# include "UI/DebugOverlay.h"
2013-07-17 05:33:26 +00:00
2023-01-24 23:43:17 +00:00
# include "Core/Reporting.h"
2020-03-08 13:32:32 +00:00
# if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
2016-03-20 19:26:52 +00:00
# include "Windows/MainWindow.h"
# endif
2016-09-02 00:09:56 +00:00
# ifndef MOBILE_DEVICE
2017-11-14 05:33:49 +00:00
static AVIDump avi ;
2016-09-02 00:09:56 +00:00
# endif
2016-08-27 18:38:05 +00:00
2021-07-08 19:30:23 +00:00
// TODO: Ugly!
2016-08-30 13:09:38 +00:00
static bool frameStep_ ;
static int lastNumFlips ;
2016-08-27 18:38:05 +00:00
static bool startDumping ;
2016-08-28 04:20:03 +00:00
2019-05-01 02:01:20 +00:00
extern bool g_TakeScreenshot ;
2016-08-28 04:20:03 +00:00
static void __EmuScreenVblank ( )
{
2023-04-05 22:34:50 +00:00
auto sy = GetI18NCategory ( I18NCat : : SYSTEM ) ;
2017-06-03 15:37:55 +00:00
2016-08-29 19:34:00 +00:00
if ( frameStep_ & & lastNumFlips ! = gpuStats . numFlips )
2016-08-28 04:20:03 +00:00
{
frameStep_ = false ;
2021-10-23 23:56:15 +00:00
Core_EnableStepping ( true , " ui.frameAdvance " , 0 ) ;
2016-08-29 19:34:00 +00:00
lastNumFlips = gpuStats . numFlips ;
2016-08-28 04:20:03 +00:00
}
2016-09-02 00:09:56 +00:00
# ifndef MOBILE_DEVICE
2016-08-27 18:38:05 +00:00
if ( g_Config . bDumpFrames & & ! startDumping )
{
avi . Start ( PSP_CoreParameter ( ) . renderWidth , PSP_CoreParameter ( ) . renderHeight ) ;
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sy - > T ( " AVI Dump started. " ) , 1.0f ) ;
2016-08-27 18:38:05 +00:00
startDumping = true ;
}
if ( g_Config . bDumpFrames & & startDumping )
{
avi . AddFrame ( ) ;
}
else if ( ! g_Config . bDumpFrames & & startDumping )
{
avi . Stop ( ) ;
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sy - > T ( " AVI Dump stopped. " ) , 1.0f ) ;
2016-08-27 18:38:05 +00:00
startDumping = false ;
}
2016-09-02 00:09:56 +00:00
# endif
2016-08-28 04:20:03 +00:00
}
2021-07-08 19:30:23 +00:00
// Handles control rotation due to internal screen rotation.
2021-07-09 09:01:56 +00:00
static void SetPSPAnalog ( int stick , float x , float y ) {
2021-07-09 08:52:48 +00:00
switch ( g_Config . iInternalScreenRotation ) {
case ROTATION_LOCKED_HORIZONTAL :
// Standard rotation. No change.
break ;
case ROTATION_LOCKED_HORIZONTAL180 :
x = - x ;
y = - y ;
break ;
case ROTATION_LOCKED_VERTICAL :
{
float new_y = - x ;
x = y ;
y = new_y ;
break ;
}
case ROTATION_LOCKED_VERTICAL180 :
{
float new_y = y = x ;
x = - y ;
y = new_y ;
break ;
}
default :
break ;
}
2021-07-09 08:41:26 +00:00
__CtrlSetAnalogXY ( stick , x , y ) ;
2021-07-08 19:30:23 +00:00
}
2021-05-05 23:31:38 +00:00
EmuScreen : : EmuScreen ( const Path & filename )
2021-07-08 19:30:23 +00:00
: gamePath_ ( filename ) {
2016-05-28 03:41:37 +00:00
saveStateSlot_ = SaveState : : GetCurrentSlot ( ) ;
2016-08-28 04:20:03 +00:00
__DisplayListenVblank ( __EmuScreenVblank ) ;
2016-08-28 22:18:44 +00:00
frameStep_ = false ;
2016-08-29 19:34:00 +00:00
lastNumFlips = gpuStats . numFlips ;
2016-08-27 18:38:05 +00:00
startDumping = false ;
2021-07-08 19:30:23 +00:00
controlMapper_ . SetCallbacks (
2023-03-30 08:47:28 +00:00
std : : bind ( & EmuScreen : : onVKey , this , _1 , _2 ) ,
2023-03-30 13:03:41 +00:00
std : : bind ( & EmuScreen : : onVKeyAnalog , this , _1 , _2 ) ,
2023-03-30 08:47:28 +00:00
[ ] ( uint32_t bitsToSet , uint32_t bitsToClear ) {
2023-04-01 06:55:45 +00:00
__CtrlUpdateButtons ( bitsToSet , bitsToClear ) ;
2023-03-30 08:47:28 +00:00
} ,
2023-03-31 09:11:46 +00:00
& SetPSPAnalog ,
nullptr ) ;
2020-07-04 18:57:05 +00:00
2018-09-18 05:45:09 +00:00
// Make sure we don't leave it at powerdown after the last game.
2020-07-04 18:57:05 +00:00
// TODO: This really should be handled elsewhere if it isn't.
2018-09-18 05:45:09 +00:00
if ( coreState = = CORE_POWERDOWN )
coreState = CORE_STEPPING ;
2017-04-18 03:33:22 +00:00
OnDevMenu . Handle ( this , & EmuScreen : : OnDevTools ) ;
2017-07-08 10:08:33 +00:00
OnChatMenu . Handle ( this , & EmuScreen : : OnChat ) ;
2021-10-08 16:23:22 +00:00
// Usually, we don't want focus movement enabled on this screen, so disable on start.
// Only if you open chat or dev tools do we want it to start working.
UI : : EnableFocusMovement ( false ) ;
2013-07-17 05:33:26 +00:00
}
2021-05-05 23:31:38 +00:00
bool EmuScreen : : bootAllowStorage ( const Path & filename ) {
2018-04-02 06:28:36 +00:00
// No permissions needed. The easy life.
2021-05-05 23:31:38 +00:00
if ( filename . Type ( ) = = PathType : : HTTP )
2018-04-02 06:28:36 +00:00
return true ;
2021-05-05 23:31:38 +00:00
2018-04-02 06:28:36 +00:00
if ( ! System_GetPropertyBool ( SYSPROP_SUPPORTS_PERMISSIONS ) )
return true ;
PermissionStatus status = System_GetPermissionStatus ( SYSTEM_PERMISSION_STORAGE ) ;
switch ( status ) {
case PERMISSION_STATUS_UNKNOWN :
System_AskForPermission ( SYSTEM_PERMISSION_STORAGE ) ;
return false ;
case PERMISSION_STATUS_DENIED :
stopRender_ = true ;
screenManager ( ) - > switchScreen ( new MainScreen ( ) ) ;
return false ;
case PERMISSION_STATUS_PENDING :
// Keep waiting.
return false ;
case PERMISSION_STATUS_GRANTED :
return true ;
}
2018-04-09 06:54:46 +00:00
_assert_ ( false ) ;
return false ;
2018-04-02 06:28:36 +00:00
}
2021-05-05 23:31:38 +00:00
void EmuScreen : : bootGame ( const Path & filename ) {
2023-07-24 10:08:15 +00:00
if ( Achievements : : IsBlockingExecution ( ) ) {
// Keep waiting.
return ;
}
2022-10-02 14:25:15 +00:00
if ( PSP_IsRebooting ( ) )
return ;
if ( PSP_IsInited ( ) ) {
bootPending_ = false ;
invalid_ = false ;
bootComplete ( ) ;
return ;
}
2014-01-19 22:17:34 +00:00
if ( PSP_IsIniting ( ) ) {
2023-07-24 10:00:16 +00:00
std : : string error_string = " (unknown error) " ;
2023-07-24 10:08:15 +00:00
2014-05-16 05:17:19 +00:00
bootPending_ = ! PSP_InitUpdate ( & error_string ) ;
2023-07-11 23:11:09 +00:00
2014-05-16 05:17:19 +00:00
if ( ! bootPending_ ) {
2014-01-19 22:17:34 +00:00
invalid_ = ! PSP_IsInited ( ) ;
if ( invalid_ ) {
errorMessage_ = error_string ;
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : Boot , " isIniting bootGame error: %s " , errorMessage_ . c_str ( ) ) ;
2014-01-19 22:17:34 +00:00
return ;
}
bootComplete ( ) ;
}
return ;
}
2021-05-05 23:31:38 +00:00
g_BackgroundAudio . SetGame ( Path ( ) ) ;
2016-09-04 15:42:20 +00:00
2018-04-02 06:28:36 +00:00
// Check permission status first, in case we came from a shortcut.
if ( ! bootAllowStorage ( filename ) )
return ;
2018-08-23 01:27:20 +00:00
invalid_ = true ;
// We don't want to boot with the wrong game specific config, so wait until info is ready.
2024-01-28 15:23:27 +00:00
std : : shared_ptr < GameInfo > info = g_gameInfoCache - > GetInfo ( nullptr , filename , GameInfoFlags : : PARAM_SFO ) ;
2024-01-28 15:56:07 +00:00
if ( ! info - > Ready ( GameInfoFlags : : PARAM_SFO ) ) {
2018-08-23 01:27:20 +00:00
return ;
2024-01-28 15:56:07 +00:00
}
2018-08-23 01:27:20 +00:00
2024-01-20 22:01:08 +00:00
auto sc = GetI18NCategory ( I18NCat : : SCREEN ) ;
if ( info - > fileType = = IdentifiedFileType : : PSP_DISC_DIRECTORY ) {
2024-02-02 08:02:40 +00:00
// Check for existence of ppsspp-index.lst - if it exists, the user likely knows what they're doing.
// TODO: Better would be to check that it was loaded successfully.
if ( ! File : : Exists ( filename / INDEX_FILENAME ) ) {
g_OSD . Show ( OSDType : : MESSAGE_CENTERED_WARNING , sc - > T ( " ExtractedIsoWarning " , " Extracted ISOs often don't work. \n Play the ISO file directly. " ) , gamePath_ . ToVisualString ( ) , 7.0f ) ;
} else {
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : Loader , " Extracted ISO loaded without warning - %s is present. " , INDEX_FILENAME . c_str ( ) ) ;
2024-02-02 08:02:40 +00:00
}
2024-01-20 22:01:08 +00:00
}
2024-01-18 22:00:43 +00:00
extraAssertInfoStr_ = info - > id + " " + info - > GetTitle ( ) ;
SetExtraAssertInfo ( extraAssertInfoStr_ . c_str ( ) ) ;
2022-12-16 13:55:56 +00:00
2018-08-23 01:27:20 +00:00
if ( ! info - > id . empty ( ) ) {
2019-07-18 04:28:21 +00:00
g_Config . loadGameConfig ( info - > id , info - > GetTitle ( ) ) ;
2017-12-09 22:11:39 +00:00
// Reset views in case controls are in a different place.
RecreateViews ( ) ;
2018-08-12 22:08:56 +00:00
2024-01-19 12:44:49 +00:00
g_Discord . SetPresenceGame ( info - > GetTitle ( ) ) ;
2018-08-12 22:08:56 +00:00
} else {
2018-08-13 18:14:34 +00:00
g_Discord . SetPresenceGame ( sc - > T ( " Untitled PSP game " ) ) ;
2014-12-14 19:33:20 +00:00
}
2017-03-06 12:50:22 +00:00
CoreParameter coreParam { } ;
2016-05-07 23:43:27 +00:00
coreParam . cpuCore = ( CPUCore ) g_Config . iCpuCore ;
2016-04-10 08:21:48 +00:00
coreParam . gpuCore = GPUCORE_GLES ;
2015-10-10 14:41:19 +00:00
switch ( GetGPUBackend ( ) ) {
2017-02-24 19:50:27 +00:00
case GPUBackend : : DIRECT3D11 :
coreParam . gpuCore = GPUCORE_DIRECTX11 ;
break ;
# if !PPSSPP_PLATFORM(UWP)
2019-05-10 21:25:57 +00:00
# if PPSSPP_API(ANY_GL)
2015-10-10 14:41:19 +00:00
case GPUBackend : : OPENGL :
2016-04-10 08:21:48 +00:00
coreParam . gpuCore = GPUCORE_GLES ;
2015-10-10 14:41:19 +00:00
break ;
2019-05-03 22:06:50 +00:00
# endif
2015-10-10 14:41:19 +00:00
case GPUBackend : : DIRECT3D9 :
2016-04-10 08:21:48 +00:00
coreParam . gpuCore = GPUCORE_DIRECTX9 ;
2015-10-10 14:41:19 +00:00
break ;
case GPUBackend : : VULKAN :
2016-04-10 08:21:48 +00:00
coreParam . gpuCore = GPUCORE_VULKAN ;
2015-10-10 14:41:19 +00:00
break ;
2017-02-24 19:50:27 +00:00
# endif
2014-08-17 19:29:36 +00:00
}
2018-11-21 21:15:01 +00:00
2016-01-03 19:05:36 +00:00
// Preserve the existing graphics context.
coreParam . graphicsContext = PSP_CoreParameter ( ) . graphicsContext ;
2012-11-01 15:19:01 +00:00
coreParam . enableSound = g_Config . bEnableSound ;
2014-01-19 22:17:34 +00:00
coreParam . fileToStart = filename ;
2021-05-10 00:24:04 +00:00
coreParam . mountIso . clear ( ) ;
coreParam . mountRoot . clear ( ) ;
2018-06-23 17:14:36 +00:00
coreParam . startBreak = ! g_Config . bAutoRun ;
2012-11-01 15:19:01 +00:00
coreParam . headLess = false ;
2013-09-10 22:19:34 +00:00
if ( g_Config . iInternalResolution = = 0 ) {
2023-02-25 12:09:44 +00:00
coreParam . renderWidth = g_display . pixel_xres ;
coreParam . renderHeight = g_display . pixel_yres ;
2013-09-10 22:19:34 +00:00
} else {
if ( g_Config . iInternalResolution < 0 )
g_Config . iInternalResolution = 1 ;
coreParam . renderWidth = 480 * g_Config . iInternalResolution ;
coreParam . renderHeight = 272 * g_Config . iInternalResolution ;
}
2023-02-25 12:09:44 +00:00
coreParam . pixelWidth = g_display . pixel_xres ;
coreParam . pixelHeight = g_display . pixel_yres ;
2013-09-10 22:19:34 +00:00
2012-11-01 15:19:01 +00:00
std : : string error_string ;
2014-01-19 22:17:34 +00:00
if ( ! PSP_InitStart ( coreParam , & error_string ) ) {
2014-05-16 05:17:19 +00:00
bootPending_ = false ;
2012-11-01 15:19:01 +00:00
invalid_ = true ;
errorMessage_ = error_string ;
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : Boot , " InitStart bootGame error: %s " , errorMessage_ . c_str ( ) ) ;
2012-11-01 15:19:01 +00:00
}
2017-03-14 09:36:51 +00:00
2022-11-06 18:20:10 +00:00
if ( PSP_CoreParameter ( ) . compat . flags ( ) . RequireBufferedRendering & & g_Config . bSkipBufferEffects ) {
2023-04-05 22:34:50 +00:00
auto gr = GetI18NCategory ( I18NCat : : GRAPHICS ) ;
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , gr - > T ( " BufferedRenderingRequired " , " Warning: This game requires Rendering Mode to be set to Buffered. " ) , 10.0f ) ;
2017-03-14 09:36:51 +00:00
}
2017-03-21 07:23:31 +00:00
2024-07-20 09:23:34 +00:00
if ( PSP_CoreParameter ( ) . compat . flags ( ) . RequireBlockTransfer & & g_Config . iSkipGPUReadbackMode ! = ( int ) SkipGPUReadbackMode : : NO_SKIP & & ! PSP_CoreParameter ( ) . compat . flags ( ) . ForceEnableGPUReadback ) {
2023-04-05 22:34:50 +00:00
auto gr = GetI18NCategory ( I18NCat : : GRAPHICS ) ;
2023-12-13 13:10:30 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , gr - > T ( " BlockTransferRequired " , " Warning: This game requires Skip GPU Readbacks be set to No. " ) , 10.0f ) ;
2017-03-21 07:23:31 +00:00
}
2017-12-03 19:56:42 +00:00
2017-12-17 12:14:06 +00:00
if ( PSP_CoreParameter ( ) . compat . flags ( ) . RequireDefaultCPUClock & & g_Config . iLockedCPUSpeed ! = 0 ) {
2023-04-05 22:34:50 +00:00
auto gr = GetI18NCategory ( I18NCat : : GRAPHICS ) ;
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , gr - > T ( " DefaultCPUClockRequired " , " Warning: This game requires the CPU clock to be set to default. " ) , 10.0f ) ;
2017-12-17 12:14:06 +00:00
}
2018-01-02 07:07:48 +00:00
loadingViewColor_ - > Divert ( 0xFFFFFFFF , 0.75f ) ;
loadingViewVisible_ - > Divert ( UI : : V_VISIBLE , 0.75f ) ;
2022-10-10 08:53:27 +00:00
screenManager ( ) - > getDrawContext ( ) - > ResetStats ( ) ;
2023-12-10 20:57:05 +00:00
if ( bootPending_ ) {
System_PostUIMessage ( UIMessage : : GAME_SELECTED , filename . c_str ( ) ) ;
}
2014-01-19 22:17:34 +00:00
}
2013-05-16 15:18:29 +00:00
2014-01-19 22:17:34 +00:00
void EmuScreen : : bootComplete ( ) {
2014-06-22 07:38:46 +00:00
UpdateUIState ( UISTATE_INGAME ) ;
2023-03-21 10:10:09 +00:00
System_Notify ( SystemNotification : : BOOT_DONE ) ;
2023-03-21 10:21:19 +00:00
System_Notify ( SystemNotification : : DISASSEMBLY ) ;
2013-03-29 19:51:14 +00:00
2024-07-14 12:42:59 +00:00
NOTICE_LOG ( Log : : Boot , " Booted %s... " , PSP_CoreParameter ( ) . fileToStart . c_str ( ) ) ;
2023-12-03 15:41:29 +00:00
if ( ! Achievements : : HardcoreModeActive ( ) ) {
2023-11-30 16:33:14 +00:00
// Don't auto-load savestates in hardcore mode.
2023-07-11 23:11:09 +00:00
autoLoad ( ) ;
}
2013-10-30 17:51:25 +00:00
2023-04-05 22:34:50 +00:00
auto sc = GetI18NCategory ( I18NCat : : SCREEN ) ;
2013-06-10 22:51:10 +00:00
2014-02-08 19:11:50 +00:00
# ifndef MOBILE_DEVICE
2013-06-10 22:51:10 +00:00
if ( g_Config . bFirstRun ) {
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " PressESC " , " Press ESC to open the pause menu " ) ) ;
2013-06-10 22:51:10 +00:00
}
# endif
2013-09-17 08:27:42 +00:00
2017-02-24 19:50:27 +00:00
# if !PPSSPP_PLATFORM(UWP)
2016-01-06 06:37:28 +00:00
if ( GetGPUBackend ( ) = = GPUBackend : : OPENGL ) {
2018-01-16 13:16:56 +00:00
const char * renderer = gl_extensions . model ;
2014-08-17 19:29:36 +00:00
if ( strstr ( renderer , " Chainfire3D " ) ! = 0 ) {
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , sc - > T ( " Chainfire3DWarning " , " WARNING: Chainfire3D detected, may cause problems " ) , 10.0f ) ;
2014-08-17 19:29:36 +00:00
} else if ( strstr ( renderer , " GLTools " ) ! = 0 ) {
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , sc - > T ( " GLToolsWarning " , " WARNING: GLTools detected, may cause problems " ) , 10.0f ) ;
2014-08-17 19:29:36 +00:00
}
2015-10-11 09:46:24 +00:00
if ( g_Config . bGfxDebugOutput ) {
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , " WARNING: GfxDebugOutput is enabled via ppsspp.ini. Things may be slow. " , 10.0f ) ;
2015-10-11 09:46:24 +00:00
}
2013-09-17 08:27:42 +00:00
}
2017-02-24 19:50:27 +00:00
# endif
2013-10-30 17:16:27 +00:00
2016-07-25 00:04:06 +00:00
if ( Core_GetPowerSaving ( ) ) {
2023-04-05 22:34:50 +00:00
auto sy = GetI18NCategory ( I18NCat : : SYSTEM ) ;
2016-10-12 09:13:16 +00:00
# ifdef __ANDROID__
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , sy - > T ( " WARNING: Android battery save mode is on " ) , 2.0f , " core_powerSaving " ) ;
2016-10-11 00:32:25 +00:00
# else
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , sy - > T ( " WARNING: Battery save mode is on " ) , 2.0f , " core_powerSaving " ) ;
2016-10-11 00:32:25 +00:00
# endif
2016-07-25 00:04:06 +00:00
}
2023-12-18 12:11:39 +00:00
if ( g_Config . bStereoRendering ) {
auto gr = GetI18NCategory ( I18NCat : : GRAPHICS ) ;
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
// Stereo rendering is experimental, so let's notify the user it's being used.
// Carefully reuse translations for this rare warning.
2024-01-19 12:44:49 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , std : : string ( gr - > T ( " Stereo rendering " ) ) + " : " + std : : string ( di - > T ( " Enabled " ) ) ) ;
2023-12-18 12:11:39 +00:00
}
2016-08-17 04:24:01 +00:00
saveStateSlot_ = SaveState : : GetCurrentSlot ( ) ;
2017-12-03 19:56:42 +00:00
loadingViewColor_ - > Divert ( 0x00FFFFFF , 0.2f ) ;
loadingViewVisible_ - > Divert ( UI : : V_INVISIBLE , 0.2f ) ;
2023-11-20 10:46:36 +00:00
std : : string gameID = g_paramSFO . GetValueString ( " DISC_ID " ) ;
g_Config . TimeTracker ( ) . Start ( gameID ) ;
2012-11-01 15:19:01 +00:00
}
2013-03-29 20:21:27 +00:00
EmuScreen : : ~ EmuScreen ( ) {
2023-11-20 10:46:36 +00:00
std : : string gameID = g_paramSFO . GetValueString ( " DISC_ID " ) ;
g_Config . TimeTracker ( ) . Stop ( gameID ) ;
2023-07-10 19:04:22 +00:00
// If we were invalid, it would already be shutdown.
2018-11-02 04:27:01 +00:00
if ( ! invalid_ | | bootPending_ ) {
2012-11-01 15:19:01 +00:00
PSP_Shutdown ( ) ;
}
2023-11-20 10:46:36 +00:00
2023-12-10 20:57:05 +00:00
System_PostUIMessage ( UIMessage : : GAME_SELECTED , " " ) ;
2023-07-10 19:04:22 +00:00
g_OSD . ClearAchievementStuff ( ) ;
2022-12-17 21:17:29 +00:00
SetExtraAssertInfo ( nullptr ) ;
2016-09-02 00:09:56 +00:00
# ifndef MOBILE_DEVICE
2016-08-27 18:38:05 +00:00
if ( g_Config . bDumpFrames & & startDumping )
{
avi . Stop ( ) ;
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , " AVI Dump stopped. " , 2.0f ) ;
2016-08-27 18:38:05 +00:00
startDumping = false ;
}
2016-09-02 00:09:56 +00:00
# endif
2018-11-10 15:48:04 +00:00
if ( GetUIState ( ) = = UISTATE_EXIT )
g_Discord . ClearPresence ( ) ;
else
g_Discord . SetPresenceMenu ( ) ;
2012-11-01 15:19:01 +00:00
}
void EmuScreen : : dialogFinished ( const Screen * dialog , DialogResult result ) {
2013-07-17 05:33:26 +00:00
// TODO: improve the way with which we got commands from PauseMenu.
2013-10-25 11:19:08 +00:00
// DR_CANCEL/DR_BACK means clicked on "continue", DR_OK means clicked on "back to menu",
2023-07-06 15:18:46 +00:00
// DR_YES means a message sent to PauseMenu by System_PostUIMessage.
2014-01-25 08:40:14 +00:00
if ( result = = DR_OK | | quit_ ) {
2013-08-18 18:14:33 +00:00
screenManager ( ) - > switchScreen ( new MainScreen ( ) ) ;
2014-01-25 08:40:14 +00:00
quit_ = false ;
2012-11-01 15:19:01 +00:00
}
2022-05-29 18:16:15 +00:00
// Returning to the PauseScreen, unless we're stepping, means we should go back to controls.
if ( Core_IsActive ( ) )
UI : : EnableFocusMovement ( false ) ;
2013-07-20 10:06:06 +00:00
RecreateViews ( ) ;
2024-01-18 22:00:43 +00:00
SetExtraAssertInfo ( extraAssertInfoStr_ . c_str ( ) ) ;
2012-11-01 15:19:01 +00:00
}
2024-01-19 12:44:49 +00:00
static void AfterSaveStateAction ( SaveState : : Status status , std : : string_view message , void * ) {
2019-06-03 10:21:22 +00:00
if ( ! message . empty ( ) & & ( ! g_Config . bDumpFrames | | ! g_Config . bDumpVideoOutput ) ) {
2023-06-20 12:40:46 +00:00
g_OSD . Show ( status = = SaveState : : Status : : SUCCESS ? OSDType : : MESSAGE_SUCCESS : OSDType : : MESSAGE_ERROR , message , status = = SaveState : : Status : : SUCCESS ? 2.0 : 5.0 ) ;
2016-05-28 04:25:05 +00:00
}
}
2024-01-19 12:44:49 +00:00
static void AfterStateBoot ( SaveState : : Status status , std : : string_view message , void * ignored ) {
2018-06-15 00:52:44 +00:00
AfterSaveStateAction ( status , message , ignored ) ;
2014-06-14 23:13:35 +00:00
Core_EnableStepping ( false ) ;
2023-03-21 10:21:19 +00:00
System_Notify ( SystemNotification : : DISASSEMBLY ) ;
2014-06-14 23:13:35 +00:00
}
2023-11-26 18:49:02 +00:00
void EmuScreen : : focusChanged ( ScreenFocusChange focusChange ) {
Screen : : focusChanged ( focusChange ) ;
std : : string gameID = g_paramSFO . GetValueString ( " DISC_ID " ) ;
if ( gameID . empty ( ) ) {
// startup or shutdown
return ;
}
switch ( focusChange ) {
case ScreenFocusChange : : FOCUS_LOST_TOP :
g_Config . TimeTracker ( ) . Stop ( gameID ) ;
2024-01-29 17:41:43 +00:00
controlMapper_ . ReleaseAll ( ) ;
2023-11-26 18:49:02 +00:00
break ;
case ScreenFocusChange : : FOCUS_BECAME_TOP :
g_Config . TimeTracker ( ) . Start ( gameID ) ;
break ;
}
}
2023-09-30 09:21:22 +00:00
void EmuScreen : : sendMessage ( UIMessage message , const char * value ) {
2013-03-29 17:50:08 +00:00
// External commands, like from the Windows UI.
2023-09-30 09:21:22 +00:00
if ( message = = UIMessage : : REQUEST_GAME_PAUSE & & screenManager ( ) - > topScreen ( ) = = this ) {
2013-08-18 18:22:30 +00:00
screenManager ( ) - > push ( new GamePauseScreen ( gamePath_ ) ) ;
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : REQUEST_GAME_STOP ) {
2013-10-12 08:40:33 +00:00
// We will push MainScreen in update().
PSP_Shutdown ( ) ;
2014-05-16 05:17:19 +00:00
bootPending_ = false ;
2017-11-13 14:45:31 +00:00
stopRender_ = true ;
2014-05-11 17:57:33 +00:00
invalid_ = true ;
2023-03-21 10:21:19 +00:00
System_Notify ( SystemNotification : : DISASSEMBLY ) ;
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : REQUEST_GAME_RESET ) {
2013-03-29 19:51:14 +00:00
PSP_Shutdown ( ) ;
2014-05-16 05:17:19 +00:00
bootPending_ = true ;
2014-05-11 17:57:33 +00:00
invalid_ = true ;
2023-03-21 10:21:19 +00:00
System_Notify ( SystemNotification : : DISASSEMBLY ) ;
2014-07-06 21:02:45 +00:00
2013-03-29 19:51:14 +00:00
std : : string resetError ;
2014-05-05 02:56:33 +00:00
if ( ! PSP_InitStart ( PSP_CoreParameter ( ) , & resetError ) ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : Loader , " Error resetting: %s " , resetError . c_str ( ) ) ;
2017-11-13 15:38:35 +00:00
stopRender_ = true ;
2013-08-18 18:14:33 +00:00
screenManager ( ) - > switchScreen ( new MainScreen ( ) ) ;
2013-03-29 19:51:14 +00:00
return ;
}
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : REQUEST_GAME_BOOT ) {
2024-05-13 22:04:40 +00:00
// TODO: Ignore or not if it's the same game that's already running?
if ( gamePath_ = = Path ( value ) ) {
2024-07-14 12:42:59 +00:00
WARN_LOG ( Log : : Loader , " Game already running, ignoring " ) ;
2024-05-13 22:04:40 +00:00
return ;
}
2014-06-14 23:13:35 +00:00
const char * ext = strrchr ( value , ' . ' ) ;
2015-03-01 00:05:13 +00:00
if ( ext ! = nullptr & & ! strcmp ( ext , " .ppst " ) ) {
2021-05-05 23:31:38 +00:00
SaveState : : Load ( Path ( value ) , - 1 , & AfterStateBoot ) ;
2014-06-14 23:13:35 +00:00
} else {
PSP_Shutdown ( ) ;
bootPending_ = true ;
2021-05-05 23:31:38 +00:00
gamePath_ = Path ( value ) ;
2021-01-08 07:16:17 +00:00
// Don't leave it on CORE_POWERDOWN, we'll sometimes aggressively bail.
Core_UpdateState ( CORE_POWERUP ) ;
2014-06-14 23:13:35 +00:00
}
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : CONFIG_LOADED ) {
2017-12-09 22:11:39 +00:00
// In case we need to position touch controls differently.
RecreateViews ( ) ;
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : SHOW_CONTROL_MAPPING & & screenManager ( ) - > topScreen ( ) = = this ) {
2018-05-01 01:57:25 +00:00
UpdateUIState ( UISTATE_PAUSEMENU ) ;
2022-11-22 21:53:54 +00:00
screenManager ( ) - > push ( new ControlMappingScreen ( gamePath_ ) ) ;
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : SHOW_DISPLAY_LAYOUT_EDITOR & & screenManager ( ) - > topScreen ( ) = = this ) {
2018-05-01 01:57:25 +00:00
UpdateUIState ( UISTATE_PAUSEMENU ) ;
2022-11-22 13:16:24 +00:00
screenManager ( ) - > push ( new DisplayLayoutScreen ( gamePath_ ) ) ;
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : SHOW_SETTINGS & & screenManager ( ) - > topScreen ( ) = = this ) {
2018-05-01 01:57:25 +00:00
UpdateUIState ( UISTATE_PAUSEMENU ) ;
2013-09-07 15:29:44 +00:00
screenManager ( ) - > push ( new GameSettingsScreen ( gamePath_ ) ) ;
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : REQUEST_GPU_DUMP_NEXT_FRAME ) {
2015-05-21 08:49:47 +00:00
if ( gpu )
gpu - > DumpNextFrame ( ) ;
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : REQUEST_CLEAR_JIT ) {
2014-12-12 22:48:48 +00:00
currentMIPS - > ClearJitCache ( ) ;
2014-05-28 06:02:28 +00:00
if ( PSP_IsInited ( ) ) {
2016-05-07 23:43:27 +00:00
currentMIPS - > UpdateCore ( ( CPUCore ) g_Config . iCpuCore ) ;
2014-05-28 06:02:28 +00:00
}
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : WINDOW_MINIMIZED ) {
2014-06-29 22:06:47 +00:00
if ( ! strcmp ( value , " true " ) ) {
gstate_c . skipDrawReason | = SKIPDRAW_WINDOW_MINIMIZED ;
} else {
gstate_c . skipDrawReason & = ~ SKIPDRAW_WINDOW_MINIMIZED ;
}
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : SHOW_CHAT_SCREEN ) {
2021-09-15 07:12:33 +00:00
if ( g_Config . bEnableNetworkChat ) {
if ( ! chatButton_ )
RecreateViews ( ) ;
2020-03-02 17:23:12 +00:00
2016-12-05 03:21:44 +00:00
# if defined(USING_WIN_UI)
2023-06-20 12:40:46 +00:00
// temporary workaround for hotkey its freeze the ui when open chat screen using hotkey and native keyboard is enable
2021-09-15 07:12:33 +00:00
if ( g_Config . bBypassOSKWithKeyboard ) {
2023-06-20 12:40:46 +00:00
// TODO: Make translatable.
g_OSD . Show ( OSDType : : MESSAGE_INFO , " Disable \" Use system native keyboard \" to use ctrl + c hotkey " , 2.0f ) ;
2021-09-15 07:12:33 +00:00
} else {
2017-07-08 10:08:33 +00:00
UI : : EventParams e { } ;
OnChatMenu . Trigger ( e ) ;
2017-06-07 18:18:45 +00:00
}
# else
2017-07-08 10:08:33 +00:00
UI : : EventParams e { } ;
OnChatMenu . Trigger ( e ) ;
2016-12-05 03:21:44 +00:00
# endif
2021-09-15 07:12:33 +00:00
}
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : APP_RESUMED & & screenManager ( ) - > topScreen ( ) = = this ) {
2021-08-11 21:26:39 +00:00
if ( System_GetPropertyInt ( SYSPROP_DEVICE_TYPE ) = = DEVICE_TYPE_TV ) {
if ( ! KeyMap : : IsKeyMapped ( DEVICE_ID_PAD_0 , VIRTKEY_PAUSE ) | | ! KeyMap : : IsKeyMapped ( DEVICE_ID_PAD_1 , VIRTKEY_PAUSE ) ) {
// If it's a TV (so no built-in back button), and there's no back button mapped to a pad,
// use this as the fallback way to get into the menu.
2021-08-11 07:07:04 +00:00
2021-08-11 21:26:39 +00:00
screenManager ( ) - > push ( new GamePauseScreen ( gamePath_ ) ) ;
}
}
2023-09-30 09:21:22 +00:00
} else if ( message = = UIMessage : : REQUEST_PLAY_SOUND ) {
2024-01-28 10:22:19 +00:00
if ( g_Config . bAchievementsSoundEffects & & g_Config . bEnableSound ) {
float achievementVolume = g_Config . iAchievementSoundVolume * 0.1f ;
2023-07-12 17:18:56 +00:00
// TODO: Handle this some nicer way.
if ( ! strcmp ( value , " achievement_unlocked " ) ) {
2024-01-28 10:22:19 +00:00
g_BackgroundAudio . SFX ( ) . Play ( UI : : UISound : : ACHIEVEMENT_UNLOCKED , achievementVolume * 1.0f ) ;
2023-07-12 17:18:56 +00:00
}
if ( ! strcmp ( value , " leaderboard_submitted " ) ) {
2024-01-28 10:22:19 +00:00
g_BackgroundAudio . SFX ( ) . Play ( UI : : UISound : : LEADERBOARD_SUBMITTED , achievementVolume * 1.0f ) ;
2023-07-12 17:18:56 +00:00
}
}
2013-11-02 20:33:27 +00:00
}
2013-03-29 17:50:08 +00:00
}
2023-09-04 08:58:11 +00:00
bool EmuScreen : : UnsyncTouch ( const TouchInput & touch ) {
2023-08-10 11:21:36 +00:00
System_Notify ( SystemNotification : : ACTIVITY ) ;
2015-03-01 06:20:14 +00:00
2021-11-19 15:12:26 +00:00
if ( chatMenu_ & & chatMenu_ - > GetVisibility ( ) = = UI : : V_VISIBLE ) {
// Avoid pressing touch button behind the chat
if ( chatMenu_ - > Contains ( touch . x , touch . y ) ) {
chatMenu_ - > Touch ( touch ) ;
2023-09-04 08:58:11 +00:00
return true ;
2021-11-19 15:12:26 +00:00
} else if ( ( touch . flags & TOUCH_DOWN ) ! = 0 ) {
chatMenu_ - > Close ( ) ;
if ( chatButton_ )
chatButton_ - > SetVisibility ( UI : : V_VISIBLE ) ;
UI : : EnableFocusMovement ( false ) ;
}
2021-09-13 02:44:44 +00:00
}
2023-12-29 16:07:49 +00:00
GamepadTouch ( ) ;
2014-06-15 11:04:59 +00:00
if ( root_ ) {
2023-11-11 10:21:36 +00:00
UIScreen : : UnsyncTouch ( touch ) ;
2014-06-15 11:04:59 +00:00
}
2023-09-04 08:58:11 +00:00
return true ;
2013-07-06 17:08:59 +00:00
}
2023-03-30 08:47:28 +00:00
void EmuScreen : : onVKey ( int virtualKeyCode , bool down ) {
2023-04-05 22:34:50 +00:00
auto sc = GetI18NCategory ( I18NCat : : SCREEN ) ;
2023-04-19 22:00:46 +00:00
auto mc = GetI18NCategory ( I18NCat : : MAPPABLECONTROLS ) ;
2013-07-07 12:08:08 +00:00
2013-07-07 08:42:39 +00:00
switch ( virtualKeyCode ) {
2021-08-17 14:48:47 +00:00
case VIRTKEY_FASTFORWARD :
2023-03-30 08:47:28 +00:00
if ( down ) {
if ( coreState = = CORE_STEPPING ) {
Core_EnableStepping ( false ) ;
}
PSP_CoreParameter ( ) . fastForward = true ;
} else {
PSP_CoreParameter ( ) . fastForward = false ;
2018-06-23 04:33:12 +00:00
}
2013-07-20 12:05:07 +00:00
break ;
2013-07-07 12:08:08 +00:00
case VIRTKEY_SPEED_TOGGLE :
2023-07-11 09:29:04 +00:00
if ( down ) {
2023-03-30 08:47:28 +00:00
// Cycle through enabled speeds.
if ( PSP_CoreParameter ( ) . fpsLimit = = FPSLimit : : NORMAL & & g_Config . iFpsLimit1 > = 0 ) {
PSP_CoreParameter ( ) . fpsLimit = FPSLimit : : CUSTOM1 ;
2023-10-01 15:59:43 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " fixed " , " Speed: alternate " ) , 1.0 , " altspeed " ) ;
2023-03-30 08:47:28 +00:00
} else if ( PSP_CoreParameter ( ) . fpsLimit = = FPSLimit : : CUSTOM1 & & g_Config . iFpsLimit2 > = 0 ) {
PSP_CoreParameter ( ) . fpsLimit = FPSLimit : : CUSTOM2 ;
2023-10-01 15:59:43 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " SpeedCustom2 " , " Speed: alternate 2 " ) , 1.0 , " altspeed " ) ;
2023-03-30 08:47:28 +00:00
} else if ( PSP_CoreParameter ( ) . fpsLimit = = FPSLimit : : CUSTOM1 | | PSP_CoreParameter ( ) . fpsLimit = = FPSLimit : : CUSTOM2 ) {
PSP_CoreParameter ( ) . fpsLimit = FPSLimit : : NORMAL ;
2023-10-01 15:59:43 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " standard " , " Speed: standard " ) , 1.0 , " altspeed " ) ;
2023-03-30 08:47:28 +00:00
}
2013-07-07 12:08:08 +00:00
}
break ;
2013-07-08 15:46:20 +00:00
2018-06-17 03:07:11 +00:00
case VIRTKEY_SPEED_CUSTOM1 :
2023-07-11 09:29:04 +00:00
if ( down ) {
if ( PSP_CoreParameter ( ) . fpsLimit = = FPSLimit : : NORMAL ) {
PSP_CoreParameter ( ) . fpsLimit = FPSLimit : : CUSTOM1 ;
2023-10-01 15:59:43 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " fixed " , " Speed: alternate " ) , 1.0 , " altspeed " ) ;
2023-07-11 09:29:04 +00:00
}
} else {
if ( PSP_CoreParameter ( ) . fpsLimit = = FPSLimit : : CUSTOM1 ) {
PSP_CoreParameter ( ) . fpsLimit = FPSLimit : : NORMAL ;
2023-10-01 15:59:43 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " standard " , " Speed: standard " ) , 1.0 , " altspeed " ) ;
2023-03-30 08:47:28 +00:00
}
2018-06-17 01:22:34 +00:00
}
break ;
2018-06-17 03:07:11 +00:00
case VIRTKEY_SPEED_CUSTOM2 :
2023-07-11 09:29:04 +00:00
if ( down ) {
if ( PSP_CoreParameter ( ) . fpsLimit = = FPSLimit : : NORMAL ) {
PSP_CoreParameter ( ) . fpsLimit = FPSLimit : : CUSTOM2 ;
2023-10-01 15:59:43 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " SpeedCustom2 " , " Speed: alternate 2 " ) , 1.0 , " altspeed " ) ;
2023-07-11 09:29:04 +00:00
}
} else {
if ( PSP_CoreParameter ( ) . fpsLimit = = FPSLimit : : CUSTOM2 ) {
PSP_CoreParameter ( ) . fpsLimit = FPSLimit : : NORMAL ;
2023-10-01 15:59:43 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " standard " , " Speed: standard " ) , 1.0 , " altspeed " ) ;
2023-03-30 08:47:28 +00:00
}
2018-06-17 03:07:11 +00:00
}
break ;
2018-06-17 01:22:34 +00:00
2013-07-07 12:08:08 +00:00
case VIRTKEY_PAUSE :
2023-03-30 08:47:28 +00:00
if ( down ) {
2023-05-02 19:36:17 +00:00
// Trigger on key-up to partially avoid repetition problems.
// This is needed whenever we pop up a menu since the mapper
// might miss the key-up. Same as VIRTKEY_OPENCHAT.
2023-03-30 08:47:28 +00:00
pauseTrigger_ = true ;
2023-05-02 19:36:17 +00:00
controlMapper_ . ForceReleaseVKey ( virtualKeyCode ) ;
2023-03-30 08:47:28 +00:00
}
2013-07-07 12:08:08 +00:00
break ;
2013-07-07 23:25:15 +00:00
2016-08-28 04:20:03 +00:00
case VIRTKEY_FRAME_ADVANCE :
2024-09-18 13:17:20 +00:00
// Can't do this reliably in an async fashion, so we just set a variable.
if ( down ) {
doFrameAdvance_ . store ( true ) ;
2016-08-28 04:20:03 +00:00
}
break ;
2017-06-07 18:26:49 +00:00
case VIRTKEY_OPENCHAT :
2023-05-02 19:36:17 +00:00
if ( down & & g_Config . bEnableNetworkChat ) {
2017-07-08 10:08:33 +00:00
UI : : EventParams e { } ;
OnChatMenu . Trigger ( e ) ;
2023-05-02 19:36:17 +00:00
controlMapper_ . ForceReleaseVKey ( virtualKeyCode ) ;
2017-06-07 18:26:49 +00:00
}
break ;
2014-11-07 12:40:53 +00:00
case VIRTKEY_AXIS_SWAP :
2023-03-30 08:47:28 +00:00
if ( down ) {
2023-04-19 22:00:46 +00:00
controlMapper_ . ToggleSwapAxes ( ) ;
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , mc - > T ( " AxisSwap " ) ) ; // best string we have.
2023-03-30 08:47:28 +00:00
}
2014-11-07 12:40:53 +00:00
break ;
2015-07-05 23:09:29 +00:00
case VIRTKEY_DEVMENU :
2023-03-30 08:47:28 +00:00
if ( down ) {
UI : : EventParams e { } ;
OnDevMenu . Trigger ( e ) ;
}
2015-07-05 23:09:29 +00:00
break ;
2024-09-18 12:18:04 +00:00
case VIRTKEY_RESET_EMULATION :
System_PostUIMessage ( UIMessage : : REQUEST_GAME_RESET ) ;
break ;
2017-12-01 22:48:58 +00:00
# ifndef MOBILE_DEVICE
case VIRTKEY_RECORD :
2023-03-30 08:47:28 +00:00
if ( down ) {
if ( g_Config . bDumpFrames = = g_Config . bDumpAudio ) {
g_Config . bDumpFrames = ! g_Config . bDumpFrames ;
g_Config . bDumpAudio = ! g_Config . bDumpAudio ;
2017-12-01 22:48:58 +00:00
} else {
2023-03-30 08:47:28 +00:00
// This hotkey should always toggle both audio and video together.
// So let's make sure that's the only outcome even if video OR audio was already being dumped.
if ( g_Config . bDumpFrames ) {
AVIDump : : Stop ( ) ;
AVIDump : : Start ( PSP_CoreParameter ( ) . renderWidth , PSP_CoreParameter ( ) . renderHeight ) ;
g_Config . bDumpAudio = true ;
} else {
WAVDump : : Reset ( ) ;
g_Config . bDumpFrames = true ;
}
2017-12-01 22:48:58 +00:00
}
}
break ;
# endif
2013-12-05 15:15:33 +00:00
case VIRTKEY_REWIND :
2023-12-03 15:41:29 +00:00
if ( down & & ! Achievements : : WarnUserIfHardcoreModeActive ( false ) ) {
2023-03-30 08:47:28 +00:00
if ( SaveState : : CanRewind ( ) ) {
SaveState : : Rewind ( & AfterSaveStateAction ) ;
} else {
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , sc - > T ( " norewind " , " No rewind save states available " ) , 2.0 ) ;
2023-03-30 08:47:28 +00:00
}
2013-12-05 15:15:33 +00:00
}
break ;
2013-12-06 14:46:56 +00:00
case VIRTKEY_SAVE_STATE :
2023-12-03 15:41:29 +00:00
if ( down & & ! Achievements : : WarnUserIfHardcoreModeActive ( true ) ) {
2023-03-30 08:47:28 +00:00
SaveState : : SaveSlot ( gamePath_ , g_Config . iCurrentStateSlot , & AfterSaveStateAction ) ;
2023-11-16 19:13:47 +00:00
}
2013-12-06 14:46:56 +00:00
break ;
case VIRTKEY_LOAD_STATE :
2023-12-03 15:41:29 +00:00
if ( down & & ! Achievements : : WarnUserIfHardcoreModeActive ( false ) ) {
2023-03-30 08:47:28 +00:00
SaveState : : LoadSlot ( gamePath_ , g_Config . iCurrentStateSlot , & AfterSaveStateAction ) ;
2023-11-16 19:13:47 +00:00
}
2013-12-06 14:46:56 +00:00
break ;
2023-05-25 11:32:49 +00:00
case VIRTKEY_PREVIOUS_SLOT :
2023-12-03 15:41:29 +00:00
if ( down & & ! Achievements : : WarnUserIfHardcoreModeActive ( true ) ) {
2023-05-25 11:32:49 +00:00
SaveState : : PrevSlot ( ) ;
2023-09-30 09:21:22 +00:00
System_PostUIMessage ( UIMessage : : SAVESTATE_DISPLAY_SLOT ) ;
2023-05-25 11:32:49 +00:00
}
break ;
2014-01-07 14:56:04 +00:00
case VIRTKEY_NEXT_SLOT :
2023-12-03 15:41:29 +00:00
if ( down & & ! Achievements : : WarnUserIfHardcoreModeActive ( true ) ) {
2023-03-30 08:47:28 +00:00
SaveState : : NextSlot ( ) ;
2023-09-30 09:21:22 +00:00
System_PostUIMessage ( UIMessage : : SAVESTATE_DISPLAY_SLOT ) ;
2023-03-30 08:47:28 +00:00
}
2014-01-07 14:56:04 +00:00
break ;
2014-01-03 14:16:23 +00:00
case VIRTKEY_TOGGLE_FULLSCREEN :
2023-03-30 08:47:28 +00:00
if ( down )
System_ToggleFullscreenState ( " " ) ;
2014-01-03 14:16:23 +00:00
break ;
2023-12-29 16:37:34 +00:00
case VIRTKEY_TOGGLE_TOUCH_CONTROLS :
if ( down ) {
if ( g_Config . bShowTouchControls ) {
// This just messes with opacity if enabled, so you can touch the screen again to bring them back.
if ( GamepadGetOpacity ( ) < 0.01f ) {
GamepadTouch ( ) ;
} else {
// Reset.
GamepadTouch ( true ) ;
}
} else {
// If touch controls are disabled though, they'll get enabled.
g_Config . bShowTouchControls = true ;
RecreateViews ( ) ;
GamepadTouch ( ) ;
}
}
break ;
case VIRTKEY_TOGGLE_MOUSE :
if ( down ) {
g_Config . bMouseControl = ! g_Config . bMouseControl ;
}
break ;
2019-05-01 02:01:20 +00:00
case VIRTKEY_SCREENSHOT :
2023-03-30 08:47:28 +00:00
if ( down )
g_TakeScreenshot = true ;
2019-05-01 02:01:20 +00:00
break ;
2019-03-26 05:12:02 +00:00
case VIRTKEY_TEXTURE_DUMP :
2023-03-30 08:47:28 +00:00
if ( down ) {
g_Config . bSaveNewTextures = ! g_Config . bSaveNewTextures ;
if ( g_Config . bSaveNewTextures ) {
2024-09-24 12:01:24 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " saveNewTextures_true " , " Textures will now be saved to your storage " ) , 2.0 , " savetexturechanged " ) ;
2023-09-30 09:21:22 +00:00
System_PostUIMessage ( UIMessage : : GPU_CONFIG_CHANGED ) ;
2023-03-30 08:47:28 +00:00
} else {
2024-09-24 12:01:24 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " saveNewTextures_false " , " Texture saving was disabled " ) , 2.0 , " savetexturechanged " ) ;
2023-03-30 08:47:28 +00:00
}
2019-03-26 05:12:02 +00:00
}
break ;
case VIRTKEY_TEXTURE_REPLACE :
2023-03-30 08:47:28 +00:00
if ( down ) {
g_Config . bReplaceTextures = ! g_Config . bReplaceTextures ;
if ( g_Config . bReplaceTextures )
2024-09-24 12:01:24 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " replaceTextures_true " , " Texture replacement enabled " ) , 2.0 , " replacetexturechanged " ) ;
2023-03-30 08:47:28 +00:00
else
2024-09-24 12:01:24 +00:00
g_OSD . Show ( OSDType : : MESSAGE_INFO , sc - > T ( " replaceTextures_false " , " Textures are no longer being replaced " ) , 2.0 , " replacetexturechanged " ) ;
2023-09-30 09:21:22 +00:00
System_PostUIMessage ( UIMessage : : GPU_CONFIG_CHANGED ) ;
2023-03-30 08:47:28 +00:00
}
2019-03-26 05:12:02 +00:00
break ;
2020-01-28 07:52:18 +00:00
case VIRTKEY_RAPID_FIRE :
2023-07-23 09:49:25 +00:00
__CtrlSetRapidFire ( down , g_Config . iRapidFireInterval ) ;
2020-01-28 07:52:18 +00:00
break ;
2020-02-25 11:26:23 +00:00
case VIRTKEY_MUTE_TOGGLE :
2023-03-30 08:47:28 +00:00
if ( down )
g_Config . bEnableSound = ! g_Config . bEnableSound ;
2020-02-25 11:26:23 +00:00
break ;
2022-04-23 06:52:51 +00:00
case VIRTKEY_SCREEN_ROTATION_VERTICAL :
2023-03-30 08:47:28 +00:00
if ( down )
g_Config . iInternalScreenRotation = ROTATION_LOCKED_VERTICAL ;
2022-04-23 06:52:51 +00:00
break ;
case VIRTKEY_SCREEN_ROTATION_VERTICAL180 :
2023-03-30 08:47:28 +00:00
if ( down )
g_Config . iInternalScreenRotation = ROTATION_LOCKED_VERTICAL180 ;
2022-04-23 06:52:51 +00:00
break ;
case VIRTKEY_SCREEN_ROTATION_HORIZONTAL :
2023-03-30 08:47:28 +00:00
if ( down )
g_Config . iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL ;
2022-04-23 06:52:51 +00:00
break ;
case VIRTKEY_SCREEN_ROTATION_HORIZONTAL180 :
2023-03-30 08:47:28 +00:00
if ( down )
g_Config . iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL180 ;
2013-07-07 08:42:39 +00:00
break ;
2023-08-23 20:14:51 +00:00
case VIRTKEY_TOGGLE_WLAN :
if ( down ) {
auto n = GetI18NCategory ( I18NCat : : NETWORKING ) ;
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
g_Config . bEnableWlan = ! g_Config . bEnableWlan ;
// Try to avoid adding more strings so we piece together a message from existing ones.
g_OSD . Show ( OSDType : : MESSAGE_INFO , StringFromFormat (
" %s: %s " , n - > T ( " Enable networking " ) , g_Config . bEnableWlan ? di - > T ( " Enabled " ) : di - > T ( " Disabled " ) ) , 2.0 , " toggle_wlan " ) ;
}
break ;
2023-10-04 12:10:14 +00:00
case VIRTKEY_EXIT_APP :
System_ExitApp ( ) ;
break ;
2013-07-07 08:42:39 +00:00
}
}
2023-03-30 13:03:41 +00:00
void EmuScreen : : onVKeyAnalog ( int virtualKeyCode , float value ) {
2023-03-30 13:11:34 +00:00
if ( virtualKeyCode ! = VIRTKEY_SPEED_ANALOG ) {
return ;
}
// We only handle VIRTKEY_SPEED_ANALOG here.
2023-03-31 18:27:30 +00:00
// Xbox controllers need a pretty big deadzone here to not leave behind small values
// on occasion when releasing the trigger. Still feels right.
2023-04-15 15:36:55 +00:00
static constexpr float DEADZONE_THRESHOLD = 0.2f ;
static constexpr float DEADZONE_SCALE = 1.0f / ( 1.0f - DEADZONE_THRESHOLD ) ;
2023-03-30 13:11:34 +00:00
FPSLimit & limitMode = PSP_CoreParameter ( ) . fpsLimit ;
// If we're using an alternate speed already, let that win.
if ( limitMode ! = FPSLimit : : NORMAL & & limitMode ! = FPSLimit : : ANALOG )
return ;
// Don't even try if the limit is invalid.
if ( g_Config . iAnalogFpsLimit < = 0 )
return ;
// Apply a small deadzone (against the resting position.)
value = std : : max ( 0.0f , ( value - DEADZONE_THRESHOLD ) * DEADZONE_SCALE ) ;
// If target is above 60, value is how much to speed up over 60. Otherwise, it's how much slower.
// So normalize the target.
int target = g_Config . iAnalogFpsLimit - 60 ;
PSP_CoreParameter ( ) . analogFpsLimit = 60 + ( int ) ( target * value ) ;
2023-03-30 13:03:41 +00:00
2023-03-30 13:11:34 +00:00
// If we've reset back to normal, turn it off.
limitMode = PSP_CoreParameter ( ) . analogFpsLimit = = 60 ? FPSLimit : : NORMAL : FPSLimit : : ANALOG ;
2023-03-30 13:03:41 +00:00
}
2023-05-26 09:49:50 +00:00
bool EmuScreen : : UnsyncKey ( const KeyInput & key ) {
2023-08-10 11:21:36 +00:00
System_Notify ( SystemNotification : : ACTIVITY ) ;
2015-03-01 06:20:14 +00:00
2021-09-13 02:41:47 +00:00
if ( UI : : IsFocusMovementEnabled ( ) ) {
2023-09-12 08:25:04 +00:00
return UIScreen : : UnsyncKey ( key ) ;
2021-09-13 02:41:47 +00:00
}
2021-07-08 19:30:23 +00:00
return controlMapper_ . Key ( key , & pauseTrigger_ ) ;
2013-07-06 17:08:59 +00:00
}
2023-09-12 08:25:04 +00:00
bool EmuScreen : : key ( const KeyInput & key ) {
bool retval = UIScreen : : key ( key ) ;
if ( ! retval & & ( key . flags & KEY_DOWN ) ! = 0 & & UI : : IsEscapeKey ( key ) ) {
if ( chatMenu_ )
chatMenu_ - > Close ( ) ;
if ( chatButton_ )
chatButton_ - > SetVisibility ( UI : : V_VISIBLE ) ;
UI : : EnableFocusMovement ( false ) ;
return true ;
}
return retval ;
}
2023-09-27 15:34:34 +00:00
void EmuScreen : : UnsyncAxis ( const AxisInput * axes , size_t count ) {
2023-08-10 11:21:36 +00:00
System_Notify ( SystemNotification : : ACTIVITY ) ;
2023-11-11 10:21:36 +00:00
if ( UI : : IsFocusMovementEnabled ( ) ) {
return UIScreen : : UnsyncAxis ( axes , count ) ;
}
2023-09-27 15:34:34 +00:00
return controlMapper_ . Axis ( axes , count ) ;
2013-07-06 17:08:59 +00:00
}
2018-01-02 05:51:09 +00:00
class GameInfoBGView : public UI : : InertView {
public :
2021-05-05 23:31:38 +00:00
GameInfoBGView ( const Path & gamePath , UI : : LayoutParams * layoutParams ) : InertView ( layoutParams ) , gamePath_ ( gamePath ) {
2018-01-02 05:51:09 +00:00
}
2021-03-08 23:09:36 +00:00
void Draw ( UIContext & dc ) override {
2018-01-02 05:51:09 +00:00
// Should only be called when visible.
2024-01-28 15:23:27 +00:00
std : : shared_ptr < GameInfo > ginfo = g_gameInfoCache - > GetInfo ( dc . GetDrawContext ( ) , gamePath_ , GameInfoFlags : : BG ) ;
2018-01-02 05:51:09 +00:00
dc . Flush ( ) ;
// PIC1 is the loading image, so let's only draw if it's available.
2024-01-28 15:56:07 +00:00
if ( ginfo - > Ready ( GameInfoFlags : : BG ) & & ginfo - > pic1 . texture ) {
2023-12-12 21:34:31 +00:00
Draw : : Texture * texture = ginfo - > pic1 . texture ;
2018-05-27 19:54:07 +00:00
if ( texture ) {
dc . GetDrawContext ( ) - > BindTexture ( 0 , texture ) ;
double loadTime = ginfo - > pic1 . timeLoaded ;
uint32_t color = alphaMul ( color_ , ease ( ( time_now_d ( ) - loadTime ) * 3 ) ) ;
dc . Draw ( ) - > DrawTexRect ( dc . GetBounds ( ) , 0 , 0 , 1 , 1 , color ) ;
dc . Flush ( ) ;
dc . RebindTexture ( ) ;
}
2018-01-02 05:51:09 +00:00
}
}
2021-02-22 00:38:02 +00:00
std : : string DescribeText ( ) const override {
return " " ;
}
2018-01-02 05:51:09 +00:00
void SetColor ( uint32_t c ) {
color_ = c ;
}
protected :
2021-05-05 23:31:38 +00:00
Path gamePath_ ;
2018-01-02 05:51:09 +00:00
uint32_t color_ = 0xFFC0C0C0 ;
} ;
2023-04-05 21:47:51 +00:00
// TODO: Shouldn't actually need bounds for this, Anchor can center too.
static UI : : AnchorLayoutParams * AnchorInCorner ( const Bounds & bounds , int corner , float xOffset , float yOffset ) {
using namespace UI ;
2023-07-16 09:03:49 +00:00
switch ( ( ScreenEdgePosition ) g_Config . iChatButtonPosition ) {
case ScreenEdgePosition : : BOTTOM_LEFT : return new AnchorLayoutParams ( WRAP_CONTENT , WRAP_CONTENT , xOffset , NONE , NONE , yOffset , true ) ;
case ScreenEdgePosition : : BOTTOM_CENTER : return new AnchorLayoutParams ( WRAP_CONTENT , WRAP_CONTENT , bounds . centerX ( ) , NONE , NONE , yOffset , true ) ;
2023-08-07 19:38:03 +00:00
case ScreenEdgePosition : : BOTTOM_RIGHT : return new AnchorLayoutParams ( WRAP_CONTENT , WRAP_CONTENT , NONE , NONE , xOffset , yOffset , true ) ;
2023-07-16 09:03:49 +00:00
case ScreenEdgePosition : : TOP_LEFT : return new AnchorLayoutParams ( WRAP_CONTENT , WRAP_CONTENT , xOffset , yOffset , NONE , NONE , true ) ;
case ScreenEdgePosition : : TOP_CENTER : return new AnchorLayoutParams ( WRAP_CONTENT , WRAP_CONTENT , bounds . centerX ( ) , yOffset , NONE , NONE , true ) ;
case ScreenEdgePosition : : TOP_RIGHT : return new AnchorLayoutParams ( WRAP_CONTENT , WRAP_CONTENT , NONE , yOffset , xOffset , NONE , true ) ;
case ScreenEdgePosition : : CENTER_LEFT : return new AnchorLayoutParams ( WRAP_CONTENT , WRAP_CONTENT , xOffset , bounds . centerY ( ) , NONE , NONE , true ) ;
case ScreenEdgePosition : : CENTER_RIGHT : return new AnchorLayoutParams ( WRAP_CONTENT , WRAP_CONTENT , NONE , bounds . centerY ( ) , xOffset , NONE , true ) ;
2023-04-05 21:47:51 +00:00
default : return new AnchorLayoutParams ( WRAP_CONTENT , WRAP_CONTENT , xOffset , NONE , NONE , yOffset , true ) ;
}
}
2013-07-20 10:06:06 +00:00
void EmuScreen : : CreateViews ( ) {
2015-02-01 17:04:06 +00:00
using namespace UI ;
2017-12-03 18:39:25 +00:00
2023-04-05 22:34:50 +00:00
auto dev = GetI18NCategory ( I18NCat : : DEVELOPER ) ;
auto sc = GetI18NCategory ( I18NCat : : SCREEN ) ;
2017-12-03 18:39:25 +00:00
2020-03-30 22:47:01 +00:00
const Bounds & bounds = screenManager ( ) - > getUIContext ( ) - > GetLayoutBounds ( ) ;
2014-02-10 14:55:21 +00:00
InitPadLayout ( bounds . w , bounds . h ) ;
2021-08-28 18:06:05 +00:00
// Devices without a back button like iOS need an on-screen touch back button.
bool showPauseButton = ! System_GetPropertyBool ( SYSPROP_HAS_BACK_BUTTON ) | | g_Config . bShowTouchPause ;
root_ = CreatePadLayout ( bounds . w , bounds . h , & pauseTrigger_ , showPauseButton , & controlMapper_ ) ;
2013-09-07 18:54:11 +00:00
if ( g_Config . bShowDeveloperMenu ) {
2017-12-03 18:39:25 +00:00
root_ - > Add ( new Button ( dev - > T ( " DevMenu " ) ) ) - > OnClick . Handle ( this , & EmuScreen : : OnDevTools ) ;
2013-09-07 18:54:11 +00:00
}
2021-08-09 06:03:19 +00:00
LinearLayout * buttons = new LinearLayout ( Orientation : : ORIENT_HORIZONTAL , new AnchorLayoutParams ( bounds . centerX ( ) , NONE , NONE , 60 , true ) ) ;
buttons - > SetSpacing ( 20.0f ) ;
root_ - > Add ( buttons ) ;
resumeButton_ = buttons - > Add ( new Button ( dev - > T ( " Resume " ) ) ) ;
2020-12-19 19:26:02 +00:00
resumeButton_ - > OnClick . Handle ( this , & EmuScreen : : OnResume ) ;
resumeButton_ - > SetVisibility ( V_GONE ) ;
2019-10-25 08:56:01 +00:00
2021-08-09 06:03:19 +00:00
resetButton_ = buttons - > Add ( new Button ( dev - > T ( " Reset " ) ) ) ;
resetButton_ - > OnClick . Handle ( this , & EmuScreen : : OnReset ) ;
resetButton_ - > SetVisibility ( V_GONE ) ;
2019-10-25 08:56:01 +00:00
cardboardDisableButton_ = root_ - > Add ( new Button ( sc - > T ( " Cardboard VR OFF " ) , new AnchorLayoutParams ( bounds . centerX ( ) , NONE , NONE , 30 , true ) ) ) ;
cardboardDisableButton_ - > OnClick . Handle ( this , & EmuScreen : : OnDisableCardboard ) ;
cardboardDisableButton_ - > SetVisibility ( V_GONE ) ;
2020-08-30 15:50:42 +00:00
cardboardDisableButton_ - > SetScale ( 0.65f ) ; // make it smaller - this button can be in the way otherwise.
2019-10-25 08:56:01 +00:00
2023-04-05 21:47:51 +00:00
if ( g_Config . bEnableNetworkChat ) {
if ( g_Config . iChatButtonPosition ! = 8 ) {
2023-12-20 09:35:02 +00:00
auto n = GetI18NCategory ( I18NCat : : NETWORKING ) ;
2023-04-05 21:47:51 +00:00
AnchorLayoutParams * layoutParams = AnchorInCorner ( bounds , g_Config . iChatButtonPosition , 80.0f , 50.0f ) ;
ChoiceWithValueDisplay * btn = new ChoiceWithValueDisplay ( & newChatMessages_ , n - > T ( " Chat " ) , layoutParams ) ;
root_ - > Add ( btn ) - > OnClick . Handle ( this , & EmuScreen : : OnChat ) ;
chatButton_ = btn ;
2016-12-05 03:04:31 +00:00
}
2024-01-18 10:55:39 +00:00
chatMenu_ = root_ - > Add ( new ChatMenu ( GetRequesterToken ( ) , screenManager ( ) - > getUIContext ( ) - > GetBounds ( ) , new LayoutParams ( FILL_PARENT , FILL_PARENT ) ) ) ;
2021-09-13 02:44:44 +00:00
chatMenu_ - > SetVisibility ( UI : : V_GONE ) ;
2021-02-16 04:37:24 +00:00
} else {
chatButton_ = nullptr ;
2021-09-13 02:44:44 +00:00
chatMenu_ = nullptr ;
2016-10-22 16:06:07 +00:00
}
2020-03-02 15:25:18 +00:00
2021-05-05 23:31:38 +00:00
saveStatePreview_ = new AsyncImageFileView ( Path ( ) , IS_FIXED , new AnchorLayoutParams ( bounds . centerX ( ) , 100 , NONE , NONE , true ) ) ;
2015-02-01 17:04:06 +00:00
saveStatePreview_ - > SetFixedSize ( 160 , 90 ) ;
saveStatePreview_ - > SetColor ( 0x90FFFFFF ) ;
saveStatePreview_ - > SetVisibility ( V_GONE ) ;
2015-02-02 09:05:23 +00:00
saveStatePreview_ - > SetCanBeFocused ( false ) ;
2015-02-01 17:04:06 +00:00
root_ - > Add ( saveStatePreview_ ) ;
2017-12-03 19:56:42 +00:00
2018-01-02 05:51:09 +00:00
GameInfoBGView * loadingBG = root_ - > Add ( new GameInfoBGView ( gamePath_ , new AnchorLayoutParams ( FILL_PARENT , FILL_PARENT ) ) ) ;
2018-03-13 10:25:00 +00:00
TextView * loadingTextView = root_ - > Add ( new TextView ( sc - > T ( PSP_GetLoading ( ) ) , new AnchorLayoutParams ( bounds . centerX ( ) , NONE , NONE , 40 , true ) ) ) ;
2018-04-29 21:44:00 +00:00
loadingTextView_ = loadingTextView ;
2018-04-20 07:55:03 +00:00
2020-02-29 20:51:14 +00:00
static const ImageID symbols [ 4 ] = {
ImageID ( " I_CROSS " ) ,
ImageID ( " I_CIRCLE " ) ,
ImageID ( " I_SQUARE " ) ,
ImageID ( " I_TRIANGLE " ) ,
2018-02-08 11:02:44 +00:00
} ;
2020-02-29 20:51:14 +00:00
2018-03-13 10:25:00 +00:00
Spinner * loadingSpinner = root_ - > Add ( new Spinner ( symbols , ARRAY_SIZE ( symbols ) , new AnchorLayoutParams ( NONE , NONE , 45 , 45 , true ) ) ) ;
2018-02-08 11:02:44 +00:00
loadingSpinner_ = loadingSpinner ;
2018-04-29 21:44:00 +00:00
loadingBG - > SetTag ( " LoadingBG " ) ;
loadingTextView - > SetTag ( " LoadingText " ) ;
loadingSpinner - > SetTag ( " LoadingSpinner " ) ;
// Don't really need this, and it creates a lot of strings to translate...
loadingTextView - > SetVisibility ( V_GONE ) ;
2018-01-02 05:51:09 +00:00
loadingTextView - > SetShadow ( true ) ;
2017-12-03 19:56:42 +00:00
2018-04-29 21:44:00 +00:00
loadingViewColor_ = loadingSpinner - > AddTween ( new CallbackColorTween ( 0x00FFFFFF , 0x00FFFFFF , 0.2f , & bezierEaseInOut ) ) ;
2018-02-08 11:02:44 +00:00
loadingViewColor_ - > SetCallback ( [ loadingBG , loadingTextView , loadingSpinner ] ( View * v , uint32_t c ) {
2018-01-02 05:51:09 +00:00
loadingBG - > SetColor ( c & 0xFFC0C0C0 ) ;
loadingTextView - > SetTextColor ( c ) ;
2018-02-08 11:02:44 +00:00
loadingSpinner - > SetColor ( alphaMul ( c , 0.7f ) ) ;
2018-01-02 05:51:09 +00:00
} ) ;
2017-12-10 08:05:10 +00:00
loadingViewColor_ - > Persist ( ) ;
2018-01-02 05:51:09 +00:00
// We start invisible here, in case of recreated views.
2018-04-29 21:44:00 +00:00
loadingViewVisible_ = loadingSpinner - > AddTween ( new VisibilityTween ( UI : : V_INVISIBLE , UI : : V_INVISIBLE , 0.2f , & bezierEaseInOut ) ) ;
2017-12-10 08:05:10 +00:00
loadingViewVisible_ - > Persist ( ) ;
2018-04-29 21:44:00 +00:00
loadingViewVisible_ - > Finish . Add ( [ loadingBG , loadingSpinner ] ( EventParams & p ) {
2018-01-02 05:51:09 +00:00
loadingBG - > SetVisibility ( p . v - > GetVisibility ( ) ) ;
// If we just became invisible, flush BGs since we don't need them anymore.
// Saves some VRAM for the game, but don't do it before we fade out...
if ( p . v - > GetVisibility ( ) = = V_INVISIBLE ) {
g_gameInfoCache - > FlushBGs ( ) ;
2018-04-29 21:44:00 +00:00
// And we can go away too. This means the tween will never run again.
loadingBG - > SetVisibility ( V_GONE ) ;
loadingSpinner - > SetVisibility ( V_GONE ) ;
2018-01-02 05:51:09 +00:00
}
return EVENT_DONE ;
} ) ;
2023-03-26 21:32:30 +00:00
// Will become visible along with the loadingView.
loadingBG - > SetVisibility ( V_INVISIBLE ) ;
2013-09-07 18:54:11 +00:00
}
UI : : EventReturn EmuScreen : : OnDevTools ( UI : : EventParams & params ) {
2023-04-05 22:34:50 +00:00
DevMenuScreen * devMenu = new DevMenuScreen ( gamePath_ , I18NCat : : DEVELOPER ) ;
2017-03-22 01:27:57 +00:00
if ( params . v )
devMenu - > SetPopupOrigin ( params . v ) ;
screenManager ( ) - > push ( devMenu ) ;
2013-09-07 18:54:11 +00:00
return UI : : EVENT_DONE ;
2013-07-20 10:06:06 +00:00
}
2019-10-25 08:56:01 +00:00
UI : : EventReturn EmuScreen : : OnDisableCardboard ( UI : : EventParams & params ) {
2019-10-25 09:01:49 +00:00
g_Config . bEnableCardboardVR = false ;
2019-10-25 08:56:01 +00:00
return UI : : EVENT_DONE ;
}
2020-12-19 19:26:02 +00:00
UI : : EventReturn EmuScreen : : OnChat ( UI : : EventParams & params ) {
2021-02-16 04:37:24 +00:00
if ( chatButton_ ! = nullptr & & chatButton_ - > GetVisibility ( ) = = UI : : V_VISIBLE ) {
chatButton_ - > SetVisibility ( UI : : V_GONE ) ;
2020-12-19 19:26:02 +00:00
}
2021-09-13 02:44:44 +00:00
if ( chatMenu_ ! = nullptr ) {
chatMenu_ - > SetVisibility ( UI : : V_VISIBLE ) ;
# if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(SDL)
UI : : EnableFocusMovement ( true ) ;
root_ - > SetDefaultFocusView ( chatMenu_ ) ;
2021-09-13 03:16:05 +00:00
2021-09-13 02:44:44 +00:00
chatMenu_ - > SetFocus ( ) ;
2021-09-13 03:16:05 +00:00
UI : : View * focused = UI : : GetFocusedView ( ) ;
if ( focused ) {
root_ - > SubviewFocused ( focused ) ;
}
2021-09-13 02:44:44 +00:00
# endif
}
2016-10-21 10:35:54 +00:00
return UI : : EVENT_DONE ;
}
2020-12-19 19:26:02 +00:00
UI : : EventReturn EmuScreen : : OnResume ( UI : : EventParams & params ) {
if ( coreState = = CoreState : : CORE_RUNTIME_ERROR ) {
// Force it!
Memory : : MemFault_IgnoreLastCrash ( ) ;
coreState = CoreState : : CORE_RUNNING ;
}
return UI : : EVENT_DONE ;
}
2021-08-09 06:03:19 +00:00
UI : : EventReturn EmuScreen : : OnReset ( UI : : EventParams & params ) {
if ( coreState = = CoreState : : CORE_RUNTIME_ERROR ) {
2023-09-30 09:21:22 +00:00
System_PostUIMessage ( UIMessage : : REQUEST_GAME_RESET ) ;
2021-08-09 06:03:19 +00:00
}
return UI : : EVENT_DONE ;
}
2017-03-15 05:01:18 +00:00
void EmuScreen : : update ( ) {
2020-12-19 19:26:02 +00:00
using namespace UI ;
2017-12-03 19:56:42 +00:00
UIScreen : : update ( ) ;
2020-12-19 19:26:02 +00:00
resumeButton_ - > SetVisibility ( coreState = = CoreState : : CORE_RUNTIME_ERROR & & Memory : : MemFault_MayBeResumable ( ) ? V_VISIBLE : V_GONE ) ;
2021-08-09 06:03:19 +00:00
resetButton_ - > SetVisibility ( coreState = = CoreState : : CORE_RUNTIME_ERROR ? V_VISIBLE : V_GONE ) ;
2017-12-03 19:56:42 +00:00
2021-09-15 00:59:46 +00:00
if ( chatButton_ & & chatMenu_ ) {
if ( chatMenu_ - > GetVisibility ( ) ! = V_GONE ) {
chatMessages_ = GetChatMessageCount ( ) ;
newChatMessages_ = 0 ;
} else {
int diff = GetChatMessageCount ( ) - chatMessages_ ;
// Cap the count at 50.
newChatMessages_ = diff > 50 ? 50 : diff ;
}
}
2020-11-22 18:08:14 +00:00
if ( bootPending_ ) {
2023-07-24 10:00:16 +00:00
// Keep trying the boot until bootPending_ is lifted.
// It may be delayed due to RetroAchievements or any other cause.
2013-07-27 11:26:26 +00:00
bootGame ( gamePath_ ) ;
2020-11-22 18:08:14 +00:00
}
2013-07-27 11:26:26 +00:00
2016-03-20 08:52:13 +00:00
// Simply forcibly update to the current screen size every frame. Doesn't cost much.
2014-02-10 14:14:45 +00:00
// If bounds is set to be smaller than the actual pixel resolution of the display, respect that.
// TODO: Should be able to use g_dpi_scale here instead. Might want to store the dpi scale in the UI context too.
2017-03-06 12:50:22 +00:00
# ifndef _WIN32
2014-02-10 14:14:45 +00:00
const Bounds & bounds = screenManager ( ) - > getUIContext ( ) - > GetBounds ( ) ;
2023-02-25 12:09:44 +00:00
PSP_CoreParameter ( ) . pixelWidth = g_display . pixel_xres * bounds . w / g_display . dp_xres ;
PSP_CoreParameter ( ) . pixelHeight = g_display . pixel_yres * bounds . h / g_display . dp_yres ;
2017-03-06 12:50:22 +00:00
# endif
2013-07-16 20:50:53 +00:00
2021-08-09 06:40:41 +00:00
if ( ! invalid_ ) {
UpdateUIState ( coreState ! = CORE_RUNTIME_ERROR ? UISTATE_INGAME : UISTATE_EXCEPTION ) ;
2014-05-21 08:13:40 +00:00
}
2013-11-15 12:11:44 +00:00
2012-11-01 15:19:01 +00:00
if ( errorMessage_ . size ( ) ) {
2023-04-05 22:34:50 +00:00
auto err = GetI18NCategory ( I18NCat : : ERRORS ) ;
2021-05-11 07:50:28 +00:00
std : : string errLoadingFile = gamePath_ . ToVisualString ( ) + " \n " ;
2019-03-13 10:24:23 +00:00
errLoadingFile . append ( err - > T ( " Error loading file " , " Could not load game " ) ) ;
2013-08-22 19:37:49 +00:00
errLoadingFile . append ( " " ) ;
2015-07-01 21:34:50 +00:00
errLoadingFile . append ( err - > T ( errorMessage_ . c_str ( ) ) ) ;
2013-08-22 19:37:49 +00:00
2022-11-29 15:29:43 +00:00
screenManager ( ) - > push ( new PromptScreen ( gamePath_ , errLoadingFile , " OK " , " " ) ) ;
2022-09-30 09:26:30 +00:00
errorMessage_ . clear ( ) ;
2014-01-25 08:40:14 +00:00
quit_ = true ;
2012-11-01 15:19:01 +00:00
return ;
}
if ( invalid_ )
return ;
2012-12-26 07:51:03 +00:00
2023-08-23 16:42:20 +00:00
double now = time_now_d ( ) ;
controlMapper_ . Update ( now ) ;
2020-03-22 23:45:22 +00:00
2013-07-20 12:05:07 +00:00
if ( pauseTrigger_ ) {
pauseTrigger_ = false ;
2013-08-18 18:14:33 +00:00
screenManager ( ) - > push ( new GamePauseScreen ( gamePath_ ) ) ;
2012-11-01 15:19:01 +00:00
}
2015-02-01 17:04:06 +00:00
2016-08-17 04:24:01 +00:00
if ( saveStatePreview_ & & ! bootPending_ ) {
2016-05-28 03:41:37 +00:00
int currentSlot = SaveState : : GetCurrentSlot ( ) ;
if ( saveStateSlot_ ! = currentSlot ) {
saveStateSlot_ = currentSlot ;
2021-05-05 23:31:38 +00:00
Path fn ;
2021-05-15 06:00:22 +00:00
if ( SaveState : : HasSaveInSlot ( gamePath_ , currentSlot ) ) {
fn = SaveState : : GenerateSaveSlotFilename ( gamePath_ , currentSlot , SaveState : : SCREENSHOT_EXTENSION ) ;
2016-05-28 03:41:37 +00:00
}
saveStatePreview_ - > SetFilename ( fn ) ;
if ( ! fn . empty ( ) ) {
saveStatePreview_ - > SetVisibility ( UI : : V_VISIBLE ) ;
2023-08-23 16:42:20 +00:00
saveStatePreviewShownTime_ = now ;
2016-05-28 03:41:37 +00:00
} else {
saveStatePreview_ - > SetVisibility ( UI : : V_GONE ) ;
}
}
2016-05-28 03:53:58 +00:00
if ( saveStatePreview_ - > GetVisibility ( ) = = UI : : V_VISIBLE ) {
double endTime = saveStatePreviewShownTime_ + 2.0 ;
2023-08-23 16:42:20 +00:00
float alpha = clamp_value ( ( endTime - now ) * 4.0 , 0.0 , 1.0 ) ;
2016-05-28 03:53:58 +00:00
saveStatePreview_ - > SetColor ( colorAlpha ( 0x00FFFFFF , alpha ) ) ;
2023-08-23 16:42:20 +00:00
if ( now - saveStatePreviewShownTime_ > 2 ) {
2016-05-28 03:53:58 +00:00
saveStatePreview_ - > SetVisibility ( UI : : V_GONE ) ;
}
2016-05-28 03:41:37 +00:00
}
2015-02-01 17:04:06 +00:00
}
2012-11-01 15:19:01 +00:00
}
2023-10-11 07:04:28 +00:00
bool EmuScreen : : checkPowerDown ( ) {
2022-10-02 14:25:15 +00:00
if ( PSP_IsRebooting ( ) ) {
bootPending_ = true ;
invalid_ = true ;
}
2022-10-02 01:13:22 +00:00
if ( coreState = = CORE_POWERDOWN & & ! PSP_IsIniting ( ) & & ! PSP_IsRebooting ( ) ) {
2014-05-21 08:13:40 +00:00
if ( PSP_IsInited ( ) ) {
PSP_Shutdown ( ) ;
}
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : System , " SELF-POWERDOWN! " ) ;
2014-05-21 08:13:40 +00:00
screenManager ( ) - > switchScreen ( new MainScreen ( ) ) ;
bootPending_ = false ;
invalid_ = true ;
2023-10-11 07:04:28 +00:00
return true ;
2014-05-21 08:13:40 +00:00
}
2023-10-11 07:04:28 +00:00
return false ;
2014-05-21 08:13:40 +00:00
}
2024-05-11 19:28:26 +00:00
ScreenRenderRole EmuScreen : : renderRole ( bool isTop ) const {
auto CanBeBackground = [ & ] ( ) - > bool {
if ( g_Config . bSkipBufferEffects ) {
return isTop | | ( g_Config . bTransparentBackground & & Core_ShouldRunBehind ( ) ) ;
}
2023-12-10 20:57:05 +00:00
2024-05-11 19:28:26 +00:00
if ( ! g_Config . bTransparentBackground & & ! isTop ) {
if ( Core_ShouldRunBehind ( ) | | screenManager ( ) - > topScreen ( ) - > wantBrightBackground ( ) )
return true ;
return false ;
}
return true ;
} ;
ScreenRenderRole role = ScreenRenderRole : : MUST_BE_FIRST ;
if ( CanBeBackground ( ) ) {
role | = ScreenRenderRole : : CAN_BE_BACKGROUND ;
2023-12-11 16:47:19 +00:00
}
2024-05-11 19:28:26 +00:00
return role ;
2023-12-10 20:57:05 +00:00
}
void EmuScreen : : darken ( ) {
if ( ! screenManager ( ) - > topScreen ( ) - > wantBrightBackground ( ) ) {
UIContext & dc = * screenManager ( ) - > getUIContext ( ) ;
uint32_t color = GetBackgroundColorWithAlpha ( dc ) ;
dc . Begin ( ) ;
dc . RebindTexture ( ) ;
dc . FillRect ( UI : : Drawable ( color ) , dc . GetBounds ( ) ) ;
dc . Flush ( ) ;
}
}
2023-12-11 11:41:44 +00:00
ScreenRenderFlags EmuScreen : : render ( ScreenRenderMode mode ) {
ScreenRenderFlags flags = ScreenRenderFlags : : NONE ;
2023-12-15 10:30:54 +00:00
Draw : : Viewport viewport { 0.0f , 0.0f , ( float ) g_display . pixel_xres , ( float ) g_display . pixel_yres , 0.0f , 1.0f } ;
2023-12-11 11:41:44 +00:00
using namespace Draw ;
DrawContext * draw = screenManager ( ) - > getDrawContext ( ) ;
2023-12-28 23:17:35 +00:00
if ( ! draw ) {
2023-12-11 11:41:44 +00:00
return flags ; // shouldn't really happen but I've seen a suspicious stack trace..
2023-12-28 23:17:35 +00:00
}
2023-12-11 11:41:44 +00:00
2024-05-11 19:28:26 +00:00
// Sanity check
# ifdef _DEBUG
Draw : : BackendState state = draw - > GetCurrentBackendState ( ) ;
_dbg_assert_ ( ! state . valid | | state . passes = = 0 ) ;
# endif
2023-12-29 16:07:49 +00:00
GamepadUpdateOpacity ( ) ;
2023-12-11 14:58:08 +00:00
bool skipBufferEffects = g_Config . bSkipBufferEffects ;
2024-01-23 23:06:01 +00:00
bool framebufferBound = false ;
2023-12-10 20:57:05 +00:00
if ( mode & ScreenRenderMode : : FIRST ) {
// Actually, always gonna be first when it exists (?)
2023-12-10 13:09:55 +00:00
// Here we do NOT bind the backbuffer or clear the screen, unless non-buffered.
// The emuscreen is different than the others - we really want to allow the game to render to framebuffers
// before we ever bind the backbuffer for rendering. On mobile GPUs, switching back and forth between render
// targets is a mortal sin so it's very important that we don't bind the backbuffer unnecessarily here.
// We only bind it in FramebufferManager::CopyDisplayToOutput (unless non-buffered)...
// We do, however, start the frame in other ways.
2024-05-29 12:47:50 +00:00
if ( skipBufferEffects & & ! g_Config . bSoftwareRendering ) {
2023-12-10 13:09:55 +00:00
// We need to clear here already so that drawing during the frame is done on a clean slate.
if ( Core_IsStepping ( ) & & gpuStats . numFlips ! = 0 ) {
2024-01-18 08:44:40 +00:00
draw - > BindFramebufferAsRenderTarget ( nullptr , { RPAction : : KEEP , RPAction : : CLEAR , RPAction : : CLEAR } , " EmuScreen_BackBuffer " ) ;
2023-12-10 13:09:55 +00:00
} else {
draw - > BindFramebufferAsRenderTarget ( nullptr , { RPAction : : CLEAR , RPAction : : CLEAR , RPAction : : CLEAR , 0xFF000000 } , " EmuScreen_BackBuffer " ) ;
}
2017-05-16 12:24:40 +00:00
2023-12-10 13:09:55 +00:00
draw - > SetViewport ( viewport ) ;
2023-12-15 10:30:54 +00:00
draw - > SetScissorRect ( 0 , 0 , g_display . pixel_xres , g_display . pixel_yres ) ;
2023-12-11 14:58:08 +00:00
skipBufferEffects = true ;
2024-01-23 23:06:01 +00:00
framebufferBound = true ;
2023-12-10 13:09:55 +00:00
}
draw - > SetTargetSize ( g_display . pixel_xres , g_display . pixel_yres ) ;
2017-05-16 12:24:40 +00:00
}
2023-07-16 06:55:48 +00:00
g_OSD . NudgeSidebar ( ) ;
2023-12-10 20:57:05 +00:00
if ( mode & ScreenRenderMode : : TOP ) {
2023-08-10 11:21:36 +00:00
System_Notify ( SystemNotification : : KEEP_SCREEN_AWAKE ) ;
2023-12-30 14:18:56 +00:00
} else if ( ! Core_ShouldRunBehind ( ) & & strcmp ( screenManager ( ) - > topScreen ( ) - > tag ( ) , " DevMenu " ) ! = 0 ) {
2023-12-10 20:57:05 +00:00
// Just to make sure.
2024-02-01 22:26:36 +00:00
if ( PSP_IsInited ( ) & & ! skipBufferEffects ) {
2024-02-02 23:20:08 +00:00
_dbg_assert_ ( gpu ) ;
2023-12-10 20:57:05 +00:00
PSP_BeginHostFrame ( ) ;
gpu - > CopyDisplayToOutput ( true ) ;
PSP_EndHostFrame ( ) ;
2024-02-01 18:58:49 +00:00
}
2024-02-02 23:20:08 +00:00
if ( ! framebufferBound & & ( ! gpu | | ! gpu - > PresentedThisFrame ( ) ) ) {
2024-02-01 18:58:49 +00:00
draw - > BindFramebufferAsRenderTarget ( nullptr , { RPAction : : CLEAR , RPAction : : CLEAR , RPAction : : CLEAR , } , " EmuScreen_Behind " ) ;
2023-12-10 20:57:05 +00:00
}
2024-01-18 17:46:52 +00:00
// Need to make sure the UI texture is available, for "darken".
screenManager ( ) - > getUIContext ( ) - > BeginFrame ( ) ;
2023-12-28 23:17:35 +00:00
draw - > SetViewport ( viewport ) ;
draw - > SetScissorRect ( 0 , 0 , g_display . pixel_xres , g_display . pixel_yres ) ;
darken ( ) ;
2023-12-11 11:41:44 +00:00
return flags ;
2023-08-10 11:21:36 +00:00
}
2014-05-21 08:13:40 +00:00
if ( invalid_ ) {
2018-03-13 10:25:00 +00:00
// Loading, or after shutdown?
2024-01-14 22:27:40 +00:00
if ( loadingTextView_ & & loadingTextView_ - > GetVisibility ( ) = = UI : : V_VISIBLE )
2018-03-13 10:25:00 +00:00
loadingTextView_ - > SetText ( PSP_GetLoading ( ) ) ;
2014-05-21 08:13:40 +00:00
// It's possible this might be set outside PSP_RunLoopFor().
// In this case, we need to double check it here.
2023-12-30 10:58:29 +00:00
if ( mode & ScreenRenderMode : : TOP ) {
checkPowerDown ( ) ;
}
2023-12-11 11:41:44 +00:00
draw - > BindFramebufferAsRenderTarget ( nullptr , { RPAction : : CLEAR , RPAction : : CLEAR , RPAction : : CLEAR } , " EmuScreen_Invalid " ) ;
2024-01-18 17:46:52 +00:00
// Need to make sure the UI texture is available, for "darken".
screenManager ( ) - > getUIContext ( ) - > BeginFrame ( ) ;
2023-12-15 10:30:54 +00:00
draw - > SetViewport ( viewport ) ;
draw - > SetScissorRect ( 0 , 0 , g_display . pixel_xres , g_display . pixel_yres ) ;
2017-12-03 18:39:25 +00:00
renderUI ( ) ;
2023-12-11 11:41:44 +00:00
return flags ;
2014-05-21 08:13:40 +00:00
}
2012-11-01 15:19:01 +00:00
2020-08-02 15:11:09 +00:00
// Freeze-frame functionality (loads a savestate on every frame).
2013-11-15 12:11:44 +00:00
if ( PSP_CoreParameter ( ) . freezeNext ) {
PSP_CoreParameter ( ) . frozen = true ;
PSP_CoreParameter ( ) . freezeNext = false ;
SaveState : : SaveToRam ( freezeState_ ) ;
} else if ( PSP_CoreParameter ( ) . frozen ) {
2020-08-02 15:11:09 +00:00
std : : string errorString ;
if ( CChunkFileReader : : ERROR_NONE ! = SaveState : : LoadFromRam ( freezeState_ , & errorString ) ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : SaveState , " Failed to load freeze state (%s). Unfreezing. " , errorString . c_str ( ) ) ;
2013-11-15 12:11:44 +00:00
PSP_CoreParameter ( ) . frozen = false ;
}
}
2023-08-03 14:19:18 +00:00
Core_UpdateDebugStats ( ( DebugOverlay ) g_Config . iDebugOverlay = = DebugOverlay : : DEBUG_STATS | | g_Config . bLogFrameDrops ) ;
2017-11-08 10:57:53 +00:00
2024-09-18 13:17:20 +00:00
if ( doFrameAdvance_ . exchange ( false ) ) {
if ( ! Achievements : : WarnUserIfHardcoreModeActive ( false ) ) {
// If game is running, pause emulation immediately. Otherwise, advance a single frame.
if ( Core_IsStepping ( ) ) {
frameStep_ = true ;
Core_EnableStepping ( false ) ;
} else if ( ! frameStep_ ) {
lastNumFlips = gpuStats . numFlips ;
Core_EnableStepping ( true , " ui.frameAdvance " , 0 ) ;
}
}
}
2023-07-03 07:18:25 +00:00
bool blockedExecution = Achievements : : IsBlockingExecution ( ) ;
2023-12-20 16:03:06 +00:00
uint32_t clearColor = 0 ;
2023-07-03 07:18:25 +00:00
if ( ! blockedExecution ) {
PSP_BeginHostFrame ( ) ;
PSP_RunLoopWhileState ( ) ;
2023-12-11 11:41:44 +00:00
flags | = ScreenRenderFlags : : HANDLED_THROTTLING ;
2023-07-03 07:18:25 +00:00
// Hopefully coreState is now CORE_NEXTFRAME
switch ( coreState ) {
case CORE_NEXTFRAME :
// Reached the end of the frame, all good. Set back to running for the next frame
coreState = CORE_RUNNING ;
break ;
case CORE_STEPPING :
case CORE_RUNTIME_ERROR :
{
// If there's an exception, display information.
const MIPSExceptionInfo & info = Core_GetExceptionInfo ( ) ;
if ( info . type ! = MIPSExceptionType : : NONE ) {
// Clear to blue background screen
bool dangerousSettings = ! Reporting : : IsSupported ( ) ;
2023-12-20 16:03:06 +00:00
clearColor = dangerousSettings ? 0xFF900050 : 0xFF900000 ;
2024-01-18 08:44:40 +00:00
draw - > BindFramebufferAsRenderTarget ( nullptr , { RPAction : : CLEAR , RPAction : : CLEAR , RPAction : : CLEAR , clearColor } , " EmuScreen_RuntimeError " ) ;
2024-01-23 23:06:01 +00:00
framebufferBound = true ;
2023-07-03 07:18:25 +00:00
// The info is drawn later in renderUI
} else {
// If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output.
// This won't work in non-buffered, but that's fine.
2024-05-29 12:47:50 +00:00
if ( ! framebufferBound & & PSP_IsInited ( ) ) {
// draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, clearColor }, "EmuScreen_Stepping");
2023-07-03 07:18:25 +00:00
gpu - > CopyDisplayToOutput ( true ) ;
}
2024-05-29 12:47:50 +00:00
framebufferBound = true ;
2020-07-04 18:57:05 +00:00
}
2023-07-03 07:18:25 +00:00
break ;
}
default :
// Didn't actually reach the end of the frame, ran out of the blockTicks cycles.
// In this case we need to bind and wipe the backbuffer, at least.
// It's possible we never ended up outputted anything - make sure we have the backbuffer cleared
2024-01-23 23:06:01 +00:00
// So, we don't set framebufferBound here.
2023-07-03 07:18:25 +00:00
break ;
2018-06-23 17:58:30 +00:00
}
2020-07-04 18:57:05 +00:00
2024-05-29 12:47:50 +00:00
if ( framebufferBound & & gpu ) {
gpu - > PresentedThisFrame ( ) ;
}
2023-07-03 07:18:25 +00:00
PSP_EndHostFrame ( ) ;
2024-01-22 18:35:26 +00:00
// This place rougly matches how libretro handles it (after retro_frame).
Achievements : : FrameUpdate ( ) ;
2023-07-03 07:18:25 +00:00
}
2012-11-01 15:19:01 +00:00
2024-02-02 12:55:38 +00:00
if ( gpu & & gpu - > PresentedThisFrame ( ) ) {
2024-01-23 23:06:01 +00:00
framebufferBound = true ;
}
2024-01-18 17:46:52 +00:00
2024-01-23 23:06:01 +00:00
if ( ! framebufferBound ) {
2023-12-20 16:03:06 +00:00
draw - > BindFramebufferAsRenderTarget ( nullptr , { RPAction : : CLEAR , RPAction : : CLEAR , RPAction : : CLEAR , clearColor } , " EmuScreen_NoFrame " ) ;
2023-12-11 14:58:08 +00:00
draw - > SetViewport ( viewport ) ;
draw - > SetScissorRect ( 0 , 0 , g_display . pixel_xres , g_display . pixel_yres ) ;
2023-12-11 11:41:44 +00:00
}
2024-01-18 21:08:33 +00:00
screenManager ( ) - > getUIContext ( ) - > BeginFrame ( ) ;
2023-12-11 11:41:44 +00:00
if ( ! ( mode & ScreenRenderMode : : TOP ) ) {
// We're in run-behind mode, but we don't want to draw chat, debug UI and stuff.
// So, darken and bail here.
2023-12-15 10:30:54 +00:00
// Reset viewport/scissor to be sure.
draw - > SetViewport ( viewport ) ;
draw - > SetScissorRect ( 0 , 0 , g_display . pixel_xres , g_display . pixel_yres ) ;
2023-12-11 11:41:44 +00:00
darken ( ) ;
return flags ;
2023-10-11 07:04:28 +00:00
}
2017-11-12 02:31:13 +00:00
2023-12-30 10:58:29 +00:00
// NOTE: We don't check for powerdown if we're not the top screen.
checkPowerDown ( ) ;
2018-12-18 09:23:22 +00:00
if ( hasVisibleUI ( ) ) {
2023-12-15 10:30:54 +00:00
draw - > SetViewport ( viewport ) ;
2019-10-25 09:01:49 +00:00
cardboardDisableButton_ - > SetVisibility ( g_Config . bEnableCardboardVR ? UI : : V_VISIBLE : UI : : V_GONE ) ;
2018-12-18 09:23:22 +00:00
screenManager ( ) - > getUIContext ( ) - > BeginFrame ( ) ;
2017-12-03 18:39:25 +00:00
renderUI ( ) ;
2014-12-31 15:50:23 +00:00
}
2022-11-17 11:19:17 +00:00
2022-11-17 14:25:35 +00:00
if ( chatMenu_ & & ( chatMenu_ - > GetVisibility ( ) = = UI : : V_VISIBLE ) ) {
SetVRAppMode ( VRAppMode : : VR_DIALOG_MODE ) ;
} else {
SetVRAppMode ( screenManager ( ) - > topScreen ( ) = = this ? VRAppMode : : VR_GAME_MODE : VRAppMode : : VR_DIALOG_MODE ) ;
}
2023-12-10 13:09:55 +00:00
2024-01-17 13:49:49 +00:00
if ( ! ( mode & ScreenRenderMode : : TOP ) ) {
2023-12-10 20:57:05 +00:00
darken ( ) ;
2023-12-10 13:09:55 +00:00
}
2023-12-11 11:41:44 +00:00
return flags ;
2012-11-01 15:19:01 +00:00
}
2018-06-02 04:07:09 +00:00
bool EmuScreen : : hasVisibleUI ( ) {
// Regular but uncommon UI.
if ( saveStatePreview_ - > GetVisibility ( ) ! = UI : : V_GONE | | loadingSpinner_ - > GetVisibility ( ) = = UI : : V_VISIBLE )
return true ;
2023-06-20 12:40:46 +00:00
if ( ! g_OSD . IsEmpty ( ) | | g_Config . bShowTouchControls | | g_Config . iShowStatusFlags ! = 0 )
2018-06-02 04:07:09 +00:00
return true ;
2021-02-16 04:38:11 +00:00
if ( g_Config . bEnableCardboardVR | | g_Config . bEnableNetworkChat )
2019-10-25 08:56:01 +00:00
return true ;
2024-05-12 16:01:25 +00:00
if ( g_Config . bShowGPOLEDs )
return true ;
2018-06-02 04:07:09 +00:00
// Debug UI.
2023-08-11 13:51:54 +00:00
if ( ( DebugOverlay ) g_Config . iDebugOverlay ! = DebugOverlay : : OFF | | g_Config . bShowDeveloperMenu )
2018-06-02 04:07:09 +00:00
return true ;
2019-02-12 12:29:37 +00:00
// Exception information.
2019-02-12 12:29:37 +00:00
if ( coreState = = CORE_RUNTIME_ERROR | | coreState = = CORE_STEPPING ) {
2019-02-12 12:29:37 +00:00
return true ;
2019-02-12 12:29:37 +00:00
}
2018-06-02 04:07:09 +00:00
return false ;
}
2017-12-03 18:39:25 +00:00
void EmuScreen : : renderUI ( ) {
using namespace Draw ;
DrawContext * thin3d = screenManager ( ) - > getDrawContext ( ) ;
2018-01-17 12:59:32 +00:00
UIContext * ctx = screenManager ( ) - > getUIContext ( ) ;
2018-03-08 15:34:27 +00:00
ctx - > BeginFrame ( ) ;
2017-12-03 18:39:25 +00:00
// This sets up some important states but not the viewport.
2018-01-17 12:59:32 +00:00
ctx - > Begin ( ) ;
2017-12-03 18:39:25 +00:00
Viewport viewport ;
viewport . TopLeftX = 0 ;
viewport . TopLeftY = 0 ;
2023-02-25 12:09:44 +00:00
viewport . Width = g_display . pixel_xres ;
viewport . Height = g_display . pixel_yres ;
2017-12-03 18:39:25 +00:00
viewport . MaxDepth = 1.0 ;
viewport . MinDepth = 0.0 ;
2023-02-24 21:09:32 +00:00
thin3d - > SetViewport ( viewport ) ;
2017-12-03 18:39:25 +00:00
if ( root_ ) {
2020-05-31 21:20:13 +00:00
UI : : LayoutViewHierarchy ( * ctx , root_ , false ) ;
2018-01-17 12:59:32 +00:00
root_ - > Draw ( * ctx ) ;
2017-12-03 18:39:25 +00:00
}
2023-03-31 18:12:48 +00:00
if ( ! invalid_ ) {
2023-08-03 14:19:18 +00:00
if ( ( DebugOverlay ) g_Config . iDebugOverlay = = DebugOverlay : : CONTROL ) {
DrawControlMapperOverlay ( ctx , ctx - > GetLayoutBounds ( ) , controlMapper_ ) ;
}
2023-03-31 18:12:48 +00:00
if ( g_Config . iShowStatusFlags ) {
DrawFPS ( ctx , ctx - > GetLayoutBounds ( ) ) ;
}
2020-02-11 07:19:30 +00:00
}
2017-12-03 18:39:25 +00:00
# ifdef USE_PROFILER
2023-08-13 16:30:27 +00:00
if ( ( DebugOverlay ) g_Config . iDebugOverlay = = DebugOverlay : : FRAME_PROFILE & & ! invalid_ ) {
2018-01-17 12:59:32 +00:00
DrawProfile ( * ctx ) ;
2017-12-03 18:39:25 +00:00
}
# endif
2019-02-12 12:29:37 +00:00
2024-05-12 16:01:25 +00:00
if ( g_Config . bShowGPOLEDs ) {
// Draw a vertical strip of LEDs at the right side of the screen.
const float ledSize = 24.0f ;
const float spacing = 4.0f ;
const float height = 8 * ledSize + 7 * spacing ;
const float x = ctx - > GetBounds ( ) . w - spacing - ledSize ;
const float y = ( ctx - > GetBounds ( ) . h - height ) * 0.5f ;
ctx - > FillRect ( UI : : Drawable ( 0xFF000000 ) , Bounds ( x - spacing , y - spacing , ledSize + spacing * 2 , height + spacing * 2 ) ) ;
for ( int i = 0 ; i < 8 ; i + + ) {
int bit = ( g_GPOBits > > i ) & 1 ;
uint32_t color = 0xFF30FF30 ;
if ( ! bit ) {
color = darkenColor ( darkenColor ( color ) ) ;
}
Bounds ledBounds ( x , y + ( spacing + ledSize ) * i , ledSize , ledSize ) ;
ctx - > FillRect ( UI : : Drawable ( color ) , ledBounds ) ;
}
ctx - > Flush ( ) ;
}
2019-02-12 12:29:37 +00:00
if ( coreState = = CORE_RUNTIME_ERROR | | coreState = = CORE_STEPPING ) {
2023-04-28 11:38:16 +00:00
const MIPSExceptionInfo & info = Core_GetExceptionInfo ( ) ;
if ( info . type ! = MIPSExceptionType : : NONE ) {
2023-01-24 23:43:17 +00:00
DrawCrashDump ( ctx , gamePath_ ) ;
2022-12-30 11:21:05 +00:00
} else {
// We're somehow in ERROR or STEPPING without a crash dump. This case is what lead
// to the bare "Resume" and "Reset" buttons without a crash dump before, in cases
// where we were unable to ignore memory errors.
2019-02-12 12:29:37 +00:00
}
}
2018-12-18 09:10:53 +00:00
ctx - > Flush ( ) ;
2017-12-03 18:39:25 +00:00
}
2013-10-30 17:54:41 +00:00
void EmuScreen : : autoLoad ( ) {
2018-06-21 08:00:57 +00:00
int autoSlot = - 1 ;
2013-10-30 17:16:27 +00:00
//check if save state has save, if so, load
2018-06-21 08:00:57 +00:00
switch ( g_Config . iAutoLoadSaveState ) {
2018-06-26 06:52:02 +00:00
case ( int ) AutoLoadSaveState : : OFF : // "AutoLoad Off"
2018-06-21 08:00:57 +00:00
return ;
2018-06-26 06:52:02 +00:00
case ( int ) AutoLoadSaveState : : OLDEST : // "Oldest Save"
2021-05-15 06:00:22 +00:00
autoSlot = SaveState : : GetOldestSlot ( gamePath_ ) ;
2018-06-21 08:00:57 +00:00
break ;
2018-06-26 06:52:02 +00:00
case ( int ) AutoLoadSaveState : : NEWEST : // "Newest Save"
2021-05-15 06:00:22 +00:00
autoSlot = SaveState : : GetNewestSlot ( gamePath_ ) ;
2018-06-21 08:00:57 +00:00
break ;
default : // try the specific save state slot specified
2021-05-15 06:00:22 +00:00
autoSlot = ( SaveState : : HasSaveInSlot ( gamePath_ , g_Config . iAutoLoadSaveState - 3 ) ) ? ( g_Config . iAutoLoadSaveState - 3 ) : - 1 ;
2018-06-21 08:00:57 +00:00
break ;
}
if ( g_Config . iAutoLoadSaveState & & autoSlot ! = - 1 ) {
2021-05-15 06:00:22 +00:00
SaveState : : LoadSlot ( gamePath_ , autoSlot , & AfterSaveStateAction ) ;
2018-06-21 08:00:57 +00:00
g_Config . iCurrentStateSlot = autoSlot ;
2013-10-30 17:16:27 +00:00
}
2013-12-15 11:49:13 +00:00
}
2015-05-21 08:49:47 +00:00
2017-02-23 08:25:33 +00:00
void EmuScreen : : resized ( ) {
RecreateViews ( ) ;
2017-03-15 03:52:30 +00:00
}