Controller: Move helper functions into their own file

pull/3750/head
Stenzek 4 months ago
parent 1e6a006bd0
commit 24fb22acf3
No known key found for this signature in database

@ -26,6 +26,7 @@ add_library(core
cheats.h cheats.h
controller.cpp controller.cpp
controller.h controller.h
controller_helpers.h
core.cpp core.cpp
core.h core.h
cpu_code_cache.cpp cpu_code_cache.cpp

@ -2,6 +2,7 @@
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "analog_controller.h" #include "analog_controller.h"
#include "controller_helpers.h"
#include "settings.h" #include "settings.h"
#include "system.h" #include "system.h"
@ -192,30 +193,30 @@ void AnalogController::SetBindState(u32 index, float value)
{ {
case HalfAxis::LLeft: case HalfAxis::LLeft:
case HalfAxis::LRight: case HalfAxis::LRight:
m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftX)] = ControllerHelpers::MergeHalfAxes(
MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)], m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)],
m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)], (m_invert_left_stick & 1)); m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)], (m_invert_left_stick & 1));
break; break;
case HalfAxis::LDown: case HalfAxis::LDown:
case HalfAxis::LUp: case HalfAxis::LUp:
m_axis_state[static_cast<u8>(Axis::LeftY)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = ControllerHelpers::MergeHalfAxes(
MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)], m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)],
m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)], (m_invert_left_stick & 2)); m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)], (m_invert_left_stick & 2));
break; break;
case HalfAxis::RLeft: case HalfAxis::RLeft:
case HalfAxis::RRight: case HalfAxis::RRight:
m_axis_state[static_cast<u8>(Axis::RightX)] = m_axis_state[static_cast<u8>(Axis::RightX)] = ControllerHelpers::MergeHalfAxes(
MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)], m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)],
m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)], (m_invert_right_stick & 1)); m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)], (m_invert_right_stick & 1));
break; break;
case HalfAxis::RDown: case HalfAxis::RDown:
case HalfAxis::RUp: case HalfAxis::RUp:
m_axis_state[static_cast<u8>(Axis::RightY)] = m_axis_state[static_cast<u8>(Axis::RightY)] = ControllerHelpers::MergeHalfAxes(
MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)], m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)],
m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)], (m_invert_right_stick & 2)); m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)], (m_invert_right_stick & 2));
break; break;
default: default:
@ -227,24 +228,24 @@ void AnalogController::SetBindState(u32 index, float value)
float pos_x, pos_y; float pos_x, pos_y;
if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft) if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft)
{ {
pos_x = pos_x = ControllerHelpers::MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)],
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)], m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)],
m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)], (m_invert_left_stick & 1)); (m_invert_left_stick & 1));
pos_y = pos_y = ControllerHelpers::MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)],
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)], m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)],
m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)], (m_invert_left_stick & 2)); (m_invert_left_stick & 2));
} }
else else
{ {
pos_x = pos_x = ControllerHelpers::MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)],
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)], m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)],
m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)], (m_invert_right_stick & 1)); (m_invert_right_stick & 1));
pos_y = pos_y = ControllerHelpers::MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)],
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)], m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)],
m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)], (m_invert_right_stick & 2)); (m_invert_right_stick & 2));
} }
if (InCircularDeadzone(m_analog_deadzone, pos_x, pos_y)) if (ControllerHelpers::InCircularDeadzone(m_analog_deadzone, pos_x, pos_y))
{ {
if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft) if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft)
m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = AXIS_CENTER; m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = AXIS_CENTER;

@ -2,6 +2,7 @@
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "analog_joystick.h" #include "analog_joystick.h"
#include "controller_helpers.h"
#include "system.h" #include "system.h"
#include "util/imgui_manager.h" #include "util/imgui_manager.h"
@ -112,30 +113,30 @@ void AnalogJoystick::SetBindState(u32 index, float value)
{ {
case HalfAxis::LLeft: case HalfAxis::LLeft:
case HalfAxis::LRight: case HalfAxis::LRight:
m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftX)] = ControllerHelpers::MergeHalfAxes(
MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)], m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)],
m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)], (m_invert_left_stick & 1)); m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)], (m_invert_left_stick & 1));
break; break;
case HalfAxis::LDown: case HalfAxis::LDown:
case HalfAxis::LUp: case HalfAxis::LUp:
m_axis_state[static_cast<u8>(Axis::LeftY)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = ControllerHelpers::MergeHalfAxes(
MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)], m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)],
m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)], (m_invert_left_stick & 2)); m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)], (m_invert_left_stick & 2));
break; break;
case HalfAxis::RLeft: case HalfAxis::RLeft:
case HalfAxis::RRight: case HalfAxis::RRight:
m_axis_state[static_cast<u8>(Axis::RightX)] = m_axis_state[static_cast<u8>(Axis::RightX)] = ControllerHelpers::MergeHalfAxes(
MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)], m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)],
m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)], (m_invert_right_stick & 1)); m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)], (m_invert_right_stick & 1));
break; break;
case HalfAxis::RDown: case HalfAxis::RDown:
case HalfAxis::RUp: case HalfAxis::RUp:
m_axis_state[static_cast<u8>(Axis::RightY)] = m_axis_state[static_cast<u8>(Axis::RightY)] = ControllerHelpers::MergeHalfAxes(
MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)], m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)],
m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)], (m_invert_right_stick & 2)); m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)], (m_invert_right_stick & 2));
break; break;
default: default:
@ -147,24 +148,24 @@ void AnalogJoystick::SetBindState(u32 index, float value)
float pos_x, pos_y; float pos_x, pos_y;
if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft) if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft)
{ {
pos_x = pos_x = ControllerHelpers::MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)],
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)], m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)],
m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)], (m_invert_left_stick & 1)); (m_invert_left_stick & 1));
pos_y = pos_y = ControllerHelpers::MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)],
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)], m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)],
m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)], (m_invert_left_stick & 2)); (m_invert_left_stick & 2));
} }
else else
{ {
pos_x = pos_x = ControllerHelpers::MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)],
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)], m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)],
m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)], (m_invert_right_stick & 1)); (m_invert_right_stick & 1));
pos_y = pos_y = ControllerHelpers::MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)],
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)], m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)],
m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)], (m_invert_right_stick & 2)); (m_invert_right_stick & 2));
} }
if (InCircularDeadzone(m_analog_deadzone, pos_x, pos_y)) if (ControllerHelpers::InCircularDeadzone(m_analog_deadzone, pos_x, pos_y))
{ {
if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft) if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft)
m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = AXIS_CENTER; m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = AXIS_CENTER;

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "controller.h" #include "controller.h"
@ -220,13 +220,6 @@ std::string Controller::GetSettingsSection(u32 pad)
return fmt::format("Pad{}", pad + 1u); return fmt::format("Pad{}", pad + 1u);
} }
bool Controller::InCircularDeadzone(float deadzone, float pos_x, float pos_y)
{
// Calculate the actual distance from center, and compare to deadzone radius.
const float distance = std::sqrt(pos_x * pos_x + pos_y * pos_y);
return (distance <= deadzone);
}
bool Controller::CanStartInAnalogMode(ControllerType ctype) bool Controller::CanStartInAnalogMode(ControllerType ctype)
{ {
if (!g_settings.apply_compatibility_settings) if (!g_settings.apply_compatibility_settings)
@ -239,17 +232,3 @@ bool Controller::CanStartInAnalogMode(ControllerType ctype)
return ((dbentry->supported_controllers & (1u << static_cast<u8>(ctype))) != 0 && return ((dbentry->supported_controllers & (1u << static_cast<u8>(ctype))) != 0 &&
!dbentry->HasTrait(GameDatabase::Trait::DisableAutoAnalogMode)); !dbentry->HasTrait(GameDatabase::Trait::DisableAutoAnalogMode));
} }
u8 Controller::MergeHalfAxes(u8 neg_value, u8 pos_value, bool invert)
{
if (invert)
std::swap(neg_value, pos_value);
return static_cast<u8>(128 + (static_cast<u32>(pos_value) / 2) - ((static_cast<u32>(neg_value) + 1) / 2));
}
float Controller::MergeHalfAxesToFloat(u8 neg_value, u8 pos_value, bool invert)
{
const float result = (static_cast<s32>(pos_value) - static_cast<s32>(neg_value)) / 255.0f;
return (invert ? -result : result);
}

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once #pragma once
@ -96,9 +96,6 @@ public:
static const ControllerInfo& GetControllerInfo(ControllerType type); static const ControllerInfo& GetControllerInfo(ControllerType type);
static const ControllerInfo* GetControllerInfo(std::string_view name); static const ControllerInfo* GetControllerInfo(std::string_view name);
/// Returns true if the specified coordinates are inside a circular deadzone.
static bool InCircularDeadzone(float deadzone, float pos_x, float pos_y);
/// Converts a global pad index to a multitap port and slot. /// Converts a global pad index to a multitap port and slot.
static std::tuple<u32, u32> ConvertPadToPortAndSlot(u32 index); static std::tuple<u32, u32> ConvertPadToPortAndSlot(u32 index);
@ -122,12 +119,6 @@ public:
/// Returns true if automatic analog mode can be used. /// Returns true if automatic analog mode can be used.
static bool CanStartInAnalogMode(ControllerType ctype); static bool CanStartInAnalogMode(ControllerType ctype);
/// Converts a 0..255 half-axis value to an unsigned 8-bit value, with 128 indicating center.
static u8 MergeHalfAxes(u8 neg_value, u8 pos_value, bool invert);
/// Converts a 0..255 half-axis value to a normalized floating-point value, with 0 indicating center.
static float MergeHalfAxesToFloat(u8 neg_value, u8 pos_value, bool invert);
protected: protected:
u32 m_index; u32 m_index;
}; };

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
#include "common/types.h"
#include <cmath>
namespace ControllerHelpers {
/// Returns true if the specified coordinates are inside a circular deadzone.
ALWAYS_INLINE bool InCircularDeadzone(float deadzone, float pos_x, float pos_y)
{
// Calculate the actual distance from center, and compare to deadzone radius.
const float distance = std::sqrt(pos_x * pos_x + pos_y * pos_y);
return (distance <= deadzone);
}
/// Converts a 0..255 half-axis value to an unsigned 8-bit value, with 128 indicating center.
ALWAYS_INLINE u8 MergeHalfAxes(u8 neg_value, u8 pos_value, bool invert)
{
if (invert)
std::swap(neg_value, pos_value);
return static_cast<u8>(128 + (static_cast<u32>(pos_value) / 2) - ((static_cast<u32>(neg_value) + 1) / 2));
}
/// Converts a 0..255 half-axis value to a normalized floating-point value, with 0 indicating center.
ALWAYS_INLINE float MergeHalfAxesToFloat(u8 neg_value, u8 pos_value, bool invert)
{
const float result = (static_cast<s32>(pos_value) - static_cast<s32>(neg_value)) / 255.0f;
return (invert ? -result : result);
}
} // namespace ControllerHelpers

@ -96,6 +96,7 @@
<ClInclude Include="cdrom_subq_replacement.h" /> <ClInclude Include="cdrom_subq_replacement.h" />
<ClInclude Include="cheats.h" /> <ClInclude Include="cheats.h" />
<ClInclude Include="achievements.h" /> <ClInclude Include="achievements.h" />
<ClInclude Include="controller_helpers.h" />
<ClInclude Include="core.h" /> <ClInclude Include="core.h" />
<ClInclude Include="core_private.h" /> <ClInclude Include="core_private.h" />
<ClInclude Include="cpu_code_cache_private.h" /> <ClInclude Include="cpu_code_cache_private.h" />

@ -160,6 +160,7 @@
<ClInclude Include="gpu_helpers.h" /> <ClInclude Include="gpu_helpers.h" />
<ClInclude Include="discord_presence.h" /> <ClInclude Include="discord_presence.h" />
<ClInclude Include="video_thread_private.h" /> <ClInclude Include="video_thread_private.h" />
<ClInclude Include="controller_helpers.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="gpu_sw_rasterizer.inl" /> <None Include="gpu_sw_rasterizer.inl" />

@ -2,6 +2,7 @@
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "negcon_rumble.h" #include "negcon_rumble.h"
#include "controller_helpers.h"
#include "settings.h" #include "settings.h"
#include "system.h" #include "system.h"
@ -172,8 +173,8 @@ void NeGconRumble::SetBindState(u32 index, float value)
static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f)); static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
m_axis_state[static_cast<u32>(Axis::Steering)] = m_axis_state[static_cast<u32>(Axis::Steering)] =
MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::SteeringLeft)], ControllerHelpers::MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::SteeringLeft)],
m_half_axis_state[static_cast<size_t>(HalfAxis::SteeringRight)], false); m_half_axis_state[static_cast<size_t>(HalfAxis::SteeringRight)], false);
} }
else if (index >= static_cast<u32>(Button::Count)) else if (index >= static_cast<u32>(Button::Count))
{ {

Loading…
Cancel
Save