#include "core/input/global_inputs.hpp" #include #include #include #include "core/input/key_config.hpp" #include "core/locale/locale.hpp" #include "core/rendering/notification_presets.hpp" #include "core/rendering/notifier.hpp" #include "core/rendering/render_info.hpp" #include "core/rendering/screen.hpp" #include "core/system/event_buffer.hpp" #include "game/options.hpp" #include "game/options_apply.hpp" #include "utils/defaults.hpp" namespace GlobalInputs { namespace { // Finestra per a confirmar l'eixida amb una segona pulsació d'Escape. // La lliguem a la durada de la notificació EXIT perquè el missatge i // la confirmació expiren alhora — si no, l'usuari podria veure el // text però ja no ser dins del rang de confirmació (o al revés). constexpr float EXIT_WINDOW_S = static_cast(Notification::EXIT.time_ms) / 1000.0F; constexpr int MIN_ZOOM = 1; constexpr int MAX_ZOOM = 6; float exit_window_s = 0.0F; bool quit_requested = false; void notifyLocale(const std::string& key) { Notification::notify(Locale::get().text(key)); } void onDecZoom() { if (Options::window().fullscreen) { return; } if (Options::window().zoom <= MIN_ZOOM) { return; } const int NEW_ZOOM = Options::window().zoom - 1; Options::setWindowZoom(NEW_ZOOM); Screen::get().setWindowZoom(NEW_ZOOM); Notification::notify(Locale::get().text("input.zoom") + " x" + std::to_string(NEW_ZOOM)); } void onIncZoom() { if (Options::window().fullscreen) { return; } if (Options::window().zoom >= MAX_ZOOM) { return; } const int NEW_ZOOM = Options::window().zoom + 1; Options::setWindowZoom(NEW_ZOOM); Screen::get().setWindowZoom(NEW_ZOOM); Notification::notify(Locale::get().text("input.zoom") + " x" + std::to_string(NEW_ZOOM)); } void onFullscreen() { const bool NEW_FS = !Options::window().fullscreen; Options::setWindowFullscreen(NEW_FS); Screen::get().setFullscreen(NEW_FS); notifyLocale(NEW_FS ? "input.fullscreen_on" : "input.fullscreen_off"); } void onToggleVSync() { const bool NEW_VSYNC = !Options::video().vsync; Options::setVideoVSync(NEW_VSYNC); Screen::get().setVSync(NEW_VSYNC); notifyLocale(NEW_VSYNC ? "input.vsync_on" : "input.vsync_off"); } void onToggleShader() { const bool NEW_ENABLED = !Options::video().shader.enabled; Options::setShaderEnabled(NEW_ENABLED); OptionsApply::reapplyShader(); notifyLocale(NEW_ENABLED ? "input.shader_on" : "input.shader_off"); } void onNextShader() { const std::string NEW_NAME = (Options::video().shader.current_shader == "crtpi") ? "postfx" : "crtpi"; Options::setActiveShaderName(NEW_NAME); OptionsApply::reapplyShader(); Notification::notify(Locale::get().text("input.shader") + ": " + NEW_NAME); } // Busca l'entrada actual al vector de presets i passa a la següent // (wrap-around). Si no en troba cap s'evita el canvi silenciosament. void onNextPreset() { const bool IS_CRTPI = Options::video().shader.current_shader == "crtpi"; std::string new_preset; if (IS_CRTPI) { const auto& presets = Options::crtpiPresets(); if (presets.empty()) { return; } auto it = std::ranges::find_if(presets, [](const auto& preset) { return preset.name == Options::video().shader.current_crtpi_preset; }); std::size_t idx = (it == presets.end()) ? 0 : std::distance(presets.begin(), it); idx = (idx + 1) % presets.size(); new_preset = presets[idx].name; Options::setCrtPiPreset(new_preset); } else { const auto& presets = Options::postfxPresets(); if (presets.empty()) { return; } auto it = std::ranges::find_if(presets, [](const auto& preset) { return preset.name == Options::video().shader.current_postfx_preset; }); std::size_t idx = (it == presets.end()) ? 0 : std::distance(presets.begin(), it); idx = (idx + 1) % presets.size(); new_preset = presets[idx].name; Options::setPostFXPreset(new_preset); } OptionsApply::reapplyShader(); Notification::notify(Locale::get().text("input.preset") + ": " + new_preset); } void onNextScalingMode() { using Mode = Defaults::ScalingMode; const auto NEW_MODE = static_cast((static_cast(Options::video().scaling_mode) + 1) % 5); Options::setVideoScalingMode(NEW_MODE); Screen::get().setScalingMode(NEW_MODE); const char* mode_key = nullptr; switch (NEW_MODE) { case Mode::DISABLED: mode_key = "input.scaling_disabled"; break; case Mode::STRETCH: mode_key = "input.scaling_stretch"; break; case Mode::LETTERBOX: mode_key = "input.scaling_letterbox"; break; case Mode::OVERSCAN: mode_key = "input.scaling_overscan"; break; case Mode::INTEGER: mode_key = "input.scaling_integer"; break; } notifyLocale(mode_key); } void onCycleRenderInfo() { const int NEXT = (static_cast(Options::video().render_info_position) + 1) % Defaults::RENDER_INFO_POSITION_COUNT; Options::setVideoRenderInfoPosition(static_cast(NEXT)); } void onToggleRenderTime() { Options::setVideoRenderInfoShowTime(!Options::video().render_info_show_time); } void onExit() { if (exit_window_s > 0.0F) { quit_requested = true; return; } exit_window_s = EXIT_WINDOW_S; Notification::notify(Locale::get().text("input.press_again_exit"), Notification::EXIT); } } // namespace void update(int delta_ms) { exit_window_s = std::max(0.0F, exit_window_s - (static_cast(delta_ms) / 1000.0F)); for (const auto& event : EventBuffer::events) { if (event.type != SDL_EVENT_KEY_DOWN) { continue; } const auto SC = event.key.scancode; if (SC == KeyConfig::get().scancode("dec_zoom")) { onDecZoom(); } else if (SC == KeyConfig::get().scancode("inc_zoom")) { onIncZoom(); } else if (SC == KeyConfig::get().scancode("fullscreen")) { onFullscreen(); } else if (SC == KeyConfig::get().scancode("toggle_shader")) { onToggleShader(); } else if (SC == KeyConfig::get().scancode("toggle_vsync")) { onToggleVSync(); } else if (SC == KeyConfig::get().scancode("next_shader")) { onNextShader(); } else if (SC == KeyConfig::get().scancode("next_preset")) { onNextPreset(); } else if (SC == KeyConfig::get().scancode("next_scaling_mode")) { onNextScalingMode(); } else if (SC == KeyConfig::get().scancode("toggle_render_info")) { onCycleRenderInfo(); } else if (SC == KeyConfig::get().scancode("toggle_render_time")) { onToggleRenderTime(); } else if (SC == KeyConfig::get().scancode("exit")) { onExit(); } } } auto wantsQuit() -> bool { return quit_requested; } } // namespace GlobalInputs