From 02aaa3b2b90fd30ec9dfedc75d75d592dd3b13e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Jun 2026 09:51:59 +0200 Subject: [PATCH 1/6] Don't have pauseTrigger as an output parameter from ControlMapper::Key() --- Core/ControlMapper.cpp | 6 +++--- Core/ControlMapper.h | 19 +++++++++++++------ UI/ControlMappingScreen.cpp | 3 +-- UI/EmuScreen.cpp | 16 ++++++++++------ UI/EmuScreen.h | 1 + UI/PauseScreen.cpp | 5 ++--- 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index bc57961e6b..d939b3c64f 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -303,7 +303,7 @@ void ControlMapper::ReleaseAll() { Axis(axes.data(), axes.size());; for (const auto &key : keys) { - Key(key, nullptr); + Key(key); } } @@ -586,7 +586,7 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping, double no return keyInputUsed; } -bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { +bool ControlMapper::Key(const KeyInput &key) { double now = time_now_d(); InputMapping mapping(key.deviceId, key.keyCode); @@ -607,7 +607,7 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { bool mappingFound = KeyMap::InputMappingToPspButton(mapping, nullptr); DEBUG_LOG(Log::System, "Key: %d DeviceId: %d", key.keyCode, key.deviceId); if (!mappingFound || key.deviceId == DEVICE_ID_DEFAULT) { - *pauseTrigger = true; + pauseTrigger_ = true; return true; } } diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 5ededf210c..70a65fae04 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -1,13 +1,14 @@ #pragma once +#include +#include +#include +#include +#include + #include "Common/Input/InputState.h" #include "Core/KeyMap.h" -#include -#include -#include -#include - struct DisplayLayoutConfig; // Pure interface. @@ -32,7 +33,7 @@ public: // Inputs to the table-based mapping // These functions are free-threaded. - bool Key(const KeyInput &key, bool *pauseTrigger); + bool Key(const KeyInput &key); void Axis(const AxisInput *axes, size_t count); // Required callbacks. @@ -60,6 +61,10 @@ public: void GetDebugString(StringWriter &w) const; + bool PollPauseTrigger() { + return pauseTrigger_.exchange(false); + } + struct InputSample { float value; double timestamp; @@ -107,6 +112,8 @@ private: bool autoRotatingAnalogCW_ = false; bool autoRotatingAnalogCCW_ = false; + std::atomic pauseTrigger_{}; + bool swapAxes_ = false; int iInternalScreenRotationCached_ = 0; diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index c9d5516541..3e730cfa94 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -518,8 +518,7 @@ bool AnalogCalibrationScreen::key(const KeyInput &key) { bool retval = UIScreen::key(key); // Allow testing auto-rotation. If it collides with UI keys, too bad. - bool pauseTrigger = false; - g_controlMapper.Key(key, &pauseTrigger); + g_controlMapper.Key(key); if (UI::IsEscapeKey(key)) { TriggerFinish(DR_BACK); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index f4a5f99711..9024a925f1 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1189,7 +1189,7 @@ bool EmuScreen::UnsyncKey(const KeyInput &key) { if (mappingFound) { for (auto b : pspButtons) { if (b == VIRTKEY_TOGGLE_DEBUGGER || b == VIRTKEY_PAUSE) { - return g_controlMapper.Key(key, &pauseTrigger_); + return g_controlMapper.Key(key); } } } @@ -1199,28 +1199,28 @@ bool EmuScreen::UnsyncKey(const KeyInput &key) { switch (key.deviceId) { case DEVICE_ID_KEYBOARD: if (!ImGui::GetIO().WantCaptureKeyboard) { - g_controlMapper.Key(key, &pauseTrigger_); + g_controlMapper.Key(key); } break; case DEVICE_ID_MOUSE: if (!ImGui::GetIO().WantCaptureMouse) { - g_controlMapper.Key(key, &pauseTrigger_); + g_controlMapper.Key(key); } break; default: - g_controlMapper.Key(key, &pauseTrigger_); + g_controlMapper.Key(key); break; } } else { // Let up-events through to the controlMapper_ so input doesn't get stuck. if (key.flags & KeyInputFlags::UP) { - g_controlMapper.Key(key, &pauseTrigger_); + g_controlMapper.Key(key); } } return UIScreen::UnsyncKey(key); } - return g_controlMapper.Key(key, &pauseTrigger_); + return g_controlMapper.Key(key); } void EmuScreen::UnsyncAxis(const AxisInput *axes, size_t count) { @@ -1525,6 +1525,10 @@ void EmuScreen::update() { return; } + if (g_controlMapper.PollPauseTrigger()) { + pauseTrigger_ = true; + } + if (pauseTrigger_) { pauseTrigger_ = false; screenManager()->push(new GamePauseScreen(gamePath_, bootPending_)); diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 60459ca66f..37ea76325c 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -114,6 +114,7 @@ private: std::string errorMessage_; // If set, pauses at the end of the frame. + // We also poll the pause trigger from the ControlMapper. That needs refactoring. bool pauseTrigger_ = false; // The last read chat message count, and how many new ones there are. diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index 19543e30ce..2d11eefe00 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -378,9 +378,8 @@ GamePauseScreen::~GamePauseScreen() { bool GamePauseScreen::UnsyncKey(const KeyInput &key) { bool retval = UIScreen::UnsyncKey(key); - bool pauseTrigger = false; - retval = g_controlMapper.Key(key, &pauseTrigger) || retval; - if (pauseTrigger) { + retval = g_controlMapper.Key(key) || retval; + if (g_controlMapper.PollPauseTrigger()) { TriggerFinish(DR_BACK); } return retval; From 7b19253d05f167c31b2a087dbe86f5a70134a0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Jun 2026 09:56:37 +0200 Subject: [PATCH 2/6] Start moving logic out of "Unsync*" input handling --- UI/EmuScreen.cpp | 43 ++++++++++++++++++------------------------- UI/EmuScreen.h | 1 - 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 9024a925f1..36ef3e2a7e 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -730,31 +730,6 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) { } } -bool EmuScreen::UnsyncTouch(const TouchInput &touch) { - System_Notify(SystemNotification::ACTIVITY); - - bool ignoreGamepad = false; - - if (chatMenu_ && chatMenu_->GetVisibility() == UI::V_VISIBLE) { - // Avoid pressing touch button behind the chat - if (chatMenu_->Contains(touch.x, touch.y)) { - ignoreGamepad = true; - } - } - - if (touch.flags & TouchInputFlags::DOWN) { - if (!(g_Config.bShowImDebugger && imguiInited_) && !ignoreGamepad) { - // This just prevents the gamepad from timing out. - GamepadTouch(); - } - } - - if (root_) { - UIScreen::UnsyncTouch(touch); - } - return true; -} - // TODO: We should replace the "fpsLimit" system with a speed factor. static void ShowFpsLimitNotice() { int fpsLimit = 60; @@ -1253,6 +1228,24 @@ bool EmuScreen::key(const KeyInput &key) { } void EmuScreen::touch(const TouchInput &touch) { + System_Notify(SystemNotification::ACTIVITY); + + bool ignoreGamepad = false; + + if (chatMenu_ && chatMenu_->GetVisibility() == UI::V_VISIBLE) { + // Avoid pressing touch button behind the chat + if (chatMenu_->Contains(touch.x, touch.y)) { + ignoreGamepad = true; + } + } + + if (touch.flags & TouchInputFlags::DOWN) { + if (!(g_Config.bShowImDebugger && imguiInited_) && !ignoreGamepad) { + // This just prevents the gamepad from timing out. + GamepadTouch(); + } + } + if (g_Config.bShowImDebugger && imguiInited_) { ImGui_ImplPlatform_TouchEvent(touch); if (!ImGui::GetIO().WantCaptureMouse) { diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 37ea76325c..a51ac364a3 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -54,7 +54,6 @@ public: // Note: Unlike your average boring UIScreen, here we override the Unsync* functions // to get minimal latency and full control. We forward to UIScreen when needed. - bool UnsyncTouch(const TouchInput &touch) override; bool UnsyncKey(const KeyInput &key) override; void UnsyncAxis(const AxisInput *axes, size_t count) override; From 84ca42271b1ef3e9681f06f80dd36eed3d0cc074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Jun 2026 10:11:48 +0200 Subject: [PATCH 3/6] Quickfix for Steam Deck keyboard issue: Ask SDL not to spawn one when we ask for text input --- SDL/SDLMain.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 6853525fb7..e0ac66db85 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -1448,6 +1448,7 @@ int main(int argc, char *argv[]) { #endif putenv((char*)"SDL_VIDEO_CENTERED=1"); SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); + SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, "0"); #ifdef SDL_HINT_TOUCH_MOUSE_EVENTS SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); From 5ab3d6042ba60fddc655da0efc3731dffc1ce01c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Jun 2026 10:12:30 +0200 Subject: [PATCH 4/6] Move modifier key tracking out from the screen system --- Common/UI/UIScreen.cpp | 42 -------------------------- Common/UI/UIScreen.h | 18 ++---------- UI/EmuScreen.cpp | 4 +++ UI/EmuScreen.h | 2 ++ UI/NativeApp.cpp | 67 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 74 insertions(+), 59 deletions(-) diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index 694212f353..501272ddfa 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -130,52 +130,10 @@ bool UIScreen::UnsyncKey(const KeyInput &key) { } } - // Track modifier keys. - if (key.flags & KeyInputFlags::DOWN) { - switch (key.keyCode) { - case NKCODE_CTRL_LEFT: modifiersPressed_ |= Modifier::LCTRL; break; - case NKCODE_CTRL_RIGHT: modifiersPressed_ |= Modifier::RCTRL; break; - case NKCODE_SHIFT_LEFT: modifiersPressed_ |= Modifier::LSHIFT; break; - case NKCODE_SHIFT_RIGHT: modifiersPressed_ |= Modifier::RSHIFT; break; - case NKCODE_ALT_LEFT: modifiersPressed_ |= Modifier::LALT; break; - case NKCODE_ALT_RIGHT: modifiersPressed_ |= Modifier::RALT; break; - case NKCODE_META_LEFT: modifiersPressed_ |= Modifier::LMETA; break; - case NKCODE_META_RIGHT: modifiersPressed_ |= Modifier::RMETA; break; - default: - break; - } - } - if (key.flags & KeyInputFlags::UP) { - switch (key.keyCode) { - case NKCODE_CTRL_LEFT: modifiersPressed_ &= ~Modifier::LCTRL; break; - case NKCODE_CTRL_RIGHT: modifiersPressed_ &= ~Modifier::RCTRL; break; - case NKCODE_SHIFT_LEFT: modifiersPressed_ &= ~Modifier::LSHIFT; break; - case NKCODE_SHIFT_RIGHT: modifiersPressed_ &= ~Modifier::RSHIFT; break; - case NKCODE_ALT_LEFT: modifiersPressed_ &= ~Modifier::LALT; break; - case NKCODE_ALT_RIGHT: modifiersPressed_ &= ~Modifier::RALT; break; - case NKCODE_META_LEFT: modifiersPressed_ &= ~Modifier::LMETA; break; - case NKCODE_META_RIGHT: modifiersPressed_ &= ~Modifier::RMETA; break; - default: - break; - } - } - QueuedEvent ev{}; ev.type = QueuedEventType::KEY; ev.key = key; - if (modifiersPressed_ & (Modifier::LCTRL | Modifier::RCTRL)) { - ev.key.flags |= KeyInputFlags::MOD_CTRL; - } - if (modifiersPressed_ & (Modifier::LSHIFT | Modifier::RSHIFT)) { - ev.key.flags |= KeyInputFlags::MOD_SHIFT; - } - if (modifiersPressed_ & (Modifier::LALT | Modifier::RALT)) { - ev.key.flags |= KeyInputFlags::MOD_ALT; - } - if (modifiersPressed_ & (Modifier::LMETA | Modifier::RMETA)) { - ev.key.flags |= KeyInputFlags::MOD_META; - } std::lock_guard guard(eventQueueLock_); eventQueue_.push_back(ev); return retval; diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 046c4a9f1d..946cb75901 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -32,19 +32,6 @@ struct QueuedEvent { }; }; -enum class Modifier { - NONE = 0, - LCTRL = 1, - RCTRL = 2, - LSHIFT = 4, - RSHIFT = 8, - LALT = 16, - RALT = 32, - LMETA = 64, - RMETA = 128, -}; -ENUM_CLASS_BITOPS(Modifier); - class UIScreen : public Screen { public: UIScreen(); @@ -76,9 +63,10 @@ public: virtual void focusChanged(ScreenFocusChange focusChange) override { Screen::focusChanged(focusChange); - modifiersPressed_ = Modifier::NONE; } + virtual bool AllowKeyboardNavigation() const { return true; } + protected: virtual void CreateViews() = 0; @@ -108,8 +96,6 @@ protected: private: std::mutex eventQueueLock_; std::deque eventQueue_; - - Modifier modifiersPressed_{}; }; class UIDialogScreen : public UIScreen { diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 36ef3e2a7e..5b075b6065 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1134,6 +1134,10 @@ void EmuScreen::OnVKeyAnalog(VirtKey virtualKeyCode, float value) { limitMode = PSP_CoreParameter().analogFpsLimit == 60 ? FPSLimit::NORMAL : FPSLimit::ANALOG; } +bool EmuScreen::AllowKeyboardNavigation() const { + return true; +} + bool EmuScreen::UnsyncKey(const KeyInput &key) { System_Notify(SystemNotification::ACTIVITY); diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index a51ac364a3..dd84f1d116 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -69,6 +69,8 @@ public: } protected: + bool AllowKeyboardNavigation() const override; + void darken(); void focusChanged(ScreenFocusChange focusChange) override; ScreenRenderFlags PreRender(ScreenRenderMode mode) override; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 5a59ea51da..d4265aa738 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1369,6 +1369,20 @@ static void ProcessWheelRelease(InputKeyCode keyCode, double now, bool keyPress) } } +enum class Modifier { + NONE = 0, + LCTRL = 1, + RCTRL = 2, + LSHIFT = 4, + RSHIFT = 8, + LALT = 16, + RALT = 32, + LMETA = 64, + RMETA = 128, +}; +ENUM_CLASS_BITOPS(Modifier); +static Modifier g_modifiersPressed{}; + bool NativeKey(const KeyInput &key) { double now = time_now_d(); @@ -1429,8 +1443,59 @@ bool NativeKey(const KeyInput &key) { } HLEPlugins::SetKey(key.keyCode, (key.flags & KeyInputFlags::DOWN) ? 1 : 0); + + // Track and update modifiers. + + // Track modifier keys. + if (key.flags & KeyInputFlags::DOWN) { + switch (key.keyCode) { + case NKCODE_CTRL_LEFT: g_modifiersPressed |= Modifier::LCTRL; break; + case NKCODE_CTRL_RIGHT: g_modifiersPressed |= Modifier::RCTRL; break; + case NKCODE_SHIFT_LEFT: g_modifiersPressed |= Modifier::LSHIFT; break; + case NKCODE_SHIFT_RIGHT: g_modifiersPressed |= Modifier::RSHIFT; break; + case NKCODE_ALT_LEFT: g_modifiersPressed |= Modifier::LALT; break; + case NKCODE_ALT_RIGHT: g_modifiersPressed |= Modifier::RALT; break; + case NKCODE_META_LEFT: g_modifiersPressed |= Modifier::LMETA; break; + case NKCODE_META_RIGHT: g_modifiersPressed |= Modifier::RMETA; break; + default: + break; + } + } + if (key.flags & KeyInputFlags::UP) { + switch (key.keyCode) { + case NKCODE_CTRL_LEFT: g_modifiersPressed &= ~Modifier::LCTRL; break; + case NKCODE_CTRL_RIGHT: g_modifiersPressed &= ~Modifier::RCTRL; break; + case NKCODE_SHIFT_LEFT: g_modifiersPressed &= ~Modifier::LSHIFT; break; + case NKCODE_SHIFT_RIGHT: g_modifiersPressed &= ~Modifier::RSHIFT; break; + case NKCODE_ALT_LEFT: g_modifiersPressed &= ~Modifier::LALT; break; + case NKCODE_ALT_RIGHT: g_modifiersPressed &= ~Modifier::RALT; break; + case NKCODE_META_LEFT: g_modifiersPressed &= ~Modifier::LMETA; break; + case NKCODE_META_RIGHT: g_modifiersPressed &= ~Modifier::RMETA; break; + default: + break; + } + } + + KeyInputFlags modifierFlags{}; + + if (g_modifiersPressed & (Modifier::LCTRL | Modifier::RCTRL)) { + modifierFlags |= KeyInputFlags::MOD_CTRL; + } + if (g_modifiersPressed & (Modifier::LSHIFT | Modifier::RSHIFT)) { + modifierFlags |= KeyInputFlags::MOD_SHIFT; + } + if (g_modifiersPressed & (Modifier::LALT | Modifier::RALT)) { + modifierFlags |= KeyInputFlags::MOD_ALT; + } + if (g_modifiersPressed & (Modifier::LMETA | Modifier::RMETA)) { + modifierFlags |= KeyInputFlags::MOD_META; + } + + KeyInput modKey = key; + modKey.flags |= modifierFlags; + // Dispatch the key event. - bool retval = g_screenManager->key(key); + bool retval = g_screenManager->key(modKey); // The Mode key can have weird consequences on some devices, see #17245. if (key.keyCode == NKCODE_BUTTON_MODE) { From d0dd10ce6b33fdb559a3dd5c4399a7900879cb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Jun 2026 11:03:53 +0200 Subject: [PATCH 5/6] Avoid memsetting a non-POD struct (fixes compiler warning) --- Core/HLE/sceNetAdhocMatching.cpp | 2 +- unittest/TestLoongArch64Emitter.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceNetAdhocMatching.cpp b/Core/HLE/sceNetAdhocMatching.cpp index 14c6116a2a..d96b0d8c90 100644 --- a/Core/HLE/sceNetAdhocMatching.cpp +++ b/Core/HLE/sceNetAdhocMatching.cpp @@ -1784,7 +1784,7 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle getLocalMac(&localmac); // Clear Memory - memset(context, 0, sizeof(SceNetAdhocMatchingContext)); + context = {}; // Allocate Receive Buffer context->rxbuf = (uint8_t*)malloc(rxbuflen); diff --git a/unittest/TestLoongArch64Emitter.cpp b/unittest/TestLoongArch64Emitter.cpp index a01e122601..94436fb193 100644 --- a/unittest/TestLoongArch64Emitter.cpp +++ b/unittest/TestLoongArch64Emitter.cpp @@ -38,7 +38,7 @@ bool TestLoongArch64Emitter(){ cpu_info.LOONGARCH_LBT_MIPS = true; cpu_info.LOONGARCH_PTW = true; - u32 code[1024]; + u32 code[1024]{}; LoongArch64Emitter emitter((u8 *)code, (u8 *)code); emitter.ADD_W(R12, R13, R14); // t0, t1, t2 @@ -77,4 +77,4 @@ bool TestLoongArch64Emitter(){ } return true; -} \ No newline at end of file +} From edcc0763cb1e6564dd01274452f17dbeeb0bff12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Jun 2026 11:07:08 +0200 Subject: [PATCH 6/6] Mac buildfix --- SDL/SDLMain.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index e0ac66db85..f9e107683c 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -1448,7 +1448,9 @@ int main(int argc, char *argv[]) { #endif putenv((char*)"SDL_VIDEO_CENTERED=1"); SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); +#ifdef SDL_HINT_ENABLE_SCREEN_KEYBOARD SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, "0"); +#endif #ifdef SDL_HINT_TOUCH_MOUSE_EVENTS SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");