#include "input.h" #include #include // for basic_ostream, operator<<, cout, basi... // Constructor Input::Input(std::string file) { // Fichero gamecontrollerdb.txt dbPath = file; // Inicializa las variables keyBindings_t kb; kb.scancode = 0; kb.active = false; keyBindings.resize(input_number_of_inputs, kb); GameControllerBindings_t gcb; gcb.button = SDL_GAMEPAD_BUTTON_INVALID; gcb.active = false; gameControllerBindings.resize(input_number_of_inputs, gcb); numGamepads = 0; verbose = true; enabled = true; } // Destructor Input::~Input() { for (auto *pad : connectedControllers) { if (pad != nullptr) { SDL_CloseGamepad(pad); } } connectedControllers.clear(); connectedControllerIds.clear(); controllerNames.clear(); numGamepads = 0; } // Actualiza el estado del objeto void Input::update() { if (disabledUntil == d_keyPressed && !checkAnyInput()) { enable(); } } // Asigna inputs a teclas void Input::bindKey(Uint8 input, SDL_Scancode code) { keyBindings[input].scancode = code; } // Asigna inputs a botones del mando void Input::bindGameControllerButton(Uint8 input, SDL_GamepadButton button) { gameControllerBindings[input].button = button; } // Comprueba si un input esta activo bool Input::checkInput(Uint8 input, bool repeat, int device, int index) { if (!enabled) { return false; } bool successKeyboard = false; bool successGameController = false; if (device == INPUT_USE_ANY) { index = 0; } if (device == INPUT_USE_KEYBOARD || device == INPUT_USE_ANY) { const bool *keyStates = SDL_GetKeyboardState(nullptr); if (repeat) { if (keyStates[keyBindings[input].scancode]) { successKeyboard = true; } else { successKeyboard = false; } } else { if (!keyBindings[input].active) { if (keyStates[keyBindings[input].scancode]) { keyBindings[input].active = true; successKeyboard = true; } else { successKeyboard = false; } } else { if (!keyStates[keyBindings[input].scancode]) { keyBindings[input].active = false; successKeyboard = false; } else { successKeyboard = false; } } } } if (gameControllerFound() && index >= 0 && index < (int)connectedControllers.size()) if ((device == INPUT_USE_GAMECONTROLLER) || (device == INPUT_USE_ANY)) { if (repeat) { if (SDL_GetGamepadButton(connectedControllers[index], gameControllerBindings[input].button)) { successGameController = true; } else { successGameController = false; } } else { if (!gameControllerBindings[input].active) { if (SDL_GetGamepadButton(connectedControllers[index], gameControllerBindings[input].button)) { gameControllerBindings[input].active = true; successGameController = true; } else { successGameController = false; } } else { if (!SDL_GetGamepadButton(connectedControllers[index], gameControllerBindings[input].button)) { gameControllerBindings[input].active = false; successGameController = false; } else { successGameController = false; } } } } return (successKeyboard || successGameController); } // Comprueba si hay almenos un input activo bool Input::checkAnyInput(int device, int index) { if (device == INPUT_USE_ANY) { index = 0; } if (device == INPUT_USE_KEYBOARD || device == INPUT_USE_ANY) { const bool *mKeystates = SDL_GetKeyboardState(nullptr); for (int i = 0; i < (int)keyBindings.size(); ++i) { if (mKeystates[keyBindings[i].scancode]) { return true; } } } if (gameControllerFound() && index >= 0 && index < (int)connectedControllers.size()) { if (device == INPUT_USE_GAMECONTROLLER || device == INPUT_USE_ANY) { for (int i = 0; i < (int)gameControllerBindings.size(); ++i) { if (SDL_GetGamepadButton(connectedControllers[index], gameControllerBindings[i].button)) { return true; } } } } return false; } // Construye el nombre visible de un mando. // Recorta des del primer '(' o '[' (per a evitar coses tipus // "Retroid Controller (vendor: 1001) ...") i talla a 25 caràcters. std::string Input::buildControllerName(SDL_Gamepad *pad, int padIndex) { (void)padIndex; const char *padName = SDL_GetGamepadName(pad); std::string name = padName ? padName : "Unknown"; const auto pos = name.find_first_of("(["); if (pos != std::string::npos) { name.erase(pos); } while (!name.empty() && name.back() == ' ') { name.pop_back(); } if (name.size() > 25) { name.resize(25); } return name; } // Busca si hay un mando conectado. Cierra y limpia el estado previo para // que la función sea idempotente si se invoca más de una vez. bool Input::discoverGameController() { // Cierra los mandos ya abiertos y limpia los vectores paralelos for (auto *pad : connectedControllers) { if (pad != nullptr) { SDL_CloseGamepad(pad); } } connectedControllers.clear(); connectedControllerIds.clear(); controllerNames.clear(); numGamepads = 0; bool found = false; if (SDL_WasInit(SDL_INIT_GAMEPAD) != SDL_INIT_GAMEPAD) { SDL_InitSubSystem(SDL_INIT_GAMEPAD); } if (SDL_AddGamepadMappingsFromFile(dbPath.c_str()) < 0) { if (verbose) { std::cout << "Error, could not load " << dbPath.c_str() << " file: " << SDL_GetError() << std::endl; } } int nJoysticks = 0; SDL_JoystickID *joysticks = SDL_GetJoysticks(&nJoysticks); if (joysticks) { int gamepadCount = 0; for (int i = 0; i < nJoysticks; ++i) { if (SDL_IsGamepad(joysticks[i])) { gamepadCount++; } } if (verbose) { std::cout << "\nChecking for game controllers...\n"; std::cout << nJoysticks << " joysticks found, " << gamepadCount << " are gamepads\n"; } if (gamepadCount > 0) { found = true; int padIndex = 0; for (int i = 0; i < nJoysticks; i++) { if (!SDL_IsGamepad(joysticks[i])) continue; SDL_Gamepad *pad = SDL_OpenGamepad(joysticks[i]); if (pad != nullptr) { const std::string name = buildControllerName(pad, padIndex); connectedControllers.push_back(pad); connectedControllerIds.push_back(joysticks[i]); controllerNames.push_back(name); numGamepads++; padIndex++; if (verbose) { std::cout << name << std::endl; } } else { if (verbose) { std::cout << "SDL_GetError() = " << SDL_GetError() << std::endl; } } } SDL_SetGamepadEventsEnabled(true); } SDL_free(joysticks); } return found; } // Procesa un evento SDL_EVENT_GAMEPAD_ADDED bool Input::handleGamepadAdded(SDL_JoystickID jid, std::string &outName) { if (!SDL_IsGamepad(jid)) { return false; } // Si el mando ya está registrado no hace nada (ej. evento retroactivo tras el scan inicial) for (SDL_JoystickID existing : connectedControllerIds) { if (existing == jid) { return false; } } SDL_Gamepad *pad = SDL_OpenGamepad(jid); if (pad == nullptr) { if (verbose) { std::cout << "Failed to open gamepad " << jid << ": " << SDL_GetError() << std::endl; } return false; } const int padIndex = (int)connectedControllers.size(); const std::string name = buildControllerName(pad, padIndex); connectedControllers.push_back(pad); connectedControllerIds.push_back(jid); controllerNames.push_back(name); numGamepads++; if (verbose) { std::cout << "Gamepad connected: " << name << std::endl; } outName = name; return true; } // Procesa un evento SDL_EVENT_GAMEPAD_REMOVED bool Input::handleGamepadRemoved(SDL_JoystickID jid, std::string &outName) { for (size_t i = 0; i < connectedControllerIds.size(); ++i) { if (connectedControllerIds[i] != jid) continue; outName = controllerNames[i]; if (connectedControllers[i] != nullptr) { SDL_CloseGamepad(connectedControllers[i]); } connectedControllers.erase(connectedControllers.begin() + i); connectedControllerIds.erase(connectedControllerIds.begin() + i); controllerNames.erase(controllerNames.begin() + i); numGamepads--; if (numGamepads < 0) numGamepads = 0; if (verbose) { std::cout << "Gamepad disconnected: " << outName << std::endl; } return true; } return false; } // Comprueba si hay algun mando conectado bool Input::gameControllerFound() { if (numGamepads > 0) { return true; } else { return false; } } // Obten el nombre de un mando de juego std::string Input::getControllerName(int index) { if (numGamepads > 0) { return controllerNames[index]; } else { return ""; } } // Obten el numero de mandos conectados int Input::getNumControllers() { return numGamepads; } // Establece si ha de mostrar mensajes void Input::setVerbose(bool value) { verbose = value; } // Deshabilita las entradas durante un periodo de tiempo void Input::disableUntil(i_disable_e value) { disabledUntil = value; enabled = false; } // Hablita las entradas void Input::enable() { enabled = true; disabledUntil = d_notDisabled; }