diff --git a/ControllerUSB/include/Controllers.h b/ControllerUSB/include/Controllers.h index 1f39a94..93662e1 100644 --- a/ControllerUSB/include/Controllers.h +++ b/ControllerUSB/include/Controllers.h @@ -2,6 +2,7 @@ #include "Controllers/Xbox360Controller.h" #include "Controllers/Xbox360WirelessController.h" +#include "Controllers/XboxController.h" #include "Controllers/XboxOneController.h" #include "Controllers/Dualshock3Controller.h" #include "Controllers/Dualshock4Controller.h" diff --git a/ControllerUSB/include/Controllers/Xbox360WirelessController.h b/ControllerUSB/include/Controllers/Xbox360WirelessController.h index d6494c0..27c01fb 100644 --- a/ControllerUSB/include/Controllers/Xbox360WirelessController.h +++ b/ControllerUSB/include/Controllers/Xbox360WirelessController.h @@ -4,7 +4,7 @@ #include "Xbox360Controller.h" //References used: -//https://cs.chromium.org/chromium/src/device/gamepad/xbox_controller_mac.mm +//https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c class Xbox360WirelessController : public IController { diff --git a/ControllerUSB/include/Controllers/XboxController.h b/ControllerUSB/include/Controllers/XboxController.h new file mode 100644 index 0000000..e035f8e --- /dev/null +++ b/ControllerUSB/include/Controllers/XboxController.h @@ -0,0 +1,84 @@ +#pragma once + +#include "IController.h" + +//References used: +//http://euc.jp/periphs/xbox-controller.ja.html + +struct XboxButtonData +{ + uint8_t type; + uint8_t length; + + bool dpad_up : 1; + bool dpad_down : 1; + bool dpad_left : 1; + bool dpad_right : 1; + + bool start : 1; + bool back : 1; + bool stick_left_click : 1; + bool stick_right_click : 1; + + uint8_t reserved; + + bool a; + bool b; + bool x; + bool y; + + bool black_buttton; + bool white_button; + + bool trigger_left; + bool trigger_right; + + int16_t stick_left_x; + int16_t stick_left_y; + int16_t stick_right_x; + int16_t stick_right_y; +}; + +struct XboxRumbleData +{ + uint8_t command; + uint8_t size; + uint8_t dummy1; + uint8_t big; + uint8_t dummy2; + uint8_t little; +}; + +class XboxController : public IController +{ +private: + IUSBEndpoint *m_inPipe = nullptr; + IUSBEndpoint *m_outPipe = nullptr; + + XboxButtonData m_buttonData; + +public: + XboxController(std::unique_ptr &&interface); + virtual ~XboxController(); + + virtual Status Initialize(); + virtual void Exit(); + + Status OpenInterfaces(); + void CloseInterfaces(); + + virtual Status GetInput(); + + virtual NormalizedButtonData GetNormalizedButtonData(); + + virtual ControllerType GetType() { return CONTROLLER_XBOX360; } + + inline const XboxButtonData &GetButtonData() { return m_buttonData; }; + + float NormalizeTrigger(uint8_t value); + void NormalizeAxis(int16_t x, int16_t y, uint8_t deadzonePercent, float *x_out, float *y_out); + + Status SetRumble(uint8_t strong_magnitude, uint8_t weak_magnitude); + + static void LoadConfig(const ControllerConfig *config); +}; \ No newline at end of file diff --git a/ControllerUSB/source/Controllers/Dualshock4Controller.cpp b/ControllerUSB/source/Controllers/Dualshock4Controller.cpp index b4ab424..b9858d1 100644 --- a/ControllerUSB/source/Controllers/Dualshock4Controller.cpp +++ b/ControllerUSB/source/Controllers/Dualshock4Controller.cpp @@ -38,7 +38,7 @@ Status Dualshock4Controller::OpenInterfaces() if (S_FAILED(rc)) return rc; - //This will open each interface and try to acquire Xbox One controller's in and out endpoints, if it hasn't already + //This will open each interface and try to acquire Dualshock 4 controller's in and out endpoints, if it hasn't already std::vector> &interfaces = m_device->GetInterfaces(); for (auto &&interface : interfaces) { diff --git a/ControllerUSB/source/Controllers/Xbox360WirelessController.cpp b/ControllerUSB/source/Controllers/Xbox360WirelessController.cpp index 275d545..539b76f 100644 --- a/ControllerUSB/source/Controllers/Xbox360WirelessController.cpp +++ b/ControllerUSB/source/Controllers/Xbox360WirelessController.cpp @@ -40,7 +40,7 @@ Status Xbox360WirelessController::OpenInterfaces() if (S_FAILED(rc)) return rc; - //This will open each interface and try to acquire Xbox One controller's in and out endpoints, if it hasn't already + //This will open each interface and try to acquire Xbox 360 controller's in and out endpoints, if it hasn't already std::vector> &interfaces = m_device->GetInterfaces(); for (auto &&interface : interfaces) { diff --git a/ControllerUSB/source/Controllers/XboxController.cpp b/ControllerUSB/source/Controllers/XboxController.cpp new file mode 100644 index 0000000..18b209d --- /dev/null +++ b/ControllerUSB/source/Controllers/XboxController.cpp @@ -0,0 +1,197 @@ +#include "Controllers/XboxController.h" +#include + +static ControllerConfig _xboxControllerConfig{}; + +XboxController::XboxController(std::unique_ptr &&interface) + : IController(std::move(interface)) +{ +} + +XboxController::~XboxController() +{ + Exit(); +} + +Status XboxController::Initialize() +{ + Status rc; + + rc = OpenInterfaces(); + if (S_FAILED(rc)) + return rc; + + return rc; +} +void XboxController::Exit() +{ + CloseInterfaces(); +} + +Status XboxController::OpenInterfaces() +{ + Status rc; + rc = m_device->Open(); + if (S_FAILED(rc)) + return rc; + + //This will open each interface and try to acquire Xbox controller's in and out endpoints, if it hasn't already + std::vector> &interfaces = m_device->GetInterfaces(); + for (auto &&interface : interfaces) + { + rc = interface->Open(); + if (S_FAILED(rc)) + return rc; + + if (interface->GetDescriptor()->bInterfaceProtocol != 0) + continue; + + if (interface->GetDescriptor()->bNumEndpoints < 2) + continue; + + if (!m_inPipe) + { + for (int i = 0; i != 15; ++i) + { + IUSBEndpoint *inEndpoint = interface->GetEndpoint(IUSBEndpoint::USB_ENDPOINT_IN, i); + if (inEndpoint) + { + rc = inEndpoint->Open(); + if (S_FAILED(rc)) + return 55555; + + m_inPipe = inEndpoint; + break; + } + } + } + + if (!m_outPipe) + { + for (int i = 0; i != 15; ++i) + { + IUSBEndpoint *outEndpoint = interface->GetEndpoint(IUSBEndpoint::USB_ENDPOINT_OUT, i); + if (outEndpoint) + { + rc = outEndpoint->Open(); + if (S_FAILED(rc)) + return 66666; + + m_outPipe = outEndpoint; + break; + } + } + } + } + + if (!m_inPipe || !m_outPipe) + return 69; + + return rc; +} +void XboxController::CloseInterfaces() +{ + //m_device->Reset(); + m_device->Close(); +} + +Status XboxController::GetInput() +{ + uint8_t input_bytes[64]; + + Status rc = m_inPipe->Read(input_bytes, sizeof(input_bytes)); + + if (S_SUCCEEDED(rc)) + m_buttonData = *reinterpret_cast(input_bytes); + + return rc; +} + +void XboxController::NormalizeAxis(int16_t x, + int16_t y, + uint8_t deadzonePercent, + float *x_out, + float *y_out) +{ + float x_val = x; + float y_val = y; + // Determine how far the stick is pushed. + //This will never exceed 32767 because if the stick is + //horizontally maxed in one direction, vertically it must be neutral(0) and vice versa + float real_magnitude = std::sqrt(x_val * x_val + y_val * y_val); + float real_deadzone = (32767 * deadzonePercent) / 100; + // Check if the controller is outside a circular dead zone. + if (real_magnitude > real_deadzone) + { + // Clip the magnitude at its expected maximum value. + float magnitude = std::min(32767.0f, real_magnitude); + // Adjust magnitude relative to the end of the dead zone. + magnitude -= real_deadzone; + // Normalize the magnitude with respect to its expected range giving a + // magnitude value of 0.0 to 1.0 + //ratio = (currentValue / maxValue) / realValue + float ratio = (magnitude / (32767 - real_deadzone)) / real_magnitude; + // Y is negated because xbox controllers have an opposite sign from + // the 'standard controller' recommendations. + *x_out = x_val * ratio; + *y_out = y_val * ratio; + } + else + { + // If the controller is in the deadzone zero out the magnitude. + *x_out = *y_out = 0.0f; + } +} + +//Pass by value should hopefully be optimized away by RVO +NormalizedButtonData XboxController::GetNormalizedButtonData() +{ + NormalizedButtonData normalData; + + normalData.triggers[0] = m_buttonData.trigger_left; + normalData.triggers[1] = m_buttonData.trigger_right; + + NormalizeAxis(m_buttonData.stick_left_x, m_buttonData.stick_left_y, _xboxControllerConfig.leftStickDeadzonePercent, + &normalData.sticks[0].axis_x, &normalData.sticks[0].axis_y); + NormalizeAxis(m_buttonData.stick_right_x, m_buttonData.stick_right_y, _xboxControllerConfig.rightStickDeadzonePercent, + &normalData.sticks[1].axis_x, &normalData.sticks[1].axis_y); + + bool buttons[NUM_CONTROLLERBUTTONS]{ + m_buttonData.y, + m_buttonData.b, + m_buttonData.a, + m_buttonData.x, + m_buttonData.stick_left_click, + m_buttonData.stick_right_click, + false, + false, + m_buttonData.trigger_left, + m_buttonData.trigger_right, + m_buttonData.back, + m_buttonData.start, + m_buttonData.dpad_up, + m_buttonData.dpad_right, + m_buttonData.dpad_down, + m_buttonData.dpad_left, + m_buttonData.white_button, + m_buttonData.black_buttton, + }; + + for (int i = 0; i != NUM_CONTROLLERBUTTONS; ++i) + { + ControllerButton button = _xboxControllerConfig.buttons[i]; + normalData.buttons[(button != NOT_SET ? button : i)] = buttons[i]; + } + + return normalData; +} + +Status XboxController::SetRumble(uint8_t strong_magnitude, uint8_t weak_magnitude) +{ + uint8_t rumbleData[]{0x00, 0x06, 0x00, strong_magnitude, weak_magnitude, 0x00, 0x00, 0x00}; + return m_outPipe->Write(rumbleData, sizeof(rumbleData)); +} +void XboxController::LoadConfig(const ControllerConfig *config) +{ + _xboxControllerConfig = *config; +} \ No newline at end of file diff --git a/source/configFile.cpp b/source/configFile.cpp index e3abece..e7149a3 100644 --- a/source/configFile.cpp +++ b/source/configFile.cpp @@ -11,6 +11,7 @@ #define GLOBALCONFIG "config_global.ini" +#define XBOXCONFIG "config_xboxorig.ini" #define XBOX360CONFIG "config_xbox360.ini" #define XBOXONECONFIG "config_xboxone.ini" #define DUALSHOCK3CONFIG "config_dualshock3.ini" @@ -102,6 +103,13 @@ static Result _ReadFromConfig(const char *path) void LoadAllConfigs() { + if (R_SUCCEEDED(_ReadFromConfig(CONFIG_PATH XBOXCONFIG))) + { + XboxController::LoadConfig(&temp_config); + } + else + WriteToLog("Failed to read from xbox orig config!"); + if (R_SUCCEEDED(_ReadFromConfig(CONFIG_PATH XBOXONECONFIG))) XboxOneController::LoadConfig(&temp_config); else @@ -131,6 +139,7 @@ bool CheckForFileChanges() bool filesChanged = false; static time_t globalConfigLastModified; + static time_t xboxConfigLastModified; static time_t xbox360ConfigLastModified; static time_t xboxOneConfigLastModified; static time_t dualshock3ConfigLastModified; @@ -144,6 +153,13 @@ bool CheckForFileChanges() filesChanged = true; } + if (stat(CONFIG_PATH XBOXCONFIG, &result) == 0) + if (xboxConfigLastModified != result.st_mtime) + { + xboxConfigLastModified = result.st_mtime; + filesChanged = true; + } + if (stat(CONFIG_PATH XBOX360CONFIG, &result) == 0) if (xbox360ConfigLastModified != result.st_mtime) { diff --git a/source/mainLoop.cpp b/source/mainLoop.cpp index 6cdb71c..e368195 100644 --- a/source/mainLoop.cpp +++ b/source/mainLoop.cpp @@ -34,8 +34,8 @@ Result mainLoop() Result rc = 0; bool useAbstractedPad = hosversionBetween(5, 7); - Event xinputEvent; - Event dinputEvent; + Event catchAllEvent; + Event ds3Event; std::vector> controllerInterfaces; UTimer filecheckTimer; @@ -47,13 +47,13 @@ Result mainLoop() { UsbHsInterfaceFilter filter; - filter.Flags = UsbHsInterfaceFilterFlags_bInterfaceClass; - filter.bInterfaceClass = USB_CLASS_VENDOR_SPEC; - rc = usbHsCreateInterfaceAvailableEvent(&xinputEvent, true, 0, &filter); + filter.Flags = UsbHsInterfaceFilterFlags_bcdDevice_Min; + filter.bcdDevice_Min = 0; + rc = usbHsCreateInterfaceAvailableEvent(&catchAllEvent, true, 0, &filter); if (R_FAILED(rc)) - WriteToLog("Failed to open event for XInput"); + WriteToLog("Failed to open catch-all event"); else - WriteToLog("Successfully created event for XInput"); + WriteToLog("Successfully created catch-all event"); //filter.Flags = UsbHsInterfaceFilterFlags_bInterfaceClass | UsbHsInterfaceFilterFlags_bcdDevice_Min; //filter.bInterfaceClass = USB_CLASS_HID; @@ -61,11 +61,11 @@ Result mainLoop() filter.Flags = UsbHsInterfaceFilterFlags_idVendor | UsbHsInterfaceFilterFlags_idProduct; filter.idVendor = VENDOR_SONY; filter.idProduct = PRODUCT_DUALSHOCK3; - rc = usbHsCreateInterfaceAvailableEvent(&dinputEvent, true, 1, &filter); + rc = usbHsCreateInterfaceAvailableEvent(&ds3Event, true, 1, &filter); if (R_FAILED(rc)) - WriteToLog("Failed to open event for DInput"); + WriteToLog("Failed to open event for Dualshock 3"); else - WriteToLog("Successfully created event for DInput"); + WriteToLog("Successfully created event for Dualshock 3"); } controllerInterfaces.reserve(8); @@ -82,10 +82,10 @@ Result mainLoop() if (kDown & KEY_B) break; #endif - rc = eventWait(&xinputEvent, 0); + rc = eventWait(&catchAllEvent, 0); if (R_SUCCEEDED(rc)) { - WriteToLog("XInput went off"); + WriteToLog("Catch-all event went off"); UsbHsInterface interfaces[8]; s32 total_entries; @@ -101,6 +101,12 @@ Result mainLoop() devicePtr = std::make_unique(interfaces, total_entries); controllerPtr = std::make_unique(std::move(devicePtr)); } + else if (R_SUCCEEDED(QueryInterfaces(interfaces, sizeof(interfaces), &total_entries, 0x58, 0x42, 0x00))) + { + WriteToLog("Registering Xbox One controller"); + devicePtr = std::make_unique(interfaces, total_entries); + controllerPtr = std::make_unique(std::move(devicePtr)); + } else if (R_SUCCEEDED(QueryInterfaces(interfaces, sizeof(interfaces), &total_entries, USB_CLASS_VENDOR_SPEC, 71, 208))) { WriteToLog("Registering Xbox One controller"); @@ -108,10 +114,10 @@ Result mainLoop() controllerPtr = std::make_unique(std::move(devicePtr)); } } - rc = eventWait(&dinputEvent, 0); + rc = eventWait(&ds3Event, 0); if (R_SUCCEEDED(rc)) { - WriteToLog("DInput went off"); + WriteToLog("Dualshock 3 event went off"); UsbHsInterface interfaces[4]; s32 total_entries; @@ -203,8 +209,8 @@ Result mainLoop() //After we break out of the loop, close all events and exit WriteToLog("Destroying events"); - usbHsDestroyInterfaceAvailableEvent(&xinputEvent, 0); - usbHsDestroyInterfaceAvailableEvent(&dinputEvent, 1); + usbHsDestroyInterfaceAvailableEvent(&catchAllEvent, 0); + usbHsDestroyInterfaceAvailableEvent(&ds3Event, 1); //controllerInterfaces.clear(); return rc;