mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-12 09:24:33 +00:00
Use DualSense Mic Mute LED for Analog Mode (#3574)
* Use DualSense Mic Mute LED for Analog Mode * Fix function casing and move function call outside loop * Refactored code to use binds - `InputManager` no longer uses API specific logic - The Mic Mute LED gets bound as `ModeLED = SDL-0/ModeLED` only for DualSense controllers - Changed DualSense detection to use Vendor & Product ID
This commit is contained in:
committed by
GitHub
parent
c790972265
commit
51942df7dd
@@ -29,7 +29,10 @@ AnalogController::AnalogController(u32 index) : Controller(index)
|
||||
m_rumble_config.fill(0xFF);
|
||||
}
|
||||
|
||||
AnalogController::~AnalogController() = default;
|
||||
AnalogController::~AnalogController()
|
||||
{
|
||||
InputManager::SetPadModeLED(m_index, false);
|
||||
}
|
||||
|
||||
ControllerType AnalogController::GetType() const
|
||||
{
|
||||
@@ -339,6 +342,8 @@ void AnalogController::SetAnalogMode(bool enabled, bool show_message)
|
||||
|
||||
m_analog_mode = enabled;
|
||||
|
||||
InputManager::SetPadModeLED(m_index, enabled);
|
||||
|
||||
INFO_LOG("Controller {} switched to {} mode.", m_index + 1u, m_analog_mode ? "analog" : "digital");
|
||||
if (show_message)
|
||||
{
|
||||
@@ -785,6 +790,8 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
genb}
|
||||
#define MOTOR(name, display_name, icon_name, index, genb) \
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::Motor, genb}
|
||||
#define MODE_LED(name, display_name, icon_name, index, genb) \
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::ModeLED, genb}
|
||||
|
||||
// clang-format off
|
||||
BUTTON("Up", TRANSLATE_NOOP("AnalogController", "D-Pad Up"), ICON_PF_DPAD_UP, AnalogController::Button::Up, GenericInputBinding::DPadUp),
|
||||
@@ -816,11 +823,14 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
|
||||
MOTOR("LargeMotor", TRANSLATE_NOOP("AnalogController", "Large Motor"), ICON_PF_VIBRATION_L, 0, GenericInputBinding::LargeMotor),
|
||||
MOTOR("SmallMotor", TRANSLATE_NOOP("AnalogController", "Small Motor"), ICON_PF_VIBRATION, 1, GenericInputBinding::SmallMotor),
|
||||
|
||||
MODE_LED("ModeLED", TRANSLATE_NOOP("AnalogController", "Mode LED"), ICON_PF_ANALOG_LEFT_RIGHT, 0, GenericInputBinding::ModeLED),
|
||||
// clang-format on
|
||||
|
||||
#undef MOTOR
|
||||
#undef AXIS
|
||||
#undef BUTTON
|
||||
#undef MODE_LED
|
||||
};
|
||||
|
||||
static constexpr const char* s_invert_settings[] = {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "system.h"
|
||||
|
||||
#include "util/imgui_manager.h"
|
||||
#include "util/input_manager.h"
|
||||
#include "util/state_wrapper.h"
|
||||
|
||||
#include "common/bitutils.h"
|
||||
@@ -25,7 +26,10 @@ AnalogJoystick::AnalogJoystick(u32 index) : Controller(index)
|
||||
Reset();
|
||||
}
|
||||
|
||||
AnalogJoystick::~AnalogJoystick() = default;
|
||||
AnalogJoystick::~AnalogJoystick()
|
||||
{
|
||||
InputManager::SetPadModeLED(m_index, false);
|
||||
}
|
||||
|
||||
ControllerType AnalogJoystick::GetType() const
|
||||
{
|
||||
@@ -40,6 +44,7 @@ bool AnalogJoystick::InAnalogMode() const
|
||||
void AnalogJoystick::Reset()
|
||||
{
|
||||
m_transfer_state = TransferState::Idle;
|
||||
InputManager::SetPadModeLED(m_index, m_analog_mode);
|
||||
}
|
||||
|
||||
bool AnalogJoystick::DoState(StateWrapper& sw, bool apply_input_state)
|
||||
@@ -240,6 +245,8 @@ void AnalogJoystick::ToggleAnalogMode()
|
||||
{
|
||||
m_analog_mode = !m_analog_mode;
|
||||
|
||||
InputManager::SetPadModeLED(m_index, m_analog_mode);
|
||||
|
||||
INFO_LOG("Joystick {} switched to {} mode.", m_index + 1u, m_analog_mode ? "analog" : "digital");
|
||||
Host::AddIconOSDMessage(
|
||||
fmt::format("analog_mode_toggle_{}", m_index), ICON_FA_GAMEPAD,
|
||||
@@ -349,6 +356,8 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
static_cast<u32>(AnalogJoystick::Button::Count) + static_cast<u32>(halfaxis), \
|
||||
InputBindingInfo::Type::HalfAxis, \
|
||||
genb}
|
||||
#define MODE_LED(name, display_name, icon_name, index, genb) \
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::ModeLED, genb}
|
||||
|
||||
// clang-format off
|
||||
BUTTON("Up", TRANSLATE_NOOP("AnalogJoystick", "D-Pad Up"), ICON_PF_DPAD_UP, AnalogJoystick::Button::Up, GenericInputBinding::DPadUp),
|
||||
@@ -377,10 +386,13 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
AXIS("RRight", TRANSLATE_NOOP("AnalogJoystick", "Right Stick Right"), ICON_PF_RIGHT_ANALOG_RIGHT, AnalogJoystick::HalfAxis::RRight, GenericInputBinding::RightStickRight),
|
||||
AXIS("RDown", TRANSLATE_NOOP("AnalogJoystick", "Right Stick Down"), ICON_PF_RIGHT_ANALOG_DOWN, AnalogJoystick::HalfAxis::RDown, GenericInputBinding::RightStickDown),
|
||||
AXIS("RUp", TRANSLATE_NOOP("AnalogJoystick", "Right Stick Up"), ICON_PF_RIGHT_ANALOG_UP, AnalogJoystick::HalfAxis::RUp, GenericInputBinding::RightStickUp),
|
||||
|
||||
MODE_LED("ModeLED", TRANSLATE_NOOP("AnalogJoystick", "Mode LED"), ICON_PF_ANALOG_LEFT_RIGHT, 0, GenericInputBinding::ModeLED),
|
||||
// clang-format on
|
||||
|
||||
#undef AXIS
|
||||
#undef BUTTON
|
||||
#undef MODE_LED
|
||||
};
|
||||
|
||||
static constexpr const char* s_invert_settings[] = {
|
||||
|
||||
@@ -4868,6 +4868,10 @@ void FullscreenUI::DrawControllerSettingsPage()
|
||||
FSUI_VSTR("Enable/Disable the Player LED on DualSense controllers."), "InputSources",
|
||||
"SDLPS5PlayerLED", false, bsi->GetBoolValue("InputSources", "SDLControllerEnhancedMode", true),
|
||||
false);
|
||||
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_LIGHTBULB, "SDL DualSense Mic Mute LED for Analog Mode"),
|
||||
FSUI_VSTR("Enable/Disable using the DualSense controller's Mic Mute LED to indicate when Analog Mode is active."), "InputSources",
|
||||
"SDLPS5MicMuteLEDForAnalogMode", false, bsi->GetBoolValue("InputSources", "SDLControllerEnhancedMode", true),
|
||||
false);
|
||||
#ifdef _WIN32
|
||||
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_GEAR, "Enable XInput Input Source"),
|
||||
FSUI_VSTR("Support for controllers that use the XInput protocol. XInput should only be used if you "
|
||||
@@ -9765,6 +9769,7 @@ TRANSLATE_NOOP("FullscreenUI", "Enable VRAM Write Replacement");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enable XInput Input Source");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enable debugging when supported by the host's renderer API. Only for developer use.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enable/Disable the Player LED on DualSense controllers.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enable/Disable using the DualSense controller's Mic Mute LED to indicate when Analog Mode is active.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables alignment and bus exceptions. Not needed for any known games.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables an additional 6MB of RAM to obtain a total of 2+6 = 8MB, usually present on dev consoles.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enables an additional three controller slots on each port. Not supported in all games.");
|
||||
@@ -10053,6 +10058,7 @@ TRANSLATE_NOOP("FullscreenUI", "Runahead");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Runahead/Rewind");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Runs the software renderer in parallel for VRAM readbacks. On some systems, this may result in greater performance when using graphical enhancements with the hardware renderer.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "SDL DualSense Player LED");
|
||||
TRANSLATE_NOOP("FullscreenUI", "SDL DualSense Mic Mute LED for Analog Mode");
|
||||
TRANSLATE_NOOP("FullscreenUI", "SDL DualShock 4 / DualSense Enhanced Mode");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Safe Mode");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Save Controller Preset");
|
||||
|
||||
@@ -15,6 +15,7 @@ struct InputBindingInfo
|
||||
Axis,
|
||||
HalfAxis,
|
||||
Motor,
|
||||
ModeLED,
|
||||
Pointer, // Absolute pointer, does not receive any events, but is queryable.
|
||||
RelativePointer, // Receive relative mouse movement events, bind_index is offset by the axis.
|
||||
Device, // Used for special-purpose device selection, e.g. force feedback.
|
||||
@@ -68,5 +69,7 @@ enum class GenericInputBinding : u8
|
||||
LargeMotor, // Low frequency vibration.
|
||||
SmallMotor, // High frequency vibration.
|
||||
|
||||
ModeLED, // Indicates Digital/Analog mode.
|
||||
|
||||
Count,
|
||||
};
|
||||
|
||||
@@ -24,17 +24,32 @@ JogCon::JogCon(u32 index) : Controller(index)
|
||||
{
|
||||
}
|
||||
|
||||
JogCon::~JogCon() = default;
|
||||
JogCon::~JogCon()
|
||||
{
|
||||
InputManager::SetPadModeLED(m_index, false);
|
||||
}
|
||||
|
||||
ControllerType JogCon::GetType() const
|
||||
{
|
||||
return ControllerType::JogCon;
|
||||
}
|
||||
|
||||
bool JogCon::InAnalogMode() const
|
||||
{
|
||||
// JogCon uses JogCon mode
|
||||
return InJogConMode();
|
||||
}
|
||||
|
||||
bool JogCon::InJogConMode() const
|
||||
{
|
||||
return m_jogcon_mode;
|
||||
}
|
||||
|
||||
void JogCon::Reset()
|
||||
{
|
||||
// Reset starts in jogcon mode?
|
||||
m_jogcon_mode = true;
|
||||
InputManager::SetPadModeLED(m_index, m_jogcon_mode);
|
||||
ResetTransferState();
|
||||
ResetMotorConfig();
|
||||
}
|
||||
@@ -184,6 +199,8 @@ void JogCon::SetJogConMode(bool enabled, bool show_message)
|
||||
m_jogcon_mode = enabled;
|
||||
m_configuration_mode = enabled && m_configuration_mode;
|
||||
|
||||
InputManager::SetPadModeLED(m_index, enabled);
|
||||
|
||||
INFO_LOG("Controller {} switched to {} mode.", m_index + 1u, m_jogcon_mode ? "JogCon" : "Digital");
|
||||
if (show_message)
|
||||
{
|
||||
@@ -624,6 +641,8 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
static_cast<u32>(JogCon::Button::MaxCount) + static_cast<u32>(halfaxis), \
|
||||
InputBindingInfo::Type::HalfAxis, \
|
||||
genb}
|
||||
#define MODE_LED(name, display_name, icon_name, index, genb) \
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::ModeLED, genb}
|
||||
|
||||
// clang-format off
|
||||
BUTTON("Up", TRANSLATE_NOOP("JogCon", "D-Pad Up"), ICON_PF_DPAD_UP, JogCon::Button::Up, GenericInputBinding::DPadUp),
|
||||
@@ -645,6 +664,7 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
AXIS("SteeringLeft", TRANSLATE_NOOP("JogCon", "Steering Left"), ICON_PF_ANALOG_LEFT, JogCon::HalfAxis::SteeringLeft, GenericInputBinding::LeftStickLeft),
|
||||
AXIS("SteeringRight", TRANSLATE_NOOP("JogCon", "Steering Right"), ICON_PF_ANALOG_RIGHT, JogCon::HalfAxis::SteeringRight, GenericInputBinding::LeftStickRight),
|
||||
|
||||
MODE_LED("ModeLED", TRANSLATE_NOOP("JogCon", "Mode LED"), ICON_PF_ANALOG_LEFT_RIGHT, 0, GenericInputBinding::ModeLED),
|
||||
// clang-format on
|
||||
|
||||
{"Motor", TRANSLATE_NOOP("JogCon", "Vibration Motor"), ICON_PF_VIBRATION, 0u, InputBindingInfo::Type::Motor,
|
||||
@@ -656,6 +676,7 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
|
||||
#undef BUTTON
|
||||
#undef AXIS
|
||||
#undef MODE_LED
|
||||
};
|
||||
|
||||
static const SettingInfo s_settings[] = {
|
||||
|
||||
@@ -49,6 +49,8 @@ public:
|
||||
static std::unique_ptr<JogCon> Create(u32 index);
|
||||
|
||||
ControllerType GetType() const override;
|
||||
bool InAnalogMode() const override;
|
||||
bool InJogConMode() const;
|
||||
|
||||
void Reset() override;
|
||||
bool DoState(StateWrapper& sw, bool apply_input_state) override;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "host.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "util/input_manager.h"
|
||||
#include "util/state_wrapper.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -26,16 +27,26 @@ NeGcon::NeGcon(u32 index) : Controller(index)
|
||||
m_axis_state[static_cast<u8>(Axis::Steering)] = 0x80;
|
||||
}
|
||||
|
||||
NeGcon::~NeGcon() = default;
|
||||
NeGcon::~NeGcon()
|
||||
{
|
||||
InputManager::SetPadModeLED(m_index, false);
|
||||
}
|
||||
|
||||
ControllerType NeGcon::GetType() const
|
||||
{
|
||||
return ControllerType::NeGcon;
|
||||
}
|
||||
|
||||
bool NeGcon::InAnalogMode() const
|
||||
{
|
||||
// NeGcon is always analog
|
||||
return true;
|
||||
}
|
||||
|
||||
void NeGcon::Reset()
|
||||
{
|
||||
m_transfer_state = TransferState::Idle;
|
||||
InputManager::SetPadModeLED(m_index, true);
|
||||
}
|
||||
|
||||
bool NeGcon::DoState(StateWrapper& sw, bool apply_input_state)
|
||||
@@ -270,6 +281,8 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
static_cast<u32>(NeGcon::Button::Count) + static_cast<u32>(halfaxis), \
|
||||
InputBindingInfo::Type::HalfAxis, \
|
||||
genb}
|
||||
#define MODE_LED(name, display_name, icon_name, index, genb) \
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::ModeLED, genb}
|
||||
|
||||
// clang-format off
|
||||
BUTTON("Up", TRANSLATE_NOOP("NeGcon", "D-Pad Up"), ICON_PF_DPAD_UP, NeGcon::Button::Up, GenericInputBinding::DPadUp),
|
||||
@@ -285,10 +298,13 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
BUTTON("R", TRANSLATE_NOOP("NeGcon", "Right Trigger"), ICON_PF_RIGHT_TRIGGER_RT, NeGcon::Button::R, GenericInputBinding::R1),
|
||||
AXIS("SteeringLeft", TRANSLATE_NOOP("NeGcon", "Steering (Twist) Left"), ICON_PF_ANALOG_LEFT, NeGcon::HalfAxis::SteeringLeft, GenericInputBinding::LeftStickLeft),
|
||||
AXIS("SteeringRight", TRANSLATE_NOOP("NeGcon", "Steering (Twist) Right"), ICON_PF_ANALOG_RIGHT, NeGcon::HalfAxis::SteeringRight, GenericInputBinding::LeftStickRight),
|
||||
|
||||
MODE_LED("ModeLED", TRANSLATE_NOOP("NeGcon", "Mode LED"), ICON_PF_ANALOG_LEFT_RIGHT, 0, GenericInputBinding::ModeLED),
|
||||
// clang-format on
|
||||
|
||||
#undef AXIS
|
||||
#undef BUTTON
|
||||
#undef MODE_LED
|
||||
};
|
||||
|
||||
static const SettingInfo s_settings[] = {
|
||||
|
||||
@@ -87,6 +87,7 @@ public:
|
||||
static std::unique_ptr<NeGcon> Create(u32 index);
|
||||
|
||||
ControllerType GetType() const override;
|
||||
bool InAnalogMode() const override;
|
||||
|
||||
void Reset() override;
|
||||
bool DoState(StateWrapper& sw, bool apply_input_state) override;
|
||||
|
||||
@@ -36,7 +36,10 @@ NeGconRumble::NeGconRumble(u32 index) : Controller(index)
|
||||
m_rumble_config.fill(0xFF);
|
||||
}
|
||||
|
||||
NeGconRumble::~NeGconRumble() = default;
|
||||
NeGconRumble::~NeGconRumble()
|
||||
{
|
||||
InputManager::SetPadModeLED(m_index, false);
|
||||
}
|
||||
|
||||
ControllerType NeGconRumble::GetType() const
|
||||
{
|
||||
@@ -235,6 +238,8 @@ void NeGconRumble::SetAnalogMode(bool enabled, bool show_message)
|
||||
if (m_analog_mode == enabled)
|
||||
return;
|
||||
|
||||
InputManager::SetPadModeLED(m_index, enabled);
|
||||
|
||||
INFO_LOG("Controller {} switched to {} mode.", m_index + 1u, m_analog_mode ? "analog" : "digital");
|
||||
if (show_message)
|
||||
{
|
||||
@@ -725,6 +730,8 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
genb}
|
||||
#define MOTOR(name, display_name, icon_name, index, genb) \
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::Motor, genb}
|
||||
#define MODE_LED(name, display_name, icon_name, index, genb) \
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::ModeLED, genb}
|
||||
|
||||
// clang-format off
|
||||
BUTTON("Up", TRANSLATE_NOOP("NeGconRumble", "D-Pad Up"), ICON_PF_DPAD_UP, NeGconRumble::Button::Up, GenericInputBinding::DPadUp),
|
||||
@@ -744,6 +751,8 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
|
||||
MOTOR("LargeMotor", TRANSLATE_NOOP("AnalogController", "Large Motor"), ICON_PF_VIBRATION_L, 0, GenericInputBinding::LargeMotor),
|
||||
MOTOR("SmallMotor", TRANSLATE_NOOP("AnalogController", "Small Motor"), ICON_PF_VIBRATION, 1, GenericInputBinding::SmallMotor),
|
||||
|
||||
MODE_LED("ModeLED", TRANSLATE_NOOP("AnalogController", "Mode LED"), ICON_PF_ANALOG_LEFT_RIGHT, 0, GenericInputBinding::ModeLED),
|
||||
// clang-format on
|
||||
|
||||
#undef MOTOR
|
||||
|
||||
@@ -29,6 +29,8 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
|
||||
"SDLTouchpadAsPointer", false);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLPS5PlayerLED, "InputSources",
|
||||
"SDLPS5PlayerLED", false);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLPS5MicMuteLEDForAnalogMode, "InputSources",
|
||||
"SDLPS5MicMuteLEDForAnalogMode", false);
|
||||
connect(m_ui.enableSDLSource, &QCheckBox::checkStateChanged, this,
|
||||
&ControllerGlobalSettingsWidget::updateSDLOptionsEnabled);
|
||||
connect(m_ui.ledSettings, &QToolButton::clicked, this, &ControllerGlobalSettingsWidget::ledSettingsClicked);
|
||||
@@ -120,6 +122,8 @@ void ControllerGlobalSettingsWidget::updateSDLOptionsEnabled()
|
||||
m_ui.enableTouchPadAsPointer->setEnabled(enabled);
|
||||
if (m_ui.enableSDLPS5PlayerLED)
|
||||
m_ui.enableSDLPS5PlayerLED->setEnabled(enabled);
|
||||
if (m_ui.enableSDLPS5MicMuteLEDForAnalogMode)
|
||||
m_ui.enableSDLPS5MicMuteLEDForAnalogMode->setEnabled(enabled);
|
||||
if (m_ui.ledSettings)
|
||||
m_ui.ledSettings->setEnabled(enabled);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@
|
||||
<string>SDL Input Source</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="sdlGridLayout">
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="enableSDLPS5MicMuteLEDForAnalogMode">
|
||||
<property name="text">
|
||||
<string>Use DualSense Mic Mute LED for Analog Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
|
||||
@@ -88,6 +88,19 @@ struct PadVibrationBinding
|
||||
}
|
||||
};
|
||||
|
||||
struct PadModeLEDBinding
|
||||
{
|
||||
struct ModeLED
|
||||
{
|
||||
InputBindingKey binding;
|
||||
InputSource* source;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
ModeLED led;
|
||||
u32 pad_index = 0;
|
||||
};
|
||||
|
||||
struct MacroButton
|
||||
{
|
||||
u16 pad_index; ///< Pad index this macro button is for.
|
||||
@@ -179,6 +192,9 @@ using BindingMap = std::unordered_multimap<InputBindingKey, std::shared_ptr<Inpu
|
||||
/// This is an array of all the pad vibration bindings, indexed by pad index.
|
||||
using VibrationBindingArray = std::vector<PadVibrationBinding>;
|
||||
|
||||
/// This is an array of all the pad Mode LED bindings, indexed by pad index.
|
||||
using ModeLEDBindingArray = std::vector<PadModeLEDBinding>;
|
||||
|
||||
/// Callback for pointer movement events. The key is the pointer key, and the value is the axis value.
|
||||
using PointerMoveCallback = std::function<void(InputBindingKey key, float value)>;
|
||||
|
||||
@@ -188,6 +204,7 @@ struct ALIGN_TO_CACHE_LINE State
|
||||
{
|
||||
BindingMap binding_map;
|
||||
VibrationBindingArray pad_vibration_array;
|
||||
ModeLEDBindingArray pad_mode_led_array;
|
||||
std::vector<MacroButton> macro_buttons;
|
||||
std::vector<std::pair<u32, PointerMoveCallback>> pointer_move_callbacks;
|
||||
std::recursive_mutex mutex;
|
||||
@@ -951,6 +968,8 @@ void InputManager::AddPadBindings(const SettingsInterface& si, const std::string
|
||||
bool vibration_binding_valid = false;
|
||||
PadVibrationBinding vibration_binding = {};
|
||||
vibration_binding.pad_index = pad_index;
|
||||
PadModeLEDBinding led_binding = {};
|
||||
led_binding.pad_index = pad_index;
|
||||
|
||||
for (const Controller::ControllerBindingInfo& bi : cinfo.bindings)
|
||||
{
|
||||
@@ -1026,6 +1045,14 @@ void InputManager::AddPadBindings(const SettingsInterface& si, const std::string
|
||||
}
|
||||
break;
|
||||
|
||||
case InputBindingInfo::Type::ModeLED:
|
||||
if (bindings.empty())
|
||||
continue;
|
||||
|
||||
if (ParseBindingAndGetSource(bindings.front(), &led_binding.led.binding, &led_binding.led.source))
|
||||
s_state.pad_mode_led_array.push_back(std::move(led_binding));
|
||||
break;
|
||||
|
||||
case InputBindingInfo::Type::Pointer:
|
||||
case InputBindingInfo::Type::Device:
|
||||
// handled in device
|
||||
@@ -1541,6 +1568,7 @@ void InputManager::SetDefaultSourceConfig(SettingsInterface& si)
|
||||
si.SetBoolValue("InputSources", "SDL", true);
|
||||
si.SetBoolValue("InputSources", "SDLControllerEnhancedMode", false);
|
||||
si.SetBoolValue("InputSources", "SDLPS5PlayerLED", false);
|
||||
si.SetBoolValue("InputSources", "SDLPS5MicMuteLEDForAnalogMode", false);
|
||||
si.SetBoolValue("InputSources", "XInput", false);
|
||||
si.SetBoolValue("InputSources", "RawInput", false);
|
||||
}
|
||||
@@ -1762,6 +1790,7 @@ void InputManager::OnInputDeviceConnected(InputBindingKey key, std::string_view
|
||||
{
|
||||
INFO_LOG("Device '{}' connected: '{}'", identifier, device_name);
|
||||
Host::OnInputDeviceConnected(key, identifier, device_name);
|
||||
SyncInputDeviceModeLEDOnConnection(identifier);
|
||||
}
|
||||
|
||||
void InputManager::OnInputDeviceDisconnected(InputBindingKey key, std::string_view identifier)
|
||||
@@ -1783,6 +1812,52 @@ std::unique_ptr<ForceFeedbackDevice> InputManager::CreateForceFeedbackDevice(con
|
||||
return {};
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Analog Mode LED
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
void InputManager::SetPadModeLED(u32 pad_index, bool enabled)
|
||||
{
|
||||
for (PadModeLEDBinding& pad : s_state.pad_mode_led_array)
|
||||
{
|
||||
if (pad.pad_index != pad_index)
|
||||
continue;
|
||||
|
||||
PadModeLEDBinding::ModeLED& led = pad.led;
|
||||
|
||||
if (led.enabled == enabled)
|
||||
continue;
|
||||
|
||||
led.enabled = enabled;
|
||||
led.source->UpdateModeLEDState(led.binding, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::SyncInputDeviceModeLEDOnConnection(std::string_view identifier)
|
||||
{
|
||||
const std::optional<s32> player_id = StringUtil::FromChars<s32>(identifier.substr(identifier.find('-') + 1));
|
||||
if (!player_id.has_value() || player_id.value() < 0)
|
||||
return;
|
||||
|
||||
for (PadModeLEDBinding& pad : s_state.pad_mode_led_array)
|
||||
{
|
||||
PadModeLEDBinding::ModeLED& led = pad.led;
|
||||
|
||||
if (!led.source->ContainsDevice(identifier))
|
||||
continue;
|
||||
|
||||
if (led.binding.source_index == static_cast<u32>(player_id.value()))
|
||||
{
|
||||
Controller* controller = System::GetController(pad.pad_index);
|
||||
if (!controller)
|
||||
return;
|
||||
|
||||
led.enabled = controller->InAnalogMode();
|
||||
led.source->UpdateModeLEDState(led.binding, led.enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Vibration
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -2101,6 +2176,7 @@ void InputManager::ReloadBindings(const SettingsInterface& binding_si, const Set
|
||||
|
||||
s_state.binding_map.clear();
|
||||
s_state.pad_vibration_array.clear();
|
||||
s_state.pad_mode_led_array.clear();
|
||||
s_state.macro_buttons.clear();
|
||||
s_state.pointer_move_callbacks.clear();
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ enum class InputSubclass : u32
|
||||
ControllerHat = 2,
|
||||
ControllerMotor = 3,
|
||||
ControllerHaptic = 4,
|
||||
ControllerModeLED = 5,
|
||||
|
||||
SensorAccelerometer = 0,
|
||||
};
|
||||
@@ -339,6 +340,9 @@ void RemoveHook();
|
||||
/// Returns true if there is an interception hook present.
|
||||
bool HasHook();
|
||||
|
||||
void SetPadModeLED(u32 pad_index, bool enabled);
|
||||
void SyncInputDeviceModeLEDOnConnection(std::string_view identifier);
|
||||
|
||||
/// Internal method used by pads to dispatch vibration updates to input sources.
|
||||
/// Intensity is normalized from 0 to 1.
|
||||
void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensity, float small_motor_intensity);
|
||||
|
||||
@@ -20,6 +20,11 @@ void InputSource::UpdateMotorState(InputBindingKey large_key, InputBindingKey sm
|
||||
UpdateMotorState(small_key, small_intensity);
|
||||
}
|
||||
|
||||
void InputSource::UpdateModeLEDState(InputBindingKey key, bool enabled)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
InputBindingKey InputSource::MakeGenericControllerDeviceKey(InputSourceType clazz, u32 controller_index)
|
||||
{
|
||||
InputBindingKey key = {};
|
||||
|
||||
@@ -72,6 +72,9 @@ public:
|
||||
virtual void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||
float small_intensity);
|
||||
|
||||
/// Enables/Disables the source's Analog Mode LED.
|
||||
virtual void UpdateModeLEDState(InputBindingKey key, bool enabled);
|
||||
|
||||
/// Creates a force-feedback device from this source.
|
||||
virtual std::unique_ptr<ForceFeedbackDevice> CreateForceFeedbackDevice(std::string_view device, Error* error) = 0;
|
||||
|
||||
|
||||
@@ -287,6 +287,7 @@ void SDLInputSource::LoadSettings(const SettingsInterface& si)
|
||||
|
||||
m_controller_enhanced_mode = si.GetBoolValue("InputSources", "SDLControllerEnhancedMode", false);
|
||||
m_controller_ps5_player_led = si.GetBoolValue("InputSources", "SDLPS5PlayerLED", false);
|
||||
m_controller_ps5_mic_mute_led_for_analog_mode = si.GetBoolValue("InputSources", "SDLPS5MicMuteLEDForAnalogMode");
|
||||
m_controller_touchpad_as_pointer = si.GetBoolValue("InputSources", "SDLTouchpadAsPointer", false);
|
||||
m_sdl_hints = si.GetKeyValueList("SDLHints");
|
||||
|
||||
@@ -312,6 +313,7 @@ void InputSource::CopySDLSourceSettings(SettingsInterface* dest_si, const Settin
|
||||
|
||||
dest_si->CopyBoolValue(src_si, "InputSources", "SDLControllerEnhancedMode");
|
||||
dest_si->CopyBoolValue(src_si, "InputSources", "SDLPS5PlayerLED");
|
||||
dest_si->CopyBoolValue(src_si, "InputSources", "SDLPS5MicMuteLEDForAnalogMode");
|
||||
dest_si->CopyBoolValue(src_si, "InputSources", "SDLTouchpadAsPointer");
|
||||
dest_si->CopySection(src_si, "SDLHints");
|
||||
|
||||
@@ -337,6 +339,92 @@ u32 SDLInputSource::ParseRGBForPlayerId(std::string_view str, u32 player_id)
|
||||
return color;
|
||||
}
|
||||
|
||||
bool SDLInputSource::IsPS5Controller(SDL_Gamepad* gp)
|
||||
{
|
||||
typedef struct
|
||||
{
|
||||
u16 vendor;
|
||||
u16 product;
|
||||
} ControllerDescriptor_t;
|
||||
|
||||
// https://github.com/libsdl-org/SDL/blob/4b93e7488f10f5d713cb12ef04deb2bec4c55481/src/joystick/controller_list.h#L153,L154
|
||||
const ControllerDescriptor_t supported_controllers[] = {
|
||||
{0x054c, 0x0ce6}, // Sony DualSense Controller
|
||||
{0x054c, 0x0df2} // Sony DualSense Edge Controller
|
||||
};
|
||||
const u16 gamepad_vendor = SDL_GetGamepadVendor(gp);
|
||||
const u16 gamepad_product = SDL_GetGamepadProduct(gp);
|
||||
|
||||
bool supported = false;
|
||||
for (auto& supported_controller : supported_controllers)
|
||||
{
|
||||
supported |= (supported_controller.vendor == gamepad_vendor && supported_controller.product == gamepad_product);
|
||||
if (supported)
|
||||
break;
|
||||
}
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
void SDLInputSource::UpdateModeLEDState(InputBindingKey key, bool enabled)
|
||||
{
|
||||
if (key.source_subtype != InputSubclass::ControllerModeLED)
|
||||
return;
|
||||
|
||||
auto it = GetControllerDataForPlayerId(key.source_index);
|
||||
if (it == m_controllers.end())
|
||||
return;
|
||||
|
||||
it->mode_led = enabled;
|
||||
|
||||
SendModeLEDUpdate(&(*it));
|
||||
}
|
||||
|
||||
void SDLInputSource::SendModeLEDUpdate(ControllerData* cd)
|
||||
{
|
||||
if (cd->has_mode_led && m_controller_ps5_mic_mute_led_for_analog_mode)
|
||||
EnablePS5MicMuteLED(cd->gamepad, cd->mode_led);
|
||||
}
|
||||
|
||||
void SDLInputSource::EnablePS5MicMuteLED(SDL_Gamepad* gp, bool enabled)
|
||||
{
|
||||
// https://github.com/libsdl-org/SDL/blob/1aba421bd301fa663e5bc83dc8af97caf6a6968a/src/joystick/hidapi/SDL_hidapi_ps5.c#L169
|
||||
typedef struct
|
||||
{
|
||||
Uint8 ucEnableBits1; // 0
|
||||
Uint8 ucEnableBits2; // 1
|
||||
Uint8 ucRumbleRight; // 2
|
||||
Uint8 ucRumbleLeft; // 3
|
||||
Uint8 ucHeadphoneVolume; // 4
|
||||
Uint8 ucSpeakerVolume; // 5
|
||||
Uint8 ucMicrophoneVolume; // 6
|
||||
Uint8 ucAudioEnableBits; // 7
|
||||
Uint8 ucMicLightMode; // 8
|
||||
Uint8 ucAudioMuteBits; // 9
|
||||
Uint8 rgucRightTriggerEffect[11]; // 10
|
||||
Uint8 rgucLeftTriggerEffect[11]; // 21
|
||||
Uint8 rgucUnknown1[6]; // 32
|
||||
Uint8 ucEnableBits3; // 38
|
||||
Uint8 rgucUnknown2[2]; // 39
|
||||
Uint8 ucLedAnim; // 41
|
||||
Uint8 ucLedBrightness; // 42
|
||||
Uint8 ucPadLights; // 43
|
||||
Uint8 ucLedRed; // 44
|
||||
Uint8 ucLedGreen; // 45
|
||||
Uint8 ucLedBlue; // 46
|
||||
} DS5EffectsState_t;
|
||||
|
||||
DS5EffectsState_t effects;
|
||||
SDL_zero(effects);
|
||||
|
||||
// https://github.com/libsdl-org/SDL/blob/1aba421bd301fa663e5bc83dc8af97caf6a6968a/src/joystick/hidapi/SDL_hidapi_ps5.c#749
|
||||
effects.ucEnableBits2 |= 0x01; // 0x00: Block Mute LED, 0x01: Allow Mute LED
|
||||
effects.ucMicLightMode = static_cast<int>(enabled); // 0x00: Disable Mute LED, 0x01: Enable Mute LED, 0x02: Enable Mute LED (Pulsing)
|
||||
|
||||
if (!SDL_SendGamepadEffect(gp, &effects, sizeof(effects)))
|
||||
ERROR_LOG("Error {} Mic Mute LED: {}", (enabled ? "enabling" : "disabling"), SDL_GetError());
|
||||
}
|
||||
|
||||
std::span<const SettingInfo> SDLInputSource::GetAdvancedSettingsInfo()
|
||||
{
|
||||
return s_sdl_advanced_settings_info;
|
||||
@@ -569,6 +657,12 @@ std::optional<InputBindingKey> SDLInputSource::ParseKeyString(std::string_view d
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (binding.starts_with("ModeLED"))
|
||||
{
|
||||
key.source_subtype = InputSubclass::ControllerModeLED;
|
||||
key.data = 0;
|
||||
return key;
|
||||
}
|
||||
else
|
||||
{
|
||||
// must be a button
|
||||
@@ -973,6 +1067,8 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller)
|
||||
if (!cd.haptic && !cd.use_gamepad_rumble)
|
||||
VERBOSE_LOG("Rumble is not supported on '{}'", name);
|
||||
|
||||
cd.has_mode_led = IsPS5Controller(gamepad);
|
||||
|
||||
cd.has_led = (gamepad && SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_RGB_LED_BOOLEAN, false));
|
||||
if (cd.has_led && player_id >= 0 && static_cast<u32>(player_id) < MAX_LED_COLORS)
|
||||
SetControllerRGBLED(gamepad, m_led_colors[player_id]);
|
||||
@@ -1268,6 +1364,9 @@ bool SDLInputSource::GetGenericBindingMapping(std::string_view device, GenericIn
|
||||
mapping->emplace_back(GenericInputBinding::LargeMotor, fmt::format("SDL-{}/Haptic", pid));
|
||||
}
|
||||
|
||||
if (it->has_mode_led)
|
||||
mapping->emplace_back(GenericInputBinding::ModeLED, fmt::format("SDL-{}/ModeLED", pid));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -38,6 +38,8 @@ public:
|
||||
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||
float small_intensity) override;
|
||||
|
||||
void UpdateModeLEDState(InputBindingKey key, bool enabled) override;
|
||||
|
||||
bool ContainsDevice(std::string_view device) const override;
|
||||
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
||||
TinyString ConvertKeyToString(InputBindingKey key) override;
|
||||
@@ -52,6 +54,9 @@ public:
|
||||
static u32 GetRGBForPlayerId(const SettingsInterface& si, u32 player_id);
|
||||
static u32 ParseRGBForPlayerId(std::string_view str, u32 player_id);
|
||||
|
||||
static bool IsPS5Controller(SDL_Gamepad* gp);
|
||||
static void EnablePS5MicMuteLED(SDL_Gamepad* gp, bool enabled);
|
||||
|
||||
static std::span<const SettingInfo> GetAdvancedSettingsInfo();
|
||||
|
||||
static bool IsHandledInputEvent(const SDL_Event* ev);
|
||||
@@ -72,6 +77,8 @@ private:
|
||||
float last_touch_y;
|
||||
bool use_gamepad_rumble : 1;
|
||||
bool has_led : 1;
|
||||
bool mode_led : 1;
|
||||
bool has_mode_led : 1;
|
||||
|
||||
// Used to disable Joystick controls that are used in GameController inputs so we don't get double events
|
||||
std::vector<bool> joy_button_used_in_gc;
|
||||
@@ -102,6 +109,7 @@ private:
|
||||
bool HandleJoystickButtonEvent(const SDL_JoyButtonEvent* ev);
|
||||
bool HandleJoystickHatEvent(const SDL_JoyHatEvent* ev);
|
||||
void SendRumbleUpdate(ControllerData* cd);
|
||||
void SendModeLEDUpdate(ControllerData* cd);
|
||||
|
||||
ControllerDataVector m_controllers;
|
||||
|
||||
@@ -117,6 +125,7 @@ private:
|
||||
{
|
||||
bool m_controller_enhanced_mode : 1;
|
||||
bool m_controller_ps5_player_led : 1;
|
||||
bool m_controller_ps5_mic_mute_led_for_analog_mode : 1;
|
||||
|
||||
bool m_joystick_xbox_hidapi : 1;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user