Skip to content

Commit

Permalink
Serialization/deserialization of mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
OFFTKP committed May 1, 2024
1 parent 21492f8 commit b2a2119
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 8 deletions.
87 changes: 87 additions & 0 deletions include/input_mappings.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#pragma once

#include <filesystem>
#include <map>
#include <optional>
#include <toml.hpp>
#include <unordered_map>

#include "helpers.hpp"
Expand All @@ -15,8 +19,91 @@ struct InputMappings {
}

void setMapping(Scancode scancode, u32 key) { container[scancode] = key; }

void serialize(const std::filesystem::path& path, const std::string& frontend) const {
toml::basic_value<toml::preserve_comments, std::map> data;

std::error_code error;
if (std::filesystem::exists(path, error)) {
try {
data = toml::parse<toml::preserve_comments, std::map>(path);
} catch (const std::exception& ex) {
Helpers::warn("Exception trying to parse mappings file. Exception: %s\n", ex.what());
return;
}
} else {
if (error) {
Helpers::warn("Filesystem error accessing %s (error: %s)\n", path.string().c_str(), error.message().c_str());
}
printf("Saving new mappings file %s\n", path.string().c_str());
}

data["Metadata"]["Name"] = name.empty() ? "Unnamed Mappings" : name;
data["Metadata"]["Device"] = device.empty() ? "Unknown Device" : device;
data["Metadata"]["Frontend"] = frontend;

data["Mappings"] = toml::table{};

for (const auto& [scancode, key] : container) {
if (data["Mappings"].contains(HID::Keys::keyToName(key)) == false) {
data["Mappings"][HID::Keys::keyToName(key)] = toml::array{};
}

data["Mappings"][HID::Keys::keyToName(key)].push_back(scancodeToName(scancode));
}

std::ofstream file(path, std::ios::out);
file << data;
}

static std::optional<InputMappings> deserialize(const std::filesystem::path& path, const std::string& wantFrontend) {
toml::basic_value<toml::preserve_comments, std::map> data;

std::error_code error;
if (!std::filesystem::exists(path, error)) {
if (error) {
Helpers::warn("Filesystem error accessing %s (error: %s)\n", path.string().c_str(), error.message().c_str());
}

return std::nullopt;
}

InputMappings mappings;

try {
data = toml::parse<toml::preserve_comments, std::map>(path);

const auto metadata = toml::find(data, "Metadata");
mappings.name = toml::find_or<std::string>(metadata, "Name", "Unnamed Mappings");
mappings.device = toml::find_or<std::string>(metadata, "Device", "Unknown Device");

std::string haveFrontend = toml::find_or<std::string>(metadata, "Frontend", "Unknown Frontend");
if (haveFrontend != wantFrontend) {
Helpers::warn("Mappings file %s was created for frontend %s, but we are using frontend %s\n", path.string().c_str(), haveFrontend.c_str(), wantFrontend.c_str());
return std::nullopt;
}
} catch (const std::exception& ex) {
Helpers::warn("Exception trying to parse config file. Exception: %s\n", ex.what());

return std::nullopt;
}

const auto& mappingsTable = toml::find_or<toml::table>(data, "Mappings", toml::table{});
for (const auto& [key, scancodes] : mappingsTable) {
for (const auto& scancode : scancodes.as_array()) {
mappings.setMapping(nameToScancode(scancode.as_string()), HID::Keys::nameToKey(key));
}
}

return mappings;
}

static InputMappings defaultKeyboardMappings();
static std::string scancodeToName(Scancode scancode);
static Scancode nameToScancode(const std::string& name);

private:
Container container;
std::string name;
std::string device;
};
6 changes: 5 additions & 1 deletion include/services/hid.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include <array>
#include <optional>
#include <string>

#include "helpers.hpp"
#include "kernel_types.hpp"
Expand Down Expand Up @@ -32,7 +33,10 @@ namespace HID::Keys {
CirclePadUp = 1 << 30, // Y >= 41
CirclePadDown = 1u << 31 // Y <= -41
};
}

const char* keyToName(u32 key);
u32 nameToKey(const std::string& name);
} // namespace HID::Keys

// Circular dependency because we need HID to spawn events
class Kernel;
Expand Down
58 changes: 57 additions & 1 deletion src/core/services/hid.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,63 @@
#include "services/hid.hpp"

#include <map>

#include "ipc.hpp"
#include "kernel.hpp"
#include <bit>

namespace HID::Keys {
const char* keyToName(u32 key) {
static std::map<u32, const char*> keyMap = {
{A, "A"},
{B, "B"},
{Select, "Select"},
{Start, "Start"},
{Right, "Right"},
{Left, "Left"},
{Up, "Up"},
{Down, "Down"},
{R, "R"},
{L, "L"},
{X, "X"},
{Y, "Y"},
{CirclePadRight, "CirclePad Right"},
{CirclePadLeft, "CirclePad Left"},
{CirclePadUp, "CirclePad Up"},
{CirclePadDown, "CirclePad Down"},
};

auto it = keyMap.find(key);
return it != keyMap.end() ? it->second : "Unknown key";
}

u32 nameToKey(const std::string& name) {
struct CaseInsensitiveComparator {
bool operator()(const std::string& a, const std::string& b) const noexcept { return ::strcasecmp(a.c_str(), b.c_str()) < 0; }
};

static std::map<std::string, u32, CaseInsensitiveComparator> keyMap = {
{"A", A},
{"B", B},
{"Select", Select},
{"Start", Start},
{"Right", Right},
{"Left", Left},
{"Up", Up},
{"Down", Down},
{"R", R},
{"L", L},
{"X", X},
{"Y", Y},
{"CirclePad Right", CirclePadRight},
{"CirclePad Left", CirclePadLeft},
{"CirclePad Up", CirclePadUp},
{"CirclePad Down", CirclePadDown},
};

auto it = keyMap.find(name);
return it != keyMap.end() ? it->second : HID::Keys::Null;
}
} // namespace HID::Keys

namespace HIDCommands {
enum : u32 {
Expand Down
11 changes: 8 additions & 3 deletions src/panda_qt/mappings.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "input_mappings.hpp"

#include <QKeyEvent>
#include <string>

#include "input_mappings.hpp"

InputMappings InputMappings::defaultKeyboardMappings() {
InputMappings mappings;
Expand All @@ -22,4 +23,8 @@ InputMappings InputMappings::defaultKeyboardMappings() {
mappings.setMapping(Qt::Key_A, HID::Keys::CirclePadLeft);

return mappings;
}
}

std::string InputMappings::scancodeToName(Scancode scancode) { return QKeySequence(scancode).toString().toStdString(); }

InputMappings::Scancode InputMappings::nameToScancode(const std::string& name) { return QKeySequence(QString::fromStdString(name))[0].key(); }
12 changes: 9 additions & 3 deletions src/panda_sdl/mappings.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "input_mappings.hpp"

#include <SDL.h>

#include <string>

#include "input_mappings.hpp"

InputMappings InputMappings::defaultKeyboardMappings() {
InputMappings mappings;
mappings.setMapping(SDLK_l, HID::Keys::A);
Expand All @@ -22,4 +24,8 @@ InputMappings InputMappings::defaultKeyboardMappings() {
mappings.setMapping(SDLK_a, HID::Keys::CirclePadLeft);

return mappings;
}
}

std::string InputMappings::scancodeToName(Scancode scancode) { return SDL_GetKeyName(scancode); }

InputMappings::Scancode InputMappings::nameToScancode(const std::string& name) { return SDL_GetKeyFromName(name.c_str()); }

0 comments on commit b2a2119

Please # to comment.