Files
projecte_2026/source/core/input/global_inputs.cpp

336 lines
14 KiB
C++

#include "core/input/global_inputs.hpp"
#include <SDL3/SDL.h>
#include <string> // Para allocator, operator+, char_traits, string
#include <vector> // Para vector
#include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT
#include "core/locale/locale.hpp" // Para Locale
#include "core/rendering/render_info.hpp" // Para RenderInfo
#include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/screenshot.hpp" // Para Screenshot
#ifdef _DEBUG
#include "core/system/debug.hpp" // Para Debug (persistencia de render_info en debug.yaml)
#endif
#include "game/options.hpp" // Para Options, options, OptionsVideo, Section
#include "game/scene_manager.hpp" // Para SceneManager
#include "game/ui/console.hpp" // Para Console
#include "game/ui/notifier.hpp" // Para Notifier, NotificationText
#include "utils/utils.hpp" // Para stringInVector
namespace GlobalInputs {
// Funciones internas
namespace {
void handleQuit() {
// En la escena GAME el comportamiento es siempre el mismo (con o sin modo kiosko)
if (SceneManager::current == SceneManager::Scene::GAME) {
const std::string CODE = "PRESS AGAIN TO RETURN TO MENU";
if (stringInVector(Notifier::get()->getCodes(), CODE)) {
SceneManager::current = SceneManager::Scene::TITLE;
} else {
Notifier::get()->show({Locale::get()->get("ui.press_again_menu")}, Notifier::Style::DEFAULT, -1, true, CODE);
}
return;
}
// En modo kiosko, fuera de GAME: mostrar el texto del kiosko y no salir nunca
if (Options::kiosk.enabled) {
const std::string KIOSK_CODE = "KIOSK_EXIT";
if (!stringInVector(Notifier::get()->getCodes(), KIOSK_CODE)) {
Notifier::get()->show({Options::kiosk.text}, Notifier::Style::DEFAULT, -1, true, KIOSK_CODE);
}
// Segunda pulsación: notificación ya activa → no hacer nada
return;
}
// Comportamiento normal fuera del modo kiosko
const std::string CODE = "PRESS AGAIN TO EXIT";
if (stringInVector(Notifier::get()->getCodes(), CODE)) {
SceneManager::current = SceneManager::Scene::QUIT;
} else {
Notifier::get()->show({Locale::get()->get("ui.press_again_exit")}, Notifier::Style::DEFAULT, -1, true, CODE);
}
}
void handleSkipSection() {
switch (SceneManager::current) {
case SceneManager::Scene::LOGO:
SceneManager::current = SceneManager::Scene::TITLE;
SceneManager::options = SceneManager::Options::NONE;
break;
default:
break;
}
}
void handleToggleBorder() {
Screen::get()->toggleBorder();
Notifier::get()->show({Locale::get()->get(Options::video.border.enabled ? "ui.border_enabled" : "ui.border_disabled")});
}
void handleToggleVideoMode() {
Screen::get()->toggleVideoMode();
Notifier::get()->show({Locale::get()->get(static_cast<int>(Options::video.fullscreen) == 0 ? "ui.fullscreen_disabled" : "ui.fullscreen_enabled")});
}
void handleDecWindowZoom() {
if (Screen::get()->decWindowZoom()) {
Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)});
}
}
void handleIncWindowZoom() {
if (Screen::get()->incWindowZoom()) {
Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)});
}
}
void handleToggleShaders() {
Screen::get()->toggleShaders();
Notifier::get()->show({Locale::get()->get(Options::video.shader.enabled ? "ui.shaders_enabled" : "ui.shaders_disabled")});
}
void handleNextShaderPreset() {
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
if (!Options::crtpi_presets.empty()) {
Options::video.shader.current_crtpi_preset = (Options::video.shader.current_crtpi_preset + 1) % static_cast<int>(Options::crtpi_presets.size());
Screen::get()->reloadCrtPi();
Notifier::get()->show({Locale::get()->get("ui.crtpi") + " " + prettyName(Options::crtpi_presets[static_cast<size_t>(Options::video.shader.current_crtpi_preset)].name)});
}
} else {
if (!Options::postfx_presets.empty()) {
Options::video.shader.current_postfx_preset = (Options::video.shader.current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
Screen::get()->reloadPostFX();
Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + prettyName(Options::postfx_presets[static_cast<size_t>(Options::video.shader.current_postfx_preset)].name)});
}
}
}
void handleNextShader() {
Screen::get()->nextShader();
Notifier::get()->show({Locale::get()->get("ui.shader") + " " +
(Options::video.shader.current_shader == Rendering::ShaderType::CRTPI ? "CRTPI" : "POSTFX")});
}
void handleNextPalette() {
Screen::get()->nextPalette();
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())});
}
void handlePreviousPalette() {
Screen::get()->previousPalette();
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())});
}
void handleScreenshot() {
std::vector<Uint32> buffer;
int width = 0;
int height = 0;
Screen::get()->captureComposite(buffer, width, height);
std::string filename = Screenshot::save(buffer.data(), width, height);
if (!filename.empty()) {
Notifier::get()->show({filename});
}
}
void handleNextPaletteSortMode() {
Screen::get()->nextPaletteSortMode();
Notifier::get()->show({Locale::get()->get("ui.palette_sort") + " " + toUpper(Screen::get()->getPaletteSortModeName())});
}
void handleToggleIntegerScale() {
Screen::get()->toggleIntegerScale();
Screen::get()->setVideoMode(Options::video.fullscreen);
Notifier::get()->show({Locale::get()->get(Options::video.integer_scale ? "ui.integer_scale_enabled" : "ui.integer_scale_disabled")});
}
void handleToggleVSync() {
Screen::get()->toggleVSync();
Notifier::get()->show({Locale::get()->get(Options::video.vertical_sync ? "ui.vsync_enabled" : "ui.vsync_disabled")});
}
// Detecta qué acción global ha sido presionada (si alguna)
auto getPressedAction() -> InputAction { // NOLINT(readability-function-cognitive-complexity)
if (Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::EXIT;
}
if (Input::get()->checkAction(InputAction::ACCEPT, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::ACCEPT;
}
if (Input::get()->checkAction(InputAction::TOGGLE_BORDER, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_BORDER;
}
if (!Options::kiosk.enabled) {
if (Input::get()->checkAction(InputAction::TOGGLE_FULLSCREEN, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_FULLSCREEN;
}
if (Input::get()->checkAction(InputAction::WINDOW_DEC_ZOOM, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::WINDOW_DEC_ZOOM;
}
if (Input::get()->checkAction(InputAction::WINDOW_INC_ZOOM, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::WINDOW_INC_ZOOM;
}
}
if (Screen::get()->isHardwareAccelerated()) {
if (Input::get()->checkAction(InputAction::TOGGLE_SHADER, Input::DO_NOT_ALLOW_REPEAT)) {
if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
return InputAction::TOGGLE_SUPERSAMPLING; // Ctrl+F4
}
if (Options::video.shader.enabled && ((SDL_GetModState() & SDL_KMOD_SHIFT) != 0U)) {
return InputAction::NEXT_SHADER_PRESET; // Shift+F4
}
return InputAction::TOGGLE_SHADER; // F4
}
}
if (Input::get()->checkAction(InputAction::NEXT_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) {
if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
return InputAction::PREVIOUS_PALETTE; // Ctrl+F5
}
return InputAction::NEXT_PALETTE; // F5
}
if (Input::get()->checkAction(InputAction::NEXT_PALETTE_SORT, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::NEXT_PALETTE_SORT; // F6
}
if (Input::get()->checkAction(InputAction::TOGGLE_INTEGER_SCALE, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_INTEGER_SCALE;
}
if (Input::get()->checkAction(InputAction::TOGGLE_VSYNC, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_VSYNC;
}
if (Input::get()->checkAction(InputAction::TOGGLE_INFO, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_INFO;
}
if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_CONSOLE;
}
if (Input::get()->checkAction(InputAction::SCREENSHOT, Input::DO_NOT_ALLOW_REPEAT) &&
((SDL_GetModState() & SDL_KMOD_CTRL) != 0U)) {
return InputAction::SCREENSHOT;
}
return InputAction::NONE;
}
} // namespace
// Funciones públicas
// Comprueba los inputs que se pueden introducir en cualquier sección del juego
void handle() {
const bool CONSOLE_ACTIVE = Console::get() != nullptr && Console::get()->isActive();
if (CONSOLE_ACTIVE) {
// TAB/ESC cierran la consola en lugar de ejecutar sus acciones normales
if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT) ||
Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) {
Console::get()->toggle();
return;
}
} else {
// Salida de administrador en modo kiosko (Ctrl+Shift+Alt+Q)
if (Options::kiosk.enabled) {
SDL_Keymod mod = SDL_GetModState();
const bool* ks = SDL_GetKeyboardState(nullptr);
if (((mod & SDL_KMOD_CTRL) != 0U) && ((mod & SDL_KMOD_SHIFT) != 0U) && ((mod & SDL_KMOD_ALT) != 0U) && ks[SDL_SCANCODE_Q]) {
SceneManager::current = SceneManager::Scene::QUIT;
return;
}
}
}
// Detectar qué acción global está siendo presionada
InputAction action = getPressedAction();
// Con consola activa, ACCEPT (saltar sección) y EXIT están bloqueados
if (CONSOLE_ACTIVE && (action == InputAction::ACCEPT || action == InputAction::EXIT)) {
return;
}
// Ejecutar el handler correspondiente usando switch statement
switch (action) {
case InputAction::EXIT:
handleQuit();
break;
case InputAction::ACCEPT:
handleSkipSection();
break;
case InputAction::TOGGLE_BORDER:
handleToggleBorder();
break;
case InputAction::TOGGLE_FULLSCREEN:
handleToggleVideoMode();
break;
case InputAction::WINDOW_DEC_ZOOM:
handleDecWindowZoom();
break;
case InputAction::WINDOW_INC_ZOOM:
handleIncWindowZoom();
break;
case InputAction::TOGGLE_SHADER:
handleToggleShaders();
break;
case InputAction::NEXT_SHADER_PRESET:
handleNextShaderPreset();
break;
case InputAction::TOGGLE_SUPERSAMPLING:
handleNextShader();
break;
case InputAction::NEXT_PALETTE:
handleNextPalette();
break;
case InputAction::PREVIOUS_PALETTE:
handlePreviousPalette();
break;
case InputAction::NEXT_PALETTE_SORT:
handleNextPaletteSortMode();
break;
case InputAction::TOGGLE_INTEGER_SCALE:
handleToggleIntegerScale();
break;
case InputAction::TOGGLE_VSYNC:
handleToggleVSync();
break;
case InputAction::TOGGLE_CONSOLE:
if (Console::get() != nullptr) { Console::get()->toggle(); }
break;
case InputAction::SCREENSHOT:
handleScreenshot();
break;
case InputAction::TOGGLE_INFO:
if (RenderInfo::get() != nullptr) {
// Leemos la intención antes del toggle: isActive() incluye la
// animación VANISHING, así que justo después de un toggle ACTIVE→OFF
// seguiría devolviendo true y la persistencia no detectaría el cambio.
const bool WAS_ACTIVE = RenderInfo::get()->isActive();
RenderInfo::get()->toggle();
#ifdef _DEBUG
if (Debug::get() != nullptr) { Debug::get()->setRenderInfoEnabled(!WAS_ACTIVE); }
#endif
}
break;
case InputAction::NONE:
default:
// No se presionó ninguna acción global
break;
}
}
} // namespace GlobalInputs