FSUI: Automatic "Swap OK/Cancel" will now swap with switch controllers

This commit is contained in:
TheLastRar 2025-03-05 21:55:05 +00:00 committed by Ty
parent ec047c5972
commit c957b558e0
15 changed files with 88 additions and 4 deletions

View File

@ -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
//////////////////////////////////////////////////////////////////////////

View File

@ -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();

View File

@ -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<BackgroundProgressDialogData> 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<const std::pair<const char*, std::string_view>> items)
{

View File

@ -18,6 +18,7 @@
#include <vector>
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<const std::pair<const char*, std::string_view>> items);
void SetFullscreenFooterText(std::string_view text);
void SetFullscreenFooterText(std::span<const std::pair<const char*, std::string_view>> items);

View File

@ -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<u32>(key)], value]() { ImGui::GetIO().AddKeyAnalogEvent(key, (value > 0.0f), value); });
[key = key_map[static_cast<u32>(key)], value, layout]() {
ImGuiFullscreen::ReportGamepadLayout(layout);
ImGui::GetIO().AddKeyAnalogEvent(key, (value > 0.0f), value);
});
return s_imgui_wants_keyboard.load(std::memory_order_acquire);
}

View File

@ -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);

View File

@ -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

View File

@ -44,6 +44,7 @@ public:
std::vector<std::pair<std::string, std::string>> EnumerateDevices() override;
std::vector<InputBindingKey> 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;

View File

@ -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<u32>(InputSourceType::SDL)]->GetControllerLayout(key.source_index);
if (ImGuiManager::ProcessGenericInputEvent(generic_key, layout, value) && value != 0.0f)
return true;
}

View File

@ -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,

View File

@ -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;

View File

@ -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)

View File

@ -32,6 +32,7 @@ public:
std::vector<std::pair<std::string, std::string>> EnumerateDevices() override;
std::vector<InputBindingKey> 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;

View File

@ -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);

View File

@ -81,6 +81,7 @@ public:
std::vector<std::pair<std::string, std::string>> EnumerateDevices() override;
std::vector<InputBindingKey> 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;