diff --git a/pcsx2/ImGui/FullscreenUI.cpp b/pcsx2/ImGui/FullscreenUI.cpp index 84d0880a6b..f6ceec8604 100644 --- a/pcsx2/ImGui/FullscreenUI.cpp +++ b/pcsx2/ImGui/FullscreenUI.cpp @@ -628,6 +628,14 @@ void FullscreenUI::ApplyConfirmSetting(const SettingsInterface* bsi) } } + // Check gamepad + const InputLayout layout = ImGuiFullscreen::GetGamepadLayout(); + if (layout == InputLayout::Nintendo) + { + io.ConfigNavSwapGamepadButtons = true; + return; + } + // X is confirm io.ConfigNavSwapGamepadButtons = false; return; @@ -642,6 +650,11 @@ void FullscreenUI::LocaleChanged() ApplyConfirmSetting(); } +void FullscreenUI::GamepadLayoutChanged() +{ + ApplyConfirmSetting(); +} + ////////////////////////////////////////////////////////////////////////// // Main ////////////////////////////////////////////////////////////////////////// diff --git a/pcsx2/ImGui/FullscreenUI.h b/pcsx2/ImGui/FullscreenUI.h index 99f6d2e0e4..5bff49b646 100644 --- a/pcsx2/ImGui/FullscreenUI.h +++ b/pcsx2/ImGui/FullscreenUI.h @@ -33,6 +33,7 @@ namespace FullscreenUI void ReturnToMainWindow(); void SetStandardSelectionFooterText(bool back_instead_of_cancel); void LocaleChanged(); + void GamepadLayoutChanged(); void Shutdown(bool clear_state); void Render(); diff --git a/pcsx2/ImGui/ImGuiFullscreen.cpp b/pcsx2/ImGui/ImGuiFullscreen.cpp index fb7c8b10ca..877ad435ea 100644 --- a/pcsx2/ImGui/ImGuiFullscreen.cpp +++ b/pcsx2/ImGui/ImGuiFullscreen.cpp @@ -7,9 +7,11 @@ #include "Host.h" #include "GS/Renderers/Common/GSDevice.h" #include "GS/Renderers/Common/GSTexture.h" +#include "ImGui/FullscreenUI.h" #include "ImGui/ImGuiAnimated.h" #include "ImGui/ImGuiFullscreen.h" #include "ImGui/ImGuiManager.h" +#include "Input/InputManager.h" #include "common/Assertions.h" #include "common/Console.h" @@ -192,6 +194,8 @@ namespace ImGuiFullscreen static std::vector s_background_progress_dialogs; static std::mutex s_background_progress_lock; + + static InputLayout s_gamepad_layout = InputLayout::Unknown; } // namespace ImGuiFullscreen void ImGuiFullscreen::SetFonts(ImFont* standard_font, ImFont* medium_font, ImFont* large_font) @@ -738,6 +742,20 @@ bool ImGuiFullscreen::IsGamepadInputSource() return (ImGui::GetCurrentContext()->NavInputSource == ImGuiInputSource_Gamepad); } +void ImGuiFullscreen::ReportGamepadLayout(InputLayout layout) +{ + if (s_gamepad_layout == layout) + return; + + s_gamepad_layout = layout; + FullscreenUI::GamepadLayoutChanged(); +} + +InputLayout ImGuiFullscreen::GetGamepadLayout() +{ + return s_gamepad_layout; +} + void ImGuiFullscreen::CreateFooterTextString(SmallStringBase& dest, std::span> items) { diff --git a/pcsx2/ImGui/ImGuiFullscreen.h b/pcsx2/ImGui/ImGuiFullscreen.h index 3510ac1219..65d0dd6fc7 100644 --- a/pcsx2/ImGui/ImGuiFullscreen.h +++ b/pcsx2/ImGui/ImGuiFullscreen.h @@ -18,6 +18,7 @@ #include class GSTexture; +enum class InputLayout : u8; namespace ImGuiFullscreen { @@ -149,6 +150,8 @@ namespace ImGuiFullscreen void EndFullscreenWindow(); bool IsGamepadInputSource(); + void ReportGamepadLayout(InputLayout layout); + InputLayout GetGamepadLayout(); void CreateFooterTextString(SmallStringBase& dest, std::span> items); void SetFullscreenFooterText(std::string_view text); void SetFullscreenFooterText(std::span> items); diff --git a/pcsx2/ImGui/ImGuiManager.cpp b/pcsx2/ImGui/ImGuiManager.cpp index 41c3963017..eaa1c7e93f 100644 --- a/pcsx2/ImGui/ImGuiManager.cpp +++ b/pcsx2/ImGui/ImGuiManager.cpp @@ -921,7 +921,7 @@ bool ImGuiManager::ProcessHostKeyEvent(InputBindingKey key, float value) return s_imgui_wants_keyboard.load(std::memory_order_acquire); } -bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, float value) +bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, InputLayout layout, float value) { static constexpr ImGuiKey key_map[] = { ImGuiKey_None, // Unknown, @@ -959,7 +959,10 @@ bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, float value return false; MTGS::RunOnGSThread( - [key = key_map[static_cast(key)], value]() { ImGui::GetIO().AddKeyAnalogEvent(key, (value > 0.0f), value); }); + [key = key_map[static_cast(key)], value, layout]() { + ImGuiFullscreen::ReportGamepadLayout(layout); + ImGui::GetIO().AddKeyAnalogEvent(key, (value > 0.0f), value); + }); return s_imgui_wants_keyboard.load(std::memory_order_acquire); } diff --git a/pcsx2/ImGui/ImGuiManager.h b/pcsx2/ImGui/ImGuiManager.h index f3acb779b5..8b5024e66c 100644 --- a/pcsx2/ImGui/ImGuiManager.h +++ b/pcsx2/ImGui/ImGuiManager.h @@ -12,6 +12,7 @@ struct ImFont; union InputBindingKey; enum class GenericInputBinding : u8; +enum class InputLayout : u8; namespace ImGuiManager { @@ -94,7 +95,7 @@ namespace ImGuiManager bool ProcessHostKeyEvent(InputBindingKey key, float value); /// Called on the CPU thread when any input event fires. Allows imgui to take over controller navigation. - bool ProcessGenericInputEvent(GenericInputBinding key, float value); + bool ProcessGenericInputEvent(GenericInputBinding key, InputLayout layout, float value); /// Sets an image and scale for a software cursor. Software cursors can be used for things like crosshairs. void SetSoftwareCursor(u32 index, std::string image_path, float image_scale, u32 multiply_color = 0xFFFFFF); diff --git a/pcsx2/Input/DInputSource.cpp b/pcsx2/Input/DInputSource.cpp index 71420b1c0e..33a18be0a9 100644 --- a/pcsx2/Input/DInputSource.cpp +++ b/pcsx2/Input/DInputSource.cpp @@ -315,6 +315,11 @@ bool DInputSource::GetGenericBindingMapping(const std::string_view device, Input return {}; } +InputLayout DInputSource::GetControllerLayout(u32 index) +{ + return InputLayout::Unknown; +} + void DInputSource::UpdateMotorState(InputBindingKey key, float intensity) { // not supported diff --git a/pcsx2/Input/DInputSource.h b/pcsx2/Input/DInputSource.h index 9bd4f89f2f..306cf4b258 100644 --- a/pcsx2/Input/DInputSource.h +++ b/pcsx2/Input/DInputSource.h @@ -44,6 +44,7 @@ public: std::vector> EnumerateDevices() override; std::vector EnumerateMotors() override; bool GetGenericBindingMapping(const std::string_view device, InputManager::GenericInputBindingMapping* mapping) override; + InputLayout GetControllerLayout(u32 index) override; void UpdateMotorState(InputBindingKey key, float intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; diff --git a/pcsx2/Input/InputManager.cpp b/pcsx2/Input/InputManager.cpp index e0723d0a3e..44db1c6d28 100644 --- a/pcsx2/Input/InputManager.cpp +++ b/pcsx2/Input/InputManager.cpp @@ -1263,7 +1263,8 @@ bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInpu } else if (generic_key != GenericInputBinding::Unknown) { - if (ImGuiManager::ProcessGenericInputEvent(generic_key, value) && value != 0.0f) + InputLayout layout = s_input_sources[static_cast(InputSourceType::SDL)]->GetControllerLayout(key.source_index); + if (ImGuiManager::ProcessGenericInputEvent(generic_key, layout, value) && value != 0.0f) return true; } diff --git a/pcsx2/Input/InputManager.h b/pcsx2/Input/InputManager.h index 0818855699..370b494f32 100644 --- a/pcsx2/Input/InputManager.h +++ b/pcsx2/Input/InputManager.h @@ -46,6 +46,15 @@ enum class InputSubclass : u32 ControllerHaptic = 4, }; +/// Layout of the source controller +enum class InputLayout : u8 +{ + Unknown, + Xbox, + Playstation, + Nintendo +}; + enum class InputModifier : u32 { None = 0, diff --git a/pcsx2/Input/InputSource.h b/pcsx2/Input/InputSource.h index c5ecf6bcee..ab66858e0b 100644 --- a/pcsx2/Input/InputSource.h +++ b/pcsx2/Input/InputSource.h @@ -43,6 +43,9 @@ public: /// Returns false if it's not one of our devices. virtual bool GetGenericBindingMapping(const std::string_view device, InputManager::GenericInputBindingMapping* mapping) = 0; + /// Gets the layout of the controller connected at index. + virtual InputLayout GetControllerLayout(u32 index) = 0; + /// Informs the source of a new vibration motor state. Changes may not take effect until the next PollEvents() call. virtual void UpdateMotorState(InputBindingKey key, float intensity) = 0; diff --git a/pcsx2/Input/SDLInputSource.cpp b/pcsx2/Input/SDLInputSource.cpp index e9e7d0b8d4..240abb8cd9 100644 --- a/pcsx2/Input/SDLInputSource.cpp +++ b/pcsx2/Input/SDLInputSource.cpp @@ -1519,6 +1519,25 @@ bool SDLInputSource::GetGenericBindingMapping(const std::string_view device, Inp } } +InputLayout SDLInputSource::GetControllerLayout(u32 index) +{ + auto it = GetControllerDataForPlayerId(index); + if (it == m_controllers.end()) + return InputLayout::Unknown; + + // Infer layout based on face button label to avoid having + // to maintain a long switch statement of gamepad types + // clang-format off + switch (SDL_GetGamepadButtonLabel(it->gamepad, SDL_GAMEPAD_BUTTON_EAST)) + { + case SDL_GAMEPAD_BUTTON_LABEL_B: return InputLayout::Xbox; + case SDL_GAMEPAD_BUTTON_LABEL_A: return InputLayout::Nintendo; + case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE: return InputLayout::Playstation; + default: return InputLayout::Unknown; + } + // clang-format on +} + void SDLInputSource::UpdateMotorState(InputBindingKey key, float intensity) { if (key.source_subtype != InputSubclass::ControllerMotor && key.source_subtype != InputSubclass::ControllerHaptic) diff --git a/pcsx2/Input/SDLInputSource.h b/pcsx2/Input/SDLInputSource.h index 1085327eae..120597899a 100644 --- a/pcsx2/Input/SDLInputSource.h +++ b/pcsx2/Input/SDLInputSource.h @@ -32,6 +32,7 @@ public: std::vector> EnumerateDevices() override; std::vector EnumerateMotors() override; bool GetGenericBindingMapping(const std::string_view device, InputManager::GenericInputBindingMapping* mapping) override; + InputLayout GetControllerLayout(u32 index) override; void UpdateMotorState(InputBindingKey key, float intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; diff --git a/pcsx2/Input/XInputSource.cpp b/pcsx2/Input/XInputSource.cpp index b4dca34924..69fccbe837 100644 --- a/pcsx2/Input/XInputSource.cpp +++ b/pcsx2/Input/XInputSource.cpp @@ -466,6 +466,11 @@ bool XInputSource::GetGenericBindingMapping(const std::string_view device, Input return true; } +InputLayout XInputSource::GetControllerLayout(u32 index) +{ + return InputLayout::Xbox; +} + void XInputSource::HandleControllerConnection(u32 index) { INFO_LOG("XInput controller {} connected.", index); diff --git a/pcsx2/Input/XInputSource.h b/pcsx2/Input/XInputSource.h index 668dd3b9af..99a9886858 100644 --- a/pcsx2/Input/XInputSource.h +++ b/pcsx2/Input/XInputSource.h @@ -81,6 +81,7 @@ public: std::vector> EnumerateDevices() override; std::vector EnumerateMotors() override; bool GetGenericBindingMapping(const std::string_view device, InputManager::GenericInputBindingMapping* mapping) override; + InputLayout GetControllerLayout(u32 index) override; void UpdateMotorState(InputBindingKey key, float intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override;