183 lines
6.4 KiB
C++
183 lines
6.4 KiB
C++
#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
|