From 706b98a0b9352ddbfbb8551c0bcdff8b44cda242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 31 Dec 2023 13:53:29 +0100 Subject: [PATCH] Joystick: Bias down the axis-to-button threshold if the "co-axis" is active. This makes it much easier to hit the diagonals in the case where you map the right stick on your 360 controller to say the DPAD for camera control in some game. No setting, let's see if that's required. Fixes #17792, it should now "just work". --- Core/ControlMapper.cpp | 52 ++++++++++++++++++++++++++++++++++++++---- Core/ControlMapper.h | 5 ++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index a8746afc085c..57e5066a1294 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -18,7 +18,28 @@ using KeyMap::MultiInputMapping; const float AXIS_BIND_THRESHOLD = 0.75f; const float AXIS_BIND_THRESHOLD_MOUSE = 0.01f; -float GetDeviceAxisThreshold(int device, const InputMapping &mapping) { + +// We reduce the threshold of some axes when another axis on the same stick is active. +// This makes it easier to hit diagonals if you bind an analog stick to four face buttons or D-Pad. +static InputAxis GetCoAxis(InputAxis axis) { + switch (axis) { + case JOYSTICK_AXIS_X: return JOYSTICK_AXIS_Y; + case JOYSTICK_AXIS_Y: return JOYSTICK_AXIS_X; + + // This looks weird, but it's simply how XInput axes are mapped. + case JOYSTICK_AXIS_Z: return JOYSTICK_AXIS_RZ; + case JOYSTICK_AXIS_RZ: return JOYSTICK_AXIS_Z; + + // Not sure if these two are used. + case JOYSTICK_AXIS_RX: return JOYSTICK_AXIS_RY; + case JOYSTICK_AXIS_RY: return JOYSTICK_AXIS_RX; + + default: + return JOYSTICK_AXIS_MAX; // invalid + } +} + +float ControlMapper::GetDeviceAxisThreshold(int device, const InputMapping &mapping) { if (device == DEVICE_ID_MOUSE) { return AXIS_BIND_THRESHOLD_MOUSE; } @@ -26,12 +47,27 @@ float GetDeviceAxisThreshold(int device, const InputMapping &mapping) { switch (KeyMap::GetAxisType((InputAxis)mapping.Axis(nullptr))) { case KeyMap::AxisType::TRIGGER: return g_Config.fAnalogTriggerThreshold; + case KeyMap::AxisType::STICK: + { + // Co-axis processing, see GetCoAxes comment. + InputAxis axis = (InputAxis)mapping.Axis(nullptr); + InputAxis coAxis = GetCoAxis(axis); + if (coAxis != JOYSTICK_AXIS_MAX) { + float absCoValue = fabsf(rawAxisValue_[(int)coAxis]); + if (absCoValue > 0.0f) { + // Bias down the threshold if the other axis is active. + float biasedThreshold = AXIS_BIND_THRESHOLD * (1.0f - absCoValue * 0.2f); + // INFO_LOG(SYSTEM, "coValue: %f threshold: %f", absCoValue, biasedThreshold); + return biasedThreshold; + } + } + break; + } default: - return AXIS_BIND_THRESHOLD; + break; } - } else { - return AXIS_BIND_THRESHOLD; } + return AXIS_BIND_THRESHOLD; } static int GetOppositeVKey(int vkey) { @@ -325,7 +361,7 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping, double no } else { curTime = iter->second.timestamp; } - bool down = iter->second.value > GetDeviceAxisThreshold(iter->first.deviceId, mapping); + bool down = iter->second.value > 0.0f && iter->second.value > GetDeviceAxisThreshold(iter->first.deviceId, mapping); if (!down) all = false; } @@ -541,10 +577,16 @@ void ControlMapper::Axis(const AxisInput *axes, size_t count) { KeyMap::LockMappings(); for (size_t i = 0; i < count; i++) { const AxisInput &axis = axes[i]; + + if (axis.deviceId == DEVICE_ID_MOUSE && !g_Config.bMouseControl) { + continue; + } + size_t deviceIndex = (size_t)axis.deviceId; // this wraps -1 up high, so will get rejected on the next line. if (deviceIndex < (size_t)DEVICE_ID_COUNT) { deviceTimestamps_[deviceIndex] = now; } + rawAxisValue_[axis.axisId] = axis.value; // these are only used for co-axis mapping if (axis.value >= 0.0f) { InputMapping mapping(axis.deviceId, axis.axisId, 1); InputMapping opposite(axis.deviceId, axis.axisId, -1); diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 1df1f39244f1..d7f2414e429d 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -59,11 +59,16 @@ class ControlMapper { void onVKeyAnalog(int deviceId, int vkey, float value); void UpdateCurInputAxis(const InputMapping &mapping, float value, double timestamp); + float GetDeviceAxisThreshold(int device, const InputMapping &mapping); // To track mappable virtual keys. We can have as many as we want. float virtKeys_[VIRTKEY_COUNT]{}; bool virtKeyOn_[VIRTKEY_COUNT]{}; // Track boolean output separaately since thresholds may differ. + // This is only used for co-axis (analog stick to buttons), so not bothering to track separately + // per device. + float rawAxisValue_[JOYSTICK_AXIS_MAX]{}; + double deviceTimestamps_[(size_t)DEVICE_ID_COUNT]{}; int lastNonDeadzoneDeviceID_[2]{};