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() {
|
void Input::initSDLGamePad() {
|
||||||
if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1) {
|
if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1) {
|
||||||
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
|
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
|
||||||
@@ -324,7 +310,9 @@ void Input::initSDLGamePad() {
|
|||||||
} else {
|
} else {
|
||||||
addGamepadMappingsFromFile();
|
addGamepadMappingsFromFile();
|
||||||
loadGamepadConfigs();
|
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;
|
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()) {
|
if (config_it != gamepad_configs_.end()) {
|
||||||
// Se encontró una configuración específica para este puerto/dispositivo. La aplicamos.
|
// 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';
|
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) {
|
void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
|
||||||
|
|||||||
@@ -206,7 +206,6 @@ class Input {
|
|||||||
auto addGamepad(int device_index) -> std::string;
|
auto addGamepad(int device_index) -> std::string;
|
||||||
auto removeGamepad(SDL_JoystickID id) -> std::string;
|
auto removeGamepad(SDL_JoystickID id) -> std::string;
|
||||||
void addGamepadMappingsFromFile();
|
void addGamepadMappingsFromFile();
|
||||||
void discoverGamepads();
|
|
||||||
|
|
||||||
// --- Métodos para integración con GamepadConfigManager ---
|
// --- Métodos para integración con GamepadConfigManager ---
|
||||||
void loadGamepadConfigs();
|
void loadGamepadConfigs();
|
||||||
|
|||||||
@@ -9,20 +9,20 @@
|
|||||||
#include <string> // Para basic_string, operator+, char_traits, to_string, string
|
#include <string> // Para basic_string, operator+, char_traits, to_string, string
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "core/input/mouse.hpp" // Para updateCursorVisibility
|
#include "core/input/mouse.hpp" // Para updateCursorVisibility
|
||||||
#ifndef NO_SHADERS
|
#ifndef NO_SHADERS
|
||||||
#include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
|
#include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
|
||||||
#endif
|
#endif
|
||||||
#include "core/rendering/text.hpp" // Para Text
|
#include "core/rendering/text.hpp" // Para Text
|
||||||
#include "core/rendering/texture.hpp" // Para Texture
|
#include "core/rendering/texture.hpp" // Para Texture
|
||||||
#include "core/resources/asset.hpp" // Para Asset
|
#include "core/resources/asset.hpp" // Para Asset
|
||||||
#include "core/resources/resource.hpp" // Para Resource
|
#include "core/resources/resource.hpp" // Para Resource
|
||||||
#include "core/system/director.hpp" // Para Director::debug_config
|
#include "core/system/director.hpp" // Para Director::debug_config
|
||||||
#include "game/options.hpp" // Para Video, video, Window, window
|
#include "game/options.hpp" // Para Video, video, Window, window
|
||||||
#include "game/ui/notifier.hpp" // Para Notifier
|
#include "game/ui/notifier.hpp" // Para Notifier
|
||||||
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||||
#include "utils/param.hpp" // Para Param, param, ParamGame, ParamDebug
|
#include "utils/param.hpp" // Para Param, param, ParamGame, ParamDebug
|
||||||
#include "utils/utils.hpp" // Para toLower
|
#include "utils/utils.hpp" // Para toLower
|
||||||
|
|
||||||
// Singleton
|
// Singleton
|
||||||
Screen* Screen::instance = nullptr;
|
Screen* Screen::instance = nullptr;
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ void Director::init() {
|
|||||||
Options::video.fullscreen = true;
|
Options::video.fullscreen = true;
|
||||||
Options::video.integer_scale = true;
|
Options::video.integer_scale = true;
|
||||||
#endif
|
#endif
|
||||||
loadParams(); // Carga los parámetros del programa
|
loadParams(); // Carga los parámetros del programa
|
||||||
loadScoreFile(); // Carga el archivo de puntuaciones
|
loadScoreFile(); // Carga el archivo de puntuaciones
|
||||||
|
|
||||||
// Inicialización de subsistemas principales
|
// Inicialización de subsistemas principales
|
||||||
Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma
|
Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma
|
||||||
@@ -435,6 +435,15 @@ auto Director::iterate() -> SDL_AppResult {
|
|||||||
return SDL_APP_SUCCESS;
|
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)
|
// Gestiona las transiciones entre secciones (destruye la anterior y construye la nueva)
|
||||||
handleSectionTransition();
|
handleSectionTransition();
|
||||||
|
|
||||||
|
|||||||
@@ -17,12 +17,28 @@
|
|||||||
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||||
|
|
||||||
namespace GlobalEvents {
|
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
|
// Comprueba los eventos de Input y muestra notificaciones
|
||||||
void handleInputEvents(const SDL_Event& event) {
|
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();
|
static auto* input_ = Input::get();
|
||||||
auto message = input_->handleEvent(event);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,10 +51,11 @@ namespace GlobalEvents {
|
|||||||
message.replace(pos, std::string(" DISCONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] DISCONNECTED"));
|
message.replace(pos, std::string(" DISCONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] DISCONNECTED"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Options::gamepad_manager.assignAndLinkGamepads();
|
|
||||||
Options::gamepad_manager.resyncGamepadsWithPlayers();
|
|
||||||
Notifier::get()->show({message});
|
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
|
// Comprueba los eventos que se pueden producir en cualquier sección del juego
|
||||||
|
|||||||
@@ -6,4 +6,10 @@
|
|||||||
namespace GlobalEvents {
|
namespace GlobalEvents {
|
||||||
// --- Funciones ---
|
// --- Funciones ---
|
||||||
void handle(const SDL_Event& event); // Comprueba los eventos que se pueden producir en cualquier sección del juego
|
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
|
} // namespace GlobalEvents
|
||||||
@@ -763,18 +763,30 @@ namespace Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Asigna los mandos físicos basándose en la configuración actual.
|
// 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() {
|
void GamepadManager::assignAndLinkGamepads() {
|
||||||
auto physical_gamepads = Input::get()->getGamepads();
|
auto physical_gamepads = Input::get()->getGamepads();
|
||||||
|
|
||||||
std::array<std::string, MAX_PLAYERS> desired_paths;
|
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) {
|
for (size_t i = 0; i < MAX_PLAYERS; ++i) {
|
||||||
desired_paths[i] = gamepads_[i].path;
|
desired_paths[i] = gamepads_[i].path;
|
||||||
|
desired_names[i] = gamepads_[i].name;
|
||||||
gamepads_[i].instance = nullptr;
|
gamepads_[i].instance = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Input::Gamepad>> assigned_instances;
|
std::vector<std::shared_ptr<Input::Gamepad>> assigned_instances;
|
||||||
|
|
||||||
assignGamepadsByPath(desired_paths, physical_gamepads, assigned_instances);
|
assignGamepadsByPath(desired_paths, physical_gamepads, assigned_instances);
|
||||||
|
assignGamepadsByName(desired_names, physical_gamepads, assigned_instances);
|
||||||
assignRemainingGamepads(physical_gamepads, assigned_instances);
|
assignRemainingGamepads(physical_gamepads, assigned_instances);
|
||||||
clearUnassignedGamepadSlots();
|
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(
|
void GamepadManager::assignRemainingGamepads(
|
||||||
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-named-parameter)
|
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-named-parameter)
|
||||||
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) {
|
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() {
|
void GamepadManager::clearUnassignedGamepadSlots() {
|
||||||
for (auto& gamepad_config : gamepads_) {
|
for (auto& gamepad_config : gamepads_) {
|
||||||
if (gamepad_config.instance == nullptr) {
|
if (gamepad_config.instance == nullptr) {
|
||||||
|
|||||||
@@ -293,6 +293,10 @@ namespace Options {
|
|||||||
const std::array<std::string, MAX_PLAYERS>& desired_paths,
|
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)
|
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);
|
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(
|
void assignRemainingGamepads(
|
||||||
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-avoid-const-params-in-decls)
|
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);
|
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
|
||||||
|
|||||||
Reference in New Issue
Block a user