diff --git a/Core/Config.cpp b/Core/Config.cpp
index 5076c7069f..dd14d9bf9d 100644
--- a/Core/Config.cpp
+++ b/Core/Config.cpp
@@ -937,6 +937,7 @@ void TouchControlConfig::ResetLayout() {
fDpadSpacing = 1.0f;
reset(&touchStartKey);
reset(&touchSelectKey);
+ reset(&touchHomeKey);
reset(&touchFastForwardKey);
reset(&touchLKey);
reset(&touchRKey);
@@ -1006,6 +1007,7 @@ static constexpr ConfigSetting touchControlSettings[] = {
ConfigSetting("DPadSpacing", SETTING(g_Config.touchControlsLandscape, fDpadSpacing), 1.0f, CfgFlag::PER_GAME),
ConfigSetting("StartKeyX", "StartKeyY", "StartKeyScale", "ShowTouchStart", SETTING(g_Config.touchControlsLandscape, touchStartKey), defaultTouchPosShow, CfgFlag::PER_GAME),
ConfigSetting("SelectKeyX", "SelectKeyY", "SelectKeyScale", "ShowTouchSelect", SETTING(g_Config.touchControlsLandscape, touchSelectKey), defaultTouchPosShow, CfgFlag::PER_GAME),
+ ConfigSetting("HomeKeyX", "HomeKeyY", "HomeKeyScale", "ShowTouchHome", SETTING(g_Config.touchControlsLandscape, touchHomeKey), defaultTouchPosHide, CfgFlag::PER_GAME),
ConfigSetting("UnthrottleKeyX", "UnthrottleKeyY", "UnthrottleKeyScale", "ShowTouchUnthrottle", SETTING(g_Config.touchControlsLandscape, touchFastForwardKey), defaultTouchPosShow, CfgFlag::PER_GAME),
ConfigSetting("LKeyX", "LKeyY", "LKeyScale", "ShowTouchLTrigger", SETTING(g_Config.touchControlsLandscape, touchLKey), defaultTouchPosShow, CfgFlag::PER_GAME),
ConfigSetting("RKeyX", "RKeyY", "RKeyScale", "ShowTouchRTrigger", SETTING(g_Config.touchControlsLandscape, touchRKey), defaultTouchPosShow, CfgFlag::PER_GAME),
diff --git a/Core/Config.h b/Core/Config.h
index 675b3be1ee..9c43205bae 100644
--- a/Core/Config.h
+++ b/Core/Config.h
@@ -104,6 +104,7 @@ struct TouchControlConfig : public ConfigBlock {
constexpr TouchControlConfig() {
// Hide all extras and custom buttons by default.
touchRightAnalogStick.show = false;
+ touchHomeKey.show = false;
for (size_t i = 0; i < CUSTOM_BUTTON_COUNT; i++) {
touchCustom[i].show = false;
}
@@ -119,6 +120,7 @@ struct TouchControlConfig : public ConfigBlock {
ConfigTouchPos touchStartKey;
ConfigTouchPos touchSelectKey;
+ ConfigTouchPos touchHomeKey;
ConfigTouchPos touchFastForwardKey;
ConfigTouchPos touchLKey;
ConfigTouchPos touchRKey;
diff --git a/Core/KeyMap.cpp b/Core/KeyMap.cpp
index 16f8c4ff80..b21cdac9e5 100644
--- a/Core/KeyMap.cpp
+++ b/Core/KeyMap.cpp
@@ -471,6 +471,7 @@ const KeyMap_IntStrPair psp_button_names[] = {
{CTRL_SELECT, "Select"},
{CTRL_LTRIGGER, "L"},
{CTRL_RTRIGGER, "R"},
+ {CTRL_HOME, "Home"},
{VIRTKEY_AXIS_Y_MAX, "An.Up"},
{VIRTKEY_AXIS_Y_MIN, "An.Down"},
@@ -538,7 +539,6 @@ const KeyMap_IntStrPair psp_button_names[] = {
{VIRTKEY_AXIS_RIGHT_X_MIN, "RightAn.Left"},
{VIRTKEY_AXIS_RIGHT_X_MAX, "RightAn.Right"},
- {CTRL_HOME, "Home"},
{CTRL_HOLD, "Hold"},
{CTRL_WLAN, "Wlan"},
{CTRL_REMOTE_HOLD, "Remote hold"},
diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp
index 80d9bcd55b..4207f59168 100644
--- a/UI/ControlMappingScreen.cpp
+++ b/UI/ControlMappingScreen.cpp
@@ -91,6 +91,7 @@ void SingleControlMapper::Refresh() {
{ "Triangle", ImageID("I_TRIANGLE") },
{ "Start", ImageID("I_START") },
{ "Select", ImageID("I_SELECT") },
+ { "Home", ImageID("I_HOME") },
{ "L", ImageID("I_L") },
{ "R", ImageID("I_R") }
};
diff --git a/UI/GameBrowser.cpp b/UI/GameBrowser.cpp
index 045e019147..f4b946db26 100644
--- a/UI/GameBrowser.cpp
+++ b/UI/GameBrowser.cpp
@@ -1043,7 +1043,7 @@ void GameBrowser::Refresh() {
topBar->Add(new Spacer(2.0f));
topBar->Add(new TextView(pathStr, ALIGN_VCENTER | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f)));
}
- Choice *homeChoice = topBar->Add(new Choice(ImageID("I_HOME"), new LayoutParams(WRAP_CONTENT, 64.0f)));
+ Choice *homeChoice = topBar->Add(new Choice(ImageID("I_HOME_ICON"), new LayoutParams(WRAP_CONTENT, 64.0f)));
homeChoice->SetRoundCorners(RoundCorners::All);
homeChoice->OnClick.Handle(this, &GameBrowser::OnHomeClick);
if (System_GetPropertyBool(SYSPROP_HAS_ADDITIONAL_STORAGE)) {
diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp
index 79833e70d4..8e481b2e5d 100644
--- a/UI/GamepadEmu.cpp
+++ b/UI/GamepadEmu.cpp
@@ -940,6 +940,10 @@ void InitPadLayout(TouchControlConfig *config, DeviceOrientation orientation, fl
int fast_forward_key_X = halfW;
int fast_forward_key_Y = screenBottom - bottom_button_Y * scale - 100;
initTouchPos(&config->touchFastForwardKey, fast_forward_key_X, fast_forward_key_Y);
+
+ int home_key_X = halfW;
+ int home_key_Y = screenBottom - bottom_button_Y * scale - 150;
+ initTouchPos(&config->touchHomeKey, home_key_X, home_key_Y);
} else {
int start_key_X = halfW + bottom_key_spacing * scale;
int start_key_Y = screenBottom - bottom_button_Y * scale;
@@ -949,6 +953,11 @@ void InitPadLayout(TouchControlConfig *config, DeviceOrientation orientation, fl
int select_key_Y = screenBottom - bottom_button_Y * scale;
initTouchPos(&config->touchSelectKey, select_key_X, select_key_Y);
+ // Above select.
+ int home_key_X = halfW;
+ int home_key_Y = select_key_Y - 50;
+ initTouchPos(&config->touchHomeKey, home_key_X, home_key_Y);
+
int fast_forward_key_X = halfW - bottom_key_spacing * scale;
int fast_forward_key_Y = screenBottom - bottom_button_Y * scale;
initTouchPos(&config->touchFastForwardKey, fast_forward_key_X, fast_forward_key_Y);
@@ -1076,6 +1085,7 @@ GamepadEmuView::GamepadEmuView(const TouchControlConfig &config, float xres, flo
addPSPButton(CTRL_START, "Start button", rectImage, ImageID("I_RECT"), ImageID("I_START"), config.touchStartKey);
addPSPButton(CTRL_SELECT, "Select button", rectImage, ImageID("I_RECT"), ImageID("I_SELECT"), config.touchSelectKey);
+ addPSPButton(CTRL_HOME, "Home button", rectImage, ImageID("I_RECT"), ImageID("I_HOME"), config.touchHomeKey);
BoolButton *fastForward = addBoolButton(&PSP_CoreParameter().fastForward, "Fast-forward button", rectImage, ImageID("I_RECT"), ImageID("I_FAST_FORWARD_LINE"), config.touchFastForwardKey);
if (fastForward) {
diff --git a/UI/GamepadEmu.h b/UI/GamepadEmu.h
index ce8463c100..64598bfe72 100644
--- a/UI/GamepadEmu.h
+++ b/UI/GamepadEmu.h
@@ -364,6 +364,7 @@ namespace CustomKeyData {
{ ImageID::invalid(), VIRTKEY_TOGGLE_DEBUGGER },
{ ImageID::invalid(), VIRTKEY_PAUSE_NO_MENU },
{ ImageID::invalid(), VIRTKEY_TOGGLE_TILT },
+ { ImageID("I_HOME"), CTRL_HOME },
// IMPORTANT: Only add at the end!
};
static_assert(ARRAY_SIZE(g_customKeyList) <= 64, "Too many key for a uint64_t bit mask");
@@ -421,6 +422,7 @@ namespace GestureKey {
VIRTKEY_AXIS_RIGHT_Y_MAX,
VIRTKEY_TOGGLE_DEBUGGER,
VIRTKEY_TOGGLE_TILT,
+ CTRL_HOME,
};
}
diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp
index 435915ff62..861395d6f7 100644
--- a/UI/ImDebugger/ImDebugger.cpp
+++ b/UI/ImDebugger/ImDebugger.cpp
@@ -2656,6 +2656,10 @@ ImDebugger::ImDebugger() {
}
ImDebugger::~ImDebugger() {
+ SaveConfig();
+}
+
+void ImDebugger::SaveConfig() {
cfg_.SaveConfig(ConfigPath());
}
diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h
index b79209b509..caf982a6b9 100644
--- a/UI/ImDebugger/ImDebugger.h
+++ b/UI/ImDebugger/ImDebugger.h
@@ -190,6 +190,7 @@ public:
externalCommand_ = cmd;
}
void DeviceLost();
+ void SaveConfig();
private:
Path ConfigPath();
diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp
index bbbf6cf386..6c3f2a73cb 100644
--- a/UI/NativeApp.cpp
+++ b/UI/NativeApp.cpp
@@ -436,6 +436,7 @@ void runImDebugger(Draw::DrawContext *draw) {
// Convert to drawlists.
ImGui::Render();
+ ImGui_ImplPlatform_SaveIni(false);
wantTextInput = io.WantTextInput;
} else {
uiContext->SetOverrideScreenFrame(nullptr);
@@ -1128,19 +1129,13 @@ void NativeShutdownGraphics(GraphicsContext *graphicsContext) {
gpu->DeviceLost();
}
+ // The imgui context and the debugger outlive the graphics, so that their state survives Android
+ // switching away from the app. Save now anyway, since the app may be killed in the background.
if (imguiInited_) {
- if (imDebugger_) {
- imDebugger_->DeviceLost();
- }
+ imDebugger_->DeviceLost();
+ imDebugger_->SaveConfig();
+ ImGui_ImplPlatform_SaveIni(true);
ImGui_ImplThin3d_DestroyDeviceObjects();
- ImGui_ImplThin3d_Shutdown();
- // Destroy the debugger here, while the things it refers to are still alive. If left to
- // static destruction, ~ImDisasmView runs after Core's globals are gone and its
- // g_disassemblyManager.clear() walks a destroyed map (and locks a destroyed mutex).
- imDebugger_.reset();
- ImGui::DestroyContext(ctx_);
- ctx_ = nullptr;
- imguiInited_ = false;
}
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
@@ -1979,6 +1974,19 @@ void NativeShutdown() {
g_screenManager = nullptr;
}
+ if (imguiInited_) {
+ ImGui_ImplPlatform_SaveIni(true);
+ // The device objects went with NativeShutdownGraphics.
+ ImGui_ImplThin3d_Shutdown();
+ // Destroy the debugger here, while the things it refers to are still alive. If left to
+ // static destruction, ~ImDisasmView runs after Core's globals are gone and its
+ // g_disassemblyManager.clear() walks a destroyed map (and locks a destroyed mutex).
+ imDebugger_.reset();
+ ImGui::DestroyContext(ctx_);
+ ctx_ = nullptr;
+ imguiInited_ = false;
+ }
+
System_Notify(SystemNotification::BEFORE_CONFIG_SAVE_ON_EXIT);
g_Config.Save("NativeShutdown");
diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp
index b50ad96733..c4b778f865 100644
--- a/UI/TouchControlLayoutScreen.cpp
+++ b/UI/TouchControlLayoutScreen.cpp
@@ -510,6 +510,7 @@ void ControlLayoutView::CreateViews() {
addDragDropButton(touch.touchSelectKey, "Select button", rectImage, ImageID("I_SELECT"));
addDragDropButton(touch.touchStartKey, "Start button", rectImage, ImageID("I_START"));
+ addDragDropButton(touch.touchHomeKey, "Home button", rectImage, ImageID("I_HOME"));
addDragDropButton(touch.touchFastForwardKey, "Fast-forward button", rectImage, ImageID("I_FAST_FORWARD_LINE"));
addDragDropButton(touch.touchLKey, "Left shoulder button", shoulderImage, ImageID("I_L"));
diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp
index fd9a5c4d0c..5a5d79c853 100644
--- a/UI/TouchControlVisibilityScreen.cpp
+++ b/UI/TouchControlVisibilityScreen.cpp
@@ -92,6 +92,7 @@ void TouchControlVisibilityScreen::CreateDialogViews(UI::ViewGroup *parent) {
toggles_.push_back({ "R", &touch.touchRKey.show, ImageID("I_R"), nullptr });
toggles_.push_back({ "Start", &touch.touchStartKey.show, ImageID("I_START"), nullptr });
toggles_.push_back({ "Select", &touch.touchSelectKey.show, ImageID("I_SELECT"), nullptr });
+ toggles_.push_back({ "Home", &touch.touchHomeKey.show, ImageID("I_HOME"), nullptr });
toggles_.push_back({ "Dpad", &touch.touchDpad.show, ImageID::invalid(), nullptr });
toggles_.push_back({ "Analog Stick", &touch.touchAnalogStick.show, ImageID::invalid(), nullptr });
toggles_.push_back({ "Right Analog Stick", &touch.touchRightAnalogStick.show, ImageID::invalid(), [=](EventParams &e) {
diff --git a/UI/UIAtlas.cpp b/UI/UIAtlas.cpp
index 75075085e1..e936252d76 100644
--- a/UI/UIAtlas.cpp
+++ b/UI/UIAtlas.cpp
@@ -57,6 +57,7 @@ static const ImageMeta g_uiImageIDs[] = {
{"I_TRIANGLE", true},
{"I_SELECT", true},
{"I_START", true},
+ {"I_HOME", true},
{"I_ARROW", false},
{"I_ROUND", false},
{"I_ROUND_LINE", true},
@@ -105,7 +106,7 @@ static const ImageMeta g_uiImageIDs[] = {
{"I_FULLSCREEN", false},
{"I_RESTORE", false},
{"I_SDCARD", false},
- {"I_HOME", false},
+ {"I_HOME_ICON", false},
{"I_A", true},
{"I_B", true},
{"I_C", true},
diff --git a/assets/ui_images/buttons.svg b/assets/ui_images/buttons.svg
index 09d60fab56..51e7c805c3 100644
--- a/assets/ui_images/buttons.svg
+++ b/assets/ui_images/buttons.svg
@@ -7,7 +7,7 @@
viewBox="0 0 158.74998 68.791658"
version="1.1"
id="svg1"
- inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
+ inkscape:version="1.4.4 (dcaf3e7, 2026-05-05)"
sodipodi:docname="buttons.svg"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
@@ -25,13 +25,13 @@
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
- inkscape:zoom="1"
- inkscape:cx="177"
- inkscape:cy="333.5"
- inkscape:window-width="1920"
- inkscape:window-height="1129"
- inkscape:window-x="-8"
- inkscape:window-y="-8"
+ inkscape:zoom="11.313708"
+ inkscape:cx="40.65864"
+ inkscape:cy="91.437746"
+ inkscape:window-width="3840"
+ inkscape:window-height="2071"
+ inkscape:window-x="-9"
+ inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showgrid="false"
@@ -618,7 +618,11 @@
d="m 27.680138,73.304133 v 1.108926 h 6.03687 v -1.108926 z m 0,2.174666 v 1.064811 h 6.03687 v -1.064811 z m 0,2.160271 v 1.079207 h 6.03687 V 77.63907 Z" />