Skip to content

Commit

Permalink
input/evdev: handle flatness deadzone value
Browse files Browse the repository at this point in the history
This may fix issues with sticky axis on evdev.
Also refactors some redundant axis scaling functions.
  • Loading branch information
Megamouse committed Dec 13, 2023
1 parent c9d8d80 commit 9531906
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 35 deletions.
73 changes: 46 additions & 27 deletions rpcs3/Emu/Io/PadHandler.cpp
Expand Up @@ -32,19 +32,52 @@ s32 PadHandlerBase::MultipliedInput(s32 raw_value, s32 multiplier)
return (multiplier * raw_value) / 100;
}

// Get new scaled value between 0 and 255 based on its minimum and maximum
f32 PadHandlerBase::ScaledInput(s32 raw_value, int minimum, int maximum, f32 range)
// Get new scaled value between 0 and range based on its minimum and maximum
f32 PadHandlerBase::ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range)
{
// value based on max range converted to [0, 1]
const f32 val = static_cast<f32>(std::clamp(raw_value, minimum, maximum) - minimum) / (abs(maximum) + abs(minimum));
if (deadzone > 0 && deadzone > minimum)
{
// adjust minimum so we smoothly start at 0 when we surpass the deadzone value
minimum = deadzone;
}

// convert [min, max] to [0, 1]
const f32 val = static_cast<f32>(std::clamp(raw_value, minimum, maximum) - minimum) / (maximum - minimum);

// convert [0, 1] to [0, range]
return range * val;
}

// Get new scaled value between -255 and 255 based on its minimum and maximum
f32 PadHandlerBase::ScaledInput2(s32 raw_value, int minimum, int maximum, f32 range)
// Get new scaled value between -range and range based on its minimum and maximum
f32 PadHandlerBase::ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range)
{
// value based on max range converted to [0, 1]
const f32 val = static_cast<f32>(std::clamp(raw_value, minimum, maximum) - minimum) / (abs(maximum) + abs(minimum));
// convert [min, max] to [0, 1]
f32 val = static_cast<f32>(std::clamp(raw_value, minimum, maximum) - minimum) / (maximum - minimum);

if (deadzone > 0)
{
// convert [0, 1] to [-0.5, 0.5]
val -= 0.5f;

// Convert deadzone to [0, 0.5]
deadzone = std::max(0.0f, std::min(1.0f, deadzone / maximum)) / 2.0f;

if (val >= 0.0f)
{
// Apply deadzone. The result will be [0, 0.5]
val = ScaledInput(val, 0.0f, 0.5f, deadzone, 0.5f);
}
else
{
// Apply deadzone. The result will be [-0.5, 0]
val = ScaledInput(std::abs(val), 0, 0.5f, deadzone, 0.5f) * -1.0f;
}

// convert [-0.5, 0.5] back to [0, 1]
val += 0.5f;
}

// convert [0, 1] to [-range, range]
return (2.0f * range * val) - range;
}

Expand All @@ -56,33 +89,19 @@ u16 PadHandlerBase::NormalizeTriggerInput(u16 value, int threshold) const
return static_cast<u16>(0);
}

if (threshold <= trigger_min)
{
return static_cast<u16>(ScaledInput(value, trigger_min, trigger_max));
}

const s32 val = static_cast<s32>(static_cast<f32>(trigger_max) * (value - threshold) / (trigger_max - threshold));
return static_cast<u16>(ScaledInput(val, trigger_min, trigger_max));
return static_cast<u16>(ScaledInput(value, trigger_min, trigger_max, threshold));
}

// normalizes a directed input, meaning it will correspond to a single "button" and not an axis with two directions
// the input values must lie in 0+
u16 PadHandlerBase::NormalizeDirectedInput(s32 raw_value, s32 threshold, s32 maximum) const
{
if (threshold >= maximum || maximum <= 0)
if (threshold >= maximum || maximum <= 0 || raw_value < 0)
{
return static_cast<u16>(0);
}

const f32 val = static_cast<f32>(std::clamp(raw_value, 0, maximum)) / maximum; // value based on max range converted to [0, 1]

if (threshold <= 0)
{
return static_cast<u16>(255.0f * val);
}

const f32 thresh = static_cast<f32>(threshold) / maximum; // threshold converted to [0, 1]
return static_cast<u16>(255.0f * std::clamp((val - thresh) / (1.0f - thresh), 0.0f, 1.0f));
return static_cast<u16>(ScaledInput(raw_value, 0, maximum, threshold));
}

u16 PadHandlerBase::NormalizeStickInput(u16 raw_value, int threshold, int multiplier, bool ignore_threshold) const
Expand All @@ -91,10 +110,10 @@ u16 PadHandlerBase::NormalizeStickInput(u16 raw_value, int threshold, int multip

if (ignore_threshold)
{
return static_cast<u16>(ScaledInput(scaled_value, 0, thumb_max));
threshold = 0;
}

return NormalizeDirectedInput(scaled_value, threshold, thumb_max);
return static_cast<u16>(ScaledInput(scaled_value, 0, thumb_max, threshold));
}

// This function normalizes stick deadzone based on the DS3's deadzone, which is ~13%
Expand Down
4 changes: 2 additions & 2 deletions rpcs3/Emu/Io/PadHandler.h
Expand Up @@ -208,10 +208,10 @@ class PadHandlerBase
static s32 MultipliedInput(s32 raw_value, s32 multiplier);

// Get new scaled value between 0 and 255 based on its minimum and maximum
static f32 ScaledInput(s32 raw_value, int minimum, int maximum, f32 range = 255.0f);
static f32 ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range = 255.0f);

// Get new scaled value between -255 and 255 based on its minimum and maximum
static f32 ScaledInput2(s32 raw_value, int minimum, int maximum, f32 range = 255.0f);
static f32 ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range = 255.0f);

// Get normalized trigger value based on the range defined by a threshold
u16 NormalizeTriggerInput(u16 value, int threshold) const;
Expand Down
13 changes: 8 additions & 5 deletions rpcs3/Input/evdev_joystick_handler.cpp
Expand Up @@ -261,16 +261,17 @@ std::unordered_map<u64, std::pair<u16, bool>> evdev_joystick_handler::GetButtonV

const int min = libevdev_get_abs_minimum(dev, code);
const int max = libevdev_get_abs_maximum(dev, code);
const int flat = libevdev_get_abs_flat(dev, code);

// Triggers do not need handling of negative values
if (min >= 0 && !m_positive_axis.contains(code))
{
const float fvalue = ScaledInput(val, min, max);
const float fvalue = ScaledInput(val, min, max, flat);
button_values.emplace(code, std::make_pair<u16, bool>(static_cast<u16>(fvalue), false));
continue;
}

const float fvalue = ScaledInput2(val, min, max);
const float fvalue = ScaledAxisInput(val, min, max, flat);
if (fvalue < 0)
button_values.emplace(code, std::make_pair<u16, bool>(static_cast<u16>(std::abs(fvalue)), true));
else
Expand Down Expand Up @@ -939,6 +940,7 @@ void evdev_joystick_handler::get_mapping(const pad_ensemble& binding)
{
axis_wrapper->min = libevdev_get_abs_minimum(dev, evt.code);
axis_wrapper->max = libevdev_get_abs_maximum(dev, evt.code);
axis_wrapper->flat = libevdev_get_abs_flat(dev, evt.code);
axis_wrapper->is_initialized = true;

// Triggers do not need handling of negative values
Expand All @@ -951,7 +953,7 @@ void evdev_joystick_handler::get_mapping(const pad_ensemble& binding)
// Triggers do not need handling of negative values
if (axis_wrapper->is_trigger)
{
const u16 new_value = static_cast<u16>(ScaledInput(evt.value, axis_wrapper->min, axis_wrapper->max));
const u16 new_value = static_cast<u16>(ScaledInput(evt.value, axis_wrapper->min, axis_wrapper->max, axis_wrapper->flat));
u16& key_value = axis_wrapper->values[false];

if (key_value != new_value)
Expand All @@ -962,7 +964,7 @@ void evdev_joystick_handler::get_mapping(const pad_ensemble& binding)
}
else
{
const float fvalue = ScaledInput2(evt.value, axis_wrapper->min, axis_wrapper->max);
const float fvalue = ScaledAxisInput(evt.value, axis_wrapper->min, axis_wrapper->max, axis_wrapper->flat);
const bool is_negative = fvalue < 0;

const u16 new_value_0 = static_cast<u16>(std::abs(fvalue));
Expand Down Expand Up @@ -1054,8 +1056,9 @@ u16 evdev_joystick_handler::get_sensor_value(const libevdev* dev, const AnalogSe
{
const int min = libevdev_get_abs_minimum(dev, evt.code);
const int max = libevdev_get_abs_maximum(dev, evt.code);
const int flat = libevdev_get_abs_flat(dev, evt.code);

s16 value = ScaledInput(evt.value, min, max, 1023.0f);
s16 value = ScaledInput(evt.value, min, max, flat, 1023.0f);

if (sensor.m_mirrored)
{
Expand Down
1 change: 1 addition & 0 deletions rpcs3/Input/evdev_joystick_handler.h
Expand Up @@ -371,6 +371,7 @@ class evdev_joystick_handler final : public PadHandlerBase
bool is_trigger{};
int min{};
int max{};
int flat{};
};

struct EvdevDevice : public PadDevice
Expand Down
3 changes: 2 additions & 1 deletion rpcs3/Input/mm_joystick_handler.cpp
Expand Up @@ -420,7 +420,8 @@ std::unordered_map<u64, u16> mm_joystick_handler::GetButtonValues(const JOYINFOE

auto add_axis_value = [&](DWORD axis, UINT min, UINT max, u64 pos, u64 neg)
{
const float val = ScaledInput2(axis, min, max);
constexpr int deadzone = 0;
const float val = ScaledAxisInput(axis, min, max, deadzone);
if (val < 0)
{
button_values.emplace(pos, 0);
Expand Down

0 comments on commit 9531906

Please sign in to comment.