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/.
2014-12-22 02:50:07 +00:00
# include <algorithm>
2015-02-01 17:04:06 +00:00
2014-02-10 11:38:23 +00:00
# include "base/display.h"
2013-03-06 23:10:53 +00:00
# include "base/logging.h"
2015-02-01 17:04:06 +00:00
# include "base/timeutil.h"
2015-05-13 21:07:19 +00:00
# include "profiler/profiler.h"
2013-03-06 23:10:53 +00:00
2015-09-06 10:23:14 +00:00
# include "gfx_es2/gpu_features.h"
2013-08-30 12:47:28 +00:00
# include "gfx_es2/draw_text.h"
2012-11-01 15:19:01 +00:00
# include "input/input_state.h"
# include "ui/ui.h"
2013-08-30 12:47:28 +00:00
# include "ui/ui_context.h"
2013-05-27 12:25:30 +00:00
# include "i18n/i18n.h"
2012-11-01 15:19:01 +00:00
2013-07-01 04:43:01 +00:00
# include "Common/KeyMap.h"
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"
# include "Core/CoreTiming.h"
# include "Core/CoreParameter.h"
# include "Core/Core.h"
# include "Core/Host.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"
# include "GPU/GPUState.h"
# include "GPU/GPUInterface.h"
2017-01-21 21:16:30 +00:00
# include "GPU/GLES/FramebufferManagerGLES.h"
2013-03-29 19:51:14 +00:00
# include "Core/HLE/sceCtrl.h"
2013-04-07 20:43:23 +00:00
# include "Core/HLE/sceDisplay.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"
2013-10-30 17:16:27 +00:00
# include "Core/SaveState.h"
2014-12-12 22:48:48 +00:00
# include "Core/MIPS/MIPS.h"
2015-01-29 11:55:49 +00:00
# include "Core/HLE/__sceAudio.h"
2012-11-01 15:19:01 +00:00
2013-04-07 20:43:23 +00:00
# include "UI/ui_atlas.h"
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"
2013-12-05 13:14:15 +00:00
# include "UI/InstallZipScreen.h"
2015-10-14 15:45:21 +00:00
# include "UI/ProfilerDraw.h"
2013-07-17 05:33:26 +00:00
2016-03-20 19:26:52 +00:00
# ifdef _WIN32
# include "Windows/MainWindow.h"
# endif
2016-09-02 00:09:56 +00:00
# ifndef MOBILE_DEVICE
2016-08-27 18:38:05 +00:00
AVIDump avi ;
2016-09-02 00:09:56 +00:00
# endif
2016-08-27 18:38:05 +00:00
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
static void __EmuScreenVblank ( )
{
2016-08-29 19:34:00 +00:00
if ( frameStep_ & & lastNumFlips ! = gpuStats . numFlips )
2016-08-28 04:20:03 +00:00
{
frameStep_ = false ;
Core_EnableStepping ( true ) ;
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 ) ;
2016-09-02 01:59:12 +00:00
osm . Show ( " AVI Dump started. " , 3.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 ( ) ;
2016-09-02 01:59:12 +00:00
osm . Show ( " AVI Dump stopped. " , 3.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
}
2013-07-21 11:31:46 +00:00
EmuScreen : : EmuScreen ( const std : : string & filename )
2015-02-01 17:04:06 +00:00
: bootPending_ ( true ) , gamePath_ ( filename ) , invalid_ ( true ) , quit_ ( false ) , pauseTrigger_ ( false ) , saveStatePreviewShownTime_ ( 0.0 ) , saveStatePreview_ ( nullptr ) {
2014-07-20 10:52:11 +00:00
memset ( axisState_ , 0 , sizeof ( axisState_ ) ) ;
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 ;
2013-07-17 05:33:26 +00:00
}
void EmuScreen : : bootGame ( const std : : string & filename ) {
2014-01-19 22:17:34 +00:00
if ( PSP_IsIniting ( ) ) {
std : : string error_string ;
2014-05-16 05:17:19 +00:00
bootPending_ = ! PSP_InitUpdate ( & error_string ) ;
if ( ! bootPending_ ) {
2014-01-19 22:17:34 +00:00
invalid_ = ! PSP_IsInited ( ) ;
if ( invalid_ ) {
errorMessage_ = error_string ;
ERROR_LOG ( BOOT , " %s " , errorMessage_ . c_str ( ) ) ;
System_SendMessage ( " event " , " failstartgame " ) ;
return ;
}
bootComplete ( ) ;
}
return ;
}
2016-09-04 21:03:59 +00:00
SetBackgroundAudioGame ( " " ) ;
2016-09-04 15:42:20 +00:00
2014-12-14 19:33:20 +00:00
//pre-emptive loading of game specific config if possible, to get all the settings
2016-02-14 21:07:10 +00:00
GameInfo * info = g_gameInfoCache - > GetInfo ( NULL , filename , 0 ) ;
2015-11-13 00:14:56 +00:00
if ( info & & ! info - > id . empty ( ) ) {
2014-12-14 19:33:20 +00:00
g_Config . loadGameConfig ( info - > id ) ;
}
2014-01-19 22:17:34 +00:00
invalid_ = true ;
2012-11-01 15:19:01 +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 ( ) ) {
case GPUBackend : : OPENGL :
2016-04-10 08:21:48 +00:00
coreParam . gpuCore = GPUCORE_GLES ;
2015-10-10 14:41:19 +00:00
break ;
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 : : DIRECT3D11 :
2016-04-10 08:21:48 +00:00
coreParam . gpuCore = GPUCORE_DIRECTX11 ;
2015-10-10 14:41:19 +00:00
break ;
case GPUBackend : : VULKAN :
2016-04-10 08:21:48 +00:00
coreParam . gpuCore = GPUCORE_VULKAN ;
2016-03-20 19:26:52 +00:00
if ( g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ) {
# ifdef _WIN32
if ( IDYES = = MessageBox ( MainWindow : : GetHWND ( ) , L " The Vulkan backend is not yet compatible with buffered rendering. Switch to non-buffered (WARNING: This will cause glitches with the other backends unless you switch back) " , L " Vulkan Experimental Support " , MB_ICONINFORMATION | MB_YESNO ) ) {
g_Config . iRenderingMode = FB_NON_BUFFERED_MODE ;
} else {
errorMessage_ = " Non-buffered rendering required for Vulkan " ;
return ;
}
# endif
}
2015-10-10 14:41:19 +00:00
break ;
2014-08-17 19:29:36 +00:00
}
2016-02-13 20:22:06 +00:00
if ( g_Config . bSoftwareRendering ) {
2016-04-10 08:21:48 +00:00
coreParam . gpuCore = GPUCORE_SOFTWARE ;
2016-02-13 20:22:06 +00:00
}
2016-01-03 19:05:36 +00:00
// Preserve the existing graphics context.
coreParam . graphicsContext = PSP_CoreParameter ( ) . graphicsContext ;
2017-01-30 13:33:38 +00:00
coreParam . thin3d = screenManager ( ) - > getDrawContext ( ) ;
2012-11-01 15:19:01 +00:00
coreParam . enableSound = g_Config . bEnableSound ;
2014-01-19 22:17:34 +00:00
coreParam . fileToStart = filename ;
2012-11-01 15:19:01 +00:00
coreParam . mountIso = " " ;
2014-04-19 19:52:43 +00:00
coreParam . mountRoot = " " ;
2012-11-01 15:19:01 +00:00
coreParam . startPaused = false ;
coreParam . printfEmuLog = false ;
coreParam . headLess = false ;
2013-09-10 22:19:34 +00:00
2014-02-10 11:38:23 +00:00
const Bounds & bounds = screenManager ( ) - > getUIContext ( ) - > GetBounds ( ) ;
2013-09-10 22:19:34 +00:00
if ( g_Config . iInternalResolution = = 0 ) {
2015-07-16 14:29:55 +00:00
coreParam . renderWidth = pixel_xres ;
coreParam . renderHeight = 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 ;
}
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 ;
ERROR_LOG ( BOOT , " %s " , errorMessage_ . c_str ( ) ) ;
2013-12-04 16:41:59 +00:00
System_SendMessage ( " event " , " failstartgame " ) ;
2012-11-01 15:19:01 +00:00
}
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 ) ;
2013-03-29 18:52:32 +00:00
host - > BootDone ( ) ;
2013-03-29 19:51:14 +00:00
host - > UpdateDisassembly ( ) ;
2016-02-14 21:07:10 +00:00
g_gameInfoCache - > FlushBGs ( ) ;
2013-04-13 19:24:07 +00:00
2014-01-19 22:17:34 +00:00
NOTICE_LOG ( BOOT , " Loading %s... " , PSP_CoreParameter ( ) . fileToStart . c_str ( ) ) ;
2013-10-30 17:51:25 +00:00
autoLoad ( ) ;
2016-08-28 04:20:03 +00:00
I18NCategory * sc = GetI18NCategory ( " 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 ) {
2015-07-01 22:50:07 +00:00
osm . Show ( sc - > T ( " PressESC " , " Press ESC to open the pause menu " ) , 3.0f ) ;
2013-06-10 22:51:10 +00:00
}
# endif
2013-07-06 18:44:34 +00:00
memset ( virtKeys , 0 , sizeof ( virtKeys ) ) ;
2013-09-17 08:27:42 +00:00
2016-01-06 06:37:28 +00:00
if ( GetGPUBackend ( ) = = GPUBackend : : OPENGL ) {
2014-08-17 19:29:36 +00:00
const char * renderer = ( const char * ) glGetString ( GL_RENDERER ) ;
if ( strstr ( renderer , " Chainfire3D " ) ! = 0 ) {
2015-07-01 22:50:07 +00:00
osm . Show ( sc - > T ( " Chainfire3DWarning " , " WARNING: Chainfire3D detected, may cause problems " ) , 10.0f , 0xFF30a0FF , - 1 , true ) ;
2014-08-17 19:29:36 +00:00
} else if ( strstr ( renderer , " GLTools " ) ! = 0 ) {
2015-07-01 22:50:07 +00:00
osm . Show ( sc - > T ( " GLToolsWarning " , " WARNING: GLTools detected, may cause problems " ) , 10.0f , 0xFF30a0FF , - 1 , true ) ;
2014-08-17 19:29:36 +00:00
}
2015-10-11 09:46:24 +00:00
if ( g_Config . bGfxDebugOutput ) {
osm . Show ( " WARNING: GfxDebugOutput is enabled via ppsspp.ini. Things may be slow. " , 10.0f , 0xFF30a0FF , - 1 , true ) ;
}
2013-09-17 08:27:42 +00:00
}
2013-10-30 17:16:27 +00:00
2016-07-25 00:04:06 +00:00
if ( Core_GetPowerSaving ( ) ) {
I18NCategory * sy = GetI18NCategory ( " System " ) ;
2016-10-12 09:13:16 +00:00
# ifdef __ANDROID__
2016-10-11 00:32:25 +00:00
osm . Show ( sy - > T ( " WARNING: Android battery save mode is on " ) , 2.0f , 0xFFFFFF , - 1 , true , " core_powerSaving " ) ;
# else
2016-07-25 00:04:06 +00:00
osm . Show ( sy - > T ( " WARNING: Battery save mode is on " ) , 2.0f , 0xFFFFFF , - 1 , true , " core_powerSaving " ) ;
2016-10-11 00:32:25 +00:00
# endif
2016-07-25 00:04:06 +00:00
}
2013-12-04 16:41:59 +00:00
System_SendMessage ( " event " , " startgame " ) ;
2016-08-17 04:24:01 +00:00
saveStateSlot_ = SaveState : : GetCurrentSlot ( ) ;
2012-11-01 15:19:01 +00:00
}
2013-03-29 20:21:27 +00:00
EmuScreen : : ~ EmuScreen ( ) {
2012-11-01 15:19:01 +00:00
if ( ! invalid_ ) {
// If we were invalid, it would already be shutdown.
PSP_Shutdown ( ) ;
}
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 ( ) ;
2016-09-02 01:59:12 +00:00
osm . Show ( " AVI Dump stopped. " , 3.0f ) ;
2016-08-27 18:38:05 +00:00
startDumping = false ;
}
2016-09-02 00:09:56 +00:00
# endif
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",
2013-07-17 05:33:26 +00:00
// DR_YES means a message sent to PauseMenu by NativeMessageReceived.
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 ( ) ) ;
2013-12-04 16:41:59 +00:00
System_SendMessage ( " event " , " exitgame " ) ;
2014-01-25 08:40:14 +00:00
quit_ = false ;
2012-11-01 15:19:01 +00:00
}
2013-07-20 10:06:06 +00:00
RecreateViews ( ) ;
2012-11-01 15:19:01 +00:00
}
2016-05-28 04:25:05 +00:00
static void AfterSaveStateAction ( bool success , const std : : string & message , void * ) {
if ( ! message . empty ( ) ) {
osm . Show ( message , 2.0 ) ;
}
}
static void AfterStateBoot ( bool success , const std : : string & message , void * ignored ) {
AfterSaveStateAction ( success , message , ignored ) ;
2014-06-14 23:13:35 +00:00
Core_EnableStepping ( false ) ;
host - > UpdateDisassembly ( ) ;
}
2013-03-29 20:21:27 +00:00
void EmuScreen : : sendMessage ( const char * message , const char * value ) {
2013-03-29 17:50:08 +00:00
// External commands, like from the Windows UI.
if ( ! strcmp ( message , " pause " ) ) {
2015-05-21 08:49:47 +00:00
releaseButtons ( ) ;
2013-08-18 18:22:30 +00:00
screenManager ( ) - > push ( new GamePauseScreen ( gamePath_ ) ) ;
2015-05-21 08:49:47 +00:00
} else if ( ! strcmp ( message , " lost_focus " ) ) {
releaseButtons ( ) ;
2013-03-29 17:50:08 +00:00
} else if ( ! strcmp ( message , " 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 ;
2014-05-11 17:57:33 +00:00
invalid_ = true ;
2014-07-06 21:02:45 +00:00
host - > UpdateDisassembly ( ) ;
2013-03-29 19:51:14 +00:00
} else if ( ! strcmp ( message , " reset " ) ) {
PSP_Shutdown ( ) ;
2014-05-16 05:17:19 +00:00
bootPending_ = true ;
2014-05-11 17:57:33 +00:00
invalid_ = true ;
2014-07-06 21:02:45 +00:00
host - > UpdateDisassembly ( ) ;
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 ) ) {
2013-03-29 20:21:27 +00:00
ELOG ( " Error resetting: %s " , resetError . c_str ( ) ) ;
2013-08-18 18:14:33 +00:00
screenManager ( ) - > switchScreen ( new MainScreen ( ) ) ;
2013-12-04 16:41:59 +00:00
System_SendMessage ( " event " , " failstartgame " ) ;
2013-03-29 19:51:14 +00:00
return ;
}
2013-11-20 06:44:12 +00:00
} else if ( ! strcmp ( message , " boot " ) ) {
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 " ) ) {
2016-05-28 04:25:05 +00:00
SaveState : : Load ( value , & AfterStateBoot ) ;
2014-06-14 23:13:35 +00:00
} else {
PSP_Shutdown ( ) ;
bootPending_ = true ;
bootGame ( value ) ;
}
2013-11-20 06:44:12 +00:00
} else if ( ! strcmp ( message , " control mapping " ) ) {
2013-09-15 16:35:58 +00:00
UpdateUIState ( UISTATE_MENU ) ;
2015-05-21 08:49:47 +00:00
releaseButtons ( ) ;
2013-09-07 15:29:44 +00:00
screenManager ( ) - > push ( new ControlMappingScreen ( ) ) ;
2015-12-21 05:32:05 +00:00
} else if ( ! strcmp ( message , " display layout editor " ) ) {
UpdateUIState ( UISTATE_MENU ) ;
releaseButtons ( ) ;
screenManager ( ) - > push ( new DisplayLayoutScreen ( ) ) ;
2013-11-20 06:44:12 +00:00
} else if ( ! strcmp ( message , " settings " ) ) {
2013-09-15 16:35:58 +00:00
UpdateUIState ( UISTATE_MENU ) ;
2015-05-21 08:49:47 +00:00
releaseButtons ( ) ;
2013-09-07 15:29:44 +00:00
screenManager ( ) - > push ( new GameSettingsScreen ( gamePath_ ) ) ;
2013-11-20 06:44:12 +00:00
} else if ( ! strcmp ( message , " gpu dump next frame " ) ) {
2015-05-21 08:49:47 +00:00
if ( gpu )
gpu - > DumpNextFrame ( ) ;
2013-11-20 06:44:12 +00:00
} else if ( ! strcmp ( message , " 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
}
2014-06-29 22:06:47 +00:00
} else if ( ! strcmp ( message , " window minimized " ) ) {
if ( ! strcmp ( value , " true " ) ) {
gstate_c . skipDrawReason | = SKIPDRAW_WINDOW_MINIMIZED ;
} else {
gstate_c . skipDrawReason & = ~ SKIPDRAW_WINDOW_MINIMIZED ;
}
2013-11-02 20:33:27 +00:00
}
2013-03-29 17:50:08 +00:00
}
2013-10-28 11:19:36 +00:00
//tiltInputCurve implements a smooth deadzone as described here:
2013-10-27 17:58:47 +00:00
//http://www.gamasutra.com/blogs/JoshSutphin/20130416/190541/Doing_Thumbstick_Dead_Zones_Right.php
2013-10-28 11:19:36 +00:00
inline float tiltInputCurve ( float x ) {
2013-10-28 11:15:27 +00:00
const float deadzone = g_Config . fDeadzoneRadius ;
2013-03-10 12:21:36 +00:00
const float factor = 1.0f / ( 1.0f - deadzone ) ;
2013-10-28 11:15:27 +00:00
2013-03-10 12:21:36 +00:00
if ( x > deadzone ) {
return ( x - deadzone ) * ( x - deadzone ) * factor ;
2013-10-28 11:15:27 +00:00
} else if ( x < - deadzone ) {
2013-03-10 12:21:36 +00:00
return - ( x + deadzone ) * ( x + deadzone ) * factor ;
} else {
return 0.0f ;
}
}
inline float clamp1 ( float x ) {
if ( x > 1.0f ) return 1.0f ;
if ( x < - 1.0f ) return - 1.0f ;
return x ;
}
2014-06-15 11:04:59 +00:00
bool EmuScreen : : touch ( const TouchInput & touch ) {
2015-03-01 06:20:14 +00:00
Core_NotifyActivity ( ) ;
2014-06-15 11:04:59 +00:00
if ( root_ ) {
2013-07-27 15:25:22 +00:00
root_ - > Touch ( touch ) ;
2014-06-15 11:04:59 +00:00
return true ;
} else {
return false ;
}
2013-07-06 17:08:59 +00:00
}
2013-07-07 08:42:39 +00:00
void EmuScreen : : onVKeyDown ( int virtualKeyCode ) {
2016-08-28 04:20:03 +00:00
I18NCategory * sc = GetI18NCategory ( " Screen " ) ;
2013-07-07 12:08:08 +00:00
2013-07-07 08:42:39 +00:00
switch ( virtualKeyCode ) {
2013-07-20 12:05:07 +00:00
case VIRTKEY_UNTHROTTLE :
PSP_CoreParameter ( ) . unthrottle = true ;
break ;
2013-07-07 12:08:08 +00:00
case VIRTKEY_SPEED_TOGGLE :
if ( PSP_CoreParameter ( ) . fpsLimit = = 0 ) {
PSP_CoreParameter ( ) . fpsLimit = 1 ;
2015-07-01 22:50:07 +00:00
osm . Show ( sc - > T ( " fixed " , " Speed: alternate " ) , 1.0 ) ;
2013-07-07 12:08:08 +00:00
}
else if ( PSP_CoreParameter ( ) . fpsLimit = = 1 ) {
PSP_CoreParameter ( ) . fpsLimit = 0 ;
2015-07-01 22:50:07 +00:00
osm . Show ( sc - > T ( " standard " , " Speed: standard " ) , 1.0 ) ;
2013-07-07 12:08:08 +00:00
}
break ;
2013-07-08 15:46:20 +00:00
2013-07-07 12:08:08 +00:00
case VIRTKEY_PAUSE :
2013-10-22 14:18:43 +00:00
pauseTrigger_ = true ;
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 :
// If game is running, pause emulation immediately. Otherwise, advance a single frame.
if ( Core_IsStepping ( ) )
{
frameStep_ = true ;
Core_EnableStepping ( false ) ;
}
else if ( ! frameStep_ )
{
Core_EnableStepping ( true ) ;
}
break ;
2014-11-07 12:40:53 +00:00
case VIRTKEY_AXIS_SWAP :
KeyMap : : SwapAxis ( ) ;
break ;
2015-07-05 23:09:29 +00:00
case VIRTKEY_DEVMENU :
releaseButtons ( ) ;
screenManager ( ) - > push ( new DevMenu ( ) ) ;
break ;
2013-07-07 23:25:15 +00:00
case VIRTKEY_AXIS_X_MIN :
case VIRTKEY_AXIS_X_MAX :
2013-07-20 19:11:35 +00:00
setVKeyAnalogX ( CTRL_STICK_LEFT , VIRTKEY_AXIS_X_MIN , VIRTKEY_AXIS_X_MAX ) ;
2013-07-07 23:25:15 +00:00
break ;
case VIRTKEY_AXIS_Y_MIN :
case VIRTKEY_AXIS_Y_MAX :
2013-07-20 19:11:35 +00:00
setVKeyAnalogY ( CTRL_STICK_LEFT , VIRTKEY_AXIS_Y_MIN , VIRTKEY_AXIS_Y_MAX ) ;
2013-07-07 23:25:15 +00:00
break ;
case VIRTKEY_AXIS_RIGHT_X_MIN :
case VIRTKEY_AXIS_RIGHT_X_MAX :
2013-07-20 19:11:35 +00:00
setVKeyAnalogX ( CTRL_STICK_RIGHT , VIRTKEY_AXIS_RIGHT_X_MIN , VIRTKEY_AXIS_RIGHT_X_MAX ) ;
2013-07-07 23:25:15 +00:00
break ;
case VIRTKEY_AXIS_RIGHT_Y_MIN :
case VIRTKEY_AXIS_RIGHT_Y_MAX :
2013-07-20 19:11:35 +00:00
setVKeyAnalogY ( CTRL_STICK_RIGHT , VIRTKEY_AXIS_RIGHT_Y_MIN , VIRTKEY_AXIS_RIGHT_Y_MAX ) ;
2013-07-07 23:25:15 +00:00
break ;
2013-12-05 15:15:33 +00:00
2014-05-28 03:34:25 +00:00
case VIRTKEY_ANALOG_LIGHTLY :
setVKeyAnalogX ( CTRL_STICK_LEFT , VIRTKEY_AXIS_X_MIN , VIRTKEY_AXIS_X_MAX ) ;
setVKeyAnalogY ( CTRL_STICK_LEFT , VIRTKEY_AXIS_Y_MIN , VIRTKEY_AXIS_Y_MAX ) ;
setVKeyAnalogX ( CTRL_STICK_RIGHT , VIRTKEY_AXIS_RIGHT_X_MIN , VIRTKEY_AXIS_RIGHT_X_MAX ) ;
setVKeyAnalogY ( CTRL_STICK_RIGHT , VIRTKEY_AXIS_RIGHT_Y_MIN , VIRTKEY_AXIS_RIGHT_Y_MAX ) ;
break ;
2013-12-05 15:15:33 +00:00
case VIRTKEY_REWIND :
if ( SaveState : : CanRewind ( ) ) {
2016-05-28 04:25:05 +00:00
SaveState : : Rewind ( & AfterSaveStateAction ) ;
2013-12-05 15:15:33 +00:00
} else {
2015-07-01 22:50:07 +00:00
osm . Show ( sc - > T ( " norewind " , " No rewind save states available " ) , 2.0 ) ;
2013-12-05 15:15:33 +00:00
}
break ;
2013-12-06 14:46:56 +00:00
case VIRTKEY_SAVE_STATE :
2016-05-28 04:25:05 +00:00
SaveState : : SaveSlot ( gamePath_ , g_Config . iCurrentStateSlot , & AfterSaveStateAction ) ;
2013-12-06 14:46:56 +00:00
break ;
case VIRTKEY_LOAD_STATE :
2016-05-28 04:25:05 +00:00
SaveState : : LoadSlot ( gamePath_ , g_Config . iCurrentStateSlot , & AfterSaveStateAction ) ;
2013-12-06 14:46:56 +00:00
break ;
2014-01-07 14:56:04 +00:00
case VIRTKEY_NEXT_SLOT :
SaveState : : NextSlot ( ) ;
2016-05-28 03:53:20 +00:00
NativeMessageReceived ( " savestate_displayslot " , " " ) ;
2014-01-07 14:56:04 +00:00
break ;
2014-01-03 14:16:23 +00:00
case VIRTKEY_TOGGLE_FULLSCREEN :
System_SendMessage ( " toggle_fullscreen " , " " ) ;
break ;
2013-07-07 08:42:39 +00:00
}
}
void EmuScreen : : onVKeyUp ( int virtualKeyCode ) {
switch ( virtualKeyCode ) {
2013-07-20 12:05:07 +00:00
case VIRTKEY_UNTHROTTLE :
PSP_CoreParameter ( ) . unthrottle = false ;
break ;
2013-07-20 19:11:35 +00:00
2013-07-07 23:25:15 +00:00
case VIRTKEY_AXIS_X_MIN :
case VIRTKEY_AXIS_X_MAX :
2013-07-20 19:11:35 +00:00
setVKeyAnalogX ( CTRL_STICK_LEFT , VIRTKEY_AXIS_X_MIN , VIRTKEY_AXIS_X_MAX ) ;
2013-07-07 23:25:15 +00:00
break ;
case VIRTKEY_AXIS_Y_MIN :
case VIRTKEY_AXIS_Y_MAX :
2013-07-20 19:11:35 +00:00
setVKeyAnalogY ( CTRL_STICK_LEFT , VIRTKEY_AXIS_Y_MIN , VIRTKEY_AXIS_Y_MAX ) ;
2013-07-07 23:25:15 +00:00
break ;
case VIRTKEY_AXIS_RIGHT_X_MIN :
case VIRTKEY_AXIS_RIGHT_X_MAX :
2013-07-20 19:11:35 +00:00
setVKeyAnalogX ( CTRL_STICK_RIGHT , VIRTKEY_AXIS_RIGHT_X_MIN , VIRTKEY_AXIS_RIGHT_X_MAX ) ;
2013-07-07 23:25:15 +00:00
break ;
case VIRTKEY_AXIS_RIGHT_Y_MIN :
case VIRTKEY_AXIS_RIGHT_Y_MAX :
2013-07-20 19:11:35 +00:00
setVKeyAnalogY ( CTRL_STICK_RIGHT , VIRTKEY_AXIS_RIGHT_Y_MIN , VIRTKEY_AXIS_RIGHT_Y_MAX ) ;
2013-07-07 23:25:15 +00:00
break ;
2013-07-20 19:11:35 +00:00
2014-05-28 03:34:25 +00:00
case VIRTKEY_ANALOG_LIGHTLY :
setVKeyAnalogX ( CTRL_STICK_LEFT , VIRTKEY_AXIS_X_MIN , VIRTKEY_AXIS_X_MAX ) ;
setVKeyAnalogY ( CTRL_STICK_LEFT , VIRTKEY_AXIS_Y_MIN , VIRTKEY_AXIS_Y_MAX ) ;
setVKeyAnalogX ( CTRL_STICK_RIGHT , VIRTKEY_AXIS_RIGHT_X_MIN , VIRTKEY_AXIS_RIGHT_X_MAX ) ;
setVKeyAnalogY ( CTRL_STICK_RIGHT , VIRTKEY_AXIS_RIGHT_Y_MIN , VIRTKEY_AXIS_RIGHT_Y_MAX ) ;
break ;
2013-07-07 22:07:45 +00:00
default :
2013-07-07 08:42:39 +00:00
break ;
}
}
2015-05-15 16:49:54 +00:00
// Handles control rotation due to internal screen rotation.
static void SetPSPAxis ( char axis , float value , int stick ) {
switch ( g_Config . iInternalScreenRotation ) {
case ROTATION_LOCKED_HORIZONTAL :
// Standard rotation.
break ;
case ROTATION_LOCKED_HORIZONTAL180 :
value = - value ;
break ;
case ROTATION_LOCKED_VERTICAL :
value = axis = = ' Y ' ? value : - value ;
axis = ( axis = = ' X ' ) ? ' Y ' : ' X ' ;
break ;
case ROTATION_LOCKED_VERTICAL180 :
value = axis = = ' Y ' ? - value : value ;
axis = ( axis = = ' X ' ) ? ' Y ' : ' X ' ;
break ;
default :
break ;
}
if ( axis = = ' X ' )
__CtrlSetAnalogX ( value , stick ) ;
else if ( axis = = ' Y ' )
__CtrlSetAnalogY ( value , stick ) ;
}
2013-07-20 19:11:35 +00:00
inline void EmuScreen : : setVKeyAnalogX ( int stick , int virtualKeyMin , int virtualKeyMax ) {
2014-08-16 19:05:37 +00:00
const float value = virtKeys [ VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST ] ? g_Config . fAnalogLimiterDeadzone : 1.0f ;
2013-07-20 19:11:35 +00:00
float axis = 0.0f ;
// The down events can repeat, so just trust the virtKeys array.
if ( virtKeys [ virtualKeyMin - VIRTKEY_FIRST ] )
2014-05-28 03:34:25 +00:00
axis - = value ;
2013-07-20 19:11:35 +00:00
if ( virtKeys [ virtualKeyMax - VIRTKEY_FIRST ] )
2014-05-28 03:34:25 +00:00
axis + = value ;
2015-05-15 16:49:54 +00:00
SetPSPAxis ( ' X ' , axis , stick ) ;
2013-07-20 19:11:35 +00:00
}
inline void EmuScreen : : setVKeyAnalogY ( int stick , int virtualKeyMin , int virtualKeyMax ) {
2014-08-16 19:05:37 +00:00
const float value = virtKeys [ VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST ] ? g_Config . fAnalogLimiterDeadzone : 1.0f ;
2013-07-20 19:11:35 +00:00
float axis = 0.0f ;
if ( virtKeys [ virtualKeyMin - VIRTKEY_FIRST ] )
2014-05-28 03:34:25 +00:00
axis - = value ;
2013-07-20 19:11:35 +00:00
if ( virtKeys [ virtualKeyMax - VIRTKEY_FIRST ] )
2014-05-28 03:34:25 +00:00
axis + = value ;
2015-05-15 16:49:54 +00:00
SetPSPAxis ( ' Y ' , axis , stick ) ;
2013-07-20 19:11:35 +00:00
}
2014-06-15 11:04:59 +00:00
bool EmuScreen : : key ( const KeyInput & key ) {
2015-03-01 06:20:14 +00:00
Core_NotifyActivity ( ) ;
2013-10-31 15:50:27 +00:00
std : : vector < int > pspKeys ;
KeyMap : : KeyToPspButton ( key . deviceId , key . keyCode , & pspKeys ) ;
2014-06-15 11:04:59 +00:00
if ( pspKeys . size ( ) & & ( key . flags & KEY_IS_REPEAT ) ) {
// Claim that we handled this. Prevents volume key repeats from popping up the volume control on Android.
return true ;
}
2013-10-31 15:50:27 +00:00
for ( size_t i = 0 ; i < pspKeys . size ( ) ; i + + ) {
pspKey ( pspKeys [ i ] , key . flags ) ;
}
2015-01-02 15:43:08 +00:00
if ( ! pspKeys . size ( ) | | key . deviceId = = DEVICE_ID_DEFAULT ) {
if ( ( key . flags & KEY_DOWN ) & & key . keyCode = = NKCODE_BACK ) {
pauseTrigger_ = true ;
return true ;
}
}
2014-06-15 11:04:59 +00:00
return pspKeys . size ( ) > 0 ;
2013-07-07 22:21:40 +00:00
}
2015-05-15 16:49:54 +00:00
static int RotatePSPKeyCode ( int x ) {
switch ( x ) {
case CTRL_UP : return CTRL_RIGHT ;
case CTRL_RIGHT : return CTRL_DOWN ;
case CTRL_DOWN : return CTRL_LEFT ;
case CTRL_LEFT : return CTRL_UP ;
default :
return x ;
}
}
2013-07-07 22:21:40 +00:00
void EmuScreen : : pspKey ( int pspKeyCode , int flags ) {
2015-05-15 16:49:54 +00:00
int rotations = 0 ;
switch ( g_Config . iInternalScreenRotation ) {
case ROTATION_LOCKED_HORIZONTAL180 :
rotations = 2 ;
break ;
case ROTATION_LOCKED_VERTICAL :
rotations = 1 ;
break ;
case ROTATION_LOCKED_VERTICAL180 :
rotations = 3 ;
break ;
}
for ( int i = 0 ; i < rotations ; i + + ) {
pspKeyCode = RotatePSPKeyCode ( pspKeyCode ) ;
}
2013-07-07 22:21:40 +00:00
if ( pspKeyCode > = VIRTKEY_FIRST ) {
int vk = pspKeyCode - VIRTKEY_FIRST ;
if ( flags & KEY_DOWN ) {
2013-07-07 08:42:39 +00:00
virtKeys [ vk ] = true ;
2013-07-07 22:21:40 +00:00
onVKeyDown ( pspKeyCode ) ;
2013-07-07 08:42:39 +00:00
}
2013-07-07 22:21:40 +00:00
if ( flags & KEY_UP ) {
2013-07-07 08:42:39 +00:00
virtKeys [ vk ] = false ;
2013-07-07 22:21:40 +00:00
onVKeyUp ( pspKeyCode ) ;
2013-07-07 08:42:39 +00:00
}
2013-07-06 18:44:34 +00:00
} else {
2013-08-06 09:09:09 +00:00
// ILOG("pspKey %i %i", pspKeyCode, flags);
2013-07-07 22:21:40 +00:00
if ( flags & KEY_DOWN )
__CtrlButtonDown ( pspKeyCode ) ;
if ( flags & KEY_UP )
__CtrlButtonUp ( pspKeyCode ) ;
2013-07-06 18:44:34 +00:00
}
2013-07-06 17:08:59 +00:00
}
2014-06-15 11:04:59 +00:00
bool EmuScreen : : axis ( const AxisInput & axis ) {
2015-03-01 06:20:14 +00:00
Core_NotifyActivity ( ) ;
2013-07-31 16:07:45 +00:00
if ( axis . value > 0 ) {
processAxis ( axis , 1 ) ;
2014-06-15 11:04:59 +00:00
return true ;
2013-07-31 16:07:45 +00:00
} else if ( axis . value < 0 ) {
processAxis ( axis , - 1 ) ;
2014-06-15 11:04:59 +00:00
return true ;
2013-07-31 16:07:45 +00:00
} else if ( axis . value = = 0 ) {
// Both directions! Prevents sticking for digital input devices that are axises (like HAT)
processAxis ( axis , 1 ) ;
processAxis ( axis , - 1 ) ;
2014-06-15 11:04:59 +00:00
return true ;
2013-07-31 16:07:45 +00:00
}
2014-06-15 11:04:59 +00:00
return false ;
2013-07-31 16:07:45 +00:00
}
2014-07-20 10:42:30 +00:00
inline bool IsAnalogStickKey ( int key ) {
switch ( key ) {
case VIRTKEY_AXIS_X_MIN :
case VIRTKEY_AXIS_X_MAX :
case VIRTKEY_AXIS_Y_MIN :
case VIRTKEY_AXIS_Y_MAX :
case VIRTKEY_AXIS_RIGHT_X_MIN :
case VIRTKEY_AXIS_RIGHT_X_MAX :
case VIRTKEY_AXIS_RIGHT_Y_MIN :
case VIRTKEY_AXIS_RIGHT_Y_MAX :
return true ;
default :
return false ;
}
}
2013-07-31 16:07:45 +00:00
void EmuScreen : : processAxis ( const AxisInput & axis , int direction ) {
2014-07-20 10:52:11 +00:00
// Sanity check
if ( axis . axisId < 0 | | axis . axisId > = JOYSTICK_AXIS_MAX ) {
return ;
}
2013-10-31 15:50:27 +00:00
std : : vector < int > results ;
KeyMap : : AxisToPspButton ( axis . deviceId , axis . axisId , direction , & results ) ;
2015-05-15 16:49:54 +00:00
2013-10-31 15:50:27 +00:00
for ( size_t i = 0 ; i < results . size ( ) ; i + + ) {
int result = results [ i ] ;
switch ( result ) {
case VIRTKEY_AXIS_X_MIN :
2015-05-15 16:49:54 +00:00
SetPSPAxis ( ' X ' , - fabs ( axis . value ) , CTRL_STICK_LEFT ) ;
2013-10-31 15:50:27 +00:00
break ;
case VIRTKEY_AXIS_X_MAX :
2015-05-15 16:49:54 +00:00
SetPSPAxis ( ' X ' , fabs ( axis . value ) , CTRL_STICK_LEFT ) ;
2013-10-31 15:50:27 +00:00
break ;
case VIRTKEY_AXIS_Y_MIN :
2015-05-15 16:49:54 +00:00
SetPSPAxis ( ' Y ' , - fabs ( axis . value ) , CTRL_STICK_LEFT ) ;
2013-10-31 15:50:27 +00:00
break ;
case VIRTKEY_AXIS_Y_MAX :
2015-05-15 16:49:54 +00:00
SetPSPAxis ( ' Y ' , fabs ( axis . value ) , CTRL_STICK_LEFT ) ;
2013-10-31 15:50:27 +00:00
break ;
case VIRTKEY_AXIS_RIGHT_X_MIN :
2015-05-15 16:49:54 +00:00
SetPSPAxis ( ' X ' , - fabs ( axis . value ) , CTRL_STICK_RIGHT ) ;
2013-10-31 15:50:27 +00:00
break ;
case VIRTKEY_AXIS_RIGHT_X_MAX :
2015-05-15 16:49:54 +00:00
SetPSPAxis ( ' X ' , fabs ( axis . value ) , CTRL_STICK_RIGHT ) ;
2013-10-31 15:50:27 +00:00
break ;
case VIRTKEY_AXIS_RIGHT_Y_MIN :
2015-05-15 16:49:54 +00:00
SetPSPAxis ( ' Y ' , - fabs ( axis . value ) , CTRL_STICK_RIGHT ) ;
2013-10-31 15:50:27 +00:00
break ;
case VIRTKEY_AXIS_RIGHT_Y_MAX :
2015-05-15 16:49:54 +00:00
SetPSPAxis ( ' Y ' , fabs ( axis . value ) , CTRL_STICK_RIGHT ) ;
2013-10-31 15:50:27 +00:00
break ;
2014-07-20 10:42:30 +00:00
}
}
2013-10-31 15:50:27 +00:00
2014-07-20 10:42:30 +00:00
std : : vector < int > resultsOpposite ;
KeyMap : : AxisToPspButton ( axis . deviceId , axis . axisId , - direction , & resultsOpposite ) ;
2014-07-20 10:52:11 +00:00
int axisState = 0 ;
if ( ( direction = = 1 & & axis . value > = AXIS_BIND_THRESHOLD ) ) {
axisState = 1 ;
} else if ( direction = = - 1 & & axis . value < = - AXIS_BIND_THRESHOLD ) {
axisState = - 1 ;
2014-07-20 10:42:30 +00:00
} else {
2014-07-20 10:52:11 +00:00
axisState = 0 ;
}
if ( axisState ! = axisState_ [ axis . axisId ] ) {
axisState_ [ axis . axisId ] = axisState ;
if ( axisState ! = 0 ) {
for ( size_t i = 0 ; i < results . size ( ) ; i + + ) {
if ( ! IsAnalogStickKey ( results [ i ] ) )
pspKey ( results [ i ] , KEY_DOWN ) ;
}
2014-12-22 02:50:07 +00:00
// Also unpress the other direction (unless both directions press the same key.)
2014-07-20 10:52:11 +00:00
for ( size_t i = 0 ; i < resultsOpposite . size ( ) ; i + + ) {
2014-12-22 02:50:07 +00:00
if ( ! IsAnalogStickKey ( resultsOpposite [ i ] ) & & std : : find ( results . begin ( ) , results . end ( ) , resultsOpposite [ i ] ) = = results . end ( ) )
2014-07-20 10:52:11 +00:00
pspKey ( resultsOpposite [ i ] , KEY_UP ) ;
}
} else if ( axisState = = 0 ) {
// Release both directions, trying to deal with some erratic controllers that can cause it to stick.
for ( size_t i = 0 ; i < results . size ( ) ; i + + ) {
if ( ! IsAnalogStickKey ( results [ i ] ) )
pspKey ( results [ i ] , KEY_UP ) ;
}
for ( size_t i = 0 ; i < resultsOpposite . size ( ) ; i + + ) {
if ( ! IsAnalogStickKey ( resultsOpposite [ i ] ) )
pspKey ( resultsOpposite [ i ] , KEY_UP ) ;
}
2013-07-07 22:21:40 +00:00
}
2013-07-06 17:08:59 +00:00
}
}
2013-07-20 10:06:06 +00:00
void EmuScreen : : CreateViews ( ) {
2015-02-01 17:04:06 +00:00
using namespace UI ;
2014-02-10 14:55:21 +00:00
const Bounds & bounds = screenManager ( ) - > getUIContext ( ) - > GetBounds ( ) ;
InitPadLayout ( bounds . w , bounds . h ) ;
root_ = CreatePadLayout ( bounds . w , bounds . h , & pauseTrigger_ ) ;
2013-09-07 18:54:11 +00:00
if ( g_Config . bShowDeveloperMenu ) {
2015-02-01 17:04:06 +00:00
root_ - > Add ( new Button ( " DevMenu " ) ) - > OnClick . Handle ( this , & EmuScreen : : OnDevTools ) ;
2013-09-07 18:54:11 +00:00
}
2015-02-01 17:04:06 +00:00
saveStatePreview_ = new AsyncImageFileView ( " " , IS_FIXED , nullptr , new AnchorLayoutParams ( bounds . centerX ( ) , 100 , NONE , NONE , true ) ) ;
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_ ) ;
root_ - > Add ( new OnScreenMessagesView ( new AnchorLayoutParams ( ( Size ) bounds . w , ( Size ) bounds . h ) ) ) ;
2013-09-07 18:54:11 +00:00
}
UI : : EventReturn EmuScreen : : OnDevTools ( UI : : EventParams & params ) {
2015-05-21 08:49:47 +00:00
releaseButtons ( ) ;
2013-09-07 18:54:11 +00:00
screenManager ( ) - > push ( new DevMenu ( ) ) ;
return UI : : EVENT_DONE ;
2013-07-20 10:06:06 +00:00
}
2013-03-29 20:21:27 +00:00
void EmuScreen : : update ( InputState & input ) {
2014-05-16 05:17:19 +00:00
if ( bootPending_ )
2013-07-27 11:26:26 +00:00
bootGame ( gamePath_ ) ;
2013-07-20 10:06:06 +00:00
UIScreen : : update ( input ) ;
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.
const Bounds & bounds = screenManager ( ) - > getUIContext ( ) - > GetBounds ( ) ;
PSP_CoreParameter ( ) . pixelWidth = pixel_xres * bounds . w / dp_xres ;
PSP_CoreParameter ( ) . pixelHeight = pixel_yres * bounds . h / dp_yres ;
2013-07-16 20:50:53 +00:00
2014-05-21 08:13:40 +00:00
if ( ! invalid_ ) {
UpdateUIState ( UISTATE_INGAME ) ;
}
2013-11-15 12:11:44 +00:00
2012-11-01 15:19:01 +00:00
if ( errorMessage_ . size ( ) ) {
2013-12-05 13:14:15 +00:00
// Special handling for ZIP files. It's not very robust to check an error message but meh,
// at least it's pre-translation.
if ( errorMessage_ . find ( " ZIP " ) ! = std : : string : : npos ) {
screenManager ( ) - > push ( new InstallZipScreen ( gamePath_ ) ) ;
errorMessage_ = " " ;
2014-01-25 08:40:14 +00:00
quit_ = true ;
2013-12-05 13:14:15 +00:00
return ;
}
2015-07-01 21:34:50 +00:00
I18NCategory * err = GetI18NCategory ( " Error " ) ;
std : : string errLoadingFile = err - > T ( " Error loading file " , " Could not load game " ) ;
2013-12-05 13:14:15 +00:00
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
2013-12-05 13:14:15 +00:00
screenManager ( ) - > push ( new PromptScreen ( errLoadingFile , " OK " , " " ) ) ;
2012-11-01 15:19:01 +00:00
errorMessage_ = " " ;
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
2013-07-06 18:44:34 +00:00
// Virtual keys.
2013-07-07 02:41:28 +00:00
__CtrlSetRapidFire ( virtKeys [ VIRTKEY_RAPID_FIRE - VIRTKEY_FIRST ] ) ;
2013-07-06 18:44:34 +00:00
2013-04-28 20:07:02 +00:00
// Apply tilt to left stick
2013-09-09 21:48:11 +00:00
// TODO: Make into an axis
2014-02-08 18:29:22 +00:00
# ifdef MOBILE_DEVICE
2013-11-11 11:52:04 +00:00
/*
2013-03-10 12:21:36 +00:00
if ( g_Config . bAccelerometerToAnalogHoriz ) {
2013-11-15 12:11:44 +00:00
// Get the "base" coordinate system which is setup by the calibration system
2013-10-27 17:58:47 +00:00
float base_x = g_Config . fTiltBaseX ;
float base_y = g_Config . fTiltBaseY ;
//convert the current input into base coordinates and normalize
//TODO: check if all phones give values between [-50, 50]. I'm not sure how iOS works.
float normalized_input_x = ( input . acc . y - base_x ) / 50.0 ;
float normalized_input_y = ( input . acc . x - base_y ) / 50.0 ;
//TODO: need a better name for computed x and y.
2013-10-28 11:19:36 +00:00
float delta_x = tiltInputCurve ( normalized_input_x * 2.0 * ( g_Config . iTiltSensitivityX ) ) ;
2013-10-27 17:58:47 +00:00
//if the invert is enabled, invert the motion
2013-10-28 11:28:46 +00:00
if ( g_Config . bInvertTiltX ) {
2013-10-27 17:58:47 +00:00
delta_x * = - 1 ;
}
2013-10-28 11:19:36 +00:00
float delta_y = tiltInputCurve ( normalized_input_y * 2.0 * ( g_Config . iTiltSensitivityY ) ) ;
2016-08-28 04:20:03 +00:00
2013-10-28 11:28:46 +00:00
if ( g_Config . bInvertTiltY ) {
2013-10-27 17:58:47 +00:00
delta_y * = - 1 ;
}
//clamp the delta between [-1, 1]
leftstick_x + = clamp1 ( delta_x ) ;
2013-07-07 23:25:15 +00:00
__CtrlSetAnalogX ( clamp1 ( leftstick_x ) , CTRL_STICK_LEFT ) ;
2013-10-27 17:58:47 +00:00
2016-08-28 04:20:03 +00:00
2013-10-27 17:58:47 +00:00
leftstick_y + = clamp1 ( delta_y ) ;
__CtrlSetAnalogY ( clamp1 ( leftstick_y ) , CTRL_STICK_LEFT ) ;
2013-03-10 12:21:36 +00:00
}
2013-11-11 11:52:04 +00:00
*/
2013-09-29 18:31:08 +00:00
# endif
2013-03-10 12:21:36 +00:00
2013-05-10 00:36:23 +00:00
// Make sure fpsLimit starts at 0
2013-07-11 19:55:33 +00:00
if ( PSP_CoreParameter ( ) . fpsLimit ! = 0 & & PSP_CoreParameter ( ) . fpsLimit ! = 1 ) {
2013-05-10 00:36:23 +00:00
PSP_CoreParameter ( ) . fpsLimit = 0 ;
}
2013-06-17 18:28:22 +00:00
2013-07-20 12:05:07 +00:00
// This is here to support the iOS on screen back button.
if ( pauseTrigger_ ) {
pauseTrigger_ = false ;
2015-05-21 08:49:47 +00:00
releaseButtons ( ) ;
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 ;
std : : string fn ;
if ( SaveState : : HasSaveInSlot ( gamePath_ , currentSlot ) ) {
fn = SaveState : : GenerateSaveSlotFilename ( gamePath_ , currentSlot , SaveState : : SCREENSHOT_EXTENSION ) ;
}
saveStatePreview_ - > SetFilename ( fn ) ;
if ( ! fn . empty ( ) ) {
saveStatePreview_ - > SetVisibility ( UI : : V_VISIBLE ) ;
saveStatePreviewShownTime_ = time_now_d ( ) ;
} 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 ;
float alpha = clamp_value ( ( endTime - time_now_d ( ) ) * 4.0 , 0.0 , 1.0 ) ;
saveStatePreview_ - > SetColor ( colorAlpha ( 0x00FFFFFF , alpha ) ) ;
if ( time_now_d ( ) - saveStatePreviewShownTime_ > 2 ) {
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
}
2014-05-21 08:13:40 +00:00
void EmuScreen : : checkPowerDown ( ) {
if ( coreState = = CORE_POWERDOWN & & ! PSP_IsIniting ( ) ) {
if ( PSP_IsInited ( ) ) {
PSP_Shutdown ( ) ;
}
ILOG ( " SELF-POWERDOWN! " ) ;
screenManager ( ) - > switchScreen ( new MainScreen ( ) ) ;
bootPending_ = false ;
invalid_ = true ;
}
}
2015-01-29 11:55:49 +00:00
static void DrawDebugStats ( DrawBuffer * draw2d ) {
2016-03-31 08:17:02 +00:00
char statbuf [ 4096 ] ;
2015-01-29 11:55:49 +00:00
__DisplayGetDebugStats ( statbuf , sizeof ( statbuf ) ) ;
draw2d - > SetFontScale ( .7f , .7f ) ;
draw2d - > DrawText ( UBUNTU24 , statbuf , 11 , 31 , 0xc0000000 , FLAG_DYNAMIC_ASCII ) ;
draw2d - > DrawText ( UBUNTU24 , statbuf , 10 , 30 , 0xFFFFFFFF , FLAG_DYNAMIC_ASCII ) ;
2015-10-28 20:20:20 +00:00
__SasGetDebugStats ( statbuf , sizeof ( statbuf ) ) ;
draw2d - > DrawText ( UBUNTU24 , statbuf , PSP_CoreParameter ( ) . pixelWidth / 2 + 11 , 31 , 0xc0000000 , FLAG_DYNAMIC_ASCII ) ;
draw2d - > DrawText ( UBUNTU24 , statbuf , PSP_CoreParameter ( ) . pixelWidth / 2 + 10 , 30 , 0xFFFFFFFF , FLAG_DYNAMIC_ASCII ) ;
2015-01-29 11:55:49 +00:00
draw2d - > SetFontScale ( 1.0f , 1.0f ) ;
}
static void DrawAudioDebugStats ( DrawBuffer * draw2d ) {
char statbuf [ 1024 ] = { 0 } ;
const AudioDebugStats * stats = __AudioGetDebugStats ( ) ;
snprintf ( statbuf , sizeof ( statbuf ) ,
" Audio buffer: %d/%d (low watermark: %d) \n "
" Underruns: %d \n "
" Overruns: %d \n "
" Sample rate: %d \n "
" Push size: %d \n " ,
stats - > buffered , stats - > bufsize , stats - > watermark ,
stats - > underrunCount ,
stats - > overrunCount ,
stats - > instantSampleRate ,
stats - > lastPushSize ) ;
draw2d - > SetFontScale ( 0.7f , 0.7f ) ;
draw2d - > DrawText ( UBUNTU24 , statbuf , 11 , 31 , 0xc0000000 , FLAG_DYNAMIC_ASCII ) ;
draw2d - > DrawText ( UBUNTU24 , statbuf , 10 , 30 , 0xFFFFFFFF , FLAG_DYNAMIC_ASCII ) ;
draw2d - > SetFontScale ( 1.0f , 1.0f ) ;
}
static void DrawFPS ( DrawBuffer * draw2d , const Bounds & bounds ) {
float vps , fps , actual_fps ;
__DisplayGetFPS ( & vps , & fps , & actual_fps ) ;
char fpsbuf [ 256 ] ;
switch ( g_Config . iShowFPSCounter ) {
case 1 :
snprintf ( fpsbuf , sizeof ( fpsbuf ) , " Speed: %0.1f%% " , vps / ( 59.94f / 100.0f ) ) ; break ;
case 2 :
snprintf ( fpsbuf , sizeof ( fpsbuf ) , " FPS: %0.1f " , actual_fps ) ; break ;
case 3 :
snprintf ( fpsbuf , sizeof ( fpsbuf ) , " %0.0f/%0.0f (%0.1f%%) " , actual_fps , fps , vps / ( 59.94f / 100.0f ) ) ; break ;
default :
return ;
}
draw2d - > SetFontScale ( 0.7f , 0.7f ) ;
draw2d - > DrawText ( UBUNTU24 , fpsbuf , bounds . x2 ( ) - 8 , 12 , 0xc0000000 , ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII ) ;
draw2d - > DrawText ( UBUNTU24 , fpsbuf , bounds . x2 ( ) - 10 , 10 , 0xFF3fFF3f , ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII ) ;
draw2d - > SetFontScale ( 1.0f , 1.0f ) ;
}
2013-03-29 20:21:27 +00:00
void EmuScreen : : render ( ) {
2016-12-25 17:18:19 +00:00
using namespace Draw ;
2014-05-21 08:13:40 +00:00
if ( invalid_ ) {
// It's possible this might be set outside PSP_RunLoopFor().
// In this case, we need to double check it here.
checkPowerDown ( ) ;
2012-11-01 15:19:01 +00:00
return ;
2014-05-21 08:13:40 +00:00
}
2012-11-01 15:19:01 +00:00
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 ) {
if ( CChunkFileReader : : ERROR_NONE ! = SaveState : : LoadFromRam ( freezeState_ ) ) {
ERROR_LOG ( HLE , " Failed to load freeze state. Unfreezing. " ) ;
PSP_CoreParameter ( ) . frozen = false ;
}
}
2015-11-18 13:18:35 +00:00
bool useBufferedRendering = g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ;
if ( ! useBufferedRendering ) {
2017-01-30 13:33:38 +00:00
DrawContext * draw = screenManager ( ) - > getDrawContext ( ) ;
2017-02-15 10:06:59 +00:00
draw - > Clear ( FBChannel : : FB_COLOR_BIT | FBChannel : : FB_DEPTH_BIT | FBChannel : : FB_STENCIL_BIT , 0xFF000000 , 0.0f , 0 ) ;
2015-11-18 13:18:35 +00:00
2016-12-25 17:52:05 +00:00
Viewport viewport ;
2015-11-18 13:18:35 +00:00
viewport . TopLeftX = 0 ;
viewport . TopLeftY = 0 ;
viewport . Width = pixel_xres ;
viewport . Height = pixel_yres ;
viewport . MaxDepth = 1.0 ;
viewport . MinDepth = 0.0 ;
2017-01-30 13:33:38 +00:00
draw - > SetViewports ( 1 , & viewport ) ;
draw - > SetTargetSize ( pixel_xres , pixel_yres ) ;
2015-11-18 13:18:35 +00:00
}
2016-01-06 22:49:02 +00:00
PSP_BeginHostFrame ( ) ;
2012-11-25 16:21:23 +00:00
2012-12-01 22:20:08 +00:00
// We just run the CPU until we get to vblank. This will quickly sync up pretty nicely.
2012-11-20 10:35:48 +00:00
// The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully...
2013-02-18 22:22:41 +00:00
int blockTicks = usToCycles ( 1000000 / 10 ) ;
2012-11-19 13:16:37 +00:00
2012-11-19 13:51:47 +00:00
// Run until CORE_NEXTFRAME
while ( coreState = = CORE_RUNNING ) {
2013-08-04 22:22:30 +00:00
PSP_RunLoopFor ( blockTicks ) ;
2012-11-19 13:51:47 +00:00
}
2016-01-06 22:08:26 +00:00
2012-11-19 13:51:47 +00:00
// Hopefully coreState is now CORE_NEXTFRAME
if ( coreState = = CORE_NEXTFRAME ) {
// set back to running for the next frame
coreState = CORE_RUNNING ;
2012-11-19 13:16:37 +00:00
}
2014-05-21 08:13:40 +00:00
checkPowerDown ( ) ;
2012-11-01 15:19:01 +00:00
2016-01-06 22:49:02 +00:00
PSP_EndHostFrame ( ) ;
2013-03-06 23:10:53 +00:00
if ( invalid_ )
return ;
2015-05-13 20:28:02 +00:00
if ( ! osm . IsEmpty ( ) | | g_Config . bShowDebugStats | | g_Config . iShowFPSCounter | | g_Config . bShowTouchControls | | g_Config . bShowDeveloperMenu | | g_Config . bShowAudioDebug | | saveStatePreview_ - > GetVisibility ( ) ! = UI : : V_GONE | | g_Config . bShowFrameProfiler ) {
2017-01-30 13:33:38 +00:00
DrawContext * thin3d = screenManager ( ) - > getDrawContext ( ) ;
2015-01-04 17:00:59 +00:00
2015-02-03 20:59:36 +00:00
// This sets up some important states but not the viewport.
2015-01-04 17:00:59 +00:00
screenManager ( ) - > getUIContext ( ) - > Begin ( ) ;
2016-12-25 17:52:05 +00:00
Viewport viewport ;
2015-01-04 17:00:59 +00:00
viewport . TopLeftX = 0 ;
viewport . TopLeftY = 0 ;
viewport . Width = pixel_xres ;
viewport . Height = pixel_yres ;
viewport . MaxDepth = 1.0 ;
viewport . MinDepth = 0.0 ;
thin3d - > SetViewports ( 1 , & viewport ) ;
DrawBuffer * draw2d = screenManager ( ) - > getUIContext ( ) - > Draw ( ) ;
2012-11-20 10:35:48 +00:00
2014-12-31 15:50:23 +00:00
if ( root_ ) {
UI : : LayoutViewHierarchy ( * screenManager ( ) - > getUIContext ( ) , root_ ) ;
root_ - > Draw ( * screenManager ( ) - > getUIContext ( ) ) ;
2013-06-19 05:08:29 +00:00
}
2014-02-10 11:38:23 +00:00
2014-12-31 15:50:23 +00:00
if ( g_Config . bShowDebugStats ) {
2015-01-29 11:55:49 +00:00
DrawDebugStats ( draw2d ) ;
2014-12-31 15:50:23 +00:00
}
2013-10-27 09:50:45 +00:00
2015-01-29 11:55:49 +00:00
if ( g_Config . bShowAudioDebug ) {
DrawAudioDebugStats ( draw2d ) ;
}
2014-12-31 15:50:23 +00:00
2015-01-29 11:55:49 +00:00
if ( g_Config . iShowFPSCounter ) {
DrawFPS ( draw2d , screenManager ( ) - > getUIContext ( ) - > GetBounds ( ) ) ;
2014-12-31 15:50:23 +00:00
}
2012-11-01 15:19:01 +00:00
2015-05-13 21:07:19 +00:00
# ifdef USE_PROFILER
2015-05-13 20:28:02 +00:00
if ( g_Config . bShowFrameProfiler ) {
2015-05-13 21:07:19 +00:00
DrawProfile ( * screenManager ( ) - > getUIContext ( ) ) ;
2015-05-13 20:28:02 +00:00
}
2015-05-13 21:07:19 +00:00
# endif
2015-05-13 20:28:02 +00:00
2015-01-04 17:00:59 +00:00
screenManager ( ) - > getUIContext ( ) - > End ( ) ;
2014-12-31 15:50:23 +00:00
}
// We have no use for backbuffer depth or stencil, so let tiled renderers discard them after tiling.
2015-10-10 14:41:19 +00:00
/*
2014-12-31 15:50:23 +00:00
if ( gl_extensions . GLES3 & & glInvalidateFramebuffer ! = nullptr ) {
2015-03-14 23:01:43 +00:00
GLenum attachments [ 2 ] = { GL_DEPTH , GL_STENCIL } ;
glInvalidateFramebuffer ( GL_FRAMEBUFFER , 2 , attachments ) ;
2014-12-31 15:50:23 +00:00
} else if ( ! gl_extensions . GLES3 ) {
2015-09-05 21:09:06 +00:00
# ifdef USING_GLES2
2014-12-31 15:50:23 +00:00
// Tiled renderers like PowerVR should benefit greatly from this. However - seems I can't call it?
bool hasDiscard = gl_extensions . EXT_discard_framebuffer ; // TODO
if ( hasDiscard ) {
//const GLenum targets[3] = { GL_COLOR_EXT, GL_DEPTH_EXT, GL_STENCIL_EXT };
//glDiscardFramebufferEXT(GL_FRAMEBUFFER, 3, targets);
}
2012-11-01 15:19:01 +00:00
# endif
2015-09-05 21:09:06 +00:00
}
2015-10-10 14:41:19 +00:00
*/
2012-11-01 15:19:01 +00:00
}
2013-03-29 20:21:27 +00:00
void EmuScreen : : deviceLost ( ) {
2013-03-06 23:10:53 +00:00
ILOG ( " EmuScreen::deviceLost() " ) ;
2013-07-27 08:05:00 +00:00
if ( gpu )
gpu - > DeviceLost ( ) ;
2016-09-11 03:29:58 +00:00
}
void EmuScreen : : deviceRestore ( ) {
ILOG ( " EmuScreen::deviceRestore() " ) ;
if ( gpu )
gpu - > DeviceRestore ( ) ;
2013-12-10 23:47:36 +00:00
RecreateViews ( ) ;
2012-11-01 15:19:01 +00:00
}
2013-10-30 17:16:27 +00:00
2013-10-30 17:54:41 +00:00
void EmuScreen : : autoLoad ( ) {
2013-10-30 17:16:27 +00:00
//check if save state has save, if so, load
2015-09-23 17:29:39 +00:00
int lastSlot = SaveState : : GetNewestSlot ( gamePath_ ) ;
2013-10-30 17:52:46 +00:00
if ( g_Config . bEnableAutoLoad & & lastSlot ! = - 1 ) {
2016-05-28 04:25:05 +00:00
SaveState : : LoadSlot ( gamePath_ , lastSlot , & AfterSaveStateAction ) ;
2014-07-15 12:14:35 +00:00
g_Config . iCurrentStateSlot = lastSlot ;
2013-10-30 17:16:27 +00:00
}
2013-12-15 11:49:13 +00:00
}
2015-05-21 08:49:47 +00:00
// TODO: Add generic loss-of-focus handling for Screens, use this.
void EmuScreen : : releaseButtons ( ) {
TouchInput input ;
input . flags = TOUCH_RELEASE_ALL ;
input . timestamp = time_now_d ( ) ;
input . id = 0 ;
touch ( input ) ;
2015-05-13 21:07:19 +00:00
}
2017-02-23 08:25:33 +00:00
void EmuScreen : : resized ( ) {
RecreateViews ( ) ;
}