Controller: Merge all binds into one index space

Simplifies things a bit.
This commit is contained in:
Stenzek
2025-10-04 16:29:00 +10:00
parent 9d8d81d092
commit 1b49f82c98
14 changed files with 126 additions and 167 deletions

View File

@@ -148,36 +148,16 @@ bool AnalogController::DoState(StateWrapper& sw, bool apply_input_state)
float AnalogController::GetBindState(u32 index) const
{
if (index >= static_cast<u32>(Button::Count))
{
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return 0.0f;
return static_cast<float>(m_half_axis_state[sub_index]) * (1.0f / 255.0f);
}
if (index >= LED_BIND_START_INDEX)
return BoolToFloat(index == LED_BIND_START_INDEX && m_analog_mode);
else if (index >= MOTOR_BIND_START_INDEX)
return m_motor_state[index - MOTOR_BIND_START_INDEX] * (1.0f / 255.0f);
else if (index >= HALFAXIS_BIND_START_INDEX)
return static_cast<float>(m_half_axis_state[index - HALFAXIS_BIND_START_INDEX]) * (1.0f / 255.0f);
else if (index < static_cast<u32>(Button::Analog))
{
return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
}
else
{
return 0.0f;
}
}
float AnalogController::GetVibrationMotorState(u32 index) const
{
// this uses the InputManager definition, where 0 = large motor, 1 = small motor
if (index == 0)
return static_cast<float>(m_motor_state[LargeMotor]) * (1.0f / 255.0f);
else
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)
@@ -392,15 +372,7 @@ void AnalogController::SetMotorState(u32 motor, u8 value)
if (m_motor_state[motor] != value)
{
m_motor_state[motor] = value;
UpdateHostVibration();
}
}
void AnalogController::UpdateHostVibration()
{
std::array<float, NUM_MOTORS> hvalues;
for (u32 motor = 0; motor < NUM_MOTORS; motor++)
{
// Small motor is only 0/1.
const u8 state =
(motor == SmallMotor) ? (((m_motor_state[SmallMotor] & 0x01) != 0x00) ? 255 : 0) : m_motor_state[LargeMotor];
@@ -410,11 +382,10 @@ void AnalogController::UpdateHostVibration()
const double strength = 0.006474549734772402 * std::pow(x, 3.0) - 1.258165252213538 * std::pow(x, 2.0) +
156.82454281087692 * x + 3.637978807091713e-11;
hvalues[motor] = (state != 0) ? static_cast<float>(strength / 65535.0) : 0.0f;
const float hvalue = (state != 0) ? static_cast<float>(strength / 65535.0) : 0.0f;
DEV_LOG("Set {} motor to {} (raw {})", (motor == LargeMotor) ? "large" : "small", hvalue, state);
InputManager::SetPadVibrationIntensity(m_index, MOTOR_BIND_START_INDEX + motor, hvalue);
}
DEV_LOG("Set small motor to {}, large motor to {}", hvalues[SmallMotor], hvalues[LargeMotor]);
InputManager::SetPadVibrationIntensity(m_index, hvalues[LargeMotor], hvalues[SmallMotor]);
}
u16 AnalogController::GetExtraButtonMask() const
@@ -770,20 +741,20 @@ std::unique_ptr<AnalogController> AnalogController::Create(u32 index)
return std::make_unique<AnalogController>(index);
}
static const Controller::ControllerBindingInfo s_binding_info[] = {
constinit const Controller::ControllerBindingInfo AnalogController::s_binding_info[] = {
#define BUTTON(name, display_name, icon_name, button, genb) \
{name, display_name, icon_name, static_cast<u32>(button), InputBindingInfo::Type::Button, genb}
#define AXIS(name, display_name, icon_name, halfaxis, genb) \
{name, \
display_name, \
icon_name, \
static_cast<u32>(AnalogController::Button::Count) + static_cast<u32>(halfaxis), \
HALFAXIS_BIND_START_INDEX + static_cast<u32>(halfaxis), \
InputBindingInfo::Type::HalfAxis, \
genb}
#define MOTOR(name, display_name, icon_name, index, genb) \
{name, display_name, icon_name, index, InputBindingInfo::Type::Motor, genb}
{name, display_name, icon_name, MOTOR_BIND_START_INDEX + index, InputBindingInfo::Type::Motor, genb}
#define MODE_LED(name, display_name, icon_name, index, genb) \
{name, display_name, icon_name, index, InputBindingInfo::Type::LED, genb}
{name, display_name, icon_name, LED_BIND_START_INDEX + 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),
@@ -813,10 +784,11 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
AXIS("RDown", TRANSLATE_NOOP("AnalogController", "Right Stick Down"), ICON_PF_RIGHT_ANALOG_DOWN, AnalogController::HalfAxis::RDown, GenericInputBinding::RightStickDown),
AXIS("RUp", TRANSLATE_NOOP("AnalogController", "Right Stick Up"), ICON_PF_RIGHT_ANALOG_UP, AnalogController::HalfAxis::RUp, GenericInputBinding::RightStickUp),
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),
MOTOR("LargeMotor", TRANSLATE_NOOP("AnalogController", "Large Motor"), ICON_PF_VIBRATION_L, LargeMotor, GenericInputBinding::LargeMotor),
MOTOR("SmallMotor", TRANSLATE_NOOP("AnalogController", "Small Motor"), ICON_PF_VIBRATION, SmallMotor, GenericInputBinding::SmallMotor),
MODE_LED("AnalogLED", TRANSLATE_NOOP("AnalogController", "Analog LED"), ICON_PF_ANALOG_LEFT_RIGHT, 0, GenericInputBinding::ModeLED),
// clang-format on
#undef MOTOR

View File

@@ -71,8 +71,6 @@ public:
bool DoState(StateWrapper& sw, bool ignore_input_state) override;
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;
@@ -102,6 +100,12 @@ private:
static constexpr s16 DEFAULT_SMALL_MOTOR_VIBRATION_BIAS = 8;
static constexpr s16 DEFAULT_LARGE_MOTOR_VIBRATION_BIAS = 8;
static constexpr u32 HALFAXIS_BIND_START_INDEX = static_cast<u32>(Button::Count);
static constexpr u32 MOTOR_BIND_START_INDEX = HALFAXIS_BIND_START_INDEX + static_cast<u32>(HalfAxis::Count);
static constexpr u32 LED_BIND_START_INDEX = MOTOR_BIND_START_INDEX + NUM_MOTORS;
static const Controller::ControllerBindingInfo s_binding_info[];
Command m_command = Command::Idle;
u8 m_command_step = 0;
u8 m_response_length = 0;
@@ -120,7 +124,6 @@ private:
void SetAnalogMode(bool enabled, bool show_message);
void ProcessAnalogModeToggle();
void SetMotorState(u32 motor, u8 value);
void UpdateHostVibration();
u16 GetExtraButtonMask() const;
void ResetRumbleConfig();
void Poll();

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "analog_joystick.h"
@@ -74,22 +74,14 @@ bool AnalogJoystick::DoState(StateWrapper& sw, bool apply_input_state)
float AnalogJoystick::GetBindState(u32 index) const
{
if (index >= static_cast<u32>(Button::Count))
{
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return 0.0f;
return static_cast<float>(m_half_axis_state[sub_index]) * (1.0f / 255.0f);
}
if (index >= LED_BIND_START_INDEX)
return BoolToFloat(index == LED_BIND_START_INDEX && m_analog_mode);
else if (index >= HALFAXIS_BIND_START_INDEX)
return static_cast<float>(m_half_axis_state[index - HALFAXIS_BIND_START_INDEX]) * (1.0f / 255.0f);
else if (index < static_cast<u32>(Button::Mode))
{
return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
}
else
{
return 0.0f;
}
}
void AnalogJoystick::SetBindState(u32 index, float value)
@@ -339,18 +331,18 @@ std::unique_ptr<AnalogJoystick> AnalogJoystick::Create(u32 index)
return std::make_unique<AnalogJoystick>(index);
}
static const Controller::ControllerBindingInfo s_binding_info[] = {
constinit const Controller::ControllerBindingInfo AnalogJoystick::s_binding_info[] = {
#define BUTTON(name, display_name, icon_name, button, genb) \
{name, display_name, icon_name, static_cast<u32>(button), InputBindingInfo::Type::Button, genb}
#define AXIS(name, display_name, icon_name, halfaxis, genb) \
{name, \
display_name, \
icon_name, \
static_cast<u32>(AnalogJoystick::Button::Count) + static_cast<u32>(halfaxis), \
HALFAXIS_BIND_START_INDEX + 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::LED, genb}
{name, display_name, icon_name, LED_BIND_START_INDEX + 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),

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
@@ -92,6 +92,11 @@ private:
LeftAxisY
};
static constexpr u32 HALFAXIS_BIND_START_INDEX = static_cast<u32>(Button::Count);
static constexpr u32 LED_BIND_START_INDEX = HALFAXIS_BIND_START_INDEX + static_cast<u32>(HalfAxis::Count);
static const Controller::ControllerBindingInfo s_binding_info[];
u16 GetID() const;
void ToggleAnalogMode();

View File

@@ -91,16 +91,6 @@ u32 Controller::GetButtonStateBits() const
return 0;
}
float Controller::GetVibrationMotorState(u32 index) const
{
return 0.0f;
}
float Controller::GetLEDState(u32 index) const
{
return 0.0f;
}
std::optional<u32> Controller::GetAnalogInputBytes() const
{
return std::nullopt;

View File

@@ -77,12 +77,6 @@ public:
/// Returns a bitmask of the current button states, 1 = on.
virtual u32 GetButtonStateBits() const;
/// 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 analog input bytes packed as a u32. Values are specific to controller type.
virtual std::optional<u32> GetAnalogInputBytes() const;

View File

@@ -69,7 +69,6 @@ struct InputOverlayState
u8 slot;
bool multitap;
u32 icon_color;
float vibration_state[InputManager::MAX_MOTORS_PER_PAD];
float bind_state[MAX_BINDS];
};
@@ -809,19 +808,14 @@ void ImGuiManager::UpdateInputOverlay()
const u32 bidx = bi.bind_index;
// this will leave some uninitalized, but who cares, it won't be read on the other side
if (bi.type >= InputBindingInfo::Type::Button && bi.type <= InputBindingInfo::Type::HalfAxis)
if (bi.type >= InputBindingInfo::Type::Button && bi.type <= InputBindingInfo::Type::Motor)
{
DebugAssert(bidx < InputOverlayState::MAX_BINDS);
pstate.bind_state[bidx] = controller->GetBindState(bidx);
}
else if (bi.type == InputBindingInfo::Type::Motor)
{
DebugAssert(bidx < InputManager::MAX_MOTORS_PER_PAD);
pstate.vibration_state[bidx] = controller->GetVibrationMotorState(bidx);
}
else if (bi.type == InputBindingInfo::Type::LED)
{
const float intensity = controller->GetLEDState(bidx);
const float intensity = controller->GetBindState(bidx);
pstate.icon_color = (GSVector4::cxpr_rgba32(NORMAL_ICON_COLOR) + (GSVector4::cxpr_rgba32(ALTERNATE_ICON_COLOR) -
GSVector4::cxpr_rgba32(NORMAL_ICON_COLOR)) *
GSVector4(intensity))
@@ -906,7 +900,7 @@ void ImGuiManager::DrawInputsOverlay()
}
else if (bi.type == InputBindingInfo::Type::Motor)
{
const float value = pstate.vibration_state[bi.bind_index];
const float value = pstate.bind_state[bi.bind_index];
if (value >= 1.0f)
text.append_format(" {}", bi.icon_name ? bi.icon_name : bi.name);
else if (value > 0.0f)

View File

@@ -14,8 +14,8 @@ struct InputBindingInfo
Button,
Axis,
HalfAxis,
Motor,
LED,
Motor, // Vibration motors, generic_mapping gets used for motor selection.
LED, // Status LEDs, e.g. analog/digital mode indicator.
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.

View File

@@ -78,27 +78,16 @@ bool JogCon::DoState(StateWrapper& sw, bool apply_input_state)
float JogCon::GetBindState(u32 index) const
{
if (index >= static_cast<u32>(Button::MaxCount))
{
const u32 sub_index = index - static_cast<u32>(Button::MaxCount);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return 0.0f;
return static_cast<float>(m_half_axis_state[sub_index]) * (1.0f / 255.0f);
}
if (index >= LED_BIND_START_INDEX)
return BoolToFloat(index == LED_BIND_START_INDEX && m_jogcon_mode);
else if (index >= MOTOR_BIND_START_INDEX)
return m_last_strength;
else if (index >= HALFAXIS_BIND_START_INDEX)
return static_cast<float>(m_half_axis_state[index - HALFAXIS_BIND_START_INDEX]) * (1.0f / 255.0f);
else if (index < static_cast<u32>(Button::Mode))
{
return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
}
else
{
return 0.0f;
}
}
float JogCon::GetVibrationMotorState(u32 index) const
{
return (index == 0) ? m_last_strength : 0.0f;
}
void JogCon::SetBindState(u32 index, float value)
@@ -281,7 +270,7 @@ void JogCon::SetMotorDirection(u8 direction_command, u8 strength)
if (m_last_strength != 0.0f)
{
m_last_strength = 0.0f;
InputManager::SetPadVibrationIntensity(m_index, 0.0f, 0.0f);
InputManager::SetPadVibrationIntensity(m_index, MOTOR_BIND_START_INDEX, 0.0f);
}
return;
@@ -301,7 +290,7 @@ void JogCon::SetMotorDirection(u8 direction_command, u8 strength)
if (f_strength != m_last_strength)
{
m_last_strength = f_strength;
InputManager::SetPadVibrationIntensity(m_index, f_strength, 0.0f);
InputManager::SetPadVibrationIntensity(m_index, MOTOR_BIND_START_INDEX, f_strength);
}
}
@@ -611,18 +600,16 @@ std::unique_ptr<JogCon> JogCon::Create(u32 index)
return std::make_unique<JogCon>(index);
}
static const Controller::ControllerBindingInfo s_binding_info[] = {
constinit const Controller::ControllerBindingInfo JogCon::s_binding_info[] = {
#define BUTTON(name, display_name, icon_name, button, genb) \
{name, display_name, icon_name, static_cast<u32>(button), InputBindingInfo::Type::Button, genb}
#define AXIS(name, display_name, icon_name, halfaxis, genb) \
{name, \
display_name, \
icon_name, \
static_cast<u32>(JogCon::Button::MaxCount) + static_cast<u32>(halfaxis), \
HALFAXIS_BIND_START_INDEX + 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::LED, genb}
// clang-format off
BUTTON("Up", TRANSLATE_NOOP("JogCon", "D-Pad Up"), ICON_PF_DPAD_UP, JogCon::Button::Up, GenericInputBinding::DPadUp),
@@ -644,19 +631,19 @@ 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,
GenericInputBinding::LargeMotor},
{"ModeLED", TRANSLATE_NOOP("JogCon", "Mode LED"), ICON_PF_ANALOG_LEFT_RIGHT, LED_BIND_START_INDEX,
InputBindingInfo::Type::LED, GenericInputBinding::ModeLED},
{"ForceFeedbackDevice", TRANSLATE_NOOP("JogCon", "Force Feedback Device"), nullptr,
static_cast<u32>(JogCon::Button::MaxCount) + static_cast<u32>(JogCon::HalfAxis::MaxCount),
{"Motor", TRANSLATE_NOOP("JogCon", "Vibration Motor"), ICON_PF_VIBRATION, MOTOR_BIND_START_INDEX,
InputBindingInfo::Type::Motor, GenericInputBinding::LargeMotor},
{"ForceFeedbackDevice", TRANSLATE_NOOP("JogCon", "Force Feedback Device"), nullptr, FFDEVICE_BIND_START_INDEX,
InputBindingInfo::Type::Device, GenericInputBinding::Unknown},
#undef BUTTON
#undef AXIS
#undef MODE_LED
};
static const SettingInfo s_settings[] = {

View File

@@ -54,7 +54,6 @@ public:
bool DoState(StateWrapper& sw, bool apply_input_state) override;
float GetBindState(u32 index) const override;
float GetVibrationMotorState(u32 index) const override;
void SetBindState(u32 index, float value) override;
u32 GetButtonStateBits() const override;
@@ -97,6 +96,13 @@ private:
static constexpr float DEFAULT_STEERING_HOLD_DEADZONE = 0.03f;
static constexpr u32 HALFAXIS_BIND_START_INDEX = static_cast<u32>(Button::MaxCount);
static constexpr u32 MOTOR_BIND_START_INDEX = HALFAXIS_BIND_START_INDEX + static_cast<u32>(HalfAxis::MaxCount);
static constexpr u32 LED_BIND_START_INDEX = MOTOR_BIND_START_INDEX + 1;
static constexpr u32 FFDEVICE_BIND_START_INDEX = LED_BIND_START_INDEX + 1;
static const Controller::ControllerBindingInfo s_binding_info[];
u8 GetIDByte() const;
u8 GetModeID() const;

View File

@@ -119,17 +119,27 @@ bool NeGconRumble::DoState(StateWrapper& sw, bool apply_input_state)
float NeGconRumble::GetBindState(u32 index) const
{
if (index >= static_cast<u32>(Button::Count))
if (index >= LED_BIND_START_INDEX)
{
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return 0.0f;
return static_cast<float>(m_half_axis_state[sub_index]) * (1.0f / 255.0f);
return BoolToFloat(index == LED_BIND_START_INDEX && m_analog_mode);
}
else if (index >= MOTOR_BIND_START_INDEX)
{
return m_motor_state[index - MOTOR_BIND_START_INDEX] * (1.0f / 255.0f);
}
else if (index >= (HALFAXIS_BIND_START_INDEX + static_cast<u32>(HalfAxis::I)))
{
return static_cast<float>(m_axis_state[index - (HALFAXIS_BIND_START_INDEX + static_cast<u32>(HalfAxis::I)) + 1]) *
(1.0f / 255.0f);
}
else if (index >= HALFAXIS_BIND_START_INDEX)
{
return static_cast<float>(m_half_axis_state[index - HALFAXIS_BIND_START_INDEX]) * (1.0f / 255.0f);
}
else if (index < static_cast<u32>(Button::Analog))
{
return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
const u16 bit = u16(1) << s_button_indices[static_cast<u8>(index)];
return BoolToFloat((m_button_state & bit) == 0);
}
else
{
@@ -137,11 +147,6 @@ float NeGconRumble::GetBindState(u32 index) const
}
}
float NeGconRumble::GetVibrationMotorState(u32 index) const
{
return ((index < m_motor_state.size()) ? m_motor_state[index] : 0) * (1.0f / 255.0f);
}
void NeGconRumble::SetBindState(u32 index, float value)
{
if (index == static_cast<s32>(Button::Analog))
@@ -273,25 +278,17 @@ void NeGconRumble::SetMotorState(u32 motor, u8 value)
if (m_motor_state[motor] != value)
{
m_motor_state[motor] = value;
UpdateHostVibration();
}
}
void NeGconRumble::UpdateHostVibration()
{
std::array<float, NUM_MOTORS> hvalues;
for (u32 motor = 0; motor < NUM_MOTORS; motor++)
{
// Curve from https://github.com/KrossX/Pokopom/blob/master/Pokopom/Input_XInput.cpp#L210
const u8 state = m_motor_state[motor];
const double x = static_cast<double>(std::clamp<s32>(static_cast<s32>(state) + m_vibration_bias[motor], 0, 255));
const double strength = 0.006474549734772402 * std::pow(x, 3.0) - 1.258165252213538 * std::pow(x, 2.0) +
156.82454281087692 * x + 3.637978807091713e-11;
hvalues[motor] = (state != 0) ? static_cast<float>(strength / 65535.0) : 0.0f;
const float hvalue = (state != 0) ? static_cast<float>(strength / 65535.0) : 0.0f;
DEV_LOG("Set {} motor to {} (raw {})", (motor == LargeMotor) ? "large" : "small", hvalue, state);
InputManager::SetPadVibrationIntensity(m_index, MOTOR_BIND_START_INDEX + motor, hvalue);
}
InputManager::SetPadVibrationIntensity(m_index, hvalues[0], hvalues[1]);
}
u8 NeGconRumble::GetExtraButtonMaskLSB() const
@@ -711,20 +708,20 @@ std::unique_ptr<NeGconRumble> NeGconRumble::Create(u32 index)
return std::make_unique<NeGconRumble>(index);
}
static const Controller::ControllerBindingInfo s_binding_info[] = {
constinit const Controller::ControllerBindingInfo NeGconRumble::s_binding_info[] = {
#define BUTTON(name, display_name, icon_name, button, genb) \
{name, display_name, icon_name, static_cast<u32>(button), InputBindingInfo::Type::Button, genb}
#define AXIS(name, display_name, icon_name, halfaxis, genb) \
{name, \
display_name, \
icon_name, \
static_cast<u32>(NeGconRumble::Button::Count) + static_cast<u32>(halfaxis), \
HALFAXIS_BIND_START_INDEX + static_cast<u32>(halfaxis), \
InputBindingInfo::Type::HalfAxis, \
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::LED, genb}
{name, display_name, icon_name, MOTOR_BIND_START_INDEX + index, InputBindingInfo::Type::Motor, genb}
#define MODE_LED(name, display_name, icon_name, index, genb) \
{name, display_name, icon_name, LED_BIND_START_INDEX + 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),
@@ -739,11 +736,11 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
AXIS("L", TRANSLATE_NOOP("NeGconRumble", "Left Trigger"), ICON_PF_LEFT_ANALOG_LEFT, NeGconRumble::HalfAxis::L, GenericInputBinding::L1),
BUTTON("R", TRANSLATE_NOOP("NeGconRumble", "Right Trigger"), ICON_PF_RIGHT_SHOULDER_R1, NeGconRumble::Button::R, GenericInputBinding::R1),
AXIS("SteeringLeft", TRANSLATE_NOOP("NeGconRumble", "Steering (Twist) Left"), ICON_PF_LEFT_ANALOG_LEFT, NeGconRumble::HalfAxis::SteeringLeft, GenericInputBinding::LeftStickLeft),
AXIS("SteeringRight", TRANSLATE_NOOP("NeGconRumble", "Steering (Twist) Right"), ICON_PF_LEFT_ANALOG_LEFT, NeGconRumble::HalfAxis::SteeringRight, GenericInputBinding::LeftStickRight),
AXIS("SteeringRight", TRANSLATE_NOOP("NeGconRumble", "Steering (Twist) Right"), ICON_PF_LEFT_ANALOG_RIGHT, NeGconRumble::HalfAxis::SteeringRight, GenericInputBinding::LeftStickRight),
BUTTON("Analog", TRANSLATE_NOOP("NeGconRumble", "Analog Toggle"), ICON_PF_ANALOG_LEFT_RIGHT, NeGconRumble::Button::Analog, GenericInputBinding::System),
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),
MOTOR("LargeMotor", TRANSLATE_NOOP("AnalogController", "Large Motor"), ICON_PF_VIBRATION_L, LargeMotor, GenericInputBinding::LargeMotor),
MOTOR("SmallMotor", TRANSLATE_NOOP("AnalogController", "Small Motor"), ICON_PF_VIBRATION, SmallMotor, GenericInputBinding::SmallMotor),
MODE_LED("ModeLED", TRANSLATE_NOOP("AnalogController", "Mode LED"), ICON_PF_ANALOG_LEFT_RIGHT, 0, GenericInputBinding::ModeLED),
// clang-format on

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
@@ -60,7 +60,6 @@ public:
bool DoState(StateWrapper& sw, bool apply_input_state) override;
float GetBindState(u32 index) const override;
float GetVibrationMotorState(u32 index) const override;
void SetBindState(u32 index, float value) override;
void ResetTransferState() override;
@@ -91,6 +90,12 @@ private:
static constexpr s16 DEFAULT_SMALL_MOTOR_VIBRATION_BIAS = 8;
static constexpr s16 DEFAULT_LARGE_MOTOR_VIBRATION_BIAS = 8;
static constexpr u32 HALFAXIS_BIND_START_INDEX = static_cast<u32>(Button::Count);
static constexpr u32 MOTOR_BIND_START_INDEX = HALFAXIS_BIND_START_INDEX + static_cast<u32>(HalfAxis::Count);
static constexpr u32 LED_BIND_START_INDEX = MOTOR_BIND_START_INDEX + NUM_MOTORS;
static const Controller::ControllerBindingInfo s_binding_info[];
std::array<s16, NUM_MOTORS> m_vibration_bias{DEFAULT_LARGE_MOTOR_VIBRATION_BIAS, DEFAULT_SMALL_MOTOR_VIBRATION_BIAS};
bool m_force_analog_on_reset = true;
@@ -140,7 +145,6 @@ private:
void SetAnalogMode(bool enabled, bool show_message);
void ProcessAnalogModeToggle();
void SetMotorState(u32 motor, u8 value);
void UpdateHostVibration();
u8 GetExtraButtonMaskLSB() const;
void ResetRumbleConfig();
void SetMotorStateForConfigIndex(int index, u8 value);

View File

@@ -10,6 +10,7 @@
#include "core/system.h"
#include "common/assert.h"
#include "common/bitutils.h"
#include "common/error.h"
#include "common/file_system.h"
#include "common/log.h"
@@ -72,6 +73,7 @@ struct PadVibrationBinding
InputBindingKey binding;
Timer::Value last_update_time;
InputSource* source;
u32 bind_index;
float last_intensity;
};
@@ -1030,16 +1032,21 @@ void InputManager::AddPadBindings(const SettingsInterface& si, const std::string
case InputBindingInfo::Type::Motor:
{
DebugAssert(bi.bind_index < std::size(vibration_binding.motors));
DebugAssert(bi.generic_mapping == GenericInputBinding::LargeMotor ||
bi.generic_mapping == GenericInputBinding::SmallMotor);
if (bindings.empty())
continue;
if (bindings.size() > 1)
WARNING_LOG("More than one vibration motor binding for {}:{}", pad_index, bi.name);
vibration_binding_valid |=
ParseBindingAndGetSource(bindings.front(), &vibration_binding.motors[bi.bind_index].binding,
&vibration_binding.motors[bi.bind_index].source);
PadVibrationBinding::Motor& motor =
vibration_binding.motors[BoolToUInt32(bi.generic_mapping == GenericInputBinding::SmallMotor)];
if (ParseBindingAndGetSource(bindings.front(), &motor.binding, &motor.source))
{
motor.bind_index = bi.bind_index;
vibration_binding_valid = true;
}
}
break;
@@ -1052,7 +1059,7 @@ void InputManager::AddPadBindings(const SettingsInterface& si, const std::string
{
// 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);
led_binding.last_intensity = controller->GetBindState(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);
@@ -1824,18 +1831,26 @@ std::unique_ptr<ForceFeedbackDevice> InputManager::CreateForceFeedbackDevice(con
// Vibration
// ------------------------------------------------------------------------
void InputManager::SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensity,
float small_motor_intensity)
void InputManager::SetPadVibrationIntensity(u32 pad_index, u32 bind_index, float intensity)
{
for (PadVibrationBinding& pad : s_state.pad_vibration_array)
{
if (pad.pad_index != pad_index)
continue;
pad.motors[0].last_intensity = large_or_single_motor_intensity;
pad.motors[0].last_update_time = 0; // force update at end of frame
pad.motors[1].last_intensity = small_motor_intensity;
pad.motors[1].last_update_time = 0; // force update at end of frame
PadVibrationBinding::Motor* motor;
if (bind_index == pad.motors[0].bind_index)
motor = &pad.motors[0];
else if (bind_index == pad.motors[1].bind_index)
motor = &pad.motors[1];
else
break;
if (motor->last_intensity == intensity)
break;
motor->last_intensity = intensity;
motor->last_update_time = 0; // force update at end of frame
break;
}
}

View File

@@ -346,7 +346,7 @@ 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.
void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensity, float small_motor_intensity);
void SetPadVibrationIntensity(u32 pad_index, u32 bind_index, float intensity);
/// Zeros all vibration intensities. Call when pausing.
/// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes.