From cefafe99e4fb9c8bd1e5c204aac43d75f7d31679 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sun, 24 May 2026 22:38:10 +0200 Subject: [PATCH] feat(service_menu): triggers L2/R2 navegables + so al rebind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El menu de servei nomes processava AXIS_MOTION dels sticks i descartava els triggers. Com SDL3 mai emet button events per a L2/R2 (nomes axis), rebindar FIRE o ACCEL a un trigger feia que no funcionaren al menu, fins i tot estant correctament al joc per via del poll de Input::checkTriggerInput. Afegim edge-detect dels dos triggers al handleGamepadAxis i, quan creuen el llindar, mirem si el codi virtual (100=L2, 101=R2) coincideix amb el binding de FIRE → activateCurrent, o ACCEL → popPage. Estat held per trigger per evitar repeticions mentre es mante premut. DefineInputs ara reprodueix el so accept del menu en cada captura valida, que estava silent i no donava feedback al rebind. Tambe extraiem processStickX/Y i processTriggerEdge per mantenir handleGamepadAxis com a dispatcher i sota el llindar de complexitat cognitiva del clang-tidy. Co-Authored-By: Claude Opus 4.7 (1M context) --- source/core/input/define_inputs.cpp | 5 +- source/core/system/service_menu.cpp | 88 ++++++++++++++++++++--------- source/core/system/service_menu.hpp | 10 ++++ 3 files changed, 76 insertions(+), 27 deletions(-) diff --git a/source/core/input/define_inputs.cpp b/source/core/input/define_inputs.cpp index 9bfb857..f1f8350 100644 --- a/source/core/input/define_inputs.cpp +++ b/source/core/input/define_inputs.cpp @@ -4,11 +4,11 @@ #include "core/input/define_inputs.hpp" #include -#include #include #include #include +#include "core/audio/audio.hpp" #include "core/defaults/service_menu.hpp" #include "core/input/input.hpp" #include "core/locale/locale.hpp" @@ -168,6 +168,9 @@ namespace System { } sequence_[index_].captured = code; ++index_; + if (auto* audio = Audio::get(); audio != nullptr) { + audio->playSound(Defaults::ServiceMenu::ACCEPT_SOUND, Audio::Group::INTERFACE); + } if (index_ >= sequence_.size()) { persistAndComplete(); } diff --git a/source/core/system/service_menu.cpp b/source/core/system/service_menu.cpp index 0f046a7..d5432c3 100644 --- a/source/core/system/service_menu.cpp +++ b/source/core/system/service_menu.cpp @@ -818,6 +818,9 @@ namespace System { // Llindar de stick per a navegacio de menu (mig camp del rang ±32767). // Mes baix que el del joc (30000) per a una resposta mes agil al menu. constexpr Sint16 MENU_STICK_THRESHOLD = 16384; + // Llindar de trigger (mateix valor que MENU_TRIGGER_THRESHOLD, que + // és private). Edge a partir del 50% del rang. + constexpr Sint16 MENU_TRIGGER_THRESHOLD = 16384; // Retorna true si el codi de boto SDL coincideix amb l'accio // configurada per algun dels dos jugadors (es a dir, el boto te el @@ -914,36 +917,69 @@ namespace System { return false; } + void ServiceMenu::processStickX(Sint16 val) { + const bool LEFT_NOW = val < -MENU_STICK_THRESHOLD; + const bool RIGHT_NOW = val > MENU_STICK_THRESHOLD; + if (LEFT_NOW && !stick_left_held_) { + changeValue(-1); + } + if (RIGHT_NOW && !stick_right_held_) { + changeValue(+1); + } + stick_left_held_ = LEFT_NOW; + stick_right_held_ = RIGHT_NOW; + } + + void ServiceMenu::processStickY(Sint16 val) { + const bool UP_NOW = val < -MENU_STICK_THRESHOLD; + const bool DOWN_NOW = val > MENU_STICK_THRESHOLD; + if (UP_NOW && !stick_up_held_) { + moveCursor(-1); + } + if (DOWN_NOW && !stick_down_held_) { + moveCursor(+1); + } + stick_up_held_ = UP_NOW; + stick_down_held_ = DOWN_NOW; + } + + // Edge-detect d'un trigger: si creua el llindar amunt, despatxa + // ENTER/BACK segons el binding (FIRE/ACCEL) que apunta al codi + // virtual del trigger (100 = L2, 101 = R2). + void ServiceMenu::processTriggerEdge(SDL_JoystickID which, Sint16 val, int virtual_button, bool& held) { + const bool NOW = val > MENU_TRIGGER_THRESHOLD; + if (NOW && !held) { + if (buttonMatchesAction(which, virtual_button, InputAction::SHOOT)) { + activateCurrent(); + } else if (buttonMatchesAction(which, virtual_button, InputAction::THRUST)) { + popPage(); + } + } + held = NOW; + } + auto ServiceMenu::handleGamepadAxis(const SDL_Event& event) -> bool { const auto AXIS = static_cast(event.gaxis.axis); const Sint16 VAL = event.gaxis.value; - if (AXIS == SDL_GAMEPAD_AXIS_LEFTX) { - const bool LEFT_NOW = VAL < -MENU_STICK_THRESHOLD; - const bool RIGHT_NOW = VAL > MENU_STICK_THRESHOLD; - if (LEFT_NOW && !stick_left_held_) { - changeValue(-1); - } - if (RIGHT_NOW && !stick_right_held_) { - changeValue(+1); - } - stick_left_held_ = LEFT_NOW; - stick_right_held_ = RIGHT_NOW; - return true; + switch (AXIS) { + case SDL_GAMEPAD_AXIS_LEFTX: + processStickX(VAL); + return true; + case SDL_GAMEPAD_AXIS_LEFTY: + processStickY(VAL); + return true; + // Triggers L2/R2: SDL3 nomes emet AXIS_MOTION, no button events. + // Per poder rebindar FIRE/ACCEL als triggers, sintetitzem aqui + // la pulsacio amb edge-detect i la passem pel mateix flux. + case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: + processTriggerEdge(event.gaxis.which, VAL, Input::TRIGGER_L2_AS_BUTTON, trigger_l2_held_); + return true; + case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: + processTriggerEdge(event.gaxis.which, VAL, Input::TRIGGER_R2_AS_BUTTON, trigger_r2_held_); + return true; + default: + return false; } - if (AXIS == SDL_GAMEPAD_AXIS_LEFTY) { - const bool UP_NOW = VAL < -MENU_STICK_THRESHOLD; - const bool DOWN_NOW = VAL > MENU_STICK_THRESHOLD; - if (UP_NOW && !stick_up_held_) { - moveCursor(-1); - } - if (DOWN_NOW && !stick_down_held_) { - moveCursor(+1); - } - stick_up_held_ = UP_NOW; - stick_down_held_ = DOWN_NOW; - return true; - } - return false; } auto ServiceMenu::computeTargetHeight() const -> float { diff --git a/source/core/system/service_menu.hpp b/source/core/system/service_menu.hpp index d3bd8b4..61f7465 100644 --- a/source/core/system/service_menu.hpp +++ b/source/core/system/service_menu.hpp @@ -100,6 +100,11 @@ namespace System { auto handleKeyDown(const SDL_Event& event) -> bool; auto handleGamepadButton(const SDL_Event& event) -> bool; auto handleGamepadAxis(const SDL_Event& event) -> bool; + // Helpers per a cada eix; permeten que handleGamepadAxis es quedi + // com a dispatcher i no bote el llindar de complexitat. + void processStickX(Sint16 val); + void processStickY(Sint16 val); + void processTriggerEdge(SDL_JoystickID which, Sint16 val, int virtual_button, bool& held); void buildRootPage(); [[nodiscard]] auto buildVideoPage() -> Page; @@ -162,6 +167,11 @@ namespace System { bool stick_right_held_ = false; bool stick_up_held_ = false; bool stick_down_held_ = false; + // Edge-detect dels triggers L2/R2 com a botons virtuals. SDL3 no + // emet button events per als triggers; els llegim com a axis i + // sintetitzem una pulsacio quan creuen el llindar. + bool trigger_l2_held_ = false; + bool trigger_r2_held_ = false; static std::unique_ptr instance; };