mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-12-01 18:15:41 +00:00
CommonHostInterface: Reimplement controller rumble support
Even better than before, supports separate motor control.
This commit is contained in:
parent
7677c95fa7
commit
d9ebb975b2
|
@ -436,3 +436,8 @@ Controller::ButtonList AnalogController::StaticGetButtonNames()
|
||||||
B(Square), B(L1), B(L2), B(R1), B(R2), B(L3), B(R3)};
|
B(Square), B(L1), B(L2), B(R1), B(R2), B(L3), B(R3)};
|
||||||
#undef B
|
#undef B
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 AnalogController::StaticGetVibrationMotorCount()
|
||||||
|
{
|
||||||
|
return NUM_MOTORS;
|
||||||
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ public:
|
||||||
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
|
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
|
||||||
static AxisList StaticGetAxisNames();
|
static AxisList StaticGetAxisNames();
|
||||||
static ButtonList StaticGetButtonNames();
|
static ButtonList StaticGetButtonNames();
|
||||||
|
static u32 StaticGetVibrationMotorCount();
|
||||||
|
|
||||||
ControllerType GetType() const override;
|
ControllerType GetType() const override;
|
||||||
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
|
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
|
||||||
|
|
|
@ -94,6 +94,22 @@ Controller::ButtonList Controller::GetButtonNames(ControllerType type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 Controller::GetVibrationMotorCount(ControllerType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ControllerType::DigitalController:
|
||||||
|
return DigitalController::StaticGetVibrationMotorCount();
|
||||||
|
|
||||||
|
case ControllerType::AnalogController:
|
||||||
|
return AnalogController::StaticGetVibrationMotorCount();
|
||||||
|
|
||||||
|
case ControllerType::None:
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<s32> Controller::GetAxisCodeByName(ControllerType type, std::string_view axis_name)
|
std::optional<s32> Controller::GetAxisCodeByName(ControllerType type, std::string_view axis_name)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
|
|
|
@ -61,4 +61,7 @@ public:
|
||||||
|
|
||||||
/// Returns a list of buttons for the specified controller type.
|
/// Returns a list of buttons for the specified controller type.
|
||||||
static ButtonList GetButtonNames(ControllerType type);
|
static ButtonList GetButtonNames(ControllerType type);
|
||||||
|
|
||||||
|
/// Returns the number of vibration motors.
|
||||||
|
static u32 GetVibrationMotorCount(ControllerType type);
|
||||||
};
|
};
|
||||||
|
|
|
@ -147,3 +147,8 @@ Controller::ButtonList DigitalController::StaticGetButtonNames()
|
||||||
B(Cross), B(Circle), B(Square), B(L1), B(L2), B(R1), B(R2)};
|
B(Cross), B(Circle), B(Square), B(L1), B(L2), B(R1), B(R2)};
|
||||||
#undef B
|
#undef B
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 DigitalController::StaticGetVibrationMotorCount()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ public:
|
||||||
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
|
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
|
||||||
static AxisList StaticGetAxisNames();
|
static AxisList StaticGetAxisNames();
|
||||||
static ButtonList StaticGetButtonNames();
|
static ButtonList StaticGetButtonNames();
|
||||||
|
static u32 StaticGetVibrationMotorCount();
|
||||||
|
|
||||||
ControllerType GetType() const override;
|
ControllerType GetType() const override;
|
||||||
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
|
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
|
||||||
|
|
|
@ -163,6 +163,7 @@ void GPU_HW_OpenGL::SetCapabilities(HostDisplay* host_display)
|
||||||
Log_WarningPrintf("GL_EXT_copy_image missing, this may affect performance.");
|
Log_WarningPrintf("GL_EXT_copy_image missing, this may affect performance.");
|
||||||
|
|
||||||
m_supports_texture_buffer = (GLAD_GL_VERSION_3_1 || GLAD_GL_ES_VERSION_3_2);
|
m_supports_texture_buffer = (GLAD_GL_VERSION_3_1 || GLAD_GL_ES_VERSION_3_2);
|
||||||
|
m_supports_texture_buffer = false;
|
||||||
if (m_supports_texture_buffer)
|
if (m_supports_texture_buffer)
|
||||||
{
|
{
|
||||||
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, reinterpret_cast<GLint*>(&m_max_texture_buffer_size));
|
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, reinterpret_cast<GLint*>(&m_max_texture_buffer_size));
|
||||||
|
@ -766,7 +767,7 @@ void GPU_HW_OpenGL::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 wid
|
||||||
src_y = m_vram_texture.GetHeight() - src_y - height;
|
src_y = m_vram_texture.GetHeight() - src_y - height;
|
||||||
dst_y = m_vram_texture.GetHeight() - dst_y - height;
|
dst_y = m_vram_texture.GetHeight() - dst_y - height;
|
||||||
|
|
||||||
if (GLAD_GL_VERSION_4_3)
|
/*if (GLAD_GL_VERSION_4_3)
|
||||||
{
|
{
|
||||||
glCopyImageSubData(m_vram_texture.GetGLId(), GL_TEXTURE_2D, 0, src_x, src_y, 0, m_vram_texture.GetGLId(),
|
glCopyImageSubData(m_vram_texture.GetGLId(), GL_TEXTURE_2D, 0, src_x, src_y, 0, m_vram_texture.GetGLId(),
|
||||||
GL_TEXTURE_2D, 0, dst_x, dst_y, 0, width, height, 1);
|
GL_TEXTURE_2D, 0, dst_x, dst_y, 0, width, height, 1);
|
||||||
|
@ -776,7 +777,7 @@ void GPU_HW_OpenGL::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 wid
|
||||||
glCopyImageSubDataEXT(m_vram_texture.GetGLId(), GL_TEXTURE_2D, 0, src_x, src_y, 0, m_vram_texture.GetGLId(),
|
glCopyImageSubDataEXT(m_vram_texture.GetGLId(), GL_TEXTURE_2D, 0, src_x, src_y, 0, m_vram_texture.GetGLId(),
|
||||||
GL_TEXTURE_2D, 0, dst_x, dst_y, 0, width, height, 1);
|
GL_TEXTURE_2D, 0, dst_x, dst_y, 0, width, height, 1);
|
||||||
}
|
}
|
||||||
else
|
else*/
|
||||||
{
|
{
|
||||||
glDisable(GL_SCISSOR_TEST);
|
glDisable(GL_SCISSOR_TEST);
|
||||||
m_vram_texture.BindFramebuffer(GL_READ_FRAMEBUFFER);
|
m_vram_texture.BindFramebuffer(GL_READ_FRAMEBUFFER);
|
||||||
|
@ -794,7 +795,7 @@ void GPU_HW_OpenGL::UpdateVRAMReadTexture()
|
||||||
const u32 x = scaled_rect.left;
|
const u32 x = scaled_rect.left;
|
||||||
const u32 y = m_vram_texture.GetHeight() - scaled_rect.top - height;
|
const u32 y = m_vram_texture.GetHeight() - scaled_rect.top - height;
|
||||||
|
|
||||||
if (GLAD_GL_VERSION_4_3)
|
/*if (GLAD_GL_VERSION_4_3)
|
||||||
{
|
{
|
||||||
glCopyImageSubData(m_vram_texture.GetGLId(), GL_TEXTURE_2D, 0, x, y, 0, m_vram_read_texture.GetGLId(),
|
glCopyImageSubData(m_vram_texture.GetGLId(), GL_TEXTURE_2D, 0, x, y, 0, m_vram_read_texture.GetGLId(),
|
||||||
GL_TEXTURE_2D, 0, x, y, 0, width, height, 1);
|
GL_TEXTURE_2D, 0, x, y, 0, width, height, 1);
|
||||||
|
@ -804,7 +805,7 @@ void GPU_HW_OpenGL::UpdateVRAMReadTexture()
|
||||||
glCopyImageSubDataEXT(m_vram_texture.GetGLId(), GL_TEXTURE_2D, 0, x, y, 0, m_vram_read_texture.GetGLId(),
|
glCopyImageSubDataEXT(m_vram_texture.GetGLId(), GL_TEXTURE_2D, 0, x, y, 0, m_vram_read_texture.GetGLId(),
|
||||||
GL_TEXTURE_2D, 0, x, y, 0, width, height, 1);
|
GL_TEXTURE_2D, 0, x, y, 0, width, height, 1);
|
||||||
}
|
}
|
||||||
else
|
else*/
|
||||||
{
|
{
|
||||||
m_vram_read_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER);
|
m_vram_read_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER);
|
||||||
m_vram_texture.BindFramebuffer(GL_READ_FRAMEBUFFER);
|
m_vram_texture.BindFramebuffer(GL_READ_FRAMEBUFFER);
|
||||||
|
|
|
@ -547,7 +547,7 @@ float4 SampleFromVRAM(int4 texpage, int2 icoord)
|
||||||
texcol.rgb /= float3(ialpha, ialpha, ialpha);
|
texcol.rgb /= float3(ialpha, ialpha, ialpha);
|
||||||
semitransparent = (texcol.a != 0.0);
|
semitransparent = (texcol.a != 0.0);
|
||||||
#else
|
#else
|
||||||
float4 texcol = SampleFromVRAM(v_texpage, int2(v_tex0));
|
float4 texcol = SampleFromVRAM(v_texpage, int2(floor(v_tex0)));
|
||||||
if (VECTOR_EQ(texcol, TRANSPARENT_PIXEL_COLOR))
|
if (VECTOR_EQ(texcol, TRANSPARENT_PIXEL_COLOR))
|
||||||
discard;
|
discard;
|
||||||
|
|
||||||
|
|
|
@ -792,6 +792,7 @@ void QtHostInterface::threadEntryPoint()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_system->RunFrame();
|
m_system->RunFrame();
|
||||||
|
UpdateControllerRumble();
|
||||||
|
|
||||||
renderDisplay();
|
renderDisplay();
|
||||||
|
|
||||||
|
|
|
@ -1301,10 +1301,11 @@ void SDLHostInterface::Run()
|
||||||
if (m_system && !m_paused)
|
if (m_system && !m_paused)
|
||||||
{
|
{
|
||||||
m_system->RunFrame();
|
m_system->RunFrame();
|
||||||
|
UpdateControllerRumble();
|
||||||
if (m_frame_step_request)
|
if (m_frame_step_request)
|
||||||
{
|
{
|
||||||
m_frame_step_request = false;
|
m_frame_step_request = false;
|
||||||
m_paused = true;
|
PauseSystem(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -335,8 +335,20 @@ void CommonHostInterface::OnSystemPaused(bool paused)
|
||||||
{
|
{
|
||||||
HostInterface::OnSystemPaused(paused);
|
HostInterface::OnSystemPaused(paused);
|
||||||
|
|
||||||
if (paused && IsFullscreen())
|
if (paused)
|
||||||
|
{
|
||||||
|
if (IsFullscreen())
|
||||||
SetFullscreen(false);
|
SetFullscreen(false);
|
||||||
|
|
||||||
|
StopControllerRumble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommonHostInterface::OnSystemDestroyed()
|
||||||
|
{
|
||||||
|
HostInterface::OnSystemDestroyed();
|
||||||
|
|
||||||
|
StopControllerRumble();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommonHostInterface::OnControllerTypeChanged(u32 slot)
|
void CommonHostInterface::OnControllerTypeChanged(u32 slot)
|
||||||
|
@ -405,6 +417,55 @@ void CommonHostInterface::UpdateInputMap(SettingsInterface& si)
|
||||||
UpdateHotkeyInputMap(si);
|
UpdateHotkeyInputMap(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommonHostInterface::AddControllerRumble(u32 controller_index, u32 num_motors, ControllerRumbleCallback callback)
|
||||||
|
{
|
||||||
|
ControllerRumbleState rumble;
|
||||||
|
rumble.controller_index = 0;
|
||||||
|
rumble.num_motors = std::min<u32>(num_motors, ControllerRumbleState::MAX_MOTORS);
|
||||||
|
rumble.last_strength.fill(0.0f);
|
||||||
|
rumble.update_callback = std::move(callback);
|
||||||
|
m_controller_vibration_motors.push_back(std::move(rumble));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommonHostInterface::UpdateControllerRumble()
|
||||||
|
{
|
||||||
|
DebugAssert(m_system);
|
||||||
|
|
||||||
|
for (ControllerRumbleState& rumble : m_controller_vibration_motors)
|
||||||
|
{
|
||||||
|
Controller* controller = m_system->GetController(rumble.controller_index);
|
||||||
|
if (!controller)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
for (u32 i = 0; i < rumble.num_motors; i++)
|
||||||
|
{
|
||||||
|
const float strength = controller->GetVibrationMotorStrength(i);
|
||||||
|
changed |= (strength != rumble.last_strength[i]);
|
||||||
|
rumble.last_strength[i] = strength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
rumble.update_callback(rumble.last_strength.data(), rumble.num_motors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommonHostInterface::StopControllerRumble()
|
||||||
|
{
|
||||||
|
for (ControllerRumbleState& rumble : m_controller_vibration_motors)
|
||||||
|
{
|
||||||
|
bool changed = true;
|
||||||
|
for (u32 i = 0; i < rumble.num_motors; i++)
|
||||||
|
{
|
||||||
|
changed |= (rumble.last_strength[i] != 0.0f);
|
||||||
|
rumble.last_strength[i] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
rumble.update_callback(rumble.last_strength.data(), rumble.num_motors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool SplitBinding(const std::string& binding, std::string_view* device, std::string_view* sub_binding)
|
static bool SplitBinding(const std::string& binding, std::string_view* device, std::string_view* sub_binding)
|
||||||
{
|
{
|
||||||
const std::string::size_type slash_pos = binding.find('/');
|
const std::string::size_type slash_pos = binding.find('/');
|
||||||
|
@ -421,6 +482,9 @@ static bool SplitBinding(const std::string& binding, std::string_view* device, s
|
||||||
|
|
||||||
void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
|
void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
|
||||||
{
|
{
|
||||||
|
StopControllerRumble();
|
||||||
|
m_controller_vibration_motors.clear();
|
||||||
|
|
||||||
for (u32 controller_index = 0; controller_index < 2; controller_index++)
|
for (u32 controller_index = 0; controller_index < 2; controller_index++)
|
||||||
{
|
{
|
||||||
const ControllerType ctype = m_settings.controller_types[controller_index];
|
const ControllerType ctype = m_settings.controller_types[controller_index];
|
||||||
|
@ -477,6 +541,14 @@ void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const u32 num_motors = Controller::GetVibrationMotorCount(ctype);
|
||||||
|
if (num_motors > 0)
|
||||||
|
{
|
||||||
|
const std::vector<std::string> bindings = si.GetStringList(category, TinyString::FromFormat("Rumble"));
|
||||||
|
for (const std::string& binding : bindings)
|
||||||
|
AddRumbleToInputMap(binding, controller_index, num_motors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,6 +672,34 @@ bool CommonHostInterface::AddAxisToInputMap(const std::string& binding, const st
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CommonHostInterface::AddRumbleToInputMap(const std::string& binding, u32 controller_index, u32 num_motors)
|
||||||
|
{
|
||||||
|
if (StringUtil::StartsWith(binding, "Controller"))
|
||||||
|
{
|
||||||
|
if (!m_controller_interface)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("No controller interface set, cannot bind '%s'", binding.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<int> host_controller_index = StringUtil::FromChars<int>(binding.substr(10));
|
||||||
|
if (!host_controller_index || *host_controller_index < 0)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Invalid controller index in rumble binding '%s'", binding.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddControllerRumble(controller_index, num_motors,
|
||||||
|
std::bind(&ControllerInterface::SetControllerRumbleStrength, m_controller_interface.get(),
|
||||||
|
host_controller_index.value(), std::placeholders::_1, std::placeholders::_2));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_WarningPrintf("Unknown input device in rumble binding '%s'", binding.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CommonHostInterface::RegisterGeneralHotkeys()
|
void CommonHostInterface::RegisterGeneralHotkeys()
|
||||||
{
|
{
|
||||||
RegisterHotkey(StaticString("General"), StaticString("FastForward"), StaticString("Toggle Fast Forward"),
|
RegisterHotkey(StaticString("General"), StaticString("FastForward"), StaticString("Toggle Fast Forward"),
|
||||||
|
|
|
@ -22,6 +22,7 @@ public:
|
||||||
|
|
||||||
using InputButtonHandler = std::function<void(bool)>;
|
using InputButtonHandler = std::function<void(bool)>;
|
||||||
using InputAxisHandler = std::function<void(float)>;
|
using InputAxisHandler = std::function<void(float)>;
|
||||||
|
using ControllerRumbleCallback = std::function<void(const float*, u32)>;
|
||||||
|
|
||||||
struct HotkeyInfo
|
struct HotkeyInfo
|
||||||
{
|
{
|
||||||
|
@ -69,6 +70,7 @@ protected:
|
||||||
|
|
||||||
virtual void OnSystemCreated() override;
|
virtual void OnSystemCreated() override;
|
||||||
virtual void OnSystemPaused(bool paused) override;
|
virtual void OnSystemPaused(bool paused) override;
|
||||||
|
virtual void OnSystemDestroyed() override;
|
||||||
virtual void OnControllerTypeChanged(u32 slot) override;
|
virtual void OnControllerTypeChanged(u32 slot) override;
|
||||||
|
|
||||||
virtual void SetDefaultSettings(SettingsInterface& si) override;
|
virtual void SetDefaultSettings(SettingsInterface& si) override;
|
||||||
|
@ -79,6 +81,7 @@ protected:
|
||||||
const std::string_view& button, InputButtonHandler handler);
|
const std::string_view& button, InputButtonHandler handler);
|
||||||
virtual bool AddAxisToInputMap(const std::string& binding, const std::string_view& device,
|
virtual bool AddAxisToInputMap(const std::string& binding, const std::string_view& device,
|
||||||
const std::string_view& axis, InputAxisHandler handler);
|
const std::string_view& axis, InputAxisHandler handler);
|
||||||
|
virtual bool AddRumbleToInputMap(const std::string& binding, u32 controller_index, u32 num_motors);
|
||||||
|
|
||||||
/// Reloads the input map from config. Callable from controller interface.
|
/// Reloads the input map from config. Callable from controller interface.
|
||||||
virtual void UpdateInputMap() = 0;
|
virtual void UpdateInputMap() = 0;
|
||||||
|
@ -87,6 +90,10 @@ protected:
|
||||||
bool HandleHostKeyEvent(HostKeyCode code, bool pressed);
|
bool HandleHostKeyEvent(HostKeyCode code, bool pressed);
|
||||||
void UpdateInputMap(SettingsInterface& si);
|
void UpdateInputMap(SettingsInterface& si);
|
||||||
|
|
||||||
|
void AddControllerRumble(u32 controller_index, u32 num_motors, ControllerRumbleCallback callback);
|
||||||
|
void UpdateControllerRumble();
|
||||||
|
void StopControllerRumble();
|
||||||
|
|
||||||
std::unique_ptr<ControllerInterface> m_controller_interface;
|
std::unique_ptr<ControllerInterface> m_controller_interface;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -101,6 +108,21 @@ private:
|
||||||
// input key maps
|
// input key maps
|
||||||
std::map<HostKeyCode, InputButtonHandler> m_keyboard_input_handlers;
|
std::map<HostKeyCode, InputButtonHandler> m_keyboard_input_handlers;
|
||||||
|
|
||||||
|
// controller vibration motors/rumble
|
||||||
|
struct ControllerRumbleState
|
||||||
|
{
|
||||||
|
enum : u32
|
||||||
|
{
|
||||||
|
MAX_MOTORS = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 controller_index;
|
||||||
|
u32 num_motors;
|
||||||
|
std::array<float, MAX_MOTORS> last_strength;
|
||||||
|
ControllerRumbleCallback update_callback;
|
||||||
|
};
|
||||||
|
std::vector<ControllerRumbleState> m_controller_vibration_motors;
|
||||||
|
|
||||||
// running in batch mode? i.e. exit after stopping emulation
|
// running in batch mode? i.e. exit after stopping emulation
|
||||||
bool m_batch_mode = false;
|
bool m_batch_mode = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -90,4 +90,3 @@ bool ControllerInterface::BindControllerAxisToButton(int controller_index, int a
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControllerInterface::UpdateControllerRumble() {}
|
|
||||||
|
|
|
@ -38,7 +38,10 @@ public:
|
||||||
ButtonCallback callback) = 0;
|
ButtonCallback callback) = 0;
|
||||||
|
|
||||||
virtual void PollEvents() = 0;
|
virtual void PollEvents() = 0;
|
||||||
virtual void UpdateControllerRumble() = 0;
|
|
||||||
|
// Changing rumble strength.
|
||||||
|
virtual u32 GetControllerRumbleMotorCount(int controller_index) = 0;
|
||||||
|
virtual void SetControllerRumbleStrength(int controller_index, const float* strengths, u32 num_motors) = 0;
|
||||||
|
|
||||||
// Input monitoring for external access.
|
// Input monitoring for external access.
|
||||||
struct Hook
|
struct Hook
|
||||||
|
|
|
@ -161,12 +161,35 @@ bool SDLControllerInterface::OpenGameController(int index)
|
||||||
cd.controller = gcontroller;
|
cd.controller = gcontroller;
|
||||||
cd.player_id = player_id;
|
cd.player_id = player_id;
|
||||||
cd.joystick_id = joystick_id;
|
cd.joystick_id = joystick_id;
|
||||||
|
cd.haptic_left_right_effect = -1;
|
||||||
|
|
||||||
SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick);
|
SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick);
|
||||||
if (SDL_HapticRumbleSupported(haptic) && SDL_HapticRumbleInit(haptic) == 0)
|
if (haptic)
|
||||||
|
{
|
||||||
|
SDL_HapticEffect ef = {};
|
||||||
|
ef.leftright.type = SDL_HAPTIC_LEFTRIGHT;
|
||||||
|
ef.leftright.length = 1000;
|
||||||
|
|
||||||
|
int ef_id = SDL_HapticNewEffect(haptic, &ef);
|
||||||
|
if (ef_id >= 0)
|
||||||
|
{
|
||||||
cd.haptic = haptic;
|
cd.haptic = haptic;
|
||||||
else if (haptic)
|
cd.haptic_left_right_effect = ef_id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create haptic left/right effect: %s", SDL_GetError());
|
||||||
|
if (SDL_HapticRumbleSupported(haptic) && SDL_HapticRumbleInit(haptic) != 0)
|
||||||
|
{
|
||||||
|
cd.haptic = haptic;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("No haptic rumble supported: %s", SDL_GetError());
|
||||||
SDL_HapticClose(haptic);
|
SDL_HapticClose(haptic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cd.haptic)
|
if (cd.haptic)
|
||||||
Log_InfoPrintf("Rumble is supported on '%s'", SDL_GameControllerName(gcontroller));
|
Log_InfoPrintf("Rumble is supported on '%s'", SDL_GameControllerName(gcontroller));
|
||||||
|
@ -314,31 +337,52 @@ bool SDLControllerInterface::HandleControllerButtonEvent(const SDL_Event* ev)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLControllerInterface::UpdateControllerRumble()
|
u32 SDLControllerInterface::GetControllerRumbleMotorCount(int controller_index)
|
||||||
{
|
{
|
||||||
for (auto& cd : m_controllers)
|
auto it = GetControllerDataForPlayerId(controller_index);
|
||||||
{
|
if (it == m_controllers.end())
|
||||||
// TODO: FIXME proper binding
|
return 0;
|
||||||
if (!cd.haptic || cd.player_id < 0 || cd.player_id >= 2)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
float new_strength = 0.0f;
|
return (it->haptic_left_right_effect >= 0) ? 2 : (it->haptic ? 1 : 0);
|
||||||
Controller* controller = GetController(cd.player_id);
|
}
|
||||||
if (controller)
|
|
||||||
|
void SDLControllerInterface::SetControllerRumbleStrength(int controller_index, const float* strengths, u32 num_motors)
|
||||||
|
{
|
||||||
|
auto it = GetControllerDataForPlayerId(controller_index);
|
||||||
|
if (it == m_controllers.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// we'll update before this duration is elapsed
|
||||||
|
static constexpr float MIN_STRENGTH = 0.01f;
|
||||||
|
static constexpr u32 DURATION = 100000;
|
||||||
|
|
||||||
|
SDL_Haptic* haptic = static_cast<SDL_Haptic*>(it->haptic);
|
||||||
|
if (it->haptic_left_right_effect >= 0 && num_motors > 1)
|
||||||
{
|
{
|
||||||
const u32 motor_count = controller->GetVibrationMotorCount();
|
if (strengths[0] >= MIN_STRENGTH || strengths[1] >= MIN_STRENGTH)
|
||||||
for (u32 i = 0; i < motor_count; i++)
|
{
|
||||||
new_strength = std::max(new_strength, controller->GetVibrationMotorStrength(i));
|
SDL_HapticEffect ef;
|
||||||
|
ef.type = SDL_HAPTIC_LEFTRIGHT;
|
||||||
|
ef.leftright.large_magnitude = static_cast<u32>(strengths[0] * 65535.0f);
|
||||||
|
ef.leftright.small_magnitude = static_cast<u32>(strengths[1] * 65535.0f);
|
||||||
|
ef.leftright.length = DURATION;
|
||||||
|
SDL_HapticUpdateEffect(haptic, it->haptic_left_right_effect, &ef);
|
||||||
|
SDL_HapticRunEffect(haptic, it->haptic_left_right_effect, SDL_HAPTIC_INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cd.last_rumble_strength == new_strength)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (new_strength > 0.01f)
|
|
||||||
SDL_HapticRumblePlay(static_cast<SDL_Haptic*>(cd.haptic), new_strength, 100000);
|
|
||||||
else
|
else
|
||||||
SDL_HapticRumbleStop(static_cast<SDL_Haptic*>(cd.haptic));
|
{
|
||||||
|
SDL_HapticStopEffect(haptic, it->haptic_left_right_effect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float max_strength = 0.0f;
|
||||||
|
for (u32 i = 0; i < num_motors; i++)
|
||||||
|
max_strength = std::max(max_strength, strengths[i]);
|
||||||
|
|
||||||
cd.last_rumble_strength = new_strength;
|
if (max_strength >= MIN_STRENGTH)
|
||||||
|
SDL_HapticRumblePlay(haptic, max_strength, DURATION);
|
||||||
|
else
|
||||||
|
SDL_HapticRumbleStop(haptic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,20 +25,22 @@ public:
|
||||||
bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback) override;
|
bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback) override;
|
||||||
bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction, ButtonCallback callback) override;
|
bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction, ButtonCallback callback) override;
|
||||||
|
|
||||||
|
// Changing rumble strength.
|
||||||
|
u32 GetControllerRumbleMotorCount(int controller_index) override;
|
||||||
|
void SetControllerRumbleStrength(int controller_index, const float* strengths, u32 num_motors) override;
|
||||||
|
|
||||||
void PollEvents() override;
|
void PollEvents() override;
|
||||||
|
|
||||||
bool ProcessSDLEvent(const SDL_Event* event);
|
bool ProcessSDLEvent(const SDL_Event* event);
|
||||||
|
|
||||||
void UpdateControllerRumble() override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ControllerData
|
struct ControllerData
|
||||||
{
|
{
|
||||||
void* controller;
|
void* controller;
|
||||||
void* haptic;
|
void* haptic;
|
||||||
|
int haptic_left_right_effect;
|
||||||
int joystick_id;
|
int joystick_id;
|
||||||
int player_id;
|
int player_id;
|
||||||
float last_rumble_strength;
|
|
||||||
|
|
||||||
std::array<AxisCallback, MAX_NUM_AXISES> axis_mapping;
|
std::array<AxisCallback, MAX_NUM_AXISES> axis_mapping;
|
||||||
std::array<ButtonCallback, MAX_NUM_BUTTONS> button_mapping;
|
std::array<ButtonCallback, MAX_NUM_BUTTONS> button_mapping;
|
||||||
|
|
Loading…
Reference in a new issue