Added new controller support

Using the SDLJoystick API, I added support for new controllers
This commit is contained in:
Ian Drake 2022-03-30 19:53:01 -04:00
commit aababcd87f
10 changed files with 682 additions and 328 deletions

View file

@ -154,6 +154,11 @@ namespace Ship {
(*this)["SDL CONTROLLER 3"]["GUID"] = ""; (*this)["SDL CONTROLLER 3"]["GUID"] = "";
(*this)["SDL CONTROLLER 4"]["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); return File.generate(Val);
} }
} }

View file

@ -9,12 +9,12 @@
extern "C" uint8_t __osMaxControllers; extern "C" uint8_t __osMaxControllers;
namespace Ship { 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() { SDLController::~SDLController() {
Close();
} }
bool SDLController::IsGuidInUse(const std::string& guid) { bool SDLController::IsGuidInUse(const std::string& guid) {
@ -32,81 +32,6 @@ namespace Ship {
return false; return false;
} }
bool SDLController::Open() {
std::string ConfSection = GetConfSection();
std::shared_ptr<ConfigFile> 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<ConfigFile> 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() { void SDLController::LoadAxisThresholds() {
std::string ConfSection = GetBindingConfSection(); std::string ConfSection = GetBindingConfSection();
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig(); std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
@ -156,241 +81,6 @@ namespace Ship {
wStickY = -ay; wStickY = -ay;
} }
void SDLController::ReadFromSource() {
std::string ConfSection = GetBindingConfSection();
std::shared_ptr<ConfigFile> 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<ConfigFile> 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) { void SDLController::SetButtonMapping(const std::string& szButtonName, int32_t dwScancode) {
if (guid.compare(INVALID_SDL_CONTROLLER_GUID)) { if (guid.compare(INVALID_SDL_CONTROLLER_GUID)) {
return; return;
@ -400,7 +90,7 @@ namespace Ship {
} }
std::string SDLController::GetControllerType() { std::string SDLController::GetControllerType() {
return "SDL"; return "Base";
} }
std::string SDLController::GetConfSection() { std::string SDLController::GetConfSection() {

View file

@ -10,9 +10,6 @@ namespace Ship {
SDLController(int32_t dwControllerNumber); SDLController(int32_t dwControllerNumber);
~SDLController(); ~SDLController();
void ReadFromSource();
void WriteToSource(ControllerCallback* controller);
std::string GetGuid() { return guid; }; std::string GetGuid() { return guid; };
protected: protected:
@ -20,17 +17,13 @@ namespace Ship {
void SetButtonMapping(const std::string& szButtonName, int32_t dwScancode); void SetButtonMapping(const std::string& szButtonName, int32_t dwScancode);
std::string GetConfSection(); std::string GetConfSection();
std::string GetBindingConfSection(); std::string GetBindingConfSection();
void CreateDefaultBinding();
static bool IsGuidInUse(const std::string& guid); static bool IsGuidInUse(const std::string& guid);
private:
std::string guid;
SDL_GameController* Cont;
std::map<int32_t, int16_t> ThresholdMapping;
void LoadAxisThresholds(); void LoadAxisThresholds();
void NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold); void NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold);
bool Open();
bool Close(); std::string guid;
std::map<int32_t, int16_t> ThresholdMapping;
private:
}; };
} }

View file

@ -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<ConfigFile> 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<ConfigFile> 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<ConfigFile> 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<ConfigFile> 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";
}
}

View file

@ -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();
};
}

View file

@ -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<ConfigFile> 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<ConfigFile> 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<ConfigFile> 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<ConfigFile> 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";
}
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "SDLController.h"
#include <SDL2/SDL.h>
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();
};
}

View file

@ -1,7 +1,8 @@
#include "Window.h" #include "Window.h"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "KeyboardController.h" #include "KeyboardController.h"
#include "SDLController.h" #include "SDLGamepadController.h"
#include "SDLJoystickController.h"
#include "GlobalCtx2.h" #include "GlobalCtx2.h"
#include "DisplayList.h" #include "DisplayList.h"
#include "Vertex.h" #include "Vertex.h"
@ -39,12 +40,19 @@ extern "C" {
exit(EXIT_FAILURE); 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"; const char* controllerDb = "gamecontrollerdb.txt";
int mappingsAdded = SDL_GameControllerAddMappingsFromFile(controllerDb); int mappingsAdded = SDL_GameControllerAddMappingsFromFile(controllerDb);
if (mappingsAdded >= 0) { if (mappingsAdded >= 0) {
SPDLOG_INFO("Added SDL game controllers from \"{}\" ({})", controllerDb, mappingsAdded); SPDLOG_INFO("Added SDL game controllers from \"{}\" ({})", controllerDb, mappingsAdded);
} else { } else {
SPDLOG_ERROR("Failed add SDL game controller mappings from \"{}\" ({})", controllerDb, SDL_GetError()); SPDLOG_ERROR("Failed add SDL game controller mappings from \"{}\" ({})", controllerDb, SDL_GetError());
} }
// TODO: This for loop is debug. Burn it with fire. // TODO: This for loop is debug. Burn it with fire.
@ -66,11 +74,13 @@ extern "C" {
if (ControllerType == "auto") { if (ControllerType == "auto") {
Ship::Window::Controllers[i].push_back(std::make_shared<Ship::KeyboardController>(i)); Ship::Window::Controllers[i].push_back(std::make_shared<Ship::KeyboardController>(i));
Ship::Window::Controllers[i].push_back(std::make_shared<Ship::SDLController>(i)); Ship::Window::Controllers[i].push_back(std::make_shared<Ship::SDLGamepadController>(i));
Ship::Window::Controllers[i].push_back(std::make_shared<Ship::SDLJoystickController>(i));
} else if (ControllerType == "keyboard") { } else if (ControllerType == "keyboard") {
Ship::Window::Controllers[i].push_back(std::make_shared<Ship::KeyboardController>(i)); Ship::Window::Controllers[i].push_back(std::make_shared<Ship::KeyboardController>(i));
} else if (ControllerType == "usb") { } else if (ControllerType == "usb") {
Ship::Window::Controllers[i].push_back(std::make_shared<Ship::SDLController>(i)); Ship::Window::Controllers[i].push_back(std::make_shared<Ship::SDLGamepadController>(i));
Ship::Window::Controllers[i].push_back(std::make_shared<Ship::SDLJoystickController>(i));
} else if (ControllerType == "unplugged") { } else if (ControllerType == "unplugged") {
// Do nothing for unplugged controllers // Do nothing for unplugged controllers
} else { } else {

View file

@ -239,6 +239,8 @@
<ClCompile Include="Cvar.cpp" /> <ClCompile Include="Cvar.cpp" />
<ClCompile Include="Environment.cpp" /> <ClCompile Include="Environment.cpp" />
<ClCompile Include="GameSettings.cpp" /> <ClCompile Include="GameSettings.cpp" />
<ClCompile Include="SDLGamepadController.cpp" />
<ClCompile Include="SDLJoystickController.cpp" />
<ClCompile Include="Lib\ImGui\backends\imgui_impl_dx11.cpp" /> <ClCompile Include="Lib\ImGui\backends\imgui_impl_dx11.cpp" />
<ClCompile Include="Lib\ImGui\backends\imgui_impl_win32.cpp" /> <ClCompile Include="Lib\ImGui\backends\imgui_impl_win32.cpp" />
<ClCompile Include="luslog.cpp" /> <ClCompile Include="luslog.cpp" />
@ -326,6 +328,8 @@
<ClInclude Include="Cvar.h" /> <ClInclude Include="Cvar.h" />
<ClInclude Include="Environment.h" /> <ClInclude Include="Environment.h" />
<ClInclude Include="GameSettings.h" /> <ClInclude Include="GameSettings.h" />
<ClInclude Include="SDLGamepadController.h" />
<ClInclude Include="SDLJoystickController.h" />
<ClInclude Include="Lib\ImGui\backends\imgui_impl_dx11.h" /> <ClInclude Include="Lib\ImGui\backends\imgui_impl_dx11.h" />
<ClInclude Include="Lib\ImGui\backends\imgui_impl_win32.h" /> <ClInclude Include="Lib\ImGui\backends\imgui_impl_win32.h" />
<ClInclude Include="Lib\stb\stb_image_write.h" /> <ClInclude Include="Lib\stb\stb_image_write.h" />

View file

@ -339,6 +339,12 @@
<ClCompile Include="GameSettings.cpp"> <ClCompile Include="GameSettings.cpp">
<Filter>Source Files\CustomImpl</Filter> <Filter>Source Files\CustomImpl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="SDLJoystickController.cpp">
<Filter>Source Files\Controller</Filter>
</ClCompile>
<ClCompile Include="SDLGamepadController.cpp">
<Filter>Source Files\Controller</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Lib\tinyxml2\tinyxml2.h"> <ClInclude Include="Lib\tinyxml2\tinyxml2.h">
@ -626,5 +632,11 @@
<ClInclude Include="GameSettings.h"> <ClInclude Include="GameSettings.h">
<Filter>Source Files\CustomImpl</Filter> <Filter>Source Files\CustomImpl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="SDLGamepadController.h">
<Filter>Source Files\Controller</Filter>
</ClInclude>
<ClInclude Include="SDLJoystickController.h">
<Filter>Source Files\Controller</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>