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"
|
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"
|
2021-04-25 02:08:39 +00:00
|
|
|
#include "Core/CwCheat.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"
|
|
|
|
#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
|
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"
|
2014-12-12 22:48:48 +00:00
|
|
|
#include "Core/MIPS/MIPS.h"
|
2023-12-07 12:51:48 +00:00
|
|
|
#include "Core/HLE/scePower.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"
|
2021-04-25 02:08:39 +00:00
|
|
|
#include "Core/HLE/Plugins.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-06-20 21:40:36 +00:00
|
|
|
auto sc = GetI18NCategory(I18NCat::SCREEN);
|
|
|
|
|
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;
|
2023-07-24 10:00:16 +00:00
|
|
|
ERROR_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.
|
2017-05-18 10:52:03 +00:00
|
|
|
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, filename, 0);
|
2018-08-23 01:27:20 +00:00
|
|
|
if (!info || info->pending)
|
|
|
|
return;
|
|
|
|
|
2022-12-16 13:55:56 +00:00
|
|
|
SetExtraAssertInfo((info->id + " " + info->GetTitle()).c_str());
|
|
|
|
|
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
|
|
|
|
|
|
|
g_Discord.SetPresenceGame(info->GetTitle().c_str());
|
|
|
|
} 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;
|
2023-07-24 10:00:16 +00:00
|
|
|
ERROR_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
|
|
|
|
2023-12-13 15:48:07 +00:00
|
|
|
if (PSP_CoreParameter().compat.flags().RequireBlockTransfer && g_Config.iSkipGPUReadbackMode != (int)SkipGPUReadbackMode::NO_SKIP) {
|
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
|
|
|
|
2023-07-24 10:00:16 +00:00
|
|
|
NOTICE_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
|
|
|
}
|
|
|
|
|
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();
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2018-06-15 00:52:44 +00:00
|
|
|
static void AfterSaveStateAction(SaveState::Status status, const std::string &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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-15 00:52:44 +00:00
|
|
|
static void AfterStateBoot(SaveState::Status status, const std::string &message, void *ignored) {
|
|
|
|
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);
|
|
|
|
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)) {
|
2020-08-15 13:22:44 +00:00
|
|
|
ERROR_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) {
|
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) {
|
2023-07-12 17:18:56 +00:00
|
|
|
if (g_Config.bAchievementsSoundEffects) {
|
|
|
|
// TODO: Handle this some nicer way.
|
|
|
|
if (!strcmp(value, "achievement_unlocked")) {
|
2023-07-16 19:43:49 +00:00
|
|
|
g_BackgroundAudio.SFX().Play(UI::UISound::ACHIEVEMENT_UNLOCKED, 0.6f);
|
2023-07-12 17:18:56 +00:00
|
|
|
}
|
|
|
|
if (!strcmp(value, "leaderboard_submitted")) {
|
2023-07-16 19:43:49 +00:00
|
|
|
g_BackgroundAudio.SFX().Play(UI::UISound::LEADERBOARD_SUBMITTED, 0.6f);
|
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
|
|
|
}
|
|
|
|
|
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:
|
2023-12-03 15:41:29 +00:00
|
|
|
if (!Achievements::WarnUserIfHardcoreModeActive(false)) {
|
2023-07-02 22:47:54 +00:00
|
|
|
if (down) {
|
|
|
|
// 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, "ui.frameAdvance", 0);
|
|
|
|
}
|
2023-03-30 08:47:28 +00:00
|
|
|
}
|
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;
|
|
|
|
|
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;
|
2019-03-26 05:12:02 +00:00
|
|
|
|
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) {
|
2023-06-20 12:40:46 +00:00
|
|
|
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("saveNewTextures_true", "Textures will now be saved to your storage"), 2.0);
|
2023-09-30 09:21:22 +00:00
|
|
|
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
|
2023-03-30 08:47:28 +00:00
|
|
|
} else {
|
2023-06-20 12:40:46 +00:00
|
|
|
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("saveNewTextures_false", "Texture saving was disabled"), 2.0);
|
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)
|
2023-06-20 12:40:46 +00:00
|
|
|
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("replaceTextures_true", "Texture replacement enabled"), 2.0);
|
2023-03-30 08:47:28 +00:00
|
|
|
else
|
2023-06-20 12:40:46 +00:00
|
|
|
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("replaceTextures_false", "Textures no longer are being replaced"), 2.0);
|
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.
|
|
|
|
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath_, GAMEINFO_WANTBG);
|
|
|
|
dc.Flush();
|
|
|
|
|
|
|
|
// PIC1 is the loading image, so let's only draw if it's available.
|
|
|
|
if (ginfo && 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 n = GetI18NCategory(I18NCat::NETWORKING);
|
|
|
|
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) {
|
|
|
|
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
|
|
|
}
|
2021-09-13 02:44:44 +00:00
|
|
|
chatMenu_ = root_->Add(new ChatMenu(screenManager()->getUIContext()->GetBounds(), new LayoutParams(FILL_PARENT, FILL_PARENT)));
|
|
|
|
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 ¶ms) {
|
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 ¶ms) {
|
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 ¶ms) {
|
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 ¶ms) {
|
|
|
|
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 ¶ms) {
|
|
|
|
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();
|
|
|
|
}
|
2020-08-15 10:25:39 +00:00
|
|
|
INFO_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
|
|
|
}
|
|
|
|
|
2020-12-19 19:08:48 +00:00
|
|
|
static const char *CPUCoreAsString(int core) {
|
|
|
|
switch (core) {
|
|
|
|
case 0: return "Interpreter";
|
|
|
|
case 1: return "JIT";
|
|
|
|
case 2: return "IR Interpreter";
|
2023-08-21 01:36:06 +00:00
|
|
|
case 3: return "JIT Using IR";
|
2020-12-19 19:08:48 +00:00
|
|
|
default: return "N/A";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-24 23:43:17 +00:00
|
|
|
static void DrawCrashDump(UIContext *ctx, const Path &gamePath) {
|
2023-04-28 11:38:16 +00:00
|
|
|
const MIPSExceptionInfo &info = Core_GetExceptionInfo();
|
2019-02-12 12:29:37 +00:00
|
|
|
|
2023-04-05 22:34:50 +00:00
|
|
|
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
2019-02-12 12:29:37 +00:00
|
|
|
FontID ubuntu24("UBUNTU24");
|
2022-02-24 11:25:04 +00:00
|
|
|
|
|
|
|
int x = 20 + System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT);
|
|
|
|
int y = 20 + System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP);
|
|
|
|
|
|
|
|
ctx->Flush();
|
|
|
|
if (ctx->Draw()->GetFontAtlas()->getFont(ubuntu24))
|
|
|
|
ctx->BindFontTexture();
|
2023-01-24 23:43:17 +00:00
|
|
|
ctx->Draw()->SetFontScale(1.1f, 1.1f);
|
2022-02-24 11:25:04 +00:00
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, sy->T("Game crashed"), x, y, 0xFFFFFFFF);
|
|
|
|
|
2019-02-12 12:29:37 +00:00
|
|
|
char statbuf[4096];
|
|
|
|
char versionString[256];
|
2019-02-17 17:46:42 +00:00
|
|
|
snprintf(versionString, sizeof(versionString), "%s", PPSSPP_GIT_VERSION);
|
|
|
|
|
2023-01-24 23:43:17 +00:00
|
|
|
char crcStr[16]{};
|
|
|
|
if (Reporting::HasCRC(gamePath)) {
|
|
|
|
u32 crc = Reporting::RetrieveCRC(gamePath);
|
|
|
|
snprintf(crcStr, sizeof(crcStr), "CRC: %08x\n", crc);
|
|
|
|
} else {
|
|
|
|
// Queue it for calculation, we want it!
|
|
|
|
// It's OK to call this repeatedly until we have it, which is natural here.
|
|
|
|
Reporting::QueueCRC(gamePath);
|
|
|
|
}
|
|
|
|
|
2019-02-12 12:29:37 +00:00
|
|
|
// TODO: Draw a lot more information. Full register set, and so on.
|
2020-07-12 14:47:53 +00:00
|
|
|
|
|
|
|
#ifdef _DEBUG
|
2020-12-19 18:16:04 +00:00
|
|
|
char build[] = "debug";
|
2020-07-12 14:47:53 +00:00
|
|
|
#else
|
2020-12-19 18:16:04 +00:00
|
|
|
char build[] = "release";
|
2020-07-12 14:47:53 +00:00
|
|
|
#endif
|
2020-12-19 18:16:04 +00:00
|
|
|
|
|
|
|
std::string sysName = System_GetProperty(SYSPROP_NAME);
|
|
|
|
int sysVersion = System_GetPropertyInt(SYSPROP_SYSTEMVERSION);
|
|
|
|
|
2020-12-25 09:40:54 +00:00
|
|
|
// First column
|
2022-02-24 11:25:04 +00:00
|
|
|
y += 65;
|
2020-12-25 09:40:54 +00:00
|
|
|
|
|
|
|
int columnWidth = (ctx->GetBounds().w - x - 10) / 2;
|
|
|
|
int height = ctx->GetBounds().h;
|
|
|
|
|
|
|
|
ctx->PushScissor(Bounds(x, y, columnWidth, height));
|
|
|
|
|
2023-03-31 08:08:12 +00:00
|
|
|
// INFO_LOG(SYSTEM, "DrawCrashDump (%d %d %d %d)", x, y, columnWidth, height);
|
2022-12-30 11:21:05 +00:00
|
|
|
|
2019-02-12 12:29:37 +00:00
|
|
|
snprintf(statbuf, sizeof(statbuf), R"(%s
|
2020-12-25 09:40:54 +00:00
|
|
|
%s (%s)
|
2020-12-19 18:16:04 +00:00
|
|
|
%s (%s)
|
|
|
|
%s v%d (%s)
|
2023-01-24 23:43:17 +00:00
|
|
|
%s
|
2019-02-12 12:29:37 +00:00
|
|
|
)",
|
|
|
|
ExceptionTypeAsString(info.type),
|
2020-12-19 18:16:04 +00:00
|
|
|
g_paramSFO.GetDiscID().c_str(), g_paramSFO.GetValueString("TITLE").c_str(),
|
|
|
|
versionString, build,
|
2023-01-24 23:43:17 +00:00
|
|
|
sysName.c_str(), sysVersion, GetCompilerABI(),
|
|
|
|
crcStr
|
2019-02-12 12:29:37 +00:00
|
|
|
);
|
|
|
|
|
2020-12-25 09:40:54 +00:00
|
|
|
ctx->Draw()->SetFontScale(.7f, .7f);
|
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
2023-01-24 23:43:17 +00:00
|
|
|
y += 160;
|
2019-02-12 12:29:37 +00:00
|
|
|
|
2023-04-28 11:38:16 +00:00
|
|
|
if (info.type == MIPSExceptionType::MEMORY) {
|
2019-02-12 12:29:37 +00:00
|
|
|
snprintf(statbuf, sizeof(statbuf), R"(
|
2023-01-01 18:22:41 +00:00
|
|
|
Access: %s at %08x (sz: %d)
|
2020-07-15 10:38:05 +00:00
|
|
|
PC: %08x
|
|
|
|
%s)",
|
2019-02-12 12:29:37 +00:00
|
|
|
MemoryExceptionTypeAsString(info.memory_type),
|
|
|
|
info.address,
|
2023-01-01 18:22:41 +00:00
|
|
|
info.accessSize,
|
2020-07-15 10:38:05 +00:00
|
|
|
info.pc,
|
|
|
|
info.info.c_str());
|
2020-12-25 09:40:54 +00:00
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
2020-07-15 10:38:05 +00:00
|
|
|
y += 180;
|
2023-04-28 11:38:16 +00:00
|
|
|
} else if (info.type == MIPSExceptionType::BAD_EXEC_ADDR) {
|
2020-07-13 04:59:08 +00:00
|
|
|
snprintf(statbuf, sizeof(statbuf), R"(
|
|
|
|
Destination: %s to %08x
|
2022-08-21 21:09:52 +00:00
|
|
|
PC: %08x
|
|
|
|
RA: %08x)",
|
2020-07-13 04:59:08 +00:00
|
|
|
ExecExceptionTypeAsString(info.exec_type),
|
|
|
|
info.address,
|
2022-08-21 21:09:52 +00:00
|
|
|
info.pc,
|
|
|
|
info.ra);
|
2020-12-25 09:40:54 +00:00
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
2020-10-14 21:45:19 +00:00
|
|
|
y += 180;
|
2023-04-28 11:38:16 +00:00
|
|
|
} else if (info.type == MIPSExceptionType::BREAK) {
|
2020-10-14 21:45:19 +00:00
|
|
|
snprintf(statbuf, sizeof(statbuf), R"(
|
|
|
|
BREAK
|
2022-08-21 21:09:52 +00:00
|
|
|
PC: %08x
|
|
|
|
)", info.pc);
|
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
|
|
|
y += 180;
|
|
|
|
} else {
|
|
|
|
snprintf(statbuf, sizeof(statbuf), R"(
|
|
|
|
Invalid / Unknown (%d)
|
|
|
|
)", (int)info.type);
|
2020-12-25 09:40:54 +00:00
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
2020-10-14 21:45:19 +00:00
|
|
|
y += 180;
|
2019-02-12 12:29:37 +00:00
|
|
|
}
|
2020-07-12 14:47:53 +00:00
|
|
|
|
|
|
|
std::string kernelState = __KernelStateSummary();
|
|
|
|
|
2020-12-25 09:40:54 +00:00
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, kernelState.c_str(), x, y, 0xFFFFFFFF);
|
|
|
|
|
2023-03-31 08:08:12 +00:00
|
|
|
y += 40;
|
|
|
|
|
|
|
|
ctx->Draw()->SetFontScale(.5f, .5f);
|
|
|
|
|
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, info.stackTrace.c_str(), x, y, 0xFFFFFFFF);
|
|
|
|
|
|
|
|
ctx->Draw()->SetFontScale(.7f, .7f);
|
|
|
|
|
2020-12-25 09:40:54 +00:00
|
|
|
ctx->PopScissor();
|
2020-12-19 18:16:04 +00:00
|
|
|
|
|
|
|
// Draw some additional stuff to the right.
|
2020-12-25 09:40:54 +00:00
|
|
|
|
|
|
|
x += columnWidth + 10;
|
2022-02-24 11:25:04 +00:00
|
|
|
y = 85;
|
2020-12-19 18:16:04 +00:00
|
|
|
snprintf(statbuf, sizeof(statbuf),
|
2021-04-25 02:08:39 +00:00
|
|
|
"CPU Core: %s (flags: %08x)\n"
|
|
|
|
"Locked CPU freq: %d MHz\n"
|
|
|
|
"Cheats: %s, Plugins: %s\n",
|
|
|
|
CPUCoreAsString(g_Config.iCpuCore), g_Config.uJitDisableFlags,
|
2023-12-07 12:51:48 +00:00
|
|
|
GetLockedCPUSpeedMhz(),
|
2021-04-25 02:08:39 +00:00
|
|
|
CheatsInEffect() ? "Y" : "N", HLEPlugins::HasEnabled() ? "Y" : "N");
|
2020-12-19 18:16:04 +00:00
|
|
|
|
2020-12-25 09:40:54 +00:00
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
2021-10-16 23:47:24 +00:00
|
|
|
ctx->Flush();
|
2023-01-24 23:43:17 +00:00
|
|
|
ctx->Draw()->SetFontScale(1.0f, 1.0f);
|
2021-10-16 23:47:24 +00:00
|
|
|
ctx->RebindTexture();
|
2019-02-12 12:29:37 +00:00
|
|
|
}
|
|
|
|
|
2021-11-15 15:04:17 +00:00
|
|
|
static void DrawFPS(UIContext *ctx, const Bounds &bounds) {
|
2020-02-29 20:51:14 +00:00
|
|
|
FontID ubuntu24("UBUNTU24");
|
2015-01-29 11:55:49 +00:00
|
|
|
float vps, fps, actual_fps;
|
|
|
|
__DisplayGetFPS(&vps, &fps, &actual_fps);
|
2023-01-29 18:50:44 +00:00
|
|
|
|
|
|
|
char fpsbuf[256]{};
|
|
|
|
if (g_Config.iShowStatusFlags == ((int)ShowStatusFlags::FPS_COUNTER | (int)ShowStatusFlags::SPEED_COUNTER)) {
|
|
|
|
snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / (59.94f / 100.0f));
|
|
|
|
} else {
|
|
|
|
if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::FPS_COUNTER) {
|
2023-01-30 17:31:49 +00:00
|
|
|
snprintf(fpsbuf, sizeof(fpsbuf), "FPS: %0.1f", actual_fps);
|
2023-01-29 18:50:44 +00:00
|
|
|
}
|
|
|
|
if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::SPEED_COUNTER) {
|
|
|
|
snprintf(fpsbuf, sizeof(fpsbuf), "%s Speed: %0.1f%%", fpsbuf, vps / (59.94f / 100.0f));
|
|
|
|
}
|
2023-01-27 08:19:29 +00:00
|
|
|
}
|
2023-03-21 10:10:09 +00:00
|
|
|
|
2023-01-27 08:19:29 +00:00
|
|
|
#ifdef CAN_DISPLAY_CURRENT_BATTERY_CAPACITY
|
2023-01-28 23:25:00 +00:00
|
|
|
if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::BATTERY_PERCENT) {
|
2023-01-27 08:19:29 +00:00
|
|
|
snprintf(fpsbuf, sizeof(fpsbuf), "%s Battery: %d%%", fpsbuf, getCurrentBatteryCapacity());
|
|
|
|
}
|
|
|
|
#endif
|
2023-01-30 17:31:49 +00:00
|
|
|
|
2021-11-15 15:04:17 +00:00
|
|
|
ctx->Flush();
|
|
|
|
ctx->BindFontTexture();
|
|
|
|
ctx->Draw()->SetFontScale(0.7f, 0.7f);
|
2023-01-27 08:19:29 +00:00
|
|
|
ctx->Draw()->DrawText(ubuntu24, fpsbuf, bounds.x2() - 8, 20, 0xc0000000, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
|
|
|
|
ctx->Draw()->DrawText(ubuntu24, fpsbuf, bounds.x2() - 10, 19, 0xFF3fFF3f, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
|
2021-11-15 15:04:17 +00:00
|
|
|
ctx->Draw()->SetFontScale(1.0f, 1.0f);
|
|
|
|
ctx->Flush();
|
|
|
|
ctx->RebindTexture();
|
2015-01-29 11:55:49 +00:00
|
|
|
}
|
|
|
|
|
2023-12-11 14:58:08 +00:00
|
|
|
bool EmuScreen::canBeBackground(bool isTop) const {
|
|
|
|
if (g_Config.bSkipBufferEffects) {
|
|
|
|
return isTop || (g_Config.bTransparentBackground && g_Config.bRunBehindPauseMenu);
|
|
|
|
}
|
2023-12-10 20:57:05 +00:00
|
|
|
|
2023-12-11 16:47:19 +00:00
|
|
|
if (!g_Config.bTransparentBackground && !isTop) {
|
|
|
|
if (g_Config.bRunBehindPauseMenu || screenManager()->topScreen()->wantBrightBackground())
|
|
|
|
return true;
|
2023-12-10 20:57:05 +00:00
|
|
|
return false;
|
2023-12-11 16:47:19 +00:00
|
|
|
}
|
2023-12-10 20:57:05 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
if (!draw)
|
|
|
|
return flags; // shouldn't really happen but I've seen a suspicious stack trace..
|
|
|
|
|
2023-12-11 14:58:08 +00:00
|
|
|
bool skipBufferEffects = g_Config.bSkipBufferEffects;
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
if ((g_Config.bSkipBufferEffects && !g_Config.bSoftwareRendering) || Core_IsStepping()) {
|
|
|
|
// We need to clear here already so that drawing during the frame is done on a clean slate.
|
|
|
|
if (Core_IsStepping() && gpuStats.numFlips != 0) {
|
|
|
|
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_BackBuffer");
|
|
|
|
} 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;
|
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-11 11:41:44 +00:00
|
|
|
} else if (!g_Config.bRunBehindPauseMenu) {
|
2023-12-10 20:57:05 +00:00
|
|
|
// Not on top. Let's not execute, only draw the image.
|
2023-12-11 11:41:44 +00:00
|
|
|
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
|
2023-12-10 20:57:05 +00:00
|
|
|
// Just to make sure.
|
|
|
|
if (PSP_IsInited() && !g_Config.bSkipBufferEffects) {
|
|
|
|
PSP_BeginHostFrame();
|
|
|
|
gpu->CopyDisplayToOutput(true);
|
|
|
|
PSP_EndHostFrame();
|
2023-12-15 10:30:54 +00:00
|
|
|
draw->SetViewport(viewport);
|
|
|
|
draw->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres);
|
2023-12-10 20:57:05 +00:00
|
|
|
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?
|
|
|
|
if (loadingTextView_->GetVisibility() == UI::V_VISIBLE)
|
|
|
|
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.
|
|
|
|
checkPowerDown();
|
2023-12-11 11:41:44 +00:00
|
|
|
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_Invalid");
|
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)) {
|
|
|
|
ERROR_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
|
|
|
|
2023-07-03 07:18:25 +00:00
|
|
|
bool blockedExecution = Achievements::IsBlockingExecution();
|
2023-10-11 07:04:28 +00:00
|
|
|
bool rebind = false;
|
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();
|
|
|
|
uint32_t color = dangerousSettings ? 0xFF900050 : 0xFF900000;
|
2023-12-11 11:41:44 +00:00
|
|
|
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, color }, "EmuScreen_RuntimeError");
|
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.
|
2023-12-11 11:41:44 +00:00
|
|
|
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
|
2023-07-03 07:18:25 +00:00
|
|
|
// Just to make sure.
|
|
|
|
if (PSP_IsInited()) {
|
|
|
|
gpu->CopyDisplayToOutput(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
|
2023-10-11 07:04:28 +00:00
|
|
|
rebind = true;
|
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
|
|
|
|
2023-07-03 07:18:25 +00:00
|
|
|
PSP_EndHostFrame();
|
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2023-12-11 14:58:08 +00:00
|
|
|
if (gpu && !gpu->PresentedThisFrame() && !skipBufferEffects) {
|
2023-12-11 11:41:44 +00:00
|
|
|
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "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
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
2018-12-18 09:23:22 +00:00
|
|
|
if (hasVisibleUI()) {
|
2020-03-15 16:21:55 +00:00
|
|
|
// In most cases, this should already be bound and a no-op.
|
2023-12-11 11:41:44 +00:00
|
|
|
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_UI");
|
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
|
|
|
|
|
|
|
if (mode & ScreenRenderMode::TOP) {
|
2023-12-10 20:57:05 +00:00
|
|
|
// TODO: Replace this with something else.
|
2023-12-10 13:09:55 +00:00
|
|
|
if (stopRender_)
|
2023-12-11 11:41:44 +00:00
|
|
|
draw->WipeQueue();
|
|
|
|
} else {
|
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;
|
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
|
|
|
|
|
|
|
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
|
|
|
}
|