mirror of
https://github.com/stenzek/duckstation.git
synced 2026-05-20 23:16:35 +00:00
Qt: Refactor LED bindings
Make it more general and bindable to both the mode and RGB LED, that way it can be used on Dualshock 4 as well.
This commit is contained in:
@@ -29,10 +29,7 @@ AnalogController::AnalogController(u32 index) : Controller(index)
|
||||
m_rumble_config.fill(0xFF);
|
||||
}
|
||||
|
||||
AnalogController::~AnalogController()
|
||||
{
|
||||
InputManager::SetPadModeLED(m_index, false);
|
||||
}
|
||||
AnalogController::~AnalogController() = default;
|
||||
|
||||
ControllerType AnalogController::GetType() const
|
||||
{
|
||||
@@ -183,6 +180,11 @@ float AnalogController::GetVibrationMotorState(u32 index) const
|
||||
return (m_motor_state[SmallMotor] > 0) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
float AnalogController::GetLEDState(u32 index) const
|
||||
{
|
||||
return BoolToFloat(index == 0 && m_analog_mode);
|
||||
}
|
||||
|
||||
void AnalogController::SetBindState(u32 index, float value)
|
||||
{
|
||||
if (index == static_cast<s32>(Button::Analog))
|
||||
@@ -342,7 +344,7 @@ void AnalogController::SetAnalogMode(bool enabled, bool show_message)
|
||||
|
||||
m_analog_mode = enabled;
|
||||
|
||||
InputManager::SetPadModeLED(m_index, enabled);
|
||||
InputManager::SetPadLEDState(m_index, BoolToFloat(enabled));
|
||||
|
||||
INFO_LOG("Controller {} switched to {} mode.", m_index + 1u, m_analog_mode ? "analog" : "digital");
|
||||
if (show_message)
|
||||
@@ -790,8 +792,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}
|
||||
#define MODE_LED(name, display_name, icon_name, index, genb) \
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::LED, genb}
|
||||
|
||||
// clang-format off
|
||||
BUTTON("Up", TRANSLATE_NOOP("AnalogController", "D-Pad Up"), ICON_PF_DPAD_UP, AnalogController::Button::Up, GenericInputBinding::DPadUp),
|
||||
@@ -824,7 +826,7 @@ 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),
|
||||
MODE_LED("AnalogLED", TRANSLATE_NOOP("AnalogController", "Analog LED"), ICON_PF_ANALOG_LEFT_RIGHT, 0, GenericInputBinding::ModeLED),
|
||||
// clang-format on
|
||||
|
||||
#undef MOTOR
|
||||
|
||||
@@ -73,6 +73,7 @@ public:
|
||||
|
||||
float GetBindState(u32 index) const override;
|
||||
float GetVibrationMotorState(u32 index) const override;
|
||||
float GetLEDState(u32 index) const override;
|
||||
void SetBindState(u32 index, float value) override;
|
||||
u32 GetButtonStateBits() const override;
|
||||
std::optional<u32> GetAnalogInputBytes() const override;
|
||||
|
||||
@@ -26,10 +26,7 @@ AnalogJoystick::AnalogJoystick(u32 index) : Controller(index)
|
||||
Reset();
|
||||
}
|
||||
|
||||
AnalogJoystick::~AnalogJoystick()
|
||||
{
|
||||
InputManager::SetPadModeLED(m_index, false);
|
||||
}
|
||||
AnalogJoystick::~AnalogJoystick() = default;
|
||||
|
||||
ControllerType AnalogJoystick::GetType() const
|
||||
{
|
||||
@@ -44,7 +41,8 @@ bool AnalogJoystick::InAnalogMode() const
|
||||
void AnalogJoystick::Reset()
|
||||
{
|
||||
m_transfer_state = TransferState::Idle;
|
||||
InputManager::SetPadModeLED(m_index, m_analog_mode);
|
||||
m_analog_mode = true;
|
||||
InputManager::SetPadLEDState(m_index, 1.0f);
|
||||
}
|
||||
|
||||
bool AnalogJoystick::DoState(StateWrapper& sw, bool apply_input_state)
|
||||
@@ -245,7 +243,7 @@ void AnalogJoystick::ToggleAnalogMode()
|
||||
{
|
||||
m_analog_mode = !m_analog_mode;
|
||||
|
||||
InputManager::SetPadModeLED(m_index, m_analog_mode);
|
||||
InputManager::SetPadLEDState(m_index, BoolToFloat(m_analog_mode));
|
||||
|
||||
INFO_LOG("Joystick {} switched to {} mode.", m_index + 1u, m_analog_mode ? "analog" : "digital");
|
||||
Host::AddIconOSDMessage(
|
||||
@@ -357,7 +355,7 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
InputBindingInfo::Type::HalfAxis, \
|
||||
genb}
|
||||
#define MODE_LED(name, display_name, icon_name, index, genb) \
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::ModeLED, genb}
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::LED, genb}
|
||||
|
||||
// clang-format off
|
||||
BUTTON("Up", TRANSLATE_NOOP("AnalogJoystick", "D-Pad Up"), ICON_PF_DPAD_UP, AnalogJoystick::Button::Up, GenericInputBinding::DPadUp),
|
||||
|
||||
@@ -96,6 +96,11 @@ float Controller::GetVibrationMotorState(u32 index) const
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float Controller::GetLEDState(u32 index) const
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool Controller::InAnalogMode() const
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -80,6 +80,9 @@ public:
|
||||
/// Returns the current state of the specified vibration motor.
|
||||
virtual float GetVibrationMotorState(u32 index) const;
|
||||
|
||||
/// Returns the current state of the specified LED.
|
||||
virtual float GetLEDState(u32 index) const;
|
||||
|
||||
/// Returns true if the controller supports analog mode, and it is active.
|
||||
virtual bool InAnalogMode() const;
|
||||
|
||||
|
||||
@@ -382,8 +382,8 @@ static void PopulateGraphicsAdapterList();
|
||||
static void PopulateGameListDirectoryCache(SettingsInterface* si);
|
||||
static void PopulatePatchesAndCheatsList();
|
||||
static void PopulatePostProcessingChain(SettingsInterface* si, const char* section);
|
||||
static void BeginVibrationMotorBinding(SettingsInterface* bsi, InputBindingInfo::Type type, const char* section,
|
||||
const char* key, std::string_view display_name);
|
||||
static void BeginEffectBinding(SettingsInterface* bsi, InputBindingInfo::Type type, const char* section,
|
||||
const char* key, std::string_view display_name);
|
||||
static void DrawInputBindingButton(SettingsInterface* bsi, InputBindingInfo::Type type, const char* section,
|
||||
const char* name, std::string_view display_name, std::string_view icon_name,
|
||||
bool show_type = true);
|
||||
@@ -2478,6 +2478,9 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn
|
||||
case InputBindingInfo::Type::Motor:
|
||||
title.format(ICON_FA_BELL " {}", display_name);
|
||||
break;
|
||||
case InputBindingInfo::Type::LED:
|
||||
title.format(ICON_FA_LIGHTBULB " {}", display_name);
|
||||
break;
|
||||
case InputBindingInfo::Type::Macro:
|
||||
title.format(ICON_FA_PIZZA_SLICE " {}", display_name);
|
||||
break;
|
||||
@@ -2512,8 +2515,8 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn
|
||||
|
||||
if (clicked)
|
||||
{
|
||||
if (type == InputBindingInfo::Type::Motor)
|
||||
BeginVibrationMotorBinding(bsi, type, section, name, display_name);
|
||||
if (type == InputBindingInfo::Type::Motor || type == InputBindingInfo::Type::LED)
|
||||
BeginEffectBinding(bsi, type, section, name, display_name);
|
||||
else
|
||||
s_state.input_binding_dialog.Start(bsi, type, section, name, display_name);
|
||||
}
|
||||
@@ -2672,46 +2675,49 @@ void FullscreenUI::InputBindingDialog::Draw()
|
||||
EndRender();
|
||||
}
|
||||
|
||||
void FullscreenUI::BeginVibrationMotorBinding(SettingsInterface* bsi, InputBindingInfo::Type type, const char* section,
|
||||
const char* key, std::string_view display_name)
|
||||
void FullscreenUI::BeginEffectBinding(SettingsInterface* bsi, InputBindingInfo::Type type, const char* section,
|
||||
const char* key, std::string_view display_name)
|
||||
{
|
||||
// vibration motors use a list to select
|
||||
const bool game_settings = IsEditingGameSettings(bsi);
|
||||
InputManager::VibrationMotorList motors = InputManager::EnumerateVibrationMotors();
|
||||
if (motors.empty())
|
||||
const InputManager::DeviceEffectList effects = InputManager::EnumerateDeviceEffects(type);
|
||||
if (effects.empty())
|
||||
{
|
||||
ShowToast({}, FSUI_STR("No devices with vibration motors were detected."));
|
||||
return;
|
||||
}
|
||||
|
||||
const TinyString current_binding = bsi->GetTinyStringValue(section, key);
|
||||
size_t current_index = motors.size();
|
||||
size_t current_index = effects.size();
|
||||
ChoiceDialogOptions options;
|
||||
options.reserve(motors.size() + 1);
|
||||
for (size_t i = 0; i < motors.size(); i++)
|
||||
options.reserve(effects.size() + 1);
|
||||
for (size_t i = 0; i < effects.size(); i++)
|
||||
{
|
||||
const TinyString text = InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type::Motor, motors[i]);
|
||||
const TinyString text = InputManager::ConvertInputBindingKeyToString(effects[i].first, effects[i].second);
|
||||
const bool this_index = (current_binding.view() == text);
|
||||
current_index = this_index ? i : current_index;
|
||||
options.emplace_back(text, this_index);
|
||||
}
|
||||
|
||||
// empty/no mapping value
|
||||
options.emplace_back(FSUI_STR("No Vibration"), current_binding.empty());
|
||||
if (type == InputBindingInfo::Type::Motor)
|
||||
options.emplace_back(FSUI_STR("No Vibration"), current_binding.empty());
|
||||
else if (type == InputBindingInfo::Type::LED)
|
||||
options.emplace_back(FSUI_STR("No LED"), current_binding.empty());
|
||||
|
||||
// add current value to list if it's not currently available
|
||||
if (!current_binding.empty() && current_index == motors.size())
|
||||
if (!current_binding.empty() && current_index == effects.size())
|
||||
options.emplace_back(std::make_pair(std::string(current_binding.view()), true));
|
||||
|
||||
OpenChoiceDialog(display_name, false, std::move(options),
|
||||
[game_settings, section = std::string(section), key = std::string(key),
|
||||
motors = std::move(motors)](s32 index, const std::string& title, bool checked) {
|
||||
effects = std::move(effects)](s32 index, const std::string& title, bool checked) {
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
|
||||
if (static_cast<size_t>(index) == motors.size())
|
||||
if (static_cast<size_t>(index) == effects.size())
|
||||
bsi->DeleteValue(section.c_str(), key.c_str());
|
||||
else
|
||||
bsi->SetStringValue(section.c_str(), key.c_str(), title.c_str());
|
||||
@@ -4866,12 +4872,7 @@ void FullscreenUI::DrawControllerSettingsPage()
|
||||
"SDLControllerEnhancedMode", false, bsi->GetBoolValue("InputSources", "SDL", true), false);
|
||||
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_LIGHTBULB, "SDL DualSense Player LED"),
|
||||
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);
|
||||
"SDLPS5PlayerLED", false, bsi->GetBoolValue("InputSources", "SDL", 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 "
|
||||
|
||||
@@ -15,13 +15,15 @@ struct InputBindingInfo
|
||||
Axis,
|
||||
HalfAxis,
|
||||
Motor,
|
||||
ModeLED,
|
||||
LED,
|
||||
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.
|
||||
Macro,
|
||||
};
|
||||
|
||||
ALWAYS_INLINE static bool IsEffectType(Type type) { return (type >= Type::Motor && type <= Type::LED); }
|
||||
|
||||
const char* name;
|
||||
const char* display_name;
|
||||
Type bind_type;
|
||||
|
||||
@@ -24,32 +24,17 @@ JogCon::JogCon(u32 index) : Controller(index)
|
||||
{
|
||||
}
|
||||
|
||||
JogCon::~JogCon()
|
||||
{
|
||||
InputManager::SetPadModeLED(m_index, false);
|
||||
}
|
||||
JogCon::~JogCon() = default;
|
||||
|
||||
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);
|
||||
SetJogConMode(true, false);
|
||||
ResetTransferState();
|
||||
ResetMotorConfig();
|
||||
}
|
||||
@@ -199,7 +184,7 @@ void JogCon::SetJogConMode(bool enabled, bool show_message)
|
||||
m_jogcon_mode = enabled;
|
||||
m_configuration_mode = enabled && m_configuration_mode;
|
||||
|
||||
InputManager::SetPadModeLED(m_index, enabled);
|
||||
InputManager::SetPadLEDState(m_index, BoolToFloat(enabled));
|
||||
|
||||
INFO_LOG("Controller {} switched to {} mode.", m_index + 1u, m_jogcon_mode ? "JogCon" : "Digital");
|
||||
if (show_message)
|
||||
@@ -642,7 +627,7 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
InputBindingInfo::Type::HalfAxis, \
|
||||
genb}
|
||||
#define MODE_LED(name, display_name, icon_name, index, genb) \
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::ModeLED, genb}
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::LED, genb}
|
||||
|
||||
// clang-format off
|
||||
BUTTON("Up", TRANSLATE_NOOP("JogCon", "D-Pad Up"), ICON_PF_DPAD_UP, JogCon::Button::Up, GenericInputBinding::DPadUp),
|
||||
|
||||
@@ -49,8 +49,6 @@ 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,7 +5,6 @@
|
||||
#include "host.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "util/input_manager.h"
|
||||
#include "util/state_wrapper.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -27,26 +26,16 @@ NeGcon::NeGcon(u32 index) : Controller(index)
|
||||
m_axis_state[static_cast<u8>(Axis::Steering)] = 0x80;
|
||||
}
|
||||
|
||||
NeGcon::~NeGcon()
|
||||
{
|
||||
InputManager::SetPadModeLED(m_index, false);
|
||||
}
|
||||
NeGcon::~NeGcon() = default;
|
||||
|
||||
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)
|
||||
@@ -281,8 +270,6 @@ 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),
|
||||
@@ -298,13 +285,10 @@ 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,7 +87,6 @@ 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,10 +36,7 @@ NeGconRumble::NeGconRumble(u32 index) : Controller(index)
|
||||
m_rumble_config.fill(0xFF);
|
||||
}
|
||||
|
||||
NeGconRumble::~NeGconRumble()
|
||||
{
|
||||
InputManager::SetPadModeLED(m_index, false);
|
||||
}
|
||||
NeGconRumble::~NeGconRumble() = default;
|
||||
|
||||
ControllerType NeGconRumble::GetType() const
|
||||
{
|
||||
@@ -238,7 +235,7 @@ void NeGconRumble::SetAnalogMode(bool enabled, bool show_message)
|
||||
if (m_analog_mode == enabled)
|
||||
return;
|
||||
|
||||
InputManager::SetPadModeLED(m_index, enabled);
|
||||
InputManager::SetPadLEDState(m_index, BoolToFloat(enabled));
|
||||
|
||||
INFO_LOG("Controller {} switched to {} mode.", m_index + 1u, m_analog_mode ? "analog" : "digital");
|
||||
if (show_message)
|
||||
@@ -731,7 +728,7 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
|
||||
#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}
|
||||
{name, display_name, icon_name, index, InputBindingInfo::Type::LED, genb}
|
||||
|
||||
// clang-format off
|
||||
BUTTON("Up", TRANSLATE_NOOP("NeGconRumble", "D-Pad Up"), ICON_PF_DPAD_UP, NeGconRumble::Button::Up, GenericInputBinding::DPadUp),
|
||||
|
||||
@@ -2037,7 +2037,7 @@ void System::DestroySystem()
|
||||
Host::ClearOSDMessages(true);
|
||||
});
|
||||
|
||||
InputManager::PauseVibration();
|
||||
InputManager::ClearEffects();
|
||||
InputManager::UpdateHostMouseMode();
|
||||
|
||||
if (g_settings.inhibit_screensaver)
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1202</width>
|
||||
<height>646</height>
|
||||
<width>1100</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -394,7 +394,7 @@
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputVibrationBindingWidget" name="LargeMotor">
|
||||
<widget class="InputBindingWidget" name="LargeMotor">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
@@ -997,7 +997,7 @@
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputVibrationBindingWidget" name="SmallMotor">
|
||||
<widget class="InputBindingWidget" name="SmallMotor">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
@@ -1090,59 +1090,6 @@
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QGridLayout" name="gridLayout_32">
|
||||
<item row="0" column="3">
|
||||
<widget class="QGroupBox" name="groupBox_28">
|
||||
<property name="title">
|
||||
<string>R3</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_29">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputBindingWidget" name="R3">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="4">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox_31">
|
||||
<property name="title">
|
||||
@@ -1205,6 +1152,81 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QGroupBox" name="groupBox_28">
|
||||
<property name="title">
|
||||
<string>R3</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_29">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputBindingWidget" name="R3">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="4">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox_33">
|
||||
<property name="title">
|
||||
<string>Analog LED</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_34">
|
||||
<item row="0" column="0">
|
||||
<widget class="InputBindingWidget" name="AnalogLED">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -1215,11 +1237,6 @@
|
||||
<extends>QPushButton</extends>
|
||||
<header>inputbindingwidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>InputVibrationBindingWidget</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>inputbindingwidgets.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="resources/duckstation-qt.qrc"/>
|
||||
|
||||
@@ -739,7 +739,7 @@
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputVibrationBindingWidget" name="LargeMotor">
|
||||
<widget class="InputBindingWidget" name="LargeMotor">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
@@ -779,7 +779,7 @@
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputVibrationBindingWidget" name="SmallMotor">
|
||||
<widget class="InputBindingWidget" name="SmallMotor">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
@@ -811,7 +811,7 @@
|
||||
<header>inputbindingwidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>InputVibrationBindingWidget</class>
|
||||
<class>InputEffectBindingWidget</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>inputbindingwidgets.h</header>
|
||||
</customwidget>
|
||||
|
||||
@@ -499,7 +499,8 @@ void ControllerBindingWidget::createBindingWidgets(QWidget* parent)
|
||||
{
|
||||
if (bi.type == InputBindingInfo::Type::Axis || bi.type == InputBindingInfo::Type::HalfAxis ||
|
||||
bi.type == InputBindingInfo::Type::Pointer || bi.type == InputBindingInfo::Type::RelativePointer ||
|
||||
bi.type == InputBindingInfo::Type::Device || bi.type == InputBindingInfo::Type::Motor)
|
||||
bi.type == InputBindingInfo::Type::Device || bi.type == InputBindingInfo::Type::Motor ||
|
||||
bi.type == InputBindingInfo::Type::LED)
|
||||
{
|
||||
if (!axis_gbox)
|
||||
{
|
||||
@@ -507,14 +508,10 @@ void ControllerBindingWidget::createBindingWidgets(QWidget* parent)
|
||||
axis_layout = new QGridLayout(axis_gbox);
|
||||
}
|
||||
|
||||
QGroupBox* gbox =
|
||||
QGroupBox* const gbox =
|
||||
new QGroupBox(QtUtils::StringViewToQString(m_controller_info->GetBindingDisplayName(bi)), axis_gbox);
|
||||
QVBoxLayout* temp = new QVBoxLayout(gbox);
|
||||
QWidget* widget;
|
||||
if (bi.type != InputBindingInfo::Type::Motor)
|
||||
widget = new InputBindingWidget(gbox, sif, bi.type, getConfigSection(), bi.name);
|
||||
else
|
||||
widget = new InputVibrationBindingWidget(gbox, getDialog(), getConfigSection(), bi.name);
|
||||
QVBoxLayout* const temp = new QVBoxLayout(gbox);
|
||||
QWidget* const widget = new InputBindingWidget(gbox, sif, bi.type, getConfigSection(), bi.name);
|
||||
|
||||
temp->addWidget(widget);
|
||||
axis_layout->addWidget(gbox, row, column);
|
||||
@@ -586,7 +583,8 @@ void ControllerBindingWidget::bindBindingWidgets(QWidget* parent)
|
||||
{
|
||||
if (bi.type == InputBindingInfo::Type::Axis || bi.type == InputBindingInfo::Type::HalfAxis ||
|
||||
bi.type == InputBindingInfo::Type::Button || bi.type == InputBindingInfo::Type::Pointer ||
|
||||
bi.type == InputBindingInfo::Type::RelativePointer)
|
||||
bi.type == InputBindingInfo::Type::RelativePointer || bi.type == InputBindingInfo::Type::Motor ||
|
||||
bi.type == InputBindingInfo::Type::LED)
|
||||
{
|
||||
InputBindingWidget* widget = parent->findChild<InputBindingWidget*>(QString::fromUtf8(bi.name));
|
||||
if (!widget)
|
||||
@@ -597,12 +595,6 @@ void ControllerBindingWidget::bindBindingWidgets(QWidget* parent)
|
||||
|
||||
widget->initialize(sif, bi.type, config_section, bi.name);
|
||||
}
|
||||
else if (bi.type == InputBindingInfo::Type::Motor)
|
||||
{
|
||||
InputVibrationBindingWidget* widget = parent->findChild<InputVibrationBindingWidget*>(QString::fromUtf8(bi.name));
|
||||
if (widget)
|
||||
widget->setKey(getDialog(), config_section, bi.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ 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);
|
||||
@@ -122,8 +120,6 @@ 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);
|
||||
}
|
||||
@@ -148,7 +144,7 @@ void ControllerLEDSettingsDialog::linkButton(ColorPickerButton* button, u32 play
|
||||
{
|
||||
std::string key = fmt::format("Player{}LED", player_id);
|
||||
const u32 current_value =
|
||||
SDLInputSource::ParseRGBForPlayerId(m_dialog->getStringValue("SDLExtra", key.c_str(), ""), player_id);
|
||||
SDLInputSource::ParseRGBForPlayerId(m_dialog->getStringValue("SDLExtra", key.c_str(), ""), player_id, false);
|
||||
button->setColor(current_value);
|
||||
|
||||
connect(button, &ColorPickerButton::colorChanged, this, [this, key = std::move(key)](u32 new_rgb) {
|
||||
|
||||
@@ -29,13 +29,6 @@
|
||||
<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>
|
||||
|
||||
@@ -277,10 +277,17 @@ void InputBindingWidget::onClicked()
|
||||
return;
|
||||
}
|
||||
|
||||
if (isListeningForInput())
|
||||
stopListeningForInput();
|
||||
if (InputBindingInfo::IsEffectType(m_bind_type))
|
||||
{
|
||||
showEffectBindingDialog();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isListeningForInput())
|
||||
stopListeningForInput();
|
||||
|
||||
startListeningForInput(TIMEOUT_FOR_SINGLE_BINDING);
|
||||
startListeningForInput(TIMEOUT_FOR_SINGLE_BINDING);
|
||||
}
|
||||
}
|
||||
|
||||
void InputBindingWidget::onInputListenTimerTimeout()
|
||||
@@ -410,90 +417,65 @@ void InputBindingWidget::openDialog()
|
||||
dlg->show();
|
||||
}
|
||||
|
||||
InputVibrationBindingWidget::InputVibrationBindingWidget(QWidget* parent)
|
||||
void InputBindingWidget::showEffectBindingDialog()
|
||||
{
|
||||
connect(this, &QPushButton::clicked, this, &InputVibrationBindingWidget::onClicked);
|
||||
}
|
||||
std::vector<InputBindingKey> options;
|
||||
QStringList option_names;
|
||||
QString current;
|
||||
|
||||
InputVibrationBindingWidget::InputVibrationBindingWidget(QWidget* parent, ControllerSettingsWindow* dialog,
|
||||
std::string section_name, std::string key_name)
|
||||
{
|
||||
setMinimumWidth(225);
|
||||
setMaximumWidth(225);
|
||||
|
||||
connect(this, &QPushButton::clicked, this, &InputVibrationBindingWidget::onClicked);
|
||||
|
||||
setKey(dialog, std::move(section_name), std::move(key_name));
|
||||
}
|
||||
|
||||
InputVibrationBindingWidget::~InputVibrationBindingWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void InputVibrationBindingWidget::setKey(ControllerSettingsWindow* dialog, std::string section_name,
|
||||
std::string key_name)
|
||||
{
|
||||
m_dialog = dialog;
|
||||
m_section_name = std::move(section_name);
|
||||
m_key_name = std::move(key_name);
|
||||
m_binding = Host::GetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str());
|
||||
setText(QString::fromStdString(m_binding));
|
||||
}
|
||||
|
||||
void InputVibrationBindingWidget::clearBinding()
|
||||
{
|
||||
m_binding = {};
|
||||
Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str());
|
||||
Host::CommitBaseSettingChanges();
|
||||
g_emu_thread->reloadInputBindings();
|
||||
setText(QString());
|
||||
}
|
||||
|
||||
void InputVibrationBindingWidget::onClicked()
|
||||
{
|
||||
QInputDialog dialog(QtUtils::GetRootWidget(this));
|
||||
|
||||
const QString full_key(
|
||||
QStringLiteral("%1/%2").arg(QString::fromStdString(m_section_name)).arg(QString::fromStdString(m_key_name)));
|
||||
const QString current(QString::fromStdString(m_binding));
|
||||
QStringList input_options = g_emu_thread->getInputDeviceListModel()->getVibrationMotorList();
|
||||
if (!current.isEmpty() && input_options.indexOf(current) < 0)
|
||||
const InputDeviceListModel::EffectList& all_options = g_emu_thread->getInputDeviceListModel()->getEffectList();
|
||||
options.reserve(all_options.size());
|
||||
option_names.reserve(all_options.size());
|
||||
for (const auto& [type, key] : g_emu_thread->getInputDeviceListModel()->getEffectList())
|
||||
{
|
||||
input_options.append(current);
|
||||
if (type != m_bind_type)
|
||||
continue;
|
||||
|
||||
const TinyString name = InputManager::ConvertInputBindingKeyToString(type, key);
|
||||
if (name.empty())
|
||||
continue;
|
||||
|
||||
QString qname = QtUtils::StringViewToQString(name);
|
||||
if (!m_bindings.empty() && name == m_bindings.front())
|
||||
current = qname;
|
||||
|
||||
options.push_back(key);
|
||||
option_names.push_back(std::move(qname));
|
||||
}
|
||||
else if (input_options.isEmpty())
|
||||
|
||||
if (options.empty())
|
||||
{
|
||||
QMessageBox::critical(QtUtils::GetRootWidget(this), tr("Error"),
|
||||
tr("No devices with vibration motors were detected."));
|
||||
(m_bind_type == InputBindingInfo::Type::Motor) ?
|
||||
tr("No devices with vibration motors were detected.") :
|
||||
tr("No devices with LEDs were detected."));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Multiple options? needs a custom dialog
|
||||
const QString full_key(
|
||||
QStringLiteral("%1/%2").arg(QString::fromStdString(m_section_name)).arg(QString::fromStdString(m_key_name)));
|
||||
QInputDialog input_dialog(this);
|
||||
input_dialog.setWindowTitle(full_key);
|
||||
input_dialog.setLabelText(tr("Select vibration motor for %1.").arg(full_key));
|
||||
input_dialog.setLabelText(tr("Select device and effect for %1.").arg(full_key));
|
||||
input_dialog.setInputMode(QInputDialog::TextInput);
|
||||
input_dialog.setOptions(QInputDialog::UseListViewForComboBoxItems);
|
||||
input_dialog.setComboBoxEditable(false);
|
||||
input_dialog.setComboBoxItems(std::move(input_options));
|
||||
input_dialog.setComboBoxItems(option_names);
|
||||
input_dialog.setTextValue(current);
|
||||
if (input_dialog.exec() == QDialog::Rejected)
|
||||
return;
|
||||
|
||||
const QString new_value(input_dialog.textValue());
|
||||
m_binding = new_value.toStdString();
|
||||
Host::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_binding.c_str());
|
||||
Host::CommitBaseSettingChanges();
|
||||
g_emu_thread->reloadInputBindings();
|
||||
setText(new_value);
|
||||
}
|
||||
|
||||
void InputVibrationBindingWidget::mouseReleaseEvent(QMouseEvent* e)
|
||||
{
|
||||
if (e->button() == Qt::RightButton)
|
||||
const QString new_value = input_dialog.textValue();
|
||||
for (qsizetype i = 0; i < option_names.size(); i++)
|
||||
{
|
||||
clearBinding();
|
||||
return;
|
||||
if (new_value == option_names[i])
|
||||
{
|
||||
m_new_bindings.clear();
|
||||
m_new_bindings.push_back(options[i]);
|
||||
setNewBinding();
|
||||
reloadBinding();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QPushButton::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ protected:
|
||||
void onInputListenTimerTimeout();
|
||||
void inputManagerHookCallback(InputBindingKey key, float value);
|
||||
|
||||
void showEffectBindingDialog();
|
||||
|
||||
SettingsInterface* m_sif = nullptr;
|
||||
InputBindingInfo::Type m_bind_type = InputBindingInfo::Type::Unknown;
|
||||
std::string m_section_name;
|
||||
@@ -68,30 +70,3 @@ protected:
|
||||
QPoint m_input_listen_start_position{};
|
||||
bool m_mouse_mapping_enabled = false;
|
||||
};
|
||||
|
||||
class InputVibrationBindingWidget : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InputVibrationBindingWidget(QWidget* parent);
|
||||
InputVibrationBindingWidget(QWidget* parent, ControllerSettingsWindow* dialog, std::string section_name,
|
||||
std::string key_name);
|
||||
~InputVibrationBindingWidget();
|
||||
|
||||
void setKey(ControllerSettingsWindow* dialog, std::string section_name, std::string key_name);
|
||||
|
||||
void clearBinding();
|
||||
|
||||
protected:
|
||||
virtual void mouseReleaseEvent(QMouseEvent* e) override;
|
||||
|
||||
void onClicked();
|
||||
|
||||
private:
|
||||
std::string m_section_name;
|
||||
std::string m_key_name;
|
||||
std::string m_binding;
|
||||
|
||||
ControllerSettingsWindow* m_dialog;
|
||||
};
|
||||
|
||||
@@ -2586,36 +2586,33 @@ void InputDeviceListModel::enumerateDevices()
|
||||
DebugAssert(g_emu_thread->isCurrentThread());
|
||||
|
||||
const InputManager::DeviceList devices = InputManager::EnumerateDevices();
|
||||
const InputManager::VibrationMotorList motors = InputManager::EnumerateVibrationMotors();
|
||||
const InputManager::DeviceEffectList effects = InputManager::EnumerateDeviceEffects();
|
||||
|
||||
DeviceList new_devices;
|
||||
new_devices.reserve(devices.size());
|
||||
for (const auto& [key, identifier, device_name] : devices)
|
||||
new_devices.emplace_back(key, QString::fromStdString(identifier), QString::fromStdString(device_name));
|
||||
|
||||
QStringList new_motors;
|
||||
new_motors.reserve(motors.size());
|
||||
for (const auto& key : motors)
|
||||
{
|
||||
new_motors.push_back(
|
||||
QtUtils::StringViewToQString(InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type::Motor, key)));
|
||||
}
|
||||
EffectList new_effects;
|
||||
new_effects.reserve(effects.size());
|
||||
for (const auto& [type, key] : effects)
|
||||
new_effects.emplace_back(type, key);
|
||||
|
||||
QMetaObject::invokeMethod(this, &InputDeviceListModel::resetLists, Qt::QueuedConnection, new_devices, new_motors);
|
||||
QMetaObject::invokeMethod(this, &InputDeviceListModel::resetLists, Qt::QueuedConnection, new_devices, new_effects);
|
||||
}
|
||||
|
||||
void InputDeviceListModel::resetLists(const DeviceList& devices, const QStringList& motors)
|
||||
void InputDeviceListModel::resetLists(const DeviceList& devices, const EffectList& effects)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
m_devices = devices;
|
||||
m_vibration_motors = motors;
|
||||
m_effects = effects;
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void InputDeviceListModel::onDeviceConnected(const InputBindingKey& key, const QString& identifier,
|
||||
const QString& device_name, const QStringList& vibration_motors)
|
||||
const QString& device_name, const EffectList& effects)
|
||||
{
|
||||
for (const auto& it : m_devices)
|
||||
{
|
||||
@@ -2628,7 +2625,7 @@ void InputDeviceListModel::onDeviceConnected(const InputBindingKey& key, const Q
|
||||
m_devices.emplace_back(key, identifier, device_name);
|
||||
endInsertRows();
|
||||
|
||||
m_vibration_motors.append(vibration_motors);
|
||||
m_effects.append(effects);
|
||||
}
|
||||
|
||||
void InputDeviceListModel::onDeviceDisconnected(const InputBindingKey& key, const QString& identifier)
|
||||
@@ -2642,12 +2639,12 @@ void InputDeviceListModel::onDeviceDisconnected(const InputBindingKey& key, cons
|
||||
m_devices.remove(i);
|
||||
endRemoveRows();
|
||||
|
||||
// remove vibration motors too
|
||||
const QString motor_prefix = QStringLiteral("%1/").arg(identifier);
|
||||
for (qsizetype j = 0; j < m_vibration_motors.size();)
|
||||
// remove effects too
|
||||
const QString effect_prefix = QStringLiteral("%1/").arg(identifier);
|
||||
for (qsizetype j = 0; j < m_effects.size();)
|
||||
{
|
||||
if (m_vibration_motors[j].startsWith(motor_prefix))
|
||||
m_vibration_motors.remove(j);
|
||||
if (m_effects[j].second.source_type == key.source_type && m_effects[j].second.source_index == key.source_index)
|
||||
m_effects.remove(j);
|
||||
else
|
||||
j++;
|
||||
}
|
||||
@@ -2659,22 +2656,19 @@ void InputDeviceListModel::onDeviceDisconnected(const InputBindingKey& key, cons
|
||||
|
||||
void Host::OnInputDeviceConnected(InputBindingKey key, std::string_view identifier, std::string_view device_name)
|
||||
{
|
||||
// get the motors for this device to append to the list
|
||||
QStringList vibration_motor_list;
|
||||
const InputManager::VibrationMotorList im_vibration_motor_list = InputManager::EnumerateVibrationMotors(key);
|
||||
if (!im_vibration_motor_list.empty())
|
||||
// get the effects for this device to append to the list
|
||||
InputDeviceListModel::EffectList qeffect_list;
|
||||
const InputManager::DeviceEffectList effect_list = InputManager::EnumerateDeviceEffects(std::nullopt, key);
|
||||
if (!effect_list.empty())
|
||||
{
|
||||
vibration_motor_list.reserve(im_vibration_motor_list.size());
|
||||
for (const InputBindingKey& motor_key : im_vibration_motor_list)
|
||||
{
|
||||
vibration_motor_list.push_back(QtUtils::StringViewToQString(
|
||||
InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type::Motor, motor_key)));
|
||||
}
|
||||
qeffect_list.reserve(effect_list.size());
|
||||
for (const auto& [eff_type, eff_key] : effect_list)
|
||||
qeffect_list.emplace_back(eff_type, eff_key);
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(g_emu_thread->getInputDeviceListModel(), &InputDeviceListModel::onDeviceConnected,
|
||||
Qt::QueuedConnection, key, QtUtils::StringViewToQString(identifier),
|
||||
QtUtils::StringViewToQString(device_name), vibration_motor_list);
|
||||
QtUtils::StringViewToQString(device_name), qeffect_list);
|
||||
|
||||
if (System::IsValid() || GPUThread::IsFullscreenUIRequested())
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "core/game_list.h"
|
||||
#include "core/host.h"
|
||||
#include "core/input_types.h"
|
||||
#include "core/system.h"
|
||||
#include "core/types.h"
|
||||
|
||||
@@ -277,15 +278,14 @@ public:
|
||||
};
|
||||
|
||||
using DeviceList = QList<Device>;
|
||||
using EffectList = QList<QPair<InputBindingInfo::Type, InputBindingKey>>;
|
||||
|
||||
explicit InputDeviceListModel(QObject* parent = nullptr);
|
||||
~InputDeviceListModel() override;
|
||||
|
||||
// Safe to access on UI thread.
|
||||
ALWAYS_INLINE const DeviceList& getDeviceList() const { return m_devices; }
|
||||
ALWAYS_INLINE const QStringList& getVibrationMotorList() const { return m_vibration_motors; }
|
||||
|
||||
static QIcon getIconForKey(const InputBindingKey& key);
|
||||
ALWAYS_INLINE const EffectList& getEffectList() const { return m_effects; }
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
@@ -294,14 +294,16 @@ public:
|
||||
void enumerateDevices();
|
||||
|
||||
void onDeviceConnected(const InputBindingKey& key, const QString& identifier, const QString& device_name,
|
||||
const QStringList& vibration_motors);
|
||||
const EffectList& effects);
|
||||
void onDeviceDisconnected(const InputBindingKey& key, const QString& identifier);
|
||||
|
||||
static QIcon getIconForKey(const InputBindingKey& key);
|
||||
|
||||
private:
|
||||
void resetLists(const DeviceList& devices, const QStringList& motors);
|
||||
void resetLists(const DeviceList& devices, const EffectList& motors);
|
||||
|
||||
DeviceList m_devices;
|
||||
QStringList m_vibration_motors;
|
||||
EffectList m_effects;
|
||||
};
|
||||
|
||||
class QtAsyncTask : public QObject
|
||||
|
||||
@@ -316,7 +316,8 @@ InputManager::DeviceList DInputSource::EnumerateDevices()
|
||||
return ret;
|
||||
}
|
||||
|
||||
InputManager::VibrationMotorList DInputSource::EnumerateVibrationMotors(std::optional<InputBindingKey> for_device)
|
||||
InputManager::DeviceEffectList DInputSource::EnumerateEffects(std::optional<InputBindingInfo::Type> type,
|
||||
std::optional<InputBindingKey> for_device)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@@ -337,6 +338,11 @@ void DInputSource::UpdateMotorState(InputBindingKey large_key, InputBindingKey s
|
||||
// not supported
|
||||
}
|
||||
|
||||
void DInputSource::UpdateLEDState(InputBindingKey key, float intensity)
|
||||
{
|
||||
// not supported
|
||||
}
|
||||
|
||||
bool DInputSource::ContainsDevice(std::string_view device) const
|
||||
{
|
||||
return device.starts_with("DInput-");
|
||||
|
||||
@@ -41,11 +41,13 @@ public:
|
||||
void PollEvents() override;
|
||||
std::optional<float> GetCurrentValue(InputBindingKey key) override;
|
||||
InputManager::DeviceList EnumerateDevices() override;
|
||||
InputManager::VibrationMotorList EnumerateVibrationMotors(std::optional<InputBindingKey> for_device) override;
|
||||
InputManager::DeviceEffectList EnumerateEffects(std::optional<InputBindingInfo::Type> type,
|
||||
std::optional<InputBindingKey> for_device) override;
|
||||
bool GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) override;
|
||||
void UpdateMotorState(InputBindingKey key, float intensity) override;
|
||||
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||
float small_intensity) override;
|
||||
void UpdateLEDState(InputBindingKey key, float intensity) override;
|
||||
|
||||
bool ContainsDevice(std::string_view device) const override;
|
||||
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
||||
|
||||
@@ -88,17 +88,12 @@ struct PadVibrationBinding
|
||||
}
|
||||
};
|
||||
|
||||
struct PadModeLEDBinding
|
||||
struct PadLEDBinding
|
||||
{
|
||||
struct ModeLED
|
||||
{
|
||||
InputBindingKey binding;
|
||||
InputSource* source;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
ModeLED led;
|
||||
u32 pad_index = 0;
|
||||
InputBindingKey binding;
|
||||
InputSource* source;
|
||||
float last_intensity;
|
||||
u32 pad_index;
|
||||
};
|
||||
|
||||
struct MacroButton
|
||||
@@ -192,8 +187,8 @@ 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>;
|
||||
/// This is an array of all the pad LED bindings, indexed by pad index.
|
||||
using PadLEDBindingArray = std::vector<PadLEDBinding>;
|
||||
|
||||
/// 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)>;
|
||||
@@ -204,7 +199,7 @@ struct ALIGN_TO_CACHE_LINE State
|
||||
{
|
||||
BindingMap binding_map;
|
||||
VibrationBindingArray pad_vibration_array;
|
||||
ModeLEDBindingArray pad_mode_led_array;
|
||||
PadLEDBindingArray pad_led_array;
|
||||
std::vector<MacroButton> macro_buttons;
|
||||
std::vector<std::pair<u32, PointerMoveCallback>> pointer_move_callbacks;
|
||||
std::recursive_mutex mutex;
|
||||
@@ -383,6 +378,9 @@ TinyString InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type b
|
||||
{
|
||||
TinyString ret;
|
||||
|
||||
// in case the source disappears, very unlikely
|
||||
const auto lock = std::unique_lock(s_state.mutex);
|
||||
|
||||
if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::RelativePointer ||
|
||||
binding_type == InputBindingInfo::Type::Device)
|
||||
{
|
||||
@@ -968,7 +966,7 @@ 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 = {};
|
||||
PadLEDBinding led_binding = {};
|
||||
led_binding.pad_index = pad_index;
|
||||
|
||||
for (const Controller::ControllerBindingInfo& bi : cinfo.bindings)
|
||||
@@ -1045,13 +1043,24 @@ void InputManager::AddPadBindings(const SettingsInterface& si, const std::string
|
||||
}
|
||||
break;
|
||||
|
||||
case InputBindingInfo::Type::ModeLED:
|
||||
case InputBindingInfo::Type::LED:
|
||||
{
|
||||
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;
|
||||
if (ParseBindingAndGetSource(bindings.front(), &led_binding.binding, &led_binding.source))
|
||||
{
|
||||
// If we're reloading bindings due to e.g. device connection, sync the LED state.
|
||||
if (Controller* controller = System::GetController(pad_index))
|
||||
led_binding.last_intensity = controller->GetLEDState(bi.bind_index);
|
||||
|
||||
// Need to pass it through unconditionally, otherwise if the LED was on it'll stay on.
|
||||
led_binding.source->UpdateLEDState(led_binding.binding, led_binding.last_intensity);
|
||||
|
||||
s_state.pad_led_array.push_back(std::move(led_binding));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case InputBindingInfo::Type::Pointer:
|
||||
case InputBindingInfo::Type::Device:
|
||||
@@ -1790,7 +1799,6 @@ 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)
|
||||
@@ -1812,52 +1820,6 @@ 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
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -1977,6 +1939,36 @@ void InputManager::UpdateContinuedVibration()
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// LEDs
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
void InputManager::SetPadLEDState(u32 pad_index, float intensity)
|
||||
{
|
||||
for (PadLEDBinding& pad : s_state.pad_led_array)
|
||||
{
|
||||
if (pad.pad_index != pad_index || pad.last_intensity == intensity)
|
||||
continue;
|
||||
|
||||
pad.last_intensity = intensity;
|
||||
pad.source->UpdateLEDState(pad.binding, intensity);
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::ClearEffects()
|
||||
{
|
||||
PauseVibration();
|
||||
|
||||
for (PadLEDBinding& pad : s_state.pad_led_array)
|
||||
{
|
||||
if (pad.last_intensity == 0.0f)
|
||||
continue;
|
||||
|
||||
pad.last_intensity = 0.0f;
|
||||
pad.source->UpdateLEDState(pad.binding, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Macros
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -2176,7 +2168,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.pad_led_array.clear();
|
||||
s_state.macro_buttons.clear();
|
||||
s_state.pointer_move_callbacks.clear();
|
||||
|
||||
@@ -2294,17 +2286,18 @@ InputManager::DeviceList InputManager::EnumerateDevices()
|
||||
return ret;
|
||||
}
|
||||
|
||||
InputManager::VibrationMotorList InputManager::EnumerateVibrationMotors(std::optional<InputBindingKey> for_device)
|
||||
InputManager::DeviceEffectList InputManager::EnumerateDeviceEffects(std::optional<InputBindingInfo::Type> type,
|
||||
std::optional<InputBindingKey> for_device)
|
||||
{
|
||||
std::unique_lock lock(s_state.mutex);
|
||||
|
||||
VibrationMotorList ret;
|
||||
DeviceEffectList ret;
|
||||
|
||||
for (u32 i = FIRST_EXTERNAL_INPUT_SOURCE; i < LAST_EXTERNAL_INPUT_SOURCE; i++)
|
||||
{
|
||||
if (s_state.input_sources[i])
|
||||
{
|
||||
VibrationMotorList devs = s_state.input_sources[i]->EnumerateVibrationMotors(for_device);
|
||||
DeviceEffectList devs = s_state.input_sources[i]->EnumerateEffects(type, for_device);
|
||||
if (ret.empty())
|
||||
ret = std::move(devs);
|
||||
else
|
||||
|
||||
@@ -53,7 +53,7 @@ enum class InputSubclass : u32
|
||||
ControllerHat = 2,
|
||||
ControllerMotor = 3,
|
||||
ControllerHaptic = 4,
|
||||
ControllerModeLED = 5,
|
||||
ControllerLED = 5,
|
||||
|
||||
SensorAccelerometer = 0,
|
||||
};
|
||||
@@ -277,8 +277,9 @@ using DeviceList = std::vector<std::tuple<InputBindingKey, std::string, std::str
|
||||
DeviceList EnumerateDevices();
|
||||
|
||||
/// Enumerates available vibration motors at the time of call.
|
||||
using VibrationMotorList = std::vector<InputBindingKey>;
|
||||
VibrationMotorList EnumerateVibrationMotors(std::optional<InputBindingKey> for_device = std::nullopt);
|
||||
using DeviceEffectList = std::vector<std::pair<InputBindingInfo::Type, InputBindingKey>>;
|
||||
DeviceEffectList EnumerateDeviceEffects(std::optional<InputBindingInfo::Type> type = std::nullopt,
|
||||
std::optional<InputBindingKey> for_device = std::nullopt);
|
||||
|
||||
/// Retrieves bindings that match the generic bindings for the specified device.
|
||||
GenericInputBindingMapping GetGenericBindingMapping(std::string_view device);
|
||||
@@ -340,8 +341,8 @@ 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 LED updates to input sources.
|
||||
void SetPadLEDState(u32 pad_index, float intensity);
|
||||
|
||||
/// Internal method used by pads to dispatch vibration updates to input sources.
|
||||
/// Intensity is normalized from 0 to 1.
|
||||
@@ -351,6 +352,9 @@ void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensi
|
||||
/// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes.
|
||||
void PauseVibration();
|
||||
|
||||
/// Disables all vibration and LED effects. Call when stopping emulation.
|
||||
void ClearEffects();
|
||||
|
||||
/// Returns the number of currently-connected pointer devices.
|
||||
u32 GetPointerCount();
|
||||
|
||||
|
||||
@@ -20,11 +20,6 @@ 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 = {};
|
||||
|
||||
@@ -59,7 +59,8 @@ public:
|
||||
virtual InputManager::DeviceList EnumerateDevices() = 0;
|
||||
|
||||
/// Enumerates available vibration motors at the time of call.
|
||||
virtual InputManager::VibrationMotorList EnumerateVibrationMotors(std::optional<InputBindingKey> for_device) = 0;
|
||||
virtual InputManager::DeviceEffectList EnumerateEffects(std::optional<InputBindingInfo::Type> type,
|
||||
std::optional<InputBindingKey> for_device) = 0;
|
||||
|
||||
/// Retrieves bindings that match the generic bindings for the specified device.
|
||||
/// Returns false if it's not one of our devices.
|
||||
@@ -72,8 +73,8 @@ 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);
|
||||
/// Adjusts intensities of LEDs or other indicators on the device.
|
||||
virtual void UpdateLEDState(InputBindingKey key, float intensity) = 0;
|
||||
|
||||
/// Creates a force-feedback device from this source.
|
||||
virtual std::unique_ptr<ForceFeedbackDevice> CreateForceFeedbackDevice(std::string_view device, Error* error) = 0;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/bitutils.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/gsvector.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -148,11 +149,11 @@ static constexpr std::array<const char*, 4> s_sdl_hat_direction_names = {{
|
||||
// clang-format on
|
||||
}};
|
||||
|
||||
static constexpr std::array<const char*, 4> s_sdl_default_led_colors = {{
|
||||
"0000ff", // SDL-0
|
||||
"ff0000", // SDL-1
|
||||
"00ff00", // SDL-2
|
||||
"ffff00", // SDL-3
|
||||
static constexpr std::array<std::array<u32, 2>, 4> s_sdl_default_led_colors = {{
|
||||
{0x000070, 0x0000ff}, // SDL-0
|
||||
{0x700000, 0xff0000}, // SDL-1
|
||||
{0x007000, 0x00ff00}, // SDL-2
|
||||
{0xffff00, 0x707000}, // SDL-3
|
||||
}};
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -198,11 +199,6 @@ static constexpr const SettingInfo s_sdl_advanced_settings_info[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static void SetControllerRGBLED(SDL_Gamepad* gp, u32 color)
|
||||
{
|
||||
SDL_SetGamepadLED(gp, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
|
||||
}
|
||||
|
||||
static void SDLLogCallback(void* userdata, int category, SDL_LogPriority priority, const char* message)
|
||||
{
|
||||
static constexpr Log::Level priority_map[SDL_LOG_PRIORITY_COUNT] = {
|
||||
@@ -272,22 +268,24 @@ void SDLInputSource::LoadSettings(const SettingsInterface& si)
|
||||
{
|
||||
for (u32 i = 0; i < MAX_LED_COLORS; i++)
|
||||
{
|
||||
const u32 color = GetRGBForPlayerId(si, i);
|
||||
if (m_led_colors[i] == color)
|
||||
continue;
|
||||
bool changed = false;
|
||||
for (u32 active = 0; active < 2; active++)
|
||||
{
|
||||
const u32 color = GetRGBForPlayerId(si, i, active != 0);
|
||||
changed = (changed || m_led_colors[i][active] != color);
|
||||
m_led_colors[i][active] = color;
|
||||
}
|
||||
|
||||
m_led_colors[i] = color;
|
||||
|
||||
const auto it = GetControllerDataForPlayerId(i);
|
||||
if (it == m_controllers.end() || !it->gamepad || !it->has_led)
|
||||
continue;
|
||||
|
||||
SetControllerRGBLED(it->gamepad, color);
|
||||
if (changed)
|
||||
{
|
||||
const auto it = GetControllerDataForPlayerId(i);
|
||||
if (it != m_controllers.end() && it->gamepad && it->has_led)
|
||||
SetControllerRGBLED(it->gamepad, it->has_rgb_led, m_led_colors[i], it->rgb_led_intensity);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
@@ -313,7 +311,6 @@ 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");
|
||||
|
||||
@@ -321,36 +318,27 @@ void InputSource::CopySDLSourceSettings(SettingsInterface* dest_si, const Settin
|
||||
si.CopyValue(dest_si, src_si, "InputSources");
|
||||
}
|
||||
|
||||
u32 SDLInputSource::GetRGBForPlayerId(const SettingsInterface& si, u32 player_id)
|
||||
u32 SDLInputSource::GetRGBForPlayerId(const SettingsInterface& si, u32 player_id, bool active)
|
||||
{
|
||||
return ParseRGBForPlayerId(si.GetStringValue("SDLExtra", TinyString::from_format("Player{}LED", player_id).c_str(),
|
||||
s_sdl_default_led_colors[player_id]),
|
||||
player_id);
|
||||
return ParseRGBForPlayerId(
|
||||
si.GetStringValue("SDLExtra", TinyString::from_format("Player{}{}LED", player_id, active ? "Active" : "")),
|
||||
player_id, active);
|
||||
}
|
||||
|
||||
u32 SDLInputSource::ParseRGBForPlayerId(std::string_view str, u32 player_id)
|
||||
u32 SDLInputSource::ParseRGBForPlayerId(std::string_view str, u32 player_id, bool active)
|
||||
{
|
||||
if (player_id >= MAX_LED_COLORS)
|
||||
return 0;
|
||||
|
||||
const u32 default_color = StringUtil::FromChars<u32>(s_sdl_default_led_colors[player_id], 16).value_or(0);
|
||||
const u32 color = StringUtil::FromChars<u32>(str, 16).value_or(default_color);
|
||||
|
||||
return color;
|
||||
return StringUtil::FromChars<u32>(str, 16).value_or(s_sdl_default_led_colors[player_id][BoolToUInt32(active)]);
|
||||
}
|
||||
|
||||
bool SDLInputSource::IsPS5Controller(SDL_Gamepad* gp)
|
||||
static bool 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
|
||||
static constexpr const std::pair<u16, u16> supported_controllers[] = {
|
||||
{static_cast<u16>(0x054c), static_cast<u16>(0x0ce6)}, // Sony DualSense Controller
|
||||
{static_cast<u16>(0x054c), static_cast<u16>(0x0df2)} // Sony DualSense Edge Controller
|
||||
};
|
||||
const u16 gamepad_vendor = SDL_GetGamepadVendor(gp);
|
||||
const u16 gamepad_product = SDL_GetGamepadProduct(gp);
|
||||
@@ -358,7 +346,7 @@ bool SDLInputSource::IsPS5Controller(SDL_Gamepad* gp)
|
||||
bool supported = false;
|
||||
for (auto& supported_controller : supported_controllers)
|
||||
{
|
||||
supported |= (supported_controller.vendor == gamepad_vendor && supported_controller.product == gamepad_product);
|
||||
supported |= (supported_controller.first == gamepad_vendor && supported_controller.second == gamepad_product);
|
||||
if (supported)
|
||||
break;
|
||||
}
|
||||
@@ -366,27 +354,56 @@ bool SDLInputSource::IsPS5Controller(SDL_Gamepad* gp)
|
||||
return supported;
|
||||
}
|
||||
|
||||
void SDLInputSource::UpdateModeLEDState(InputBindingKey key, bool enabled)
|
||||
void SDLInputSource::UpdateLEDState(InputBindingKey key, float intensity)
|
||||
{
|
||||
if (key.source_subtype != InputSubclass::ControllerModeLED)
|
||||
return;
|
||||
DebugAssert(key.source_type == InputSourceType::SDL && key.source_subtype == InputSubclass::ControllerLED);
|
||||
|
||||
auto it = GetControllerDataForPlayerId(key.source_index);
|
||||
if (it == m_controllers.end())
|
||||
return;
|
||||
|
||||
it->mode_led = enabled;
|
||||
if (key.data == 0)
|
||||
{
|
||||
// RGB LED
|
||||
if (!it->has_led || it->rgb_led_intensity == intensity)
|
||||
return;
|
||||
|
||||
SendModeLEDUpdate(&(*it));
|
||||
it->rgb_led_intensity = intensity;
|
||||
SetControllerRGBLED(it->gamepad, it->has_rgb_led, m_led_colors[std::min(key.source_index, MAX_LED_COLORS)],
|
||||
intensity);
|
||||
}
|
||||
else if (key.data == 1)
|
||||
{
|
||||
// Mode LED
|
||||
const bool mode_led_enabled = (intensity > 0.0f);
|
||||
if (!it->has_mode_led || it->mode_led_state == mode_led_enabled)
|
||||
return;
|
||||
|
||||
it->mode_led_state = mode_led_enabled;
|
||||
SetControllerMicMuteLED(it->gamepad, mode_led_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLInputSource::SendModeLEDUpdate(ControllerData* cd)
|
||||
void SDLInputSource::SetControllerRGBLED(SDL_Gamepad* gp, bool has_rgb_led, const std::array<u32, 2>& colors,
|
||||
float intensity)
|
||||
{
|
||||
if (cd->has_mode_led && m_controller_ps5_mic_mute_led_for_analog_mode)
|
||||
EnablePS5MicMuteLED(cd->gamepad, cd->mode_led);
|
||||
if (has_rgb_led)
|
||||
{
|
||||
const GSVector4 c0 = GSVector4::rgba32(colors[0]);
|
||||
const GSVector4 c1 = GSVector4::rgba32(colors[1]);
|
||||
const GSVector4 c = c0 + (c1 - c0) * GSVector4(intensity);
|
||||
const u32 color = c.rgba32();
|
||||
|
||||
SDL_SetGamepadLED(gp, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
|
||||
}
|
||||
else
|
||||
{
|
||||
const u8 color = static_cast<u8>(std::clamp(static_cast<int>(std::round(255.0f * intensity)), 0, 255));
|
||||
SDL_SetGamepadLED(gp, color, color, color);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLInputSource::EnablePS5MicMuteLED(SDL_Gamepad* gp, bool enabled)
|
||||
void SDLInputSource::SetControllerMicMuteLED(SDL_Gamepad* gp, bool enabled)
|
||||
{
|
||||
// https://github.com/libsdl-org/SDL/blob/1aba421bd301fa663e5bc83dc8af97caf6a6968a/src/joystick/hidapi/SDL_hidapi_ps5.c#L169
|
||||
typedef struct
|
||||
@@ -418,11 +435,12 @@ void SDLInputSource::EnablePS5MicMuteLED(SDL_Gamepad* gp, bool enabled)
|
||||
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)
|
||||
effects.ucEnableBits2 |= 0x01; // 0x00: Block Mute LED, 0x01: Allow Mute LED
|
||||
effects.ucMicLightMode =
|
||||
static_cast<Uint8>(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());
|
||||
ERROR_LOG("Failed to set mic mute LED to {}: {}", enabled, SDL_GetError());
|
||||
}
|
||||
|
||||
std::span<const SettingInfo> SDLInputSource::GetAdvancedSettingsInfo()
|
||||
@@ -657,10 +675,16 @@ std::optional<InputBindingKey> SDLInputSource::ParseKeyString(std::string_view d
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (binding.starts_with("ModeLED"))
|
||||
else if (binding.ends_with("LED"))
|
||||
{
|
||||
key.source_subtype = InputSubclass::ControllerModeLED;
|
||||
key.data = 0;
|
||||
key.source_subtype = InputSubclass::ControllerLED;
|
||||
if (binding == "RGBLED")
|
||||
key.data = 0;
|
||||
else if (binding == "MuteLED")
|
||||
key.data = 1;
|
||||
else
|
||||
return std::nullopt;
|
||||
|
||||
return key;
|
||||
}
|
||||
else
|
||||
@@ -738,6 +762,10 @@ TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key)
|
||||
{
|
||||
ret.format("SDL-{}/Haptic", static_cast<u32>(key.source_index));
|
||||
}
|
||||
else if (key.source_subtype == InputSubclass::ControllerLED)
|
||||
{
|
||||
ret.format("SDL-{}/{}", static_cast<u32>(key.source_index), (key.data != 0) ? "MuteLED" : "RGBLED");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1067,11 +1095,15 @@ 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);
|
||||
// Check for LED support, reset it in case a previous instance left it enabled
|
||||
if ((cd.has_mode_led = IsPS5Controller(gamepad)))
|
||||
SetControllerMicMuteLED(gamepad, false);
|
||||
|
||||
cd.has_led = (gamepad && SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_RGB_LED_BOOLEAN, false));
|
||||
cd.has_rgb_led = (gamepad && SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_RGB_LED_BOOLEAN, false));
|
||||
cd.has_led =
|
||||
(cd.has_rgb_led || (gamepad && SDL_GetBooleanProperty(properties, SDL_PROP_GAMEPAD_CAP_MONO_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]);
|
||||
SetControllerRGBLED(gamepad, cd.has_rgb_led, m_led_colors[player_id], 0.0f);
|
||||
|
||||
m_controllers.push_back(std::move(cd));
|
||||
|
||||
@@ -1281,9 +1313,10 @@ std::optional<float> SDLInputSource::GetCurrentValue(InputBindingKey key)
|
||||
return ret;
|
||||
}
|
||||
|
||||
InputManager::VibrationMotorList SDLInputSource::EnumerateVibrationMotors(std::optional<InputBindingKey> for_device)
|
||||
InputManager::DeviceEffectList SDLInputSource::EnumerateEffects(std::optional<InputBindingInfo::Type> type,
|
||||
std::optional<InputBindingKey> for_device)
|
||||
{
|
||||
InputManager::VibrationMotorList ret;
|
||||
InputManager::DeviceEffectList ret;
|
||||
|
||||
if (for_device.has_value() && for_device->source_type != InputSourceType::SDL)
|
||||
return ret;
|
||||
@@ -1298,21 +1331,41 @@ InputManager::VibrationMotorList SDLInputSource::EnumerateVibrationMotors(std::o
|
||||
|
||||
key.source_index = cd.player_id;
|
||||
|
||||
if (cd.use_gamepad_rumble || cd.haptic_left_right_effect)
|
||||
if (type.value_or(InputBindingInfo::Type::Motor) == InputBindingInfo::Type::Motor)
|
||||
{
|
||||
// two motors
|
||||
key.source_subtype = InputSubclass::ControllerMotor;
|
||||
key.data = 0;
|
||||
ret.push_back(key);
|
||||
key.data = 1;
|
||||
ret.push_back(key);
|
||||
if (cd.use_gamepad_rumble || cd.haptic_left_right_effect)
|
||||
{
|
||||
// two motors
|
||||
key.source_subtype = InputSubclass::ControllerMotor;
|
||||
key.data = 0;
|
||||
ret.emplace_back(InputBindingInfo::Type::Motor, key);
|
||||
key.data = 1;
|
||||
ret.emplace_back(InputBindingInfo::Type::Motor, key);
|
||||
}
|
||||
else if (cd.haptic)
|
||||
{
|
||||
// haptic effect
|
||||
key.source_subtype = InputSubclass::ControllerHaptic;
|
||||
key.data = 0;
|
||||
ret.emplace_back(InputBindingInfo::Type::Motor, key);
|
||||
}
|
||||
}
|
||||
else if (cd.haptic)
|
||||
|
||||
if (type.value_or(InputBindingInfo::Type::LED) == InputBindingInfo::Type::LED)
|
||||
{
|
||||
// haptic effect
|
||||
key.source_subtype = InputSubclass::ControllerHaptic;
|
||||
key.data = 0;
|
||||
ret.push_back(key);
|
||||
if (cd.has_led)
|
||||
{
|
||||
key.source_subtype = InputSubclass::ControllerLED;
|
||||
key.data = 0;
|
||||
ret.emplace_back(InputBindingInfo::Type::LED, key);
|
||||
}
|
||||
|
||||
if (cd.has_mode_led)
|
||||
{
|
||||
key.source_subtype = InputSubclass::ControllerLED;
|
||||
key.data = 1;
|
||||
ret.emplace_back(InputBindingInfo::Type::LED, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1365,7 +1418,9 @@ bool SDLInputSource::GetGenericBindingMapping(std::string_view device, GenericIn
|
||||
}
|
||||
|
||||
if (it->has_mode_led)
|
||||
mapping->emplace_back(GenericInputBinding::ModeLED, fmt::format("SDL-{}/ModeLED", pid));
|
||||
mapping->emplace_back(GenericInputBinding::ModeLED, fmt::format("SDL-{}/MuteLED", pid));
|
||||
else if (it->has_led)
|
||||
mapping->emplace_back(GenericInputBinding::ModeLED, fmt::format("SDL-{}/RGBLED", pid));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,13 +32,13 @@ public:
|
||||
void PollEvents() override;
|
||||
std::optional<float> GetCurrentValue(InputBindingKey key) override;
|
||||
InputManager::DeviceList EnumerateDevices() override;
|
||||
InputManager::VibrationMotorList EnumerateVibrationMotors(std::optional<InputBindingKey> for_device) override;
|
||||
InputManager::DeviceEffectList EnumerateEffects(std::optional<InputBindingInfo::Type> type,
|
||||
std::optional<InputBindingKey> for_device) override;
|
||||
bool GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) override;
|
||||
void UpdateMotorState(InputBindingKey key, float intensity) override;
|
||||
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||
float small_intensity) override;
|
||||
|
||||
void UpdateModeLEDState(InputBindingKey key, bool enabled) override;
|
||||
void UpdateLEDState(InputBindingKey key, float intensity) override;
|
||||
|
||||
bool ContainsDevice(std::string_view device) const override;
|
||||
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
||||
@@ -51,11 +51,8 @@ public:
|
||||
|
||||
SDL_Joystick* GetJoystickForDevice(std::string_view device);
|
||||
|
||||
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 u32 GetRGBForPlayerId(const SettingsInterface& si, u32 player_id, bool active);
|
||||
static u32 ParseRGBForPlayerId(std::string_view str, u32 player_id, bool active);
|
||||
|
||||
static std::span<const SettingInfo> GetAdvancedSettingsInfo();
|
||||
|
||||
@@ -75,10 +72,12 @@ private:
|
||||
int player_id;
|
||||
float last_touch_x;
|
||||
float last_touch_y;
|
||||
float rgb_led_intensity;
|
||||
bool use_gamepad_rumble : 1;
|
||||
bool has_led : 1;
|
||||
bool mode_led : 1;
|
||||
bool has_rgb_led : 1;
|
||||
bool has_mode_led : 1;
|
||||
bool mode_led_state : 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;
|
||||
@@ -109,11 +108,14 @@ private:
|
||||
bool HandleJoystickButtonEvent(const SDL_JoyButtonEvent* ev);
|
||||
bool HandleJoystickHatEvent(const SDL_JoyHatEvent* ev);
|
||||
void SendRumbleUpdate(ControllerData* cd);
|
||||
void SendModeLEDUpdate(ControllerData* cd);
|
||||
|
||||
static bool ControllerHasMicLED(SDL_Gamepad* gp);
|
||||
static void SetControllerRGBLED(SDL_Gamepad* gp, bool has_rgb_led, const std::array<u32, 2>& colors, float intensity);
|
||||
static void SetControllerMicMuteLED(SDL_Gamepad* gp, bool enabled);
|
||||
|
||||
ControllerDataVector m_controllers;
|
||||
|
||||
std::array<u32, MAX_LED_COLORS> m_led_colors{};
|
||||
std::array<std::array<u32, 2>, MAX_LED_COLORS> m_led_colors{};
|
||||
std::vector<std::pair<std::string, std::string>> m_sdl_hints;
|
||||
|
||||
bool m_sdl_subsystem_initialized = false;
|
||||
@@ -125,7 +127,6 @@ 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;
|
||||
|
||||
|
||||
@@ -103,6 +103,10 @@ void Win32RawInputSource::UpdateMotorState(InputBindingKey large_key, InputBindi
|
||||
{
|
||||
}
|
||||
|
||||
void Win32RawInputSource::UpdateLEDState(InputBindingKey key, float intensity)
|
||||
{
|
||||
}
|
||||
|
||||
bool Win32RawInputSource::ContainsDevice(std::string_view device) const
|
||||
{
|
||||
return false;
|
||||
@@ -130,8 +134,8 @@ std::unique_ptr<ForceFeedbackDevice> Win32RawInputSource::CreateForceFeedbackDev
|
||||
return {};
|
||||
}
|
||||
|
||||
InputManager::VibrationMotorList
|
||||
Win32RawInputSource::EnumerateVibrationMotors(std::optional<InputBindingKey> for_device)
|
||||
InputManager::DeviceEffectList Win32RawInputSource::EnumerateEffects(std::optional<InputBindingInfo::Type> type,
|
||||
std::optional<InputBindingKey> for_device)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -28,11 +28,13 @@ public:
|
||||
void PollEvents() override;
|
||||
std::optional<float> GetCurrentValue(InputBindingKey key) override;
|
||||
InputManager::DeviceList EnumerateDevices() override;
|
||||
InputManager::VibrationMotorList EnumerateVibrationMotors(std::optional<InputBindingKey> for_device) override;
|
||||
InputManager::DeviceEffectList EnumerateEffects(std::optional<InputBindingInfo::Type> type,
|
||||
std::optional<InputBindingKey> for_device) override;
|
||||
bool GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) override;
|
||||
void UpdateMotorState(InputBindingKey key, float intensity) override;
|
||||
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||
float small_intensity) override;
|
||||
void UpdateLEDState(InputBindingKey key, float intensity) override;
|
||||
|
||||
bool ContainsDevice(std::string_view device) const override;
|
||||
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
||||
|
||||
@@ -369,13 +369,17 @@ std::unique_ptr<ForceFeedbackDevice> XInputSource::CreateForceFeedbackDevice(std
|
||||
return {};
|
||||
}
|
||||
|
||||
InputManager::VibrationMotorList XInputSource::EnumerateVibrationMotors(std::optional<InputBindingKey> for_device)
|
||||
InputManager::DeviceEffectList XInputSource::EnumerateEffects(std::optional<InputBindingInfo::Type> type,
|
||||
std::optional<InputBindingKey> for_device)
|
||||
{
|
||||
InputManager::VibrationMotorList ret;
|
||||
InputManager::DeviceEffectList ret;
|
||||
|
||||
if (for_device.has_value() && for_device->source_type != InputSourceType::XInput)
|
||||
return ret;
|
||||
|
||||
if (type.has_value() && type.value() != InputBindingInfo::Type::Motor)
|
||||
return ret;
|
||||
|
||||
for (u32 i = 0; i < NUM_CONTROLLERS; i++)
|
||||
{
|
||||
if (for_device.has_value() && for_device->source_index != i)
|
||||
@@ -386,10 +390,10 @@ InputManager::VibrationMotorList XInputSource::EnumerateVibrationMotors(std::opt
|
||||
continue;
|
||||
|
||||
if (cd.has_large_motor)
|
||||
ret.push_back(MakeGenericControllerMotorKey(InputSourceType::XInput, i, 0));
|
||||
ret.emplace_back(InputBindingInfo::Type::Motor, MakeGenericControllerMotorKey(InputSourceType::XInput, i, 0));
|
||||
|
||||
if (cd.has_small_motor)
|
||||
ret.push_back(MakeGenericControllerMotorKey(InputSourceType::XInput, i, 1));
|
||||
ret.emplace_back(InputBindingInfo::Type::Motor, MakeGenericControllerMotorKey(InputSourceType::XInput, i, 1));
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -585,6 +589,11 @@ void XInputSource::UpdateMotorState(InputBindingKey large_key, InputBindingKey s
|
||||
m_xinput_set_state(large_key.source_index, &cd.last_vibration);
|
||||
}
|
||||
|
||||
void XInputSource::UpdateLEDState(InputBindingKey key, float intensity)
|
||||
{
|
||||
// not supported
|
||||
}
|
||||
|
||||
std::unique_ptr<InputSource> InputSource::CreateXInputSource()
|
||||
{
|
||||
return std::make_unique<XInputSource>();
|
||||
|
||||
@@ -43,11 +43,13 @@ public:
|
||||
void PollEvents() override;
|
||||
std::optional<float> GetCurrentValue(InputBindingKey key) override;
|
||||
InputManager::DeviceList EnumerateDevices() override;
|
||||
InputManager::VibrationMotorList EnumerateVibrationMotors(std::optional<InputBindingKey> for_device) override;
|
||||
InputManager::DeviceEffectList EnumerateEffects(std::optional<InputBindingInfo::Type> type,
|
||||
std::optional<InputBindingKey> for_device) override;
|
||||
bool GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) override;
|
||||
void UpdateMotorState(InputBindingKey key, float intensity) override;
|
||||
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||
float small_intensity) override;
|
||||
void UpdateLEDState(InputBindingKey key, float intensity) override;
|
||||
|
||||
bool ContainsDevice(std::string_view device) const override;
|
||||
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
||||
|
||||
Reference in New Issue
Block a user