From aababcd87f540cba5befa9877077fe52e9c816fa Mon Sep 17 00:00:00 2001 From: Ian Drake Date: Wed, 30 Mar 2022 19:53:01 -0400 Subject: [PATCH] Added new controller support Using the SDLJoystick API, I added support for new controllers --- libultraship/libultraship/ConfigFile.cpp | 5 + libultraship/libultraship/SDLController.cpp | 316 +----------------- libultraship/libultraship/SDLController.h | 17 +- .../libultraship/SDLGamepadController.cpp | 315 +++++++++++++++++ .../libultraship/SDLGamepadController.h | 25 ++ .../libultraship/SDLJoystickController.cpp | 275 +++++++++++++++ .../libultraship/SDLJoystickController.h | 25 ++ libultraship/libultraship/Window.cpp | 16 +- .../libultraship/libultraship.vcxproj | 4 + .../libultraship/libultraship.vcxproj.filters | 12 + 10 files changed, 682 insertions(+), 328 deletions(-) create mode 100644 libultraship/libultraship/SDLGamepadController.cpp create mode 100644 libultraship/libultraship/SDLGamepadController.h create mode 100644 libultraship/libultraship/SDLJoystickController.cpp create mode 100644 libultraship/libultraship/SDLJoystickController.h diff --git a/libultraship/libultraship/ConfigFile.cpp b/libultraship/libultraship/ConfigFile.cpp index 1b4952139..d079dcec0 100644 --- a/libultraship/libultraship/ConfigFile.cpp +++ b/libultraship/libultraship/ConfigFile.cpp @@ -154,6 +154,11 @@ namespace Ship { (*this)["SDL CONTROLLER 3"]["GUID"] = ""; (*this)["SDL CONTROLLER 4"]["GUID"] = ""; + (*this)["Joystick CONTROLLER 1"]["GUID"] = ""; + (*this)["Joystick CONTROLLER 2"]["GUID"] = ""; + (*this)["Joystick CONTROLLER 3"]["GUID"] = ""; + (*this)["Joystick CONTROLLER 4"]["GUID"] = ""; + return File.generate(Val); } } diff --git a/libultraship/libultraship/SDLController.cpp b/libultraship/libultraship/SDLController.cpp index 47fb57441..c95093670 100644 --- a/libultraship/libultraship/SDLController.cpp +++ b/libultraship/libultraship/SDLController.cpp @@ -9,12 +9,12 @@ extern "C" uint8_t __osMaxControllers; namespace Ship { - SDLController::SDLController(int32_t dwControllerNumber) : Controller(dwControllerNumber), Cont(nullptr), guid(INVALID_SDL_CONTROLLER_GUID) { + + SDLController::SDLController(int32_t dwControllerNumber) : Controller(dwControllerNumber), guid(INVALID_SDL_CONTROLLER_GUID) { } SDLController::~SDLController() { - Close(); } bool SDLController::IsGuidInUse(const std::string& guid) { @@ -32,81 +32,6 @@ namespace Ship { return false; } - bool SDLController::Open() { - std::string ConfSection = GetConfSection(); - std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); - ConfigFile& Conf = *pConf.get(); - - for (int i = 0; i < SDL_NumJoysticks(); i++) { - if (SDL_IsGameController(i)) { - // Get the GUID from SDL - char GuidBuf[33]; - SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i), GuidBuf, sizeof(GuidBuf)); - auto NewGuid = std::string(GuidBuf); - - // Invalid GUID read. Go to next. - if (NewGuid.compare(INVALID_SDL_CONTROLLER_GUID) == 0) { - SPDLOG_ERROR("SDL Controller returned invalid guid"); - continue; - } - - // The GUID is in use, we want to use a different physical controller. Go to next. - if (IsGuidInUse(NewGuid)) { - continue; - } - - // If the GUID is blank from the config, OR if the config GUID matches, load the controller. - if (Conf[ConfSection]["GUID"].compare("") == 0 || Conf[ConfSection]["GUID"].compare(INVALID_SDL_CONTROLLER_GUID) == 0 || Conf[ConfSection]["GUID"].compare(NewGuid) == 0) { - auto NewCont = SDL_GameControllerOpen(i); - - if (SDL_GameControllerHasSensor(NewCont, SDL_SENSOR_GYRO)) - { - SDL_GameControllerSetSensorEnabled(NewCont, SDL_SENSOR_GYRO, SDL_TRUE); - } - - // We failed to load the controller. Go to next. - if (NewCont == nullptr) { - SPDLOG_ERROR("SDL Controller failed to open: ({})", SDL_GetError()); - continue; - } - - guid = NewGuid; - Cont = NewCont; - - std::string BindingConfSection = GetBindingConfSection(); - std::shared_ptr pBindingConf = GlobalCtx2::GetInstance()->GetConfig(); - ConfigFile& BindingConf = *pBindingConf.get(); - - if (!BindingConf.has(BindingConfSection)) { - CreateDefaultBinding(); - } - - LoadBinding(); - LoadAxisThresholds(); - - break; - } - } - } - - return Cont != nullptr; - } - - bool SDLController::Close() { - if (Cont != nullptr) { - SDL_GameControllerClose(Cont); - } - Cont = nullptr; - guid = ""; - ButtonMapping.clear(); - ThresholdMapping.clear(); - dwPressedButtons = 0; - wStickX = 0; - wStickY = 0; - - return true; - } - void SDLController::LoadAxisThresholds() { std::string ConfSection = GetBindingConfSection(); std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); @@ -156,241 +81,6 @@ namespace Ship { wStickY = -ay; } - void SDLController::ReadFromSource() { - std::string ConfSection = GetBindingConfSection(); - std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); - ConfigFile& Conf = *pConf.get(); - - SDL_GameControllerUpdate(); - - // If the controller is disconnected, close it. - if (Cont != nullptr && !SDL_GameControllerGetAttached(Cont)) { - Close(); - } - - // Attempt to load the controller if it's not loaded - if (Cont == nullptr) { - // If we failed to load the controller, don't process it. - if (!Open()) { - return; - } - } - - if (SDL_GameControllerHasSensor(Cont, SDL_SENSOR_GYRO)) - { - float gyroData[3]; - SDL_GameControllerGetSensorData(Cont, SDL_SENSOR_GYRO, gyroData, 3); - - const char* contName = SDL_GameControllerName(Cont); - const int isSpecialController = !strcmp("PS5 Controller", contName); - const float gyroSensitivity = Game::Settings.controller.gyro_sensitivity; - - if (Game::Settings.controller.gyroDriftX == 0) { - Game::Settings.controller.gyroDriftX = gyroData[0]; - } - - if (Game::Settings.controller.gyroDriftY == 0) { - if (isSpecialController == 1) { - Game::Settings.controller.gyroDriftY = gyroData[2]; - } - else { - Game::Settings.controller.gyroDriftY = gyroData[1]; - } - } - - if (isSpecialController == 1) { - wGyroX = gyroData[0] - Game::Settings.controller.gyroDriftX; - wGyroY = -gyroData[2] - Game::Settings.controller.gyroDriftY; - } - else { - wGyroX = gyroData[0] - Game::Settings.controller.gyroDriftX; - wGyroY = gyroData[1] - Game::Settings.controller.gyroDriftY; - } - - wGyroX *= gyroSensitivity; - wGyroY *= gyroSensitivity; - } - - for (int32_t i = SDL_CONTROLLER_BUTTON_A; i < SDL_CONTROLLER_BUTTON_MAX; i++) { - if (ButtonMapping.contains(i)) { - if (SDL_GameControllerGetButton(Cont, (SDL_GameControllerButton)i)) { - dwPressedButtons |= ButtonMapping[i]; - } - else { - dwPressedButtons &= ~ButtonMapping[i]; - } - } - } - - SDL_GameControllerAxis StickAxisX = SDL_CONTROLLER_AXIS_INVALID; - SDL_GameControllerAxis StickAxisY = SDL_CONTROLLER_AXIS_INVALID; - int32_t StickDeadzone = 0; - - for (int32_t i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) { - auto Axis = (SDL_GameControllerAxis)i; - auto PosScancode = i + AXIS_SCANCODE_BIT; - auto NegScancode = -PosScancode; - auto AxisThreshold = ThresholdMapping[i]; - auto PosButton = ButtonMapping[PosScancode]; - auto NegButton = ButtonMapping[NegScancode]; - auto AxisValue = SDL_GameControllerGetAxis(Cont, Axis); - -#ifdef TARGET_WEB - // Firefox has a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1606562 - // It sets down y to 32768.0f / 32767.0f, which is greater than the allowed 1.0f, - // which SDL then converts to a int16_t by multiplying by 32767.0f, which overflows into -32768. - // Maximum up will hence never become -32768 with the current version of SDL2, - // so this workaround should be safe in compliant browsers. - if (AxisValue == -32768) { - AxisValue = 32767; - } -#endif - - // If the axis is NOT mapped to the control stick. - if (!( - PosButton == BTN_STICKLEFT || PosButton == BTN_STICKRIGHT || - PosButton == BTN_STICKUP || PosButton == BTN_STICKDOWN || - NegButton == BTN_STICKLEFT || NegButton == BTN_STICKRIGHT || - NegButton == BTN_STICKUP || NegButton == BTN_STICKDOWN)) { - if (AxisValue > AxisThreshold) { - dwPressedButtons |= PosButton; - dwPressedButtons &= ~NegButton; - } - else if (AxisValue < -AxisThreshold) { - dwPressedButtons &= ~PosButton; - dwPressedButtons |= NegButton; - } - else { - dwPressedButtons &= ~PosButton; - dwPressedButtons &= ~NegButton; - } - } - else { - if (PosButton == BTN_STICKLEFT || PosButton == BTN_STICKRIGHT) { - if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisX != Axis) { - SPDLOG_TRACE("Invalid PosStickX configured. Neg was {} and Pos is {}", StickAxisX, Axis); - } - - if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { - SPDLOG_TRACE("Invalid Deadzone configured. Up/Down was {} and Left/Right is {}", StickDeadzone, AxisThreshold); - } - - StickDeadzone = AxisThreshold; - StickAxisX = Axis; - } - - if (PosButton == BTN_STICKUP || PosButton == BTN_STICKDOWN) { - if (StickAxisY != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != Axis) { - SPDLOG_TRACE("Invalid PosStickY configured. Neg was {} and Pos is {}", StickAxisY, Axis); - } - - if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { - SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold); - } - - StickDeadzone = AxisThreshold; - StickAxisY = Axis; - } - - if (NegButton == BTN_STICKLEFT || NegButton == BTN_STICKRIGHT) { - if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisX != Axis) { - SPDLOG_TRACE("Invalid NegStickX configured. Pos was {} and Neg is {}", StickAxisX, Axis); - } - - if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { - SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold); - } - - StickDeadzone = AxisThreshold; - StickAxisX = Axis; - } - - if (NegButton == BTN_STICKUP || NegButton == BTN_STICKDOWN) { - if (StickAxisY != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != Axis) { - SPDLOG_TRACE("Invalid NegStickY configured. Pos was {} and Neg is {}", StickAxisY, Axis); - } - - if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { - SPDLOG_TRACE("Invalid Deadzone misconfigured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold); - } - - StickDeadzone = AxisThreshold; - StickAxisY = Axis; - } - } - - if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != SDL_CONTROLLER_AXIS_INVALID) { - auto AxisValueX = SDL_GameControllerGetAxis(Cont, StickAxisX); - auto AxisValueY = SDL_GameControllerGetAxis(Cont, StickAxisY); - NormalizeStickAxis(AxisValueX, AxisValueY, StickDeadzone); - } - } - } - - void SDLController::WriteToSource(ControllerCallback* controller) - { - if (SDL_GameControllerHasRumble(Cont)) { - if (controller->rumble > 0) { - SDL_GameControllerRumble(Cont, 0xFFFF * Game::Settings.controller.rumble_strength, 0xFFFF * Game::Settings.controller.rumble_strength, 1); - } - } - - if (SDL_GameControllerHasLED(Cont)) { - switch (controller->ledColor) { - case 0: - SDL_JoystickSetLED(SDL_GameControllerGetJoystick(Cont), 255, 0, 0); - break; - case 1: - SDL_JoystickSetLED(SDL_GameControllerGetJoystick(Cont), 0x1E, 0x69, 0x1B); - break; - case 2: - SDL_JoystickSetLED(SDL_GameControllerGetJoystick(Cont), 0x64, 0x14, 0x00); - break; - case 3: - SDL_JoystickSetLED(SDL_GameControllerGetJoystick(Cont), 0x00, 0x3C, 0x64); - break; - } - } - } - - void SDLController::CreateDefaultBinding() { - std::string ConfSection = GetBindingConfSection(); - std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); - ConfigFile& Conf = *pConf.get(); - - Conf[ConfSection][STR(BTN_CRIGHT)] = std::to_string((SDL_CONTROLLER_AXIS_RIGHTX + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_CLEFT)] = std::to_string(-(SDL_CONTROLLER_AXIS_RIGHTX + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_CDOWN)] = std::to_string((SDL_CONTROLLER_AXIS_RIGHTY + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_CUP)] = std::to_string(-(SDL_CONTROLLER_AXIS_RIGHTY + AXIS_SCANCODE_BIT)); - //Conf[ConfSection][STR(BTN_CRIGHT + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_X); - //Conf[ConfSection][STR(BTN_CLEFT + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_Y); - //Conf[ConfSection][STR(BTN_CDOWN + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); - //Conf[ConfSection][STR(BTN_CUP + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_RIGHTSTICK); - Conf[ConfSection][STR(BTN_R)] = std::to_string((SDL_CONTROLLER_AXIS_TRIGGERRIGHT + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_L)] = std::to_string(SDL_CONTROLLER_BUTTON_LEFTSHOULDER); - Conf[ConfSection][STR(BTN_DRIGHT)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_RIGHT); - Conf[ConfSection][STR(BTN_DLEFT)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_LEFT); - Conf[ConfSection][STR(BTN_DDOWN)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_DOWN); - Conf[ConfSection][STR(BTN_DUP)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_UP); - Conf[ConfSection][STR(BTN_START)] = std::to_string(SDL_CONTROLLER_BUTTON_START); - Conf[ConfSection][STR(BTN_Z)] = std::to_string((SDL_CONTROLLER_AXIS_TRIGGERLEFT + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_B)] = std::to_string(SDL_CONTROLLER_BUTTON_B); - Conf[ConfSection][STR(BTN_A)] = std::to_string(SDL_CONTROLLER_BUTTON_A); - Conf[ConfSection][STR(BTN_STICKRIGHT)] = std::to_string((SDL_CONTROLLER_AXIS_LEFTX + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_STICKLEFT)] = std::to_string(-(SDL_CONTROLLER_AXIS_LEFTX + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_STICKDOWN)] = std::to_string((SDL_CONTROLLER_AXIS_LEFTY + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_STICKUP)] = std::to_string(-(SDL_CONTROLLER_AXIS_LEFTY + AXIS_SCANCODE_BIT)); - - Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_LEFTX) + "_threshold"] = std::to_string(16.0); - Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_LEFTY) + "_threshold"] = std::to_string(16.0); - Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_RIGHTX) + "_threshold"] = std::to_string(0x4000); - Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_RIGHTY) + "_threshold"] = std::to_string(0x4000); - Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_TRIGGERLEFT) + "_threshold"] = std::to_string(0x1E00); - Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + "_threshold"] = std::to_string(0x1E00); - - Conf.Save(); - } - void SDLController::SetButtonMapping(const std::string& szButtonName, int32_t dwScancode) { if (guid.compare(INVALID_SDL_CONTROLLER_GUID)) { return; @@ -400,7 +90,7 @@ namespace Ship { } std::string SDLController::GetControllerType() { - return "SDL"; + return "Base"; } std::string SDLController::GetConfSection() { diff --git a/libultraship/libultraship/SDLController.h b/libultraship/libultraship/SDLController.h index 5c71c50a9..915d07336 100644 --- a/libultraship/libultraship/SDLController.h +++ b/libultraship/libultraship/SDLController.h @@ -10,9 +10,6 @@ namespace Ship { SDLController(int32_t dwControllerNumber); ~SDLController(); - void ReadFromSource(); - void WriteToSource(ControllerCallback* controller); - std::string GetGuid() { return guid; }; protected: @@ -20,17 +17,13 @@ namespace Ship { void SetButtonMapping(const std::string& szButtonName, int32_t dwScancode); std::string GetConfSection(); std::string GetBindingConfSection(); - void CreateDefaultBinding(); static bool IsGuidInUse(const std::string& guid); - - private: - std::string guid; - SDL_GameController* Cont; - std::map ThresholdMapping; - void LoadAxisThresholds(); void NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold); - bool Open(); - bool Close(); + + std::string guid; + std::map ThresholdMapping; + + private: }; } diff --git a/libultraship/libultraship/SDLGamepadController.cpp b/libultraship/libultraship/SDLGamepadController.cpp new file mode 100644 index 000000000..94868497b --- /dev/null +++ b/libultraship/libultraship/SDLGamepadController.cpp @@ -0,0 +1,315 @@ +#include "SDLGamepadController.h" + +#include "GameSettings.h" +#include "GlobalCtx2.h" +#include "spdlog/spdlog.h" +#include "stox.h" +#include "Window.h" + +extern "C" uint8_t __osMaxControllers; +float gyroDriftX; +float gyroDriftY; + +namespace Ship { + + + SDLGamepadController::SDLGamepadController(int32_t dwControllerNumber) : SDLController(dwControllerNumber), Cont(nullptr) { + + } + + SDLGamepadController::~SDLGamepadController() { + Close(); + } + + bool SDLGamepadController::Open() { + std::string ConfSection = GetConfSection(); + std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); + ConfigFile& Conf = *pConf.get(); + + for (int i = 0; i < SDL_NumJoysticks(); i++) { + if (SDL_IsGameController(i)) { + // Get the GUID from SDL + char GuidBuf[33]; + SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i), GuidBuf, sizeof(GuidBuf)); + auto NewGuid = std::string(GuidBuf); + + // Invalid GUID read. Go to next. + if (NewGuid.compare(INVALID_SDL_CONTROLLER_GUID) == 0) { + SPDLOG_ERROR("SDL Controller returned invalid guid"); + continue; + } + + // The GUID is in use, we want to use a different physical controller. Go to next. + if (IsGuidInUse(NewGuid)) { + continue; + } + + // If the GUID is blank from the config, OR if the config GUID matches, load the controller. + if (Conf[ConfSection]["GUID"].compare("") == 0 || Conf[ConfSection]["GUID"].compare(INVALID_SDL_CONTROLLER_GUID) == 0 || Conf[ConfSection]["GUID"].compare(NewGuid) == 0) { + auto NewCont = SDL_GameControllerOpen(i); + + if (SDL_GameControllerHasSensor(NewCont, SDL_SENSOR_GYRO)) + { + SDL_GameControllerSetSensorEnabled(NewCont, SDL_SENSOR_GYRO, SDL_TRUE); + } + + // We failed to load the controller. Go to next. + if (NewCont == nullptr) { + SPDLOG_ERROR("SDL Controller failed to open: ({})", SDL_GetError()); + continue; + } + + guid = NewGuid; + Cont = NewCont; + + std::string BindingConfSection = GetBindingConfSection(); + std::shared_ptr pBindingConf = GlobalCtx2::GetInstance()->GetConfig(); + ConfigFile& BindingConf = *pBindingConf.get(); + + if (!BindingConf.has(BindingConfSection)) { + CreateDefaultBinding(); + } + + LoadBinding(); + LoadAxisThresholds(); + + break; + } + } + } + + return Cont != nullptr; + } + + bool SDLGamepadController::Close() { + if (Cont != nullptr) { + SDL_GameControllerClose(Cont); + } + Cont = nullptr; + guid = ""; + ButtonMapping.clear(); + ThresholdMapping.clear(); + dwPressedButtons = 0; + wStickX = 0; + wStickY = 0; + + return true; + } + + void SDLGamepadController::ReadFromSource() { + std::string ConfSection = GetBindingConfSection(); + std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); + ConfigFile& Conf = *pConf.get(); + + SDL_GameControllerUpdate(); + + // If the controller is disconnected, close it. + if (Cont != nullptr && !SDL_GameControllerGetAttached(Cont)) { + Close(); + } + + // Attempt to load the controller if it's not loaded + if (Cont == nullptr) { + // If we failed to load the controller, don't process it. + if (!Open()) { + return; + } + } + + if (SDL_GameControllerHasSensor(Cont, SDL_SENSOR_GYRO)) + { + float gyroData[3]; + SDL_GameControllerGetSensorData(Cont, SDL_SENSOR_GYRO, gyroData, 3); + + const char* contName = SDL_GameControllerName(Cont); + const int isSpecialController = strcmp("PS5 Controller", contName); + const float gyroSensitivity = Game::Settings.controller.gyro_sensitivity; + + if (gyroDriftX == 0) { + if (isSpecialController == 0) { + gyroDriftX = gyroData[2]; + } + else { + gyroDriftX = gyroData[0]; + } + } + + if (gyroDriftY == 0) { + gyroDriftY = gyroData[1]; + } + + if (isSpecialController == 0) { + wGyroX = gyroData[2] - gyroDriftX; + } + else { + wGyroX = gyroData[0] - gyroDriftX; + } + + wGyroY = gyroData[1] - gyroDriftY; + + wGyroX *= gyroSensitivity; + wGyroY *= gyroSensitivity; + } + + for (int32_t i = SDL_CONTROLLER_BUTTON_A; i < SDL_CONTROLLER_BUTTON_MAX; i++) { + if (ButtonMapping.contains(i)) { + if (SDL_GameControllerGetButton(Cont, (SDL_GameControllerButton)i)) { + dwPressedButtons |= ButtonMapping[i]; + } + else { + dwPressedButtons &= ~ButtonMapping[i]; + } + } + } + + SDL_GameControllerAxis StickAxisX = SDL_CONTROLLER_AXIS_INVALID; + SDL_GameControllerAxis StickAxisY = SDL_CONTROLLER_AXIS_INVALID; + int32_t StickDeadzone = 0; + + for (int32_t i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) { + auto Axis = (SDL_GameControllerAxis)i; + auto PosScancode = i + AXIS_SCANCODE_BIT; + auto NegScancode = -PosScancode; + auto AxisThreshold = ThresholdMapping[i]; + auto PosButton = ButtonMapping[PosScancode]; + auto NegButton = ButtonMapping[NegScancode]; + auto AxisValue = SDL_GameControllerGetAxis(Cont, Axis); + + + // If the axis is NOT mapped to the control stick. + if (!( + PosButton == BTN_STICKLEFT || PosButton == BTN_STICKRIGHT || + PosButton == BTN_STICKUP || PosButton == BTN_STICKDOWN || + NegButton == BTN_STICKLEFT || NegButton == BTN_STICKRIGHT || + NegButton == BTN_STICKUP || NegButton == BTN_STICKDOWN)) { + if (AxisValue > AxisThreshold) { + dwPressedButtons |= PosButton; + dwPressedButtons &= ~NegButton; + } + else if (AxisValue < -AxisThreshold) { + dwPressedButtons &= ~PosButton; + dwPressedButtons |= NegButton; + } + else { + dwPressedButtons &= ~PosButton; + dwPressedButtons &= ~NegButton; + } + } + else { + if (PosButton == BTN_STICKLEFT || PosButton == BTN_STICKRIGHT) { + if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisX != Axis) { + SPDLOG_TRACE("Invalid PosStickX configured. Neg was {} and Pos is {}", StickAxisX, Axis); + } + + if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { + SPDLOG_TRACE("Invalid Deadzone configured. Up/Down was {} and Left/Right is {}", StickDeadzone, AxisThreshold); + } + + StickDeadzone = AxisThreshold; + StickAxisX = Axis; + } + + if (PosButton == BTN_STICKUP || PosButton == BTN_STICKDOWN) { + if (StickAxisY != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != Axis) { + SPDLOG_TRACE("Invalid PosStickY configured. Neg was {} and Pos is {}", StickAxisY, Axis); + } + + if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { + SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold); + } + + StickDeadzone = AxisThreshold; + StickAxisY = Axis; + } + + if (NegButton == BTN_STICKLEFT || NegButton == BTN_STICKRIGHT) { + if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisX != Axis) { + SPDLOG_TRACE("Invalid NegStickX configured. Pos was {} and Neg is {}", StickAxisX, Axis); + } + + if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { + SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold); + } + + StickDeadzone = AxisThreshold; + StickAxisX = Axis; + } + + if (NegButton == BTN_STICKUP || NegButton == BTN_STICKDOWN) { + if (StickAxisY != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != Axis) { + SPDLOG_TRACE("Invalid NegStickY configured. Pos was {} and Neg is {}", StickAxisY, Axis); + } + + if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { + SPDLOG_TRACE("Invalid Deadzone misconfigured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold); + } + + StickDeadzone = AxisThreshold; + StickAxisY = Axis; + } + } + + if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != SDL_CONTROLLER_AXIS_INVALID) { + auto AxisValueX = SDL_GameControllerGetAxis(Cont, StickAxisX); + auto AxisValueY = SDL_GameControllerGetAxis(Cont, StickAxisY); + NormalizeStickAxis(AxisValueX, AxisValueY, StickDeadzone); + } + } + } + + void SDLGamepadController::WriteToSource(ControllerCallback* controller) + { + if (SDL_GameControllerHasRumble(Cont)) { + if (controller->rumble > 0) { + SDL_GameControllerRumble(Cont, 0xFFFF * Game::Settings.controller.rumble_strength, 0xFFFF * Game::Settings.controller.rumble_strength, 1); + } + } + + if (SDL_GameControllerHasLED(Cont)) { + if (controller->ledColor == 1) { + SDL_JoystickSetLED(SDL_GameControllerGetJoystick(Cont), 255, 0, 0); + } + else { + SDL_JoystickSetLED(SDL_GameControllerGetJoystick(Cont), 0, 255, 0); + } + } + } + + void SDLGamepadController::CreateDefaultBinding() { + std::string ConfSection = GetBindingConfSection(); + std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); + ConfigFile& Conf = *pConf.get(); + + Conf[ConfSection][STR(BTN_CRIGHT)] = std::to_string((SDL_CONTROLLER_AXIS_RIGHTX + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_CLEFT)] = std::to_string(-(SDL_CONTROLLER_AXIS_RIGHTX + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_CDOWN)] = std::to_string((SDL_CONTROLLER_AXIS_RIGHTY + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_CUP)] = std::to_string(-(SDL_CONTROLLER_AXIS_RIGHTY + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_R)] = std::to_string((SDL_CONTROLLER_AXIS_TRIGGERRIGHT + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_L)] = std::to_string(SDL_CONTROLLER_BUTTON_LEFTSHOULDER); + Conf[ConfSection][STR(BTN_DRIGHT)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_RIGHT); + Conf[ConfSection][STR(BTN_DLEFT)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_LEFT); + Conf[ConfSection][STR(BTN_DDOWN)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_DOWN); + Conf[ConfSection][STR(BTN_DUP)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_UP); + Conf[ConfSection][STR(BTN_START)] = std::to_string(SDL_CONTROLLER_BUTTON_START); + Conf[ConfSection][STR(BTN_Z)] = std::to_string((SDL_CONTROLLER_AXIS_TRIGGERLEFT + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_B)] = std::to_string(SDL_CONTROLLER_BUTTON_B); + Conf[ConfSection][STR(BTN_A)] = std::to_string(SDL_CONTROLLER_BUTTON_A); + Conf[ConfSection][STR(BTN_STICKRIGHT)] = std::to_string((SDL_CONTROLLER_AXIS_LEFTX + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_STICKLEFT)] = std::to_string(-(SDL_CONTROLLER_AXIS_LEFTX + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_STICKDOWN)] = std::to_string((SDL_CONTROLLER_AXIS_LEFTY + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_STICKUP)] = std::to_string(-(SDL_CONTROLLER_AXIS_LEFTY + AXIS_SCANCODE_BIT)); + + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_LEFTX) + "_threshold"] = std::to_string(16.0); + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_LEFTY) + "_threshold"] = std::to_string(16.0); + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_RIGHTX) + "_threshold"] = std::to_string(0x4000); + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_RIGHTY) + "_threshold"] = std::to_string(0x4000); + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_TRIGGERLEFT) + "_threshold"] = std::to_string(0x1E00); + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + "_threshold"] = std::to_string(0x1E00); + + Conf.Save(); + } + + std::string SDLGamepadController::GetControllerType() { + return "SDL"; + } +} diff --git a/libultraship/libultraship/SDLGamepadController.h b/libultraship/libultraship/SDLGamepadController.h new file mode 100644 index 000000000..32c0ae54c --- /dev/null +++ b/libultraship/libultraship/SDLGamepadController.h @@ -0,0 +1,25 @@ +#pragma once +#include "SDLController.h" + + +namespace Ship { + class SDLGamepadController : public SDLController { + public: + SDLGamepadController(int32_t dwControllerNumber); + ~SDLGamepadController(); + + void ReadFromSource(); + void WriteToSource(ControllerCallback* controller); + + + protected: + std::string GetControllerType(); + void CreateDefaultBinding(); + + private: + SDL_GameController* Cont; + + bool Open(); + bool Close(); + }; +} diff --git a/libultraship/libultraship/SDLJoystickController.cpp b/libultraship/libultraship/SDLJoystickController.cpp new file mode 100644 index 000000000..e45f238ab --- /dev/null +++ b/libultraship/libultraship/SDLJoystickController.cpp @@ -0,0 +1,275 @@ +#include "SDLJoystickController.h" + +#include "GameSettings.h" +#include "GlobalCtx2.h" +#include "spdlog/spdlog.h" +#include "stox.h" +#include "Window.h" + +extern "C" uint8_t __osMaxControllers; +float gyroDriftX; +float gyroDriftY; + +namespace Ship { + + + SDLJoystickController::SDLJoystickController(int32_t dwControllerNumber) : SDLController(dwControllerNumber), Cont(nullptr), numButtons(0) { + + } + + SDLJoystickController::~SDLJoystickController() { + Close(); + } + + bool SDLJoystickController::Open() { + std::string ConfSection = GetConfSection(); + std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); + ConfigFile& Conf = *pConf.get(); + + for (int i = 0; i < SDL_NumJoysticks(); i++) { + // This is for HID devices. So don't use the GameController API + if (!SDL_IsGameController(i)) { + // Get the GUID from SDL + char GuidBuf[33]; + SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i), GuidBuf, sizeof(GuidBuf)); + auto NewGuid = std::string(GuidBuf); + + // Invalid GUID read. Go to next. + if (NewGuid.compare(INVALID_SDL_CONTROLLER_GUID) == 0) { + SPDLOG_ERROR("SDL Controller returned invalid guid"); + continue; + } + + // The GUID is in use, we want to use a different physical controller. Go to next. + if (IsGuidInUse(NewGuid)) { + continue; + } + + // If the GUID is blank from the config, OR if the config GUID matches, load the controller. + if (Conf[ConfSection]["GUID"].compare("") == 0 || Conf[ConfSection]["GUID"].compare(INVALID_SDL_CONTROLLER_GUID) == 0 || Conf[ConfSection]["GUID"].compare(NewGuid) == 0) { + auto NewCont = SDL_JoystickOpen(i); + + // We failed to load the controller. Go to next. + if (NewCont == nullptr) { + SPDLOG_ERROR("SDL Controller failed to open: ({})", SDL_GetError()); + continue; + } + + guid = NewGuid; + Cont = NewCont; + numButtons = SDL_JoystickNumButtons(Cont); + + std::string BindingConfSection = GetBindingConfSection(); + std::shared_ptr pBindingConf = GlobalCtx2::GetInstance()->GetConfig(); + ConfigFile& BindingConf = *pBindingConf.get(); + + if (!BindingConf.has(BindingConfSection)) { + CreateDefaultBinding(); + } + + LoadBinding(); + + break; + } + } + } + + return Cont != nullptr; + } + + bool SDLJoystickController::Close() { + if (Cont != nullptr) { + SDL_JoystickClose(Cont); + } + Cont = nullptr; + guid = ""; + ButtonMapping.clear(); + dwPressedButtons = 0; + wStickX = 0; + wStickY = 0; + + return true; + } + + void SDLJoystickController::ReadFromSource() { + std::string ConfSection = GetBindingConfSection(); + std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); + ConfigFile& Conf = *pConf.get(); + + SDL_GameControllerUpdate(); + + // If the controller is disconnected, close it. + if (Cont != nullptr && !SDL_JoystickGetAttached(Cont)) { + Close(); + } + + // Attempt to load the controller if it's not loaded + if (Cont == nullptr) { + // If we failed to load the controller, don't process it. + if (!Open()) { + return; + } + } + + for (int32_t i = 0; i < numButtons; i++) { + if (ButtonMapping.contains(i)) { + if (SDL_JoystickGetButton(Cont, i)) { + dwPressedButtons |= ButtonMapping[i]; + } + else { + dwPressedButtons &= ~ButtonMapping[i]; + } + } + } + + int StickAxisX = SDL_CONTROLLER_AXIS_INVALID; + int StickAxisY = SDL_CONTROLLER_AXIS_INVALID; + int32_t StickDeadzone = 0; + + for (int32_t i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) { + auto Axis = i; + auto PosScancode = i + AXIS_SCANCODE_BIT; + auto NegScancode = -PosScancode; + auto AxisThreshold = ThresholdMapping[i]; + auto PosButton = ButtonMapping[PosScancode]; + auto NegButton = ButtonMapping[NegScancode]; + auto AxisValue = SDL_JoystickGetAxis(Cont, Axis); + + + // If the axis is NOT mapped to the control stick. + if (!( + PosButton == BTN_STICKLEFT || PosButton == BTN_STICKRIGHT || + PosButton == BTN_STICKUP || PosButton == BTN_STICKDOWN || + NegButton == BTN_STICKLEFT || NegButton == BTN_STICKRIGHT || + NegButton == BTN_STICKUP || NegButton == BTN_STICKDOWN)) { + if (AxisValue > AxisThreshold) { + dwPressedButtons |= PosButton; + dwPressedButtons &= ~NegButton; + } + else if (AxisValue < -AxisThreshold) { + dwPressedButtons &= ~PosButton; + dwPressedButtons |= NegButton; + } + else { + dwPressedButtons &= ~PosButton; + dwPressedButtons &= ~NegButton; + } + } + else { + if (PosButton == BTN_STICKLEFT || PosButton == BTN_STICKRIGHT) { + if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisX != Axis) { + SPDLOG_TRACE("Invalid PosStickX configured. Neg was {} and Pos is {}", StickAxisX, Axis); + } + + if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { + SPDLOG_TRACE("Invalid Deadzone configured. Up/Down was {} and Left/Right is {}", StickDeadzone, AxisThreshold); + } + + StickDeadzone = AxisThreshold; + StickAxisX = Axis; + } + + if (PosButton == BTN_STICKUP || PosButton == BTN_STICKDOWN) { + if (StickAxisY != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != Axis) { + SPDLOG_TRACE("Invalid PosStickY configured. Neg was {} and Pos is {}", StickAxisY, Axis); + } + + if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { + SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold); + } + + StickDeadzone = AxisThreshold; + StickAxisY = Axis; + } + + if (NegButton == BTN_STICKLEFT || NegButton == BTN_STICKRIGHT) { + if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisX != Axis) { + SPDLOG_TRACE("Invalid NegStickX configured. Pos was {} and Neg is {}", StickAxisX, Axis); + } + + if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { + SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold); + } + + StickDeadzone = AxisThreshold; + StickAxisX = Axis; + } + + if (NegButton == BTN_STICKUP || NegButton == BTN_STICKDOWN) { + if (StickAxisY != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != Axis) { + SPDLOG_TRACE("Invalid NegStickY configured. Pos was {} and Neg is {}", StickAxisY, Axis); + } + + if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) { + SPDLOG_TRACE("Invalid Deadzone misconfigured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold); + } + + StickDeadzone = AxisThreshold; + StickAxisY = Axis; + } + } + + if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != SDL_CONTROLLER_AXIS_INVALID) { + auto AxisValueX = SDL_JoystickGetAxis(Cont, StickAxisX); + auto AxisValueY = SDL_JoystickGetAxis(Cont, StickAxisY); + NormalizeStickAxis(AxisValueX, AxisValueY, StickDeadzone); + } + } + } + + void SDLJoystickController::WriteToSource(ControllerCallback* controller) + { + if (SDL_JoystickHasRumble(Cont)) { + if (controller->rumble > 0) { + SDL_JoystickRumble(Cont, 0xFFFF * Game::Settings.controller.rumble_strength, 0xFFFF * Game::Settings.controller.rumble_strength, 1); + } + } + + if (SDL_JoystickHasLED(Cont)) { + if (controller->ledColor == 1) { + SDL_JoystickSetLED(Cont, 255, 0, 0); + } + else { + SDL_JoystickSetLED(Cont, 0, 255, 0); + } + } + } + + void SDLJoystickController::CreateDefaultBinding() { + std::string ConfSection = GetBindingConfSection(); + std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); + ConfigFile& Conf = *pConf.get(); + + Conf[ConfSection][STR(BTN_CRIGHT)] = std::to_string(9); + Conf[ConfSection][STR(BTN_CLEFT)] = std::to_string(8); + Conf[ConfSection][STR(BTN_CDOWN)] = std::to_string(7); + Conf[ConfSection][STR(BTN_CUP)] = std::to_string(6); + Conf[ConfSection][STR(BTN_R)] = std::to_string(5); + Conf[ConfSection][STR(BTN_L)] = std::to_string(4); + Conf[ConfSection][STR(BTN_DRIGHT)] = std::to_string(13); + Conf[ConfSection][STR(BTN_DLEFT)] = std::to_string(12); + Conf[ConfSection][STR(BTN_DDOWN)] = std::to_string(11); + Conf[ConfSection][STR(BTN_DUP)] = std::to_string(10); + Conf[ConfSection][STR(BTN_START)] = std::to_string(3); + Conf[ConfSection][STR(BTN_Z)] = std::to_string(2); + Conf[ConfSection][STR(BTN_B)] = std::to_string(1); + Conf[ConfSection][STR(BTN_A)] = std::to_string(0); + Conf[ConfSection][STR(BTN_STICKRIGHT)] = std::to_string((SDL_CONTROLLER_AXIS_LEFTX + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_STICKLEFT)] = std::to_string(-(SDL_CONTROLLER_AXIS_LEFTX + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_STICKDOWN)] = std::to_string((SDL_CONTROLLER_AXIS_LEFTY + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_STICKUP)] = std::to_string(-(SDL_CONTROLLER_AXIS_LEFTY + AXIS_SCANCODE_BIT)); + + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_LEFTX) + "_threshold"] = std::to_string(16.0); + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_LEFTY) + "_threshold"] = std::to_string(16.0); + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_RIGHTX) + "_threshold"] = std::to_string(0x4000); + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_RIGHTY) + "_threshold"] = std::to_string(0x4000); + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_TRIGGERLEFT) + "_threshold"] = std::to_string(0x1E00); + Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + "_threshold"] = std::to_string(0x1E00); + + Conf.Save(); + } + + std::string SDLJoystickController::GetControllerType() { + return "Joystick"; + } +} \ No newline at end of file diff --git a/libultraship/libultraship/SDLJoystickController.h b/libultraship/libultraship/SDLJoystickController.h new file mode 100644 index 000000000..cddd44f74 --- /dev/null +++ b/libultraship/libultraship/SDLJoystickController.h @@ -0,0 +1,25 @@ +#pragma once +#include "SDLController.h" +#include + +namespace Ship { + class SDLJoystickController : public SDLController { + public: + SDLJoystickController(int32_t dwControllerNumber); + ~SDLJoystickController(); + + void ReadFromSource(); + void WriteToSource(ControllerCallback* controller); + + protected: + std::string GetControllerType(); + void CreateDefaultBinding(); + + private: + SDL_Joystick* Cont; + int numButtons; + + bool Open(); + bool Close(); + }; +} diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp index 665483b35..06bce059d 100644 --- a/libultraship/libultraship/Window.cpp +++ b/libultraship/libultraship/Window.cpp @@ -1,7 +1,8 @@ #include "Window.h" #include "spdlog/spdlog.h" #include "KeyboardController.h" -#include "SDLController.h" +#include "SDLGamepadController.h" +#include "SDLJoystickController.h" #include "GlobalCtx2.h" #include "DisplayList.h" #include "Vertex.h" @@ -39,12 +40,19 @@ extern "C" { exit(EXIT_FAILURE); } + + if (SDL_Init(SDL_INIT_JOYSTICK) != 0) { + SPDLOG_ERROR("Failed to initialize HID game controllers ({})", SDL_GetError()); + exit(EXIT_FAILURE); + } + const char* controllerDb = "gamecontrollerdb.txt"; int mappingsAdded = SDL_GameControllerAddMappingsFromFile(controllerDb); if (mappingsAdded >= 0) { SPDLOG_INFO("Added SDL game controllers from \"{}\" ({})", controllerDb, mappingsAdded); } else { SPDLOG_ERROR("Failed add SDL game controller mappings from \"{}\" ({})", controllerDb, SDL_GetError()); + } // TODO: This for loop is debug. Burn it with fire. @@ -66,11 +74,13 @@ extern "C" { if (ControllerType == "auto") { Ship::Window::Controllers[i].push_back(std::make_shared(i)); - Ship::Window::Controllers[i].push_back(std::make_shared(i)); + Ship::Window::Controllers[i].push_back(std::make_shared(i)); + Ship::Window::Controllers[i].push_back(std::make_shared(i)); } else if (ControllerType == "keyboard") { Ship::Window::Controllers[i].push_back(std::make_shared(i)); } else if (ControllerType == "usb") { - Ship::Window::Controllers[i].push_back(std::make_shared(i)); + Ship::Window::Controllers[i].push_back(std::make_shared(i)); + Ship::Window::Controllers[i].push_back(std::make_shared(i)); } else if (ControllerType == "unplugged") { // Do nothing for unplugged controllers } else { diff --git a/libultraship/libultraship/libultraship.vcxproj b/libultraship/libultraship/libultraship.vcxproj index 8443b6b11..528c21603 100644 --- a/libultraship/libultraship/libultraship.vcxproj +++ b/libultraship/libultraship/libultraship.vcxproj @@ -239,6 +239,8 @@ + + @@ -326,6 +328,8 @@ + + diff --git a/libultraship/libultraship/libultraship.vcxproj.filters b/libultraship/libultraship/libultraship.vcxproj.filters index deed867c8..7c2a4479f 100644 --- a/libultraship/libultraship/libultraship.vcxproj.filters +++ b/libultraship/libultraship/libultraship.vcxproj.filters @@ -339,6 +339,12 @@ Source Files\CustomImpl + + Source Files\Controller + + + Source Files\Controller + @@ -626,5 +632,11 @@ Source Files\CustomImpl + + Source Files\Controller + + + Source Files\Controller + \ No newline at end of file