From 792aa38d34fd033d527ac11c0b406f4cfb8e40f3 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 26 Nov 2024 16:06:13 +0100 Subject: [PATCH] input: implement ramp up/down for analog axes mapped to buttons Full analog axes now have a ramp up/down of 100 ms when the mapped button is pressed/released. Works also for keyboards. Issue #1017 --- core/input/gamepad_device.cpp | 76 +++++++++++++++++++++++++++++++++++ core/input/gamepad_device.h | 22 +++++----- core/oslib/oslib.cpp | 9 +++-- 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index 7ca658414..5ddc72c6b 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -584,6 +584,82 @@ void GamepadDevice::SaveMaplePorts() } } +s16 (&GamepadDevice::getTargetArray(DigAnalog axis))[4] +{ + switch (axis) + { + case DIGANA_LEFT: + case DIGANA_RIGHT: + return joyx; + case DIGANA_UP:; + case DIGANA_DOWN: + return joyy; + case DIGANA2_LEFT: + case DIGANA2_RIGHT: + return joyrx; + case DIGANA2_UP: + case DIGANA2_DOWN: + return joyry; + case DIGANA3_LEFT: + case DIGANA3_RIGHT: + return joy3x; + case DIGANA3_UP: + case DIGANA3_DOWN: + return joy3y; + default: + die("unknown axis"); + } +} + +void GamepadDevice::rampAnalog() +{ + if (lastAnalogUpdate == 0) + // also used as a flag that no analog ramping is needed on this device (yet) + return; + + const u64 now = getTimeMs(); + const int delta = std::round(static_cast(now - lastAnalogUpdate) * AnalogRamp); + lastAnalogUpdate = now; + for (unsigned port = 0; port < std::size(digitalToAnalogState); port++) + { + for (int axis = 0; axis < 12; axis += 2) // 3 sticks with 2 axes each + { + DigAnalog negDir = static_cast(1 << axis); + if ((rampAnalogState[port] & negDir) == 0) + // axis not active + continue; + DigAnalog posDir = static_cast(1 << (axis + 1)); + const int socd = digitalToAnalogState[port] & (negDir | posDir); + s16& axisValue = getTargetArray(negDir)[port]; + if (socd != 0 && socd != (negDir | posDir)) + { + // One axis is pressed => ramp up + if (socd == posDir) + axisValue = std::min(32767, axisValue + delta); + else + axisValue = std::max(-32768, axisValue - delta); + } + else + { + // No axis is pressed (or both) => ramp down + if (axisValue > 0) + axisValue = std::max(0, axisValue - delta); + else if (axisValue < 0) + axisValue = std::min(0, axisValue + delta); + else + rampAnalogState[port] &= ~negDir; + } + } + } +} + +void GamepadDevice::RampAnalog() +{ + std::lock_guard _(_gamepads_mutex); + for (auto& gamepad : _gamepads) + gamepad->rampAnalog(); +} + #ifdef TEST_AUTOMATION #include "cfg/option.h" static bool replay_inited; diff --git a/core/input/gamepad_device.h b/core/input/gamepad_device.h index aef633870..6e94b70ad 100644 --- a/core/input/gamepad_device.h +++ b/core/input/gamepad_device.h @@ -20,6 +20,7 @@ #pragma once #include "types.h" #include "mapping.h" +#include "stdclass.h" #include #include @@ -98,6 +99,7 @@ class GamepadDevice static int GetGamepadCount(); static std::shared_ptr GetGamepad(int index); static void SaveMaplePorts(); + static void RampAnalog(); static void load_system_mappings(); bool find_mapping(int system = settings.platform.system); @@ -163,16 +165,14 @@ class GamepadDevice digitalToAnalogState[port] |= axis; else digitalToAnalogState[port] &= ~axis; - const u32 socd = digitalToAnalogState[port] & (NegDir | PosDir); - if (socd == 0 || socd == (NegDir | PosDir)) - joystick = 0; - else if (socd == NegDir) - joystick = -32768; - else - joystick = 32767; - + rampAnalogState[port] |= NegDir; + if (lastAnalogUpdate == 0) + lastAnalogUpdate = getTimeMs(); } + s16 (&getTargetArray(DigAnalog axis))[4]; + void rampAnalog(); + std::string _api_name; int _maple_port; bool _detecting_button = false; @@ -184,7 +184,11 @@ class GamepadDevice std::map lastAxisValue[4]; bool perGameMapping = false; bool instanceMapping = false; - + + u64 lastAnalogUpdate = 0; + u32 rampAnalogState[4] {}; + static constexpr float AnalogRamp = 32767.f / 100.f; // 100 ms ramp time + static std::vector> _gamepads; static std::mutex _gamepads_mutex; }; diff --git a/core/oslib/oslib.cpp b/core/oslib/oslib.cpp index fa7a3793e..67bc3393b 100644 --- a/core/oslib/oslib.cpp +++ b/core/oslib/oslib.cpp @@ -40,6 +40,7 @@ #include #endif #include "profiler/fc_profiler.h" +#include "input/gamepad_device.h" namespace hostfs { @@ -370,12 +371,12 @@ void os_UpdateInputState() { FC_PROFILE_SCOPE; + // FIXME threading (android) this will be called on the render thread, events are on the main app thread + GamepadDevice::RampAnalog(); #if defined(USE_SDL) input_sdl_handle(); -#else - #if defined(USE_EVDEV) - input_evdev_handle(); - #endif +#elif defined(USE_EVDEV) + input_evdev_handle(); #endif }