varies coses i detallets

This commit is contained in:
2026-04-16 18:46:58 +02:00
parent fe41919e1e
commit 6394e9afab
17 changed files with 513 additions and 156 deletions

View File

@@ -1,10 +1,13 @@
#include "core/input/gamepad.hpp"
#include <cstdio>
#include <string>
#include "core/input/key_config.hpp"
#include "core/jail/jinput.hpp"
#include "core/locale/locale.hpp"
#include "core/rendering/menu.hpp"
#include "game/options.hpp"
#include "core/rendering/overlay.hpp"
namespace Gamepad {
@@ -19,11 +22,23 @@ namespace Gamepad {
static bool prev_down_ = false;
static bool prev_left_ = false;
static bool prev_right_ = false;
static bool prev_a_ = false;
static bool prev_b_ = false;
static bool prev_south_ = false;
static bool prev_east_ = false;
static bool prev_west_ = false;
static bool prev_north_ = false;
static bool prev_start_ = false;
static bool prev_back_ = false;
static void notify(const char* name, const char* status_key) {
std::string msg = (name && *name) ? name : "Gamepad";
msg += ' ';
msg += Locale::get(status_key);
Overlay::showNotification(msg.c_str(), 2.5F);
}
static void notifyConnected(const char* name) { notify(name, "notifications.gamepad_connected"); }
static void notifyDisconnected(const char* name) { notify(name, "notifications.gamepad_disconnected"); }
static void openFirstGamepad() {
int count = 0;
SDL_JoystickID* ids = SDL_GetGamepads(&count);
@@ -70,12 +85,16 @@ namespace Gamepad {
pad_ = SDL_OpenGamepad(event.gdevice.which);
if (pad_) {
pad_id_ = event.gdevice.which;
SDL_Log("Gamepad connectat: %s", SDL_GetGamepadName(pad_));
const char* name = SDL_GetGamepadName(pad_);
SDL_Log("Gamepad connectat: %s", name ? name : "");
notifyConnected(name);
}
}
} else if (event.type == SDL_EVENT_GAMEPAD_REMOVED) {
if (pad_ && event.gdevice.which == pad_id_) {
SDL_Log("Gamepad desconnectat");
const char* name = SDL_GetGamepadName(pad_);
std::string saved_name = name ? name : "";
SDL_Log("Gamepad desconnectat: %s", saved_name.c_str());
SDL_CloseGamepad(pad_);
pad_ = nullptr;
pad_id_ = 0;
@@ -84,6 +103,7 @@ namespace Gamepad {
JI_SetVirtualKey(SDL_SCANCODE_DOWN, JI_VSRC_GAMEPAD, false);
JI_SetVirtualKey(SDL_SCANCODE_LEFT, JI_VSRC_GAMEPAD, false);
JI_SetVirtualKey(SDL_SCANCODE_RIGHT, JI_VSRC_GAMEPAD, false);
notifyDisconnected(saved_name.c_str());
}
}
}
@@ -125,16 +145,18 @@ namespace Gamepad {
bool lt = dlt || slt;
bool rt = drt || srt;
// Botons
bool a = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_SOUTH); // A/Cross
bool b = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_EAST); // B/Circle
// Botons frontals (layout SDL: SOUTH=A/Cross, EAST=B/Circle, WEST=X/Square, NORTH=Y/Triangle)
bool south = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_SOUTH);
bool east = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_EAST);
bool west = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_WEST);
bool north = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_NORTH);
bool start = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_START);
bool back = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_BACK);
// Start → obre/tanca menú (flanc)
if (start && !prev_start_) pushKey(Options::keys_gui.menu_toggle);
// Back → ESC (flanc)
if (back && !prev_back_) pushKey(SDL_SCANCODE_ESCAPE);
// Select (Back) → obre/tanca menú de servei (flanc)
if (back && !prev_back_) pushKey(KeyConfig::scancode("menu_toggle"));
// Start → pausa (flanc)
if (start && !prev_start_) pushKey(KeyConfig::scancode("pause_toggle"));
if (Menu::isOpen()) {
// Navegació del menú per flanc
@@ -142,8 +164,9 @@ namespace Gamepad {
if (dn && !prev_down_) pushKey(SDL_SCANCODE_DOWN);
if (lt && !prev_left_) pushKey(SDL_SCANCODE_LEFT);
if (rt && !prev_right_) pushKey(SDL_SCANCODE_RIGHT);
if (a && !prev_a_) pushKey(SDL_SCANCODE_RETURN);
if (b && !prev_b_) pushKey(SDL_SCANCODE_BACKSPACE);
// EAST accepta, SOUTH cancela / endarrere
if (east && !prev_east_) pushKey(SDL_SCANCODE_RETURN);
if (south && !prev_south_) pushKey(SDL_SCANCODE_BACKSPACE);
// Assegura que el joc no rep tecles de moviment mentre el menú està obert
JI_SetVirtualKey(SDL_SCANCODE_UP, JI_VSRC_GAMEPAD, false);
@@ -156,16 +179,21 @@ namespace Gamepad {
JI_SetVirtualKey(SDL_SCANCODE_DOWN, JI_VSRC_GAMEPAD, dn);
JI_SetVirtualKey(SDL_SCANCODE_LEFT, JI_VSRC_GAMEPAD, lt);
JI_SetVirtualKey(SDL_SCANCODE_RIGHT, JI_VSRC_GAMEPAD, rt);
// Botó A al joc: emet Enter per avançar seqüències (JI_AnyKey)
if (a && !prev_a_) pushKey(SDL_SCANCODE_RETURN);
// Qualsevol dels 4 botons frontals avança escenes (JI_AnyKey via Enter sintètic)
if ((south && !prev_south_) || (east && !prev_east_) ||
(west && !prev_west_) || (north && !prev_north_)) {
pushKey(SDL_SCANCODE_RETURN);
}
}
prev_up_ = up;
prev_down_ = dn;
prev_left_ = lt;
prev_right_ = rt;
prev_a_ = a;
prev_b_ = b;
prev_south_ = south;
prev_east_ = east;
prev_west_ = west;
prev_north_ = north;
prev_start_ = start;
prev_back_ = back;
}

View File

@@ -3,6 +3,7 @@
#include <cstdio>
#include <string>
#include "core/input/key_config.hpp"
#include "core/jail/jinput.hpp"
#include "core/locale/locale.hpp"
#include "core/rendering/overlay.hpp"
@@ -26,7 +27,7 @@ namespace GlobalInputs {
bool consumed = false;
// F1 — Reduir zoom
bool dec_zoom = JI_KeyPressed(Options::keys_gui.dec_zoom);
bool dec_zoom = JI_KeyPressed(KeyConfig::scancode("dec_zoom"));
if (dec_zoom && !dec_zoom_prev) {
Screen::get()->decZoom();
char msg[32];
@@ -37,7 +38,7 @@ namespace GlobalInputs {
dec_zoom_prev = dec_zoom;
// F2 — Augmentar zoom
bool inc_zoom = JI_KeyPressed(Options::keys_gui.inc_zoom);
bool inc_zoom = JI_KeyPressed(KeyConfig::scancode("inc_zoom"));
if (inc_zoom && !inc_zoom_prev) {
Screen::get()->incZoom();
char msg[32];
@@ -48,7 +49,7 @@ namespace GlobalInputs {
inc_zoom_prev = inc_zoom;
// F3 — Toggle pantalla completa
bool fullscreen = JI_KeyPressed(Options::keys_gui.fullscreen);
bool fullscreen = JI_KeyPressed(KeyConfig::scancode("fullscreen"));
if (fullscreen && !fullscreen_prev) {
Screen::get()->toggleFullscreen();
Overlay::showNotification(Screen::get()->isFullscreen() ? Locale::get("notifications.fullscreen") : Locale::get("notifications.windowed"));
@@ -57,7 +58,7 @@ namespace GlobalInputs {
fullscreen_prev = fullscreen;
// F4 — Toggle shaders
bool shader = JI_KeyPressed(Options::keys_gui.toggle_shader);
bool shader = JI_KeyPressed(KeyConfig::scancode("toggle_shader"));
if (shader && !shader_prev) {
Screen::get()->toggleShaders();
Overlay::showNotification(Options::video.shader_enabled ? Locale::get("notifications.shader_on") : Locale::get("notifications.shader_off"));
@@ -66,7 +67,7 @@ namespace GlobalInputs {
shader_prev = shader;
// F5 — Toggle aspect ratio 4:3
bool aspect = JI_KeyPressed(Options::keys_gui.toggle_aspect_ratio);
bool aspect = JI_KeyPressed(KeyConfig::scancode("toggle_aspect_ratio"));
if (aspect && !aspect_prev) {
Screen::get()->toggleAspectRatio();
Overlay::showNotification(Options::video.aspect_ratio_4_3 ? Locale::get("notifications.aspect_43") : Locale::get("notifications.aspect_square"));
@@ -75,7 +76,7 @@ namespace GlobalInputs {
aspect_prev = aspect;
// F6 — Toggle supersampling
bool ss = JI_KeyPressed(Options::keys_gui.toggle_supersampling);
bool ss = JI_KeyPressed(KeyConfig::scancode("toggle_supersampling"));
if (ss && !ss_prev) {
Screen::get()->toggleSupersampling();
Overlay::showNotification(Options::video.supersampling ? Locale::get("notifications.ss_on") : Locale::get("notifications.ss_off"));
@@ -84,7 +85,7 @@ namespace GlobalInputs {
ss_prev = ss;
// F7 — Canviar tipus de shader (PostFX ↔ CrtPi)
bool next_shader = JI_KeyPressed(Options::keys_gui.next_shader);
bool next_shader = JI_KeyPressed(KeyConfig::scancode("next_shader"));
if (next_shader && !next_shader_prev) {
Screen::get()->nextShaderType();
char msg[64];
@@ -95,7 +96,7 @@ namespace GlobalInputs {
next_shader_prev = next_shader;
// F8 — Pròxim preset del shader actiu
bool next_preset = JI_KeyPressed(Options::keys_gui.next_shader_preset);
bool next_preset = JI_KeyPressed(KeyConfig::scancode("next_shader_preset"));
if (next_preset && !next_preset_prev) {
Screen::get()->nextPreset();
char msg[64];
@@ -106,7 +107,7 @@ namespace GlobalInputs {
next_preset_prev = next_preset;
// F9 — Toggle filtre d'estirament 4:3 (NEAREST ↔ LINEAR)
bool stretch_filter = JI_KeyPressed(Options::keys_gui.toggle_stretch_filter);
bool stretch_filter = JI_KeyPressed(KeyConfig::scancode("toggle_stretch_filter"));
if (stretch_filter && !stretch_filter_prev) {
Screen::get()->toggleStretchFilter();
Overlay::showNotification(Options::video.stretch_filter_linear ? Locale::get("notifications.filter_linear") : Locale::get("notifications.filter_nearest"));
@@ -115,7 +116,7 @@ namespace GlobalInputs {
stretch_filter_prev = stretch_filter;
// F10 — Toggle render info (FPS, driver, shader)
bool render_info = JI_KeyPressed(Options::keys_gui.toggle_render_info);
bool render_info = JI_KeyPressed(KeyConfig::scancode("toggle_render_info"));
if (render_info && !render_info_prev) {
Overlay::toggleRenderInfo();
}

View File

@@ -0,0 +1,182 @@
#include "core/input/key_config.hpp"
#include <fstream>
#include <iostream>
#include <utility>
#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"
namespace KeyConfig {
namespace {
std::vector<KeyEntry> entries_;
std::unordered_map<std::string, size_t> index_;
std::string overrides_path_;
auto findIndex(const std::string& id) -> size_t {
auto it = index_.find(id);
if (it == index_.end()) return SIZE_MAX;
return it->second;
}
void loadDefaults(const std::string& defaults_resource_path) {
auto buf = ResourceHelper::loadFile(defaults_resource_path);
if (buf.empty()) {
std::cerr << "KeyConfig: no s'ha pogut llegir " << defaults_resource_path << '\n';
return;
}
std::string content(buf.begin(), buf.end());
try {
auto yaml = fkyaml::node::deserialize(content);
if (!yaml.contains("keys")) return;
for (const auto& node : yaml["keys"]) {
KeyEntry entry;
entry.id = node["id"].get_value<std::string>();
entry.code = node["code"].get_value<std::string>();
if (node.contains("desc")) {
entry.desc = node["desc"].get_value<std::string>();
}
SDL_Scancode sc = SDL_GetScancodeFromName(entry.code.c_str());
if (sc == SDL_SCANCODE_UNKNOWN) {
std::cerr << "KeyConfig: scancode desconegut '" << entry.code
<< "' per '" << entry.id << "'\n";
}
entry.scancode = sc;
entry.default_scancode = sc;
index_[entry.id] = entries_.size();
entries_.push_back(std::move(entry));
}
std::cout << "KeyConfig: " << entries_.size() << " tecles carregades de "
<< defaults_resource_path << '\n';
} catch (const fkyaml::exception& e) {
std::cerr << "KeyConfig: error parsejant YAML: " << e.what() << '\n';
}
}
void applyOverrides(const std::string& disk_path) {
std::ifstream file(disk_path);
if (!file.good()) return;
std::string content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
try {
auto yaml = fkyaml::node::deserialize(content);
if (!yaml.contains("overrides")) return;
int applied = 0;
for (const auto& kv : yaml["overrides"].as_map()) {
auto id = kv.first.get_value<std::string>();
auto code = kv.second.get_value<std::string>();
auto idx = findIndex(id);
if (idx == SIZE_MAX) {
std::cerr << "KeyConfig: override per id desconegut '" << id << "'\n";
continue;
}
SDL_Scancode sc = SDL_GetScancodeFromName(code.c_str());
if (sc == SDL_SCANCODE_UNKNOWN) {
std::cerr << "KeyConfig: override amb scancode invàlid '" << code
<< "' per '" << id << "'\n";
continue;
}
entries_[idx].scancode = sc;
entries_[idx].code = code;
applied++;
}
if (applied > 0) {
std::cout << "KeyConfig: aplicats " << applied
<< " overrides de " << disk_path << '\n';
}
} catch (const fkyaml::exception& e) {
std::cerr << "KeyConfig: error parsejant overrides: " << e.what() << '\n';
}
}
} // namespace
void init(const std::string& defaults_resource_path,
const std::string& user_overrides_disk_path) {
entries_.clear();
index_.clear();
overrides_path_ = user_overrides_disk_path;
loadDefaults(defaults_resource_path);
if (!overrides_path_.empty()) {
applyOverrides(overrides_path_);
}
}
void destroy() {
entries_.clear();
index_.clear();
overrides_path_.clear();
}
auto scancode(const std::string& id) -> SDL_Scancode {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return SDL_SCANCODE_UNKNOWN;
return entries_[idx].scancode;
}
auto scancodePtr(const std::string& id) -> SDL_Scancode* {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return nullptr;
return &entries_[idx].scancode;
}
void setScancode(const std::string& id, SDL_Scancode sc) {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return;
entries_[idx].scancode = sc;
const char* name = SDL_GetScancodeName(sc);
entries_[idx].code = (name != nullptr) ? name : "";
}
auto isGuiKey(SDL_Scancode sc) -> bool {
if (sc == SDL_SCANCODE_UNKNOWN) return false;
for (const auto& e : entries_) {
if (e.scancode == sc) return true;
}
return false;
}
auto entries() -> const std::vector<KeyEntry>& {
return entries_;
}
auto saveOverrides() -> bool {
if (overrides_path_.empty()) return false;
// Recull només les entrades remapeades.
std::vector<const KeyEntry*> changed;
for (const auto& e : entries_) {
if (e.scancode != e.default_scancode) changed.push_back(&e);
}
std::ofstream file(overrides_path_);
if (!file.is_open()) {
std::cerr << "KeyConfig: no es pot escriure " << overrides_path_ << '\n';
return false;
}
file << "# AEE - Overrides de tecles d'UI\n";
file << "# Auto-generat. Només llista les tecles modificades respecte\n";
file << "# els valors per defecte de data/input/keys.yaml.\n";
file << "\n";
if (changed.empty()) {
file << "overrides: {}\n";
} else {
file << "overrides:\n";
for (const auto* e : changed) {
file << " " << e->id << ": \"" << e->code << "\"\n";
}
}
return true;
}
} // namespace KeyConfig

View File

@@ -0,0 +1,52 @@
#pragma once
#include <SDL3/SDL.h>
#include <string>
#include <unordered_map>
#include <vector>
// KeyConfig: font única de veritat per a les tecles d'UI/sistema.
//
// Llegeix els valors per defecte des de `data/input/keys.yaml` (recurs read-only)
// i opcionalment aplica overrides des d'un fitxer de l'usuari (per a remapejos
// fets des del menú de servei). Els callers consulten per `id` (ex. "menu_toggle").
//
// Les tecles de moviment del jugador NO viuen ací — es queden a Options::keys_game.
struct KeyEntry {
std::string id;
std::string code; // nom SDL del scancode tal com apareix al YAML
std::string desc;
SDL_Scancode scancode{SDL_SCANCODE_UNKNOWN};
SDL_Scancode default_scancode{SDL_SCANCODE_UNKNOWN};
};
namespace KeyConfig {
// Inicialitza KeyConfig llegint defaults des d'un recurs (via ResourceHelper)
// i opcionalment sobreposant overrides des d'un fitxer de disc.
void init(const std::string& defaults_resource_path,
const std::string& user_overrides_disk_path);
void destroy();
// Consulta el scancode actual associat a un id. Torna SDL_SCANCODE_UNKNOWN si no existix.
[[nodiscard]] auto scancode(const std::string& id) -> SDL_Scancode;
// Punter estable al scancode d'un id — útil per a Menu::ItemKind::KeyBind.
// Torna nullptr si l'id no existix.
[[nodiscard]] auto scancodePtr(const std::string& id) -> SDL_Scancode*;
// Estableix el scancode d'un id. No persistix per si sol — cal cridar saveOverrides().
void setScancode(const std::string& id, SDL_Scancode sc);
// True si el scancode coincidix amb alguna tecla d'UI registrada.
// Usat pel Director per a evitar que tecles d'UI activen `key_pressed_` al joc.
[[nodiscard]] auto isGuiKey(SDL_Scancode sc) -> bool;
// Llistat complet de les entrades (per a HELP / debug / iteració).
[[nodiscard]] auto entries() -> const std::vector<KeyEntry>&;
// Persistix al fitxer d'overrides les entrades que difereixen del default.
// Si no s'ha proporcionat user_overrides_disk_path al init, és no-op.
auto saveOverrides() -> bool;
} // namespace KeyConfig