From aa4c7687eeaf79552b3563cd61172b0d6a7a99e1 Mon Sep 17 00:00:00 2001 From: german Date: Wed, 30 Dec 2020 22:29:20 -0600 Subject: Port citra-emu/citra#5509 --- src/input_common/sdl/sdl_impl.cpp | 47 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 7827e324c..d56b7587b 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -1014,11 +1014,44 @@ public: } return {}; } - [[nodiscard]] std::optional FromEvent(const SDL_Event& event) const { + [[nodiscard]] std::optional FromEvent(SDL_Event& event) { switch (event.type) { case SDL_JOYAXISMOTION: - if (std::abs(event.jaxis.value / 32767.0) < 0.5) { + if (!axis_memory.count(event.jaxis.which) || + !axis_memory[event.jaxis.which].count(event.jaxis.axis)) { + axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value; + axis_event_count[event.jaxis.which][event.jaxis.axis] = 1; break; + } else { + axis_event_count[event.jaxis.which][event.jaxis.axis]++; + // The joystick and axis exist in our map if we take this branch, so no checks + // needed + if (std::abs( + (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) / + 32767.0) < 0.5) { + break; + } else { + if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 && + IsAxisAtPole(event.jaxis.value) && + IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) { + // If we have exactly two events and both are near a pole, this is + // likely a digital input masquerading as an analog axis; Instead of + // trying to look at the direction the axis travelled, assume the first + // event was press and the second was release; This should handle most + // digital axes while deferring to the direction of travel for analog + // axes + event.jaxis.value = static_cast( + std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis])); + } else { + // There are more than two events, so this is likely a true analog axis, + // check the direction it travelled + event.jaxis.value = static_cast(std::copysign( + 32767, + event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis])); + } + axis_memory.clear(); + axis_event_count.clear(); + } } [[fallthrough]]; case SDL_JOYBUTTONUP: @@ -1027,6 +1060,16 @@ public: } return std::nullopt; } + +private: + // Determine whether an axis value is close to an extreme or center + // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per + // axis, which is why the center must be considered a pole + bool IsAxisAtPole(int16_t value) const { + return std::abs(value) >= 32767 || std::abs(value) < 327; + } + std::unordered_map> axis_memory; + std::unordered_map> axis_event_count; }; class SDLMotionPoller final : public SDLPoller { -- cgit v1.2.3