mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-18 04:14:36 +00:00
Controller: Merge all binds into one index space
Simplifies things a bit.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user