From a3f18f739aab943220b23aa83d76afced505dc4a Mon Sep 17 00:00:00 2001 From: Kenix3 Date: Wed, 24 May 2023 21:37:08 -0400 Subject: [PATCH] Controller now stores the scancode as the key in the mapping. (#290) * Controller now stores the scancode as the key in the mapping. This allows us to support multiple physical buttons going to a single N64 button. * Runs clang-format * SetButtonMapping takes the physical button id as the first argument * Comments need to release all buttons in InputEditorWindow. * Erases all one controller to many json blocks. * Runs clang-format --- src/controller/ControlDeck.cpp | 62 +++++++++++++++++++++------ src/controller/Controller.cpp | 6 +-- src/controller/Controller.h | 14 +++--- src/controller/DummyController.cpp | 2 +- src/controller/DummyController.h | 2 +- src/controller/KeyboardController.cpp | 15 ++++++- src/controller/KeyboardController.h | 2 +- src/controller/SDLController.cpp | 5 ++- src/controller/SDLController.h | 2 +- src/port/wiiu/WiiUController.cpp | 2 +- src/port/wiiu/WiiUController.h | 2 +- src/port/wiiu/WiiUGamepad.cpp | 2 +- src/port/wiiu/WiiUGamepad.h | 2 +- src/window/gui/InputEditorWindow.cpp | 13 +++++- 14 files changed, 91 insertions(+), 40 deletions(-) diff --git a/src/controller/ControlDeck.cpp b/src/controller/ControlDeck.cpp index d8069a3a1..b173e8ec7 100644 --- a/src/controller/ControlDeck.cpp +++ b/src/controller/ControlDeck.cpp @@ -161,11 +161,11 @@ void ControlDeck::LoadSettings() { profile->AxisMinimumPress.clear(); profile->GyroData.clear(); - profile->Version = config->GetInt(NESTED("Version", ""), DEVICE_PROFILE_VERSION_V0); + profile->Version = config->GetInt(NESTED("Version", ""), DEVICE_PROFILE_VERSION_0); switch (profile->Version) { - case DEVICE_PROFILE_VERSION_V0: + case DEVICE_PROFILE_VERSION_0: // Load up defaults for the things we can't load. device->CreateDefaultBinding(virtualSlot); @@ -175,12 +175,12 @@ void ControlDeck::LoadSettings() { profile->UseGyro = config->GetBool(NESTED("Gyro.Enabled", "")); for (auto const& val : rawProfile["Mappings"].items()) { - device->SetButtonMapping(virtualSlot, std::stoi(val.key().substr(4)), val.value()); + device->SetButtonMapping(virtualSlot, val.value(), std::stoi(val.key().substr(4))); } break; - case DEVICE_PROFILE_VERSION_V1: + case DEVICE_PROFILE_VERSION_1: profile->UseRumble = config->GetBool(NESTED("Rumble.Enabled", "")); profile->RumbleStrength = config->GetFloat(NESTED("Rumble.Strength", "")); profile->UseGyro = config->GetBool(NESTED("Gyro.Enabled", "")); @@ -199,7 +199,31 @@ void ControlDeck::LoadSettings() { } for (auto const& val : rawProfile["Mappings"].items()) { - device->SetButtonMapping(virtualSlot, std::stoi(val.key().substr(4)), val.value()); + device->SetButtonMapping(virtualSlot, val.value(), std::stoi(val.key().substr(4))); + } + + break; + + case DEVICE_PROFILE_VERSION_2: + profile->UseRumble = config->GetBool(NESTED("Rumble.Enabled", "")); + profile->RumbleStrength = config->GetFloat(NESTED("Rumble.Strength", "")); + profile->UseGyro = config->GetBool(NESTED("Gyro.Enabled", "")); + profile->NotchProximityThreshold = config->GetInt(NESTED("Notches.ProximityThreshold", "")); + + for (auto const& val : rawProfile["AxisDeadzones"].items()) { + profile->AxisDeadzones[std::stoi(val.key())] = val.value(); + } + + for (auto const& val : rawProfile["AxisMinimumPress"].items()) { + profile->AxisMinimumPress[std::stoi(val.key())] = val.value(); + } + + for (auto const& val : rawProfile["GyroData"].items()) { + profile->GyroData[std::stoi(val.key())] = val.value(); + } + + for (auto const& val : rawProfile["Mappings"].items()) { + device->SetButtonMapping(virtualSlot, std::stoi(val.key()), val.value()); } break; @@ -218,7 +242,7 @@ void ControlDeck::SaveSettings() { for (size_t i = 0; i < mPortList.size(); i++) { std::shared_ptr backend = mDevices[mPortList[i]]; - config->SetString(StringHelper::Sprintf("Controllers.Deck.Slot_%d", (int)i), backend->GetGuid()); + config->SetString(StringHelper::Sprintf("Controllers.Deck.Slot_%d", (int32_t)i), backend->GetGuid()); } for (const auto& device : mDevices) { @@ -231,16 +255,30 @@ void ControlDeck::SaveSettings() { continue; } - auto rawProfile = - config->GetNestedJson()["Controllers"][guid][StringHelper::Sprintf("Slot_%d", virtualSlot)]; + // We always save to the most recent version. + profile->Version = DEVICE_PROFILE_CURRENT_VERSION; + + auto conf = config->GetNestedJson()["Controllers"][guid][StringHelper::Sprintf("Slot_%d", virtualSlot)]; + config->SetInt(NESTED("Version", ""), profile->Version); config->SetBool(NESTED("Rumble.Enabled", ""), profile->UseRumble); config->SetFloat(NESTED("Rumble.Strength", ""), profile->RumbleStrength); config->SetBool(NESTED("Gyro.Enabled", ""), profile->UseGyro); config->SetInt(NESTED("Notches.ProximityThreshold", ""), profile->NotchProximityThreshold); - for (auto const& val : rawProfile["Mappings"].items()) { - config->SetInt(NESTED("Mappings.%s", val.key().c_str()), -1); + // Clear all sections with a one controller to many relationship. + const static std::vector sClearSections = { "Mappings", "AxisDeadzones", "AxisMinimumPress", + "GyroData" }; + for (auto const& section : sClearSections) { + if (conf.contains(section)) { + for (auto const& val : conf[section].items()) { + config->Erase(NESTED("%s.%s", section.c_str(), val.key().c_str())); + } + } + } + + for (auto const& [key, val] : profile->Mappings) { + config->SetInt(NESTED("Mappings.%d", key), val); } for (auto const& [key, val] : profile->AxisDeadzones) { @@ -255,10 +293,6 @@ void ControlDeck::SaveSettings() { config->SetFloat(NESTED("GyroData.%d", key), val); } - for (auto const& [key, val] : profile->Mappings) { - config->SetInt(NESTED("Mappings.BTN_%d", val), key); - } - virtualSlot++; } } diff --git a/src/controller/Controller.cpp b/src/controller/Controller.cpp index 484134e6b..f54b2c22b 100644 --- a/src/controller/Controller.cpp +++ b/src/controller/Controller.cpp @@ -207,10 +207,8 @@ void Controller::ReadToPad(OSContPad* pad, int32_t portIndex) { } } -void Controller::SetButtonMapping(int32_t portIndex, int32_t n64Button, int32_t scancode) { - std::map& mappings = GetProfile(portIndex)->Mappings; - std::erase_if(mappings, [n64Button](const std::pair& bin) { return bin.second == n64Button; }); - mappings[scancode] = n64Button; +void Controller::SetButtonMapping(int32_t portIndex, int32_t deviceButtonId, int32_t n64bitmask) { + GetProfile(portIndex)->Mappings[deviceButtonId] = n64bitmask; } int8_t& Controller::GetLeftStickX(int32_t portIndex) { diff --git a/src/controller/Controller.h b/src/controller/Controller.h index 28fbc6032..176fc6a73 100644 --- a/src/controller/Controller.h +++ b/src/controller/Controller.h @@ -17,9 +17,9 @@ namespace LUS { enum GyroData { DRIFT_X, DRIFT_Y, GYRO_SENSITIVITY }; enum Stick { LEFT, RIGHT }; enum Axis { X, Y }; -enum DeviceProfileVersion { DEVICE_PROFILE_VERSION_V0 = 0, DEVICE_PROFILE_VERSION_V1 = 1 }; +enum DeviceProfileVersion { DEVICE_PROFILE_VERSION_0 = 0, DEVICE_PROFILE_VERSION_1 = 1, DEVICE_PROFILE_VERSION_2 = 2 }; -#define DEVICE_PROFILE_CURRENT_VERSION DEVICE_PROFILE_VERSION_V1 +#define DEVICE_PROFILE_CURRENT_VERSION DEVICE_PROFILE_VERSION_2 struct DeviceProfile { int32_t Version = 0; @@ -33,8 +33,6 @@ struct DeviceProfile { std::map Mappings; }; -class ControlDeck; - class Controller { public: Controller(int32_t deviceIndex); @@ -47,13 +45,13 @@ class Controller { virtual void CreateDefaultBinding(int32_t portIndex) = 0; virtual void ClearRawPress() = 0; virtual int32_t ReadRawPress() = 0; - virtual const std::string GetButtonName(int32_t portIndex, int32_t n64Button) = 0; + virtual const std::string GetButtonName(int32_t portIndex, int32_t n64bitmask) = 0; virtual int32_t SetRumble(int32_t portIndex, bool rumble) = 0; virtual int32_t SetLedColor(int32_t portIndex, Color_RGB8 color) = 0; std::string GetControllerName(); void ReadToPad(OSContPad* pad, int32_t portIndex); - void SetButtonMapping(int32_t portIndex, int32_t n64Button, int32_t scancode); + void SetButtonMapping(int32_t portIndex, int32_t deviceButtonId, int32_t n64bitmask); std::shared_ptr GetProfile(int32_t portIndex); int8_t& GetLeftStickX(int32_t portIndex); @@ -74,9 +72,9 @@ class Controller { int32_t mDeviceIndex; std::string mControllerName = "Unknown"; - void LoadBinding(); int8_t ReadStick(int32_t portIndex, Stick stick, Axis axis); void ProcessStick(int8_t& x, int8_t& y, float deadzoneX, float deadzoneY, int32_t notchProxmityThreshold); + double GetClosestNotch(double angle, double approximationThreshold); private: struct Buttons { @@ -92,7 +90,5 @@ class Controller { std::unordered_map> mProfiles; std::unordered_map> mButtonData = {}; std::deque mPadBuffer; - - double GetClosestNotch(double angle, double approximationThreshold); }; } // namespace LUS diff --git a/src/controller/DummyController.cpp b/src/controller/DummyController.cpp index a35ce4e24..8b336f752 100644 --- a/src/controller/DummyController.cpp +++ b/src/controller/DummyController.cpp @@ -13,7 +13,7 @@ DummyController::DummyController(int32_t deviceIndex, const std::string& guid, c void DummyController::ReadDevice(int32_t portIndex) { } -const std::string DummyController::GetButtonName(int32_t portIndex, int32_t n64Button) { +const std::string DummyController::GetButtonName(int32_t portIndex, int32_t n64bitmask) { return mButtonName; } diff --git a/src/controller/DummyController.h b/src/controller/DummyController.h index b066fd2e3..32963d123 100644 --- a/src/controller/DummyController.h +++ b/src/controller/DummyController.h @@ -9,7 +9,7 @@ class DummyController final : public Controller { public: DummyController(int32_t deviceIndex, const std::string& guid, const std::string& keyName, bool connected); void ReadDevice(int32_t portIndex) override; - const std::string GetButtonName(int32_t portIndex, int32_t n64Button) override; + const std::string GetButtonName(int32_t portIndex, int32_t n64bitmask) override; bool Connected() const override; bool CanRumble() const override; bool CanSetLed() const override; diff --git a/src/controller/KeyboardController.cpp b/src/controller/KeyboardController.cpp index 535777594..406a8dcc6 100644 --- a/src/controller/KeyboardController.cpp +++ b/src/controller/KeyboardController.cpp @@ -59,11 +59,12 @@ int32_t KeyboardController::ReadRawPress() { return mLastKey; } -const std::string KeyboardController::GetButtonName(int32_t portIndex, int32_t n64Button) { +const std::string KeyboardController::GetButtonName(int32_t portIndex, int32_t n64bitmask) { std::map& mappings = GetProfile(portIndex)->Mappings; + // OTRTODO: This should get the scancode of all bits in the mask. const auto find = std::find_if(mappings.begin(), mappings.end(), - [n64Button](const std::pair& pair) { return pair.second == n64Button; }); + [n64bitmask](const std::pair& pair) { return pair.second == n64bitmask; }); if (find == mappings.end()) { return "Unknown"; @@ -74,6 +75,16 @@ const std::string KeyboardController::GetButtonName(int32_t portIndex, int32_t n void KeyboardController::CreateDefaultBinding(int32_t portIndex) { auto profile = GetProfile(portIndex); + profile->Mappings.clear(); + profile->AxisDeadzones.clear(); + profile->AxisMinimumPress.clear(); + profile->GyroData.clear(); + + profile->Version = DEVICE_PROFILE_CURRENT_VERSION; + profile->UseRumble = false; + profile->RumbleStrength = 1.0f; + profile->UseGyro = false; + profile->Mappings[0x14D] = BTN_CRIGHT; profile->Mappings[0x14B] = BTN_CLEFT; profile->Mappings[0x150] = BTN_CDOWN; diff --git a/src/controller/KeyboardController.h b/src/controller/KeyboardController.h index 72266b2bf..c07e58c4c 100644 --- a/src/controller/KeyboardController.h +++ b/src/controller/KeyboardController.h @@ -8,7 +8,7 @@ class KeyboardController : public Controller { KeyboardController(int32_t deviceIndex); void ReadDevice(int32_t portIndex) override; - const std::string GetButtonName(int32_t portIndex, int32_t n64Button) override; + const std::string GetButtonName(int32_t portIndex, int32_t n64bitmask) override; bool PressButton(int32_t scancode); bool ReleaseButton(int32_t scancode); bool Connected() const override; diff --git a/src/controller/SDLController.cpp b/src/controller/SDLController.cpp index e616a9f4d..8538a1be1 100644 --- a/src/controller/SDLController.cpp +++ b/src/controller/SDLController.cpp @@ -315,13 +315,14 @@ int32_t SDLController::SetLedColor(int32_t portIndex, Color_RGB8 color) { return SDL_JoystickSetLED(SDL_GameControllerGetJoystick(mController), mLedColor.r, mLedColor.g, mLedColor.b); } -const std::string SDLController::GetButtonName(int32_t portIndex, int32_t n64Button) { +const std::string SDLController::GetButtonName(int32_t portIndex, int32_t n64bitmask) { char buffer[50]; + // OTRTODO: This should get the scancode of all bits in the mask. std::map& mappings = GetProfile(portIndex)->Mappings; const auto find = std::find_if(mappings.begin(), mappings.end(), - [n64Button](const std::pair& pair) { return pair.second == n64Button; }); + [n64bitmask](const std::pair& pair) { return pair.second == n64bitmask; }); if (find == mappings.end()) { return "Unknown"; diff --git a/src/controller/SDLController.h b/src/controller/SDLController.h index 9af5549b2..05d4f5120 100644 --- a/src/controller/SDLController.h +++ b/src/controller/SDLController.h @@ -7,7 +7,7 @@ class SDLController : public Controller { public: SDLController(int32_t deviceIndex); void ReadDevice(int32_t portIndex) override; - const std::string GetButtonName(int32_t portIndex, int32_t n64Button) override; + const std::string GetButtonName(int32_t portIndex, int32_t n64bitmask) override; int32_t SetRumble(int32_t portIndex, bool rumble) override; int32_t SetLedColor(int32_t portIndex, Color_RGB8 color) override; bool Connected() const override; diff --git a/src/port/wiiu/WiiUController.cpp b/src/port/wiiu/WiiUController.cpp index 8b15da746..db4407ce7 100644 --- a/src/port/wiiu/WiiUController.cpp +++ b/src/port/wiiu/WiiUController.cpp @@ -303,7 +303,7 @@ int32_t WiiUController::ReadRawPress() { return -1; } -const std::string WiiUController::GetButtonName(int32_t portIndex, int n64Button) { +const std::string WiiUController::GetButtonName(int32_t portIndex, int32_t n64bitmask) { std::map& Mappings = GetProfile(portIndex)->Mappings; const auto find = std::find_if(Mappings.begin(), Mappings.end(), diff --git a/src/port/wiiu/WiiUController.h b/src/port/wiiu/WiiUController.h index 7fb44c01e..2dec4dd4f 100644 --- a/src/port/wiiu/WiiUController.h +++ b/src/port/wiiu/WiiUController.h @@ -22,7 +22,7 @@ class WiiUController : public Controller { int32_t ReadRawPress() override; int32_t SetRumble(int32_t portIndex, bool rumble) override; int32_t SetLedColor(int32_t portIndex, Color_RGB8 color) override; - const std::string GetButtonName(int32_t portIndex, int n64Button) override; + const std::string GetButtonName(int32_t portIndex, int32_t n64bitmask) override; protected: void CreateDefaultBinding(int32_t portIndex) override; diff --git a/src/port/wiiu/WiiUGamepad.cpp b/src/port/wiiu/WiiUGamepad.cpp index b17d54d79..46197e8e1 100644 --- a/src/port/wiiu/WiiUGamepad.cpp +++ b/src/port/wiiu/WiiUGamepad.cpp @@ -225,7 +225,7 @@ int32_t WiiUGamepad::ReadRawPress() { return -1; } -const std::string WiiUGamepad::GetButtonName(int32_t portIndex, int n64Button) { +const std::string WiiUGamepad::GetButtonName(int32_t portIndex, int32_t n64bitmask) { std::map& mappings = GetProfile(portIndex)->Mappings; const auto find = std::find_if(mappings.begin(), mappings.end(), diff --git a/src/port/wiiu/WiiUGamepad.h b/src/port/wiiu/WiiUGamepad.h index 09f29f825..bbd609c80 100644 --- a/src/port/wiiu/WiiUGamepad.h +++ b/src/port/wiiu/WiiUGamepad.h @@ -19,7 +19,7 @@ class WiiUGamepad : public Controller { int32_t ReadRawPress() override; int32_t SetRumble(int32_t portIndex, bool rumble) override; int32_t SetLedColor(int32_t portIndex, Color_RGB8 color) override; - const std::string GetButtonName(int32_t portIndex, int n64Button) override; + const std::string GetButtonName(int32_t portIndex, int32_t n64bitmask) override; protected: void CreateDefaultBinding(int32_t portIndex) override; diff --git a/src/window/gui/InputEditorWindow.cpp b/src/window/gui/InputEditorWindow.cpp index d5bf8f86e..723118749 100644 --- a/src/window/gui/InputEditorWindow.cpp +++ b/src/window/gui/InputEditorWindow.cpp @@ -1,5 +1,6 @@ #include "InputEditorWindow.h" #include "controller/Controller.h" +#include "controller/KeyboardController.h" #include "Context.h" #include "Gui.h" #include @@ -48,7 +49,17 @@ void InputEditorWindow::DrawButton(const char* label, int32_t n64Btn, int32_t cu Context::GetInstance()->GetControlDeck()->BlockGameInput(mGameInputBlockId); if (btn != -1) { - backend->SetButtonMapping(currentPort, n64Btn, btn); + auto profile = backend->GetProfile(currentPort); + // Remove other mappings that include the n64 bitmask. Note that the n64 button is really a mask and is not + // unique, but the UI as-is needs a way to unset the old n64 button. + std::erase_if(profile->Mappings, + [n64Btn](const std::pair& bin) { return bin.second == n64Btn; }); + backend->SetButtonMapping(currentPort, btn, n64Btn); + auto keyboardBackend = dynamic_pointer_cast(backend); + if (keyboardBackend != nullptr) { + // Window backend has not called release on the scancode and thus we need to release all buttons. + keyboardBackend->ReleaseAllButtons(); + } Context::GetInstance()->GetControlDeck()->SaveSettings(); *btnReading = -1;