correccions en la detecció de mandos
This commit is contained in:
@@ -303,20 +303,6 @@ void Input::addGamepadMappingsFromFile() {
|
||||
}
|
||||
}
|
||||
|
||||
void Input::discoverGamepads() {
|
||||
// Enumera los gamepads ya conectados sin drenar la cola de eventos de SDL
|
||||
// (necesario con SDL_MAIN_USE_CALLBACKS, que entrega los eventos por SDL_AppEvent).
|
||||
int count = 0;
|
||||
SDL_JoystickID* joysticks = SDL_GetGamepads(&count);
|
||||
if (joysticks == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
addGamepad(joysticks[i]);
|
||||
}
|
||||
SDL_free(joysticks);
|
||||
}
|
||||
|
||||
void Input::initSDLGamePad() {
|
||||
if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1) {
|
||||
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
|
||||
@@ -324,7 +310,9 @@ void Input::initSDLGamePad() {
|
||||
} else {
|
||||
addGamepadMappingsFromFile();
|
||||
loadGamepadConfigs();
|
||||
discoverGamepads();
|
||||
// Los mandos ya conectados llegan como SDL_EVENT_GAMEPAD_ADDED en el
|
||||
// primer pase del pump de eventos (antes del primer SDL_AppIterate),
|
||||
// por lo que no hace falta enumerarlos aquí a mano.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -444,6 +432,27 @@ void Input::applyGamepadConfig(std::shared_ptr<Gamepad> gamepad) {
|
||||
return config.path == gamepad->path;
|
||||
});
|
||||
|
||||
// Fallback por nombre: si el mismo dispositivo se enchufa a otro puerto, su
|
||||
// path cambia pero el nombre suele mantenerse. Recuperamos su configuración
|
||||
// y actualizamos el path guardado al actual. Solo se acepta un match cuyo
|
||||
// path guardado NO corresponda a otro mando ya conectado, para no arruinar
|
||||
// setups con varios mandos idénticos en puertos distintos (cabinet arcade).
|
||||
if (config_it == gamepad_configs_.end() && !gamepad->name.empty()) {
|
||||
auto is_path_active = [this](const std::string& query_path) -> bool {
|
||||
return std::ranges::any_of(gamepads_, [&query_path](const std::shared_ptr<Gamepad>& g) -> bool {
|
||||
return g && g->path == query_path;
|
||||
});
|
||||
};
|
||||
config_it = std::ranges::find_if(gamepad_configs_, [&gamepad, &is_path_active](const GamepadConfig& config) -> bool {
|
||||
return config.name == gamepad->name && !is_path_active(config.path);
|
||||
});
|
||||
if (config_it != gamepad_configs_.end()) {
|
||||
std::cout << "Gamepad '" << gamepad->name << "' found by name, refreshing path to: " << gamepad->path << '\n';
|
||||
config_it->path = gamepad->path;
|
||||
saveGamepadConfigs();
|
||||
}
|
||||
}
|
||||
|
||||
if (config_it != gamepad_configs_.end()) {
|
||||
// Se encontró una configuración específica para este puerto/dispositivo. La aplicamos.
|
||||
std::cout << "Applying custom config for gamepad at path: " << gamepad->path << '\n';
|
||||
@@ -453,7 +462,6 @@ void Input::applyGamepadConfig(std::shared_ptr<Gamepad> gamepad) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Opcional: Podrías añadir un fallback para buscar por nombre si no se encuentra por ruta.
|
||||
}
|
||||
|
||||
void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
|
||||
|
||||
@@ -206,7 +206,6 @@ class Input {
|
||||
auto addGamepad(int device_index) -> std::string;
|
||||
auto removeGamepad(SDL_JoystickID id) -> std::string;
|
||||
void addGamepadMappingsFromFile();
|
||||
void discoverGamepads();
|
||||
|
||||
// --- Métodos para integración con GamepadConfigManager ---
|
||||
void loadGamepadConfigs();
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
#include <string> // Para basic_string, operator+, char_traits, to_string, string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "core/input/mouse.hpp" // Para updateCursorVisibility
|
||||
#include "core/input/mouse.hpp" // Para updateCursorVisibility
|
||||
#ifndef NO_SHADERS
|
||||
#include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
|
||||
#endif
|
||||
#include "core/rendering/text.hpp" // Para Text
|
||||
#include "core/rendering/texture.hpp" // Para Texture
|
||||
#include "core/resources/asset.hpp" // Para Asset
|
||||
#include "core/resources/resource.hpp" // Para Resource
|
||||
#include "core/system/director.hpp" // Para Director::debug_config
|
||||
#include "game/options.hpp" // Para Video, video, Window, window
|
||||
#include "game/ui/notifier.hpp" // Para Notifier
|
||||
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||
#include "utils/param.hpp" // Para Param, param, ParamGame, ParamDebug
|
||||
#include "utils/utils.hpp" // Para toLower
|
||||
#include "core/rendering/text.hpp" // Para Text
|
||||
#include "core/rendering/texture.hpp" // Para Texture
|
||||
#include "core/resources/asset.hpp" // Para Asset
|
||||
#include "core/resources/resource.hpp" // Para Resource
|
||||
#include "core/system/director.hpp" // Para Director::debug_config
|
||||
#include "game/options.hpp" // Para Video, video, Window, window
|
||||
#include "game/ui/notifier.hpp" // Para Notifier
|
||||
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||
#include "utils/param.hpp" // Para Param, param, ParamGame, ParamDebug
|
||||
#include "utils/utils.hpp" // Para toLower
|
||||
|
||||
// Singleton
|
||||
Screen* Screen::instance = nullptr;
|
||||
|
||||
@@ -125,8 +125,8 @@ void Director::init() {
|
||||
Options::video.fullscreen = true;
|
||||
Options::video.integer_scale = true;
|
||||
#endif
|
||||
loadParams(); // Carga los parámetros del programa
|
||||
loadScoreFile(); // Carga el archivo de puntuaciones
|
||||
loadParams(); // Carga los parámetros del programa
|
||||
loadScoreFile(); // Carga el archivo de puntuaciones
|
||||
|
||||
// Inicialización de subsistemas principales
|
||||
Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma
|
||||
@@ -435,6 +435,15 @@ auto Director::iterate() -> SDL_AppResult {
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
|
||||
// En el primer frame, SDL ya ha drenado los SDL_EVENT_GAMEPAD_ADDED de los
|
||||
// mandos conectados al iniciar. A partir de ahora los eventos sí son hotplug
|
||||
// real y deben mostrar notificación.
|
||||
static bool first_iterate = true;
|
||||
if (first_iterate) {
|
||||
first_iterate = false;
|
||||
GlobalEvents::markStartupComplete();
|
||||
}
|
||||
|
||||
// Gestiona las transiciones entre secciones (destruye la anterior y construye la nueva)
|
||||
handleSectionTransition();
|
||||
|
||||
|
||||
@@ -17,12 +17,28 @@
|
||||
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||
|
||||
namespace GlobalEvents {
|
||||
namespace {
|
||||
// Durante el arranque se drenan los SDL_EVENT_GAMEPAD_ADDED de los mandos
|
||||
// ya conectados. Esos eventos sí deben reasignar mandos a jugadores, pero
|
||||
// no deben mostrar notificación: no son hotplug, son detección inicial.
|
||||
bool startup_in_progress = true;
|
||||
} // namespace
|
||||
|
||||
// Comprueba los eventos de Input y muestra notificaciones
|
||||
void handleInputEvents(const SDL_Event& event) {
|
||||
if (event.type != SDL_EVENT_GAMEPAD_ADDED && event.type != SDL_EVENT_GAMEPAD_REMOVED) {
|
||||
return;
|
||||
}
|
||||
|
||||
static auto* input_ = Input::get();
|
||||
auto message = input_->handleEvent(event);
|
||||
|
||||
if (message.empty()) {
|
||||
// Reasignar siempre: tanto en arranque como en hotplug en caliente.
|
||||
Options::gamepad_manager.assignAndLinkGamepads();
|
||||
Options::gamepad_manager.resyncGamepadsWithPlayers();
|
||||
ServiceMenu::get()->refresh();
|
||||
|
||||
if (startup_in_progress || message.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -35,10 +51,11 @@ namespace GlobalEvents {
|
||||
message.replace(pos, std::string(" DISCONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] DISCONNECTED"));
|
||||
}
|
||||
|
||||
Options::gamepad_manager.assignAndLinkGamepads();
|
||||
Options::gamepad_manager.resyncGamepadsWithPlayers();
|
||||
Notifier::get()->show({message});
|
||||
ServiceMenu::get()->refresh();
|
||||
}
|
||||
|
||||
void markStartupComplete() {
|
||||
startup_in_progress = false;
|
||||
}
|
||||
|
||||
// Comprueba los eventos que se pueden producir en cualquier sección del juego
|
||||
|
||||
@@ -6,4 +6,10 @@
|
||||
namespace GlobalEvents {
|
||||
// --- Funciones ---
|
||||
void handle(const SDL_Event& event); // Comprueba los eventos que se pueden producir en cualquier sección del juego
|
||||
|
||||
// Marca el fin del arranque: a partir de aquí, los eventos de mando
|
||||
// generan notificaciones en pantalla. Se debe llamar desde Director::iterate
|
||||
// en el primer fotograma, después de que SDL haya drenado los
|
||||
// SDL_EVENT_GAMEPAD_ADDED de los mandos ya conectados al iniciar.
|
||||
void markStartupComplete();
|
||||
} // namespace GlobalEvents
|
||||
@@ -763,18 +763,30 @@ namespace Options {
|
||||
}
|
||||
|
||||
// Asigna los mandos físicos basándose en la configuración actual.
|
||||
//
|
||||
// Tres pases en orden decreciente de prioridad:
|
||||
// 1) Match por path exacto: resuelve el caso arcade (dos sticks idénticos
|
||||
// enchufados siempre a los mismos puertos USB).
|
||||
// 2) Match por nombre cuando el path no coincidió: recoloca un mando
|
||||
// conocido que se ha reenchufado a otro puerto (ej. DualSense). En ese
|
||||
// caso se refresca el path guardado al nuevo.
|
||||
// 3) Reparto en orden de los mandos físicos sobrantes a los slots que
|
||||
// sigan libres.
|
||||
void GamepadManager::assignAndLinkGamepads() {
|
||||
auto physical_gamepads = Input::get()->getGamepads();
|
||||
|
||||
std::array<std::string, MAX_PLAYERS> desired_paths;
|
||||
std::array<std::string, MAX_PLAYERS> desired_names;
|
||||
for (size_t i = 0; i < MAX_PLAYERS; ++i) {
|
||||
desired_paths[i] = gamepads_[i].path;
|
||||
desired_names[i] = gamepads_[i].name;
|
||||
gamepads_[i].instance = nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Input::Gamepad>> assigned_instances;
|
||||
|
||||
assignGamepadsByPath(desired_paths, physical_gamepads, assigned_instances);
|
||||
assignGamepadsByName(desired_names, physical_gamepads, assigned_instances);
|
||||
assignRemainingGamepads(physical_gamepads, assigned_instances);
|
||||
clearUnassignedGamepadSlots();
|
||||
}
|
||||
@@ -802,7 +814,37 @@ namespace Options {
|
||||
}
|
||||
}
|
||||
|
||||
// --- SEGUNDA PASADA: Asigna los mandos físicos restantes a los jugadores libres ---
|
||||
// --- SEGUNDA PASADA: Match por nombre para slots cuyo path guardado no casó ---
|
||||
// Si un slot tiene nombre guardado pero el path no ha coincidido (mando reenchufado
|
||||
// a otro puerto, o path del sistema cambiado), lo enlazamos por nombre y
|
||||
// refrescamos el path guardado al del dispositivo físico actual.
|
||||
void GamepadManager::assignGamepadsByName(
|
||||
const std::array<std::string, MAX_PLAYERS>& desired_names,
|
||||
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-named-parameter)
|
||||
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) {
|
||||
for (size_t i = 0; i < MAX_PLAYERS; ++i) {
|
||||
if (gamepads_[i].instance != nullptr) {
|
||||
continue;
|
||||
}
|
||||
const std::string& desired_name = desired_names[i];
|
||||
if (desired_name.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& physical_gamepad : physical_gamepads) {
|
||||
if (physical_gamepad->name == desired_name && !isGamepadAssigned(physical_gamepad, assigned_instances)) {
|
||||
gamepads_[i].instance = physical_gamepad;
|
||||
gamepads_[i].name = physical_gamepad->name;
|
||||
gamepads_[i].path = physical_gamepad->path;
|
||||
|
||||
assigned_instances.push_back(physical_gamepad);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- TERCERA PASADA: Asigna los mandos físicos restantes a los jugadores libres ---
|
||||
void GamepadManager::assignRemainingGamepads(
|
||||
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-named-parameter)
|
||||
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) {
|
||||
@@ -824,7 +866,7 @@ namespace Options {
|
||||
}
|
||||
}
|
||||
|
||||
// --- TERCERA PASADA: Limpia la información "fantasma" de los slots no asignados ---
|
||||
// --- LIMPIEZA FINAL: Limpia la información "fantasma" de los slots no asignados ---
|
||||
void GamepadManager::clearUnassignedGamepadSlots() {
|
||||
for (auto& gamepad_config : gamepads_) {
|
||||
if (gamepad_config.instance == nullptr) {
|
||||
|
||||
@@ -293,6 +293,10 @@ namespace Options {
|
||||
const std::array<std::string, MAX_PLAYERS>& desired_paths,
|
||||
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-avoid-const-params-in-decls)
|
||||
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
|
||||
void assignGamepadsByName(
|
||||
const std::array<std::string, MAX_PLAYERS>& desired_names,
|
||||
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-avoid-const-params-in-decls)
|
||||
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
|
||||
void assignRemainingGamepads(
|
||||
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-avoid-const-params-in-decls)
|
||||
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
|
||||
|
||||
Reference in New Issue
Block a user