From f2dca0265fc0bcb2edbf21dab48d9567af4536d7 Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Wed, 22 Apr 2026 01:14:15 -0700 Subject: [PATCH] rawinput: custom threshold for BAT (button-analog type) (#654) ## Link to GitHub Issue or related Pull Request, if one exists #653 ## Description of change By default, BAT Positive / Negative binds trigger when the stick is tilted 10% off the center. This needs a bit more customization since: 1. User may want to customize the center point (e.g., guitar controller neutral position) 2. User may want to adjust the sensitivity (e.g., guitar controller tilt angle to trigger wailing). ## Testing WIP --- src/spice2x/cfg/api.cpp | 14 ++++- src/spice2x/cfg/button.h | 10 ++++ src/spice2x/cfg/config.cpp | 12 +++++ src/spice2x/cfg/controller_presets.cpp | 5 ++ src/spice2x/overlay/windows/config.cpp | 52 ++++++++++++++++++- .../overlay/windows/controller_presets.h | 2 + 6 files changed, 92 insertions(+), 3 deletions(-) diff --git a/src/spice2x/cfg/api.cpp b/src/spice2x/cfg/api.cpp index 704004d..0961f2e 100644 --- a/src/spice2x/cfg/api.cpp +++ b/src/spice2x/cfg/api.cpp @@ -199,9 +199,19 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma if (vKey < value_states->size()) { auto value = value_states->at(vKey); if (current_button->getAnalogType() == BAT_POSITIVE) { - state = value > 0.6f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; + float threshold = 0.6f; + if (current_button->getBatThreshold() > 0) { + threshold = std::clamp(current_button->getBatThreshold() / 100.f, 0.01f, 0.99f); + } + state = value > threshold ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; + } else if (current_button->getAnalogType() == BAT_NEGATIVE) { - state = value < 0.4f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; + float threshold = 0.4f; + if (current_button->getBatThreshold() > 0) { + threshold = std::clamp((100 - current_button->getBatThreshold()) / 100.f, 0.01f, 0.99f); + } + state = value < threshold ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; + } else { state = value > 0.01f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; } diff --git a/src/spice2x/cfg/button.h b/src/spice2x/cfg/button.h index 4089190..36b4f5c 100644 --- a/src/spice2x/cfg/button.h +++ b/src/spice2x/cfg/button.h @@ -56,6 +56,7 @@ private: ButtonAnalogType analog_type = BAT_NONE; double debounce_up = 0.0; double debounce_down = 0.0; + int bat_threshold = 0; // for positive/negative only, value in percentage, zero is default bool invert = false; bool is_temporary = false; @@ -109,6 +110,7 @@ public: alternatives.clear(); device_identifier = ""; analog_type = BAT_NONE; + bat_threshold = 0; } std::string getDisplayString(rawinput::RawInputManager* manager); @@ -169,6 +171,14 @@ public: this->debounce_down = debounce_time_down; } + inline void setBatThreshold(int bat_threshold) { + this->bat_threshold = bat_threshold; + } + + inline int getBatThreshold() const { + return this->bat_threshold; + } + inline bool getInvert() const { return this->invert; } diff --git a/src/spice2x/cfg/config.cpp b/src/spice2x/cfg/config.cpp index 27a56ef..698ef0c 100644 --- a/src/spice2x/cfg/config.cpp +++ b/src/spice2x/cfg/config.cpp @@ -162,6 +162,7 @@ bool Config::addGame(Game &game) { auto analogType = (int) BAT_NONE; double debounce_up = 0.0; double debounce_down = 0.0; + int bat_threshold = 0; int velocity_threshold = 0; bool invert = false; tinyxml2::XMLError attrError = gameButtonNode->QueryIntAttribute("vkey", &vKey); @@ -169,6 +170,7 @@ bool Config::addGame(Game &game) { gameButtonNode->QueryIntAttribute("analogtype", &analogType); gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up); gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down); + gameButtonNode->QueryIntAttribute("bat_threshold", &bat_threshold); gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold); gameButtonNode->QueryBoolAttribute("invert", &invert); if (attrError != tinyxml2::XMLError::XML_SUCCESS) { @@ -180,6 +182,7 @@ bool Config::addGame(Game &game) { gameButtonNode->SetAttribute("devid", button->getDeviceIdentifier().c_str()); gameButtonNode->SetAttribute("debounce_up", debounce_up); gameButtonNode->SetAttribute("debounce_down", debounce_down); + gameButtonNode->SetAttribute("bat_threshold", bat_threshold); gameButtonNode->SetAttribute("velocity_threshold", velocity_threshold); gameButtonNode->SetAttribute("invert", invert); gameButtonsNode->InsertEndChild(gameButtonNode); @@ -188,6 +191,7 @@ bool Config::addGame(Game &game) { button->setAnalogType((ButtonAnalogType) analogType); button->setDebounceUp(debounce_up); button->setDebounceDown(debounce_down); + button->setBatThreshold(bat_threshold); button->setVelocityThreshold(velocity_threshold); button->setInvert(invert); if (devid) { @@ -207,6 +211,7 @@ bool Config::addGame(Game &game) { gameButtonNode->SetAttribute("analogtype", (int) it.getAnalogType()); gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp()); gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown()); + gameButtonNode->SetAttribute("bat_threshold", it.getBatThreshold()); gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold()); gameButtonNode->SetAttribute("invert", it.getInvert()); gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str()); @@ -432,6 +437,7 @@ bool Config::addGame(Game &game) { gameButtonNode->SetAttribute("analogtype", it.getAnalogType()); gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp()); gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown()); + gameButtonNode->SetAttribute("bat_threshold", it.getBatThreshold()); gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold()); gameButtonNode->SetAttribute("invert", it.getInvert()); gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str()); @@ -535,6 +541,7 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati gameButtonNode->SetAttribute("analogtype", (int) button.getAnalogType()); gameButtonNode->SetAttribute("debounce_up", button.getDebounceUp()); gameButtonNode->SetAttribute("debounce_down", button.getDebounceDown()); + gameButtonNode->SetAttribute("bat_threshold", button.getBatThreshold()); gameButtonNode->SetAttribute("velocity_threshold", button.getVelocityThreshold()); gameButtonNode->SetAttribute("invert", button.getInvert()); gameButtonNode->SetAttribute("devid", button.getDeviceIdentifier().c_str()); @@ -551,6 +558,7 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati gameButtonNode->SetAttribute("analogtype", 0); gameButtonNode->SetAttribute("debounce_up", 0.0); gameButtonNode->SetAttribute("debounce_down", 0.0); + gameButtonNode->SetAttribute("bat_threshold", 0); gameButtonNode->SetAttribute("velocity_threshold", 0); gameButtonNode->SetAttribute("invert", false); gameButtonNode->SetAttribute("devid", ""); @@ -946,12 +954,14 @@ std::vector