From ca320aa01e98fe4b41770576da216e9a35843972 Mon Sep 17 00:00:00 2001 From: o0Zz Date: Thu, 15 Aug 2024 11:33:14 +0200 Subject: [PATCH] Fully implemented Switch pro controller (USB mode) --- .../Controllers/BaseController.cpp | 92 ++++++++++++++++--- .../Controllers/BaseController.h | 9 +- .../Controllers/Dualshock3Controller.cpp | 17 ++-- .../Controllers/Dualshock3Controller.h | 2 +- .../Controllers/GenericHIDController.cpp | 20 ++-- .../Controllers/GenericHIDController.h | 2 +- .../Controllers/SwitchController.cpp | 54 +++++++---- .../Controllers/SwitchController.h | 20 +++- .../Controllers/Xbox360Controller.cpp | 16 +--- .../Controllers/Xbox360Controller.h | 2 +- .../Controllers/Xbox360WirelessController.cpp | 32 +++---- .../Controllers/Xbox360WirelessController.h | 3 +- .../Controllers/XboxController.cpp | 14 +-- .../Controllers/XboxController.h | 2 +- .../Controllers/XboxOneController.cpp | 27 ++---- .../Controllers/XboxOneController.h | 2 +- tests/test_controller_switch.cpp | 48 ++++++++++ tests/test_input_binding.cpp | 64 ++++++------- tests/test_normalize.cpp | 9 ++ 19 files changed, 280 insertions(+), 155 deletions(-) create mode 100644 tests/test_controller_switch.cpp diff --git a/source/ControllerLib/Controllers/BaseController.cpp b/source/ControllerLib/Controllers/BaseController.cpp index 18eae36..5cb1230 100644 --- a/source/ControllerLib/Controllers/BaseController.cpp +++ b/source/ControllerLib/Controllers/BaseController.cpp @@ -126,18 +126,49 @@ ControllerResult BaseController::SetRumble(uint16_t input_idx, float amp_high, f return CONTROLLER_STATUS_NOT_IMPLEMENTED; } -static_assert(MAX_PIN_BY_BUTTONS == 2, "You need to update IsPinPressed macro !"); -#define IsPinPressed(rawData, controllerButton) \ - (rawData.buttons[GetConfig().buttons_pin[controllerButton][0]] || rawData.buttons[GetConfig().buttons_pin[controllerButton][1]]) ? true : false +ControllerResult BaseController::ReadNextBuffer(uint8_t *buffer, size_t *size, uint16_t *input_idx, uint32_t timeout_us) +{ + uint16_t controller_idx = m_current_controller_idx; + m_current_controller_idx = (m_current_controller_idx + 1) % m_inPipe.size(); + + *size = std::min((size_t)m_inPipe[controller_idx]->GetDescriptor()->wMaxPacketSize, *size); + + ControllerResult result = m_inPipe[controller_idx]->Read(buffer, size, timeout_us); + if (result != CONTROLLER_STATUS_SUCCESS) + return result; + + if (*size == 0) + return CONTROLLER_STATUS_NOTHING_TODO; + + *input_idx = controller_idx; + + return CONTROLLER_STATUS_SUCCESS; +} ControllerResult BaseController::ReadInput(NormalizedButtonData *normalData, uint16_t *input_idx, uint32_t timeout_us) { RawInputData rawData; + uint8_t input_bytes[CONTROLLER_INPUT_BUFFER_SIZE]; + size_t size = sizeof(input_bytes); + + ControllerResult result = ReadNextBuffer(input_bytes, &size, input_idx, timeout_us); + if (result != CONTROLLER_STATUS_SUCCESS) + return result; - ControllerResult result = ReadRawInput(&rawData, input_idx, timeout_us); + result = ParseData(input_bytes, size, &rawData, input_idx); if (result != CONTROLLER_STATUS_SUCCESS) return result; + MapRawInputToNormalized(rawData, normalData); + return CONTROLLER_STATUS_SUCCESS; +} + +static_assert(MAX_PIN_BY_BUTTONS == 2, "You need to update IsPinPressed macro !"); +#define IsPinPressed(rawData, controllerButton) \ + (rawData.buttons[GetConfig().buttons_pin[controllerButton][0]] || rawData.buttons[GetConfig().buttons_pin[controllerButton][1]]) ? true : false + +void BaseController::MapRawInputToNormalized(RawInputData &rawData, NormalizedButtonData *normalData) +{ Log(LogLevelDebug, "Controller[%04x-%04x] DATA: X=%d%%, Y=%d%%, Z=%d%%, Rz=%d%%, B1=%d, B2=%d, B3=%d, B4=%d, B5=%d, B6=%d, B7=%d, B8=%d, B9=%d, B10=%d", m_device->GetVendor(), m_device->GetProduct(), (int)(rawData.X * 100.0), (int)(rawData.Y * 100.0), (int)(rawData.Z * 100.0), (int)(rawData.Rz * 100.0), @@ -282,8 +313,6 @@ ControllerResult BaseController::ReadInput(NormalizedButtonData *normalData, uin normalData->buttons[GetConfig().simulateCapture[1]] = false; } } - - return CONTROLLER_STATUS_SUCCESS; } float BaseController::ApplyDeadzone(uint8_t deadzonePercent, float value) @@ -303,13 +332,24 @@ float BaseController::ApplyDeadzone(uint8_t deadzonePercent, float value) float BaseController::Normalize(int32_t value, int32_t min, int32_t max) { - float range = floor((max - min) / 2.0f); - float offset = range; - - if (range == max) - offset = 0; + return Normalize(value, min, max, (max + min) / 2); +} - float ret = (value - offset) / range; +float BaseController::Normalize(int32_t value, int32_t min, int32_t max, int32_t center) +{ + float ret = 0.0f; + if (value < center) + { + float offset = (float)min; + float range = (float)(center - min); + ret = ((value - offset) / range) - 1.0f; + } + else + { + float offset = (float)center; + float range = (float)(max - center); + ret = (value - offset) / range; + } if (ret > 1.0f) ret = 1.0f; @@ -317,4 +357,32 @@ float BaseController::Normalize(int32_t value, int32_t min, int32_t max) ret = -1.0f; return ret; +} + +uint32_t BaseController::ReadBitsLE(uint8_t *buffer, uint32_t bitOffset, uint32_t bitLength) +{ + // Calculate the starting byte index and bit index within that byte + uint32_t byteIndex = bitOffset / 8; + uint32_t bitIndex = bitOffset % 8; // Little endian, LSB is at index 0 + + uint32_t result = 0; + + for (uint32_t i = 0; i < bitLength; ++i) + { + // Check if we need to move to the next byte + if (bitIndex > 7) + { + ++byteIndex; + bitIndex = 0; + } + + // Get the bit at the current position and add it to the result + uint8_t bit = (buffer[byteIndex] >> bitIndex) & 0x01; + result |= (bit << i); + + // Move to the next bit + ++bitIndex; + } + + return result; } \ No newline at end of file diff --git a/source/ControllerLib/Controllers/BaseController.h b/source/ControllerLib/Controllers/BaseController.h index 04eb815..eaa7123 100644 --- a/source/ControllerLib/Controllers/BaseController.h +++ b/source/ControllerLib/Controllers/BaseController.h @@ -33,6 +33,12 @@ class BaseController : public IController std::vector m_inPipe; std::vector m_outPipe; std::vector m_interfaces; + uint8_t m_current_controller_idx = 0; + + virtual ControllerResult ReadNextBuffer(uint8_t *buffer, size_t *size, uint16_t *input_idx, uint32_t timeout_us); + virtual void MapRawInputToNormalized(RawInputData &rawData, NormalizedButtonData *normalData); + + virtual ControllerResult ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) = 0; public: BaseController(std::unique_ptr &&device, const ControllerConfig &config, std::unique_ptr &&logger); @@ -49,11 +55,12 @@ class BaseController : public IController virtual uint16_t GetInputCount() override; ControllerResult ReadInput(NormalizedButtonData *normalData, uint16_t *input_idx, uint32_t timeout_us) override; - virtual ControllerResult ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) = 0; ControllerResult SetRumble(uint16_t input_idx, float amp_high, float amp_low) override; // Helper functions static float Normalize(int32_t value, int32_t min, int32_t max); + static float Normalize(int32_t value, int32_t min, int32_t max, int32_t center); static float ApplyDeadzone(uint8_t deadzonePercent, float value); + static uint32_t ReadBitsLE(uint8_t *buffer, uint32_t bitOffset, uint32_t bitLength); }; \ No newline at end of file diff --git a/source/ControllerLib/Controllers/Dualshock3Controller.cpp b/source/ControllerLib/Controllers/Dualshock3Controller.cpp index 4adb408..44224e6 100644 --- a/source/ControllerLib/Controllers/Dualshock3Controller.cpp +++ b/source/ControllerLib/Controllers/Dualshock3Controller.cpp @@ -36,21 +36,16 @@ ControllerResult Dualshock3Controller::OpenInterfaces() return CONTROLLER_STATUS_SUCCESS; } -ControllerResult Dualshock3Controller::ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) +ControllerResult Dualshock3Controller::ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) { - uint8_t input_bytes[CONTROLLER_INPUT_BUFFER_SIZE]; - size_t size = sizeof(input_bytes); + (void)input_idx; + Dualshock3ButtonData *buttonData = reinterpret_cast(buffer); - ControllerResult result = m_inPipe[0]->Read(input_bytes, &size, timeout_us); - if (result != CONTROLLER_STATUS_SUCCESS) - return result; + if (size < sizeof(Dualshock3ButtonData)) + return CONTROLLER_STATUS_UNEXPECTED_DATA; - *input_idx = 0; - - if (input_bytes[0] == Ds3InputPacket_Button) + if (buttonData->type == Ds3InputPacket_Button) { - Dualshock3ButtonData *buttonData = reinterpret_cast(input_bytes); - rawData->buttons[1] = buttonData->button1; rawData->buttons[2] = buttonData->button2; rawData->buttons[3] = buttonData->button3; diff --git a/source/ControllerLib/Controllers/Dualshock3Controller.h b/source/ControllerLib/Controllers/Dualshock3Controller.h index 91fba5d..3350a29 100644 --- a/source/ControllerLib/Controllers/Dualshock3Controller.h +++ b/source/ControllerLib/Controllers/Dualshock3Controller.h @@ -121,5 +121,5 @@ class Dualshock3Controller : public BaseController virtual ControllerResult Initialize() override; virtual ControllerResult OpenInterfaces() override; - ControllerResult ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us); + virtual ControllerResult ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) override; }; \ No newline at end of file diff --git a/source/ControllerLib/Controllers/GenericHIDController.cpp b/source/ControllerLib/Controllers/GenericHIDController.cpp index 936cae2..517e940 100644 --- a/source/ControllerLib/Controllers/GenericHIDController.cpp +++ b/source/ControllerLib/Controllers/GenericHIDController.cpp @@ -65,20 +65,11 @@ uint16_t GenericHIDController::GetInputCount() return std::min((int)m_joystick_count, CONTROLLER_MAX_INPUTS); } -ControllerResult GenericHIDController::ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) +ControllerResult GenericHIDController::ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) { HIDJoystickData joystick_data; - uint8_t input_bytes[CONTROLLER_INPUT_BUFFER_SIZE]; - size_t size = std::min((size_t)m_inPipe[0]->GetDescriptor()->wMaxPacketSize, sizeof(input_bytes)); - ControllerResult result = m_inPipe[0]->Read(input_bytes, &size, timeout_us); - if (result != CONTROLLER_STATUS_SUCCESS) - return result; - - if (size == 0) - return CONTROLLER_STATUS_NOTHING_TODO; - - if (!m_joystick->parseData(input_bytes, (uint16_t)size, &joystick_data)) + if (!m_joystick->parseData(buffer, (uint16_t)size, &joystick_data)) { Log(LogLevelError, "GenericHIDController[%04x-%04x] Failed to parse input data (size=%d)", m_device->GetVendor(), m_device->GetProduct(), size); return CONTROLLER_STATUS_UNEXPECTED_DATA; @@ -87,7 +78,12 @@ ControllerResult GenericHIDController::ReadRawInput(RawInputData *rawData, uint1 if (joystick_data.index >= GetInputCount()) return CONTROLLER_STATUS_UNEXPECTED_DATA; - *input_idx = joystick_data.index; + /* + Special case for generic HID, input_idx might be bigger than 0 in case of multiple interfaces. + If this is the case we expect to have 1 input per interface, thus we don't want to overwrite the input index. + */ + if (input_idx != NULL && *input_idx == 0) + *input_idx = joystick_data.index; for (int i = 0; i < MAX_CONTROLLER_BUTTONS; i++) rawData->buttons[i] = joystick_data.buttons[i]; diff --git a/source/ControllerLib/Controllers/GenericHIDController.h b/source/ControllerLib/Controllers/GenericHIDController.h index 62b6771..2bcbc4e 100644 --- a/source/ControllerLib/Controllers/GenericHIDController.h +++ b/source/ControllerLib/Controllers/GenericHIDController.h @@ -19,5 +19,5 @@ class GenericHIDController : public BaseController virtual uint16_t GetInputCount() override; - virtual ControllerResult ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) override; + virtual ControllerResult ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) override; }; \ No newline at end of file diff --git a/source/ControllerLib/Controllers/SwitchController.cpp b/source/ControllerLib/Controllers/SwitchController.cpp index 1c1409d..15b868d 100644 --- a/source/ControllerLib/Controllers/SwitchController.cpp +++ b/source/ControllerLib/Controllers/SwitchController.cpp @@ -1,8 +1,12 @@ #include "Controllers/SwitchController.h" +static_assert(CONTROLLER_INPUT_BUFFER_SIZE == 64, "Input byte for switch as to be 64 bytes long"); + SwitchController::SwitchController(std::unique_ptr &&device, const ControllerConfig &config, std::unique_ptr &&logger) : BaseController(std::move(device), config, std::move(logger)) { + cal_left_x.max = cal_left_y.max = cal_right_x.max = cal_right_y.max = 3400; + cal_left_x.min = cal_left_y.min = cal_right_x.min = cal_right_y.min = 600; } SwitchController::~SwitchController() @@ -31,20 +35,13 @@ ControllerResult SwitchController::Initialize() return CONTROLLER_STATUS_SUCCESS; } -ControllerResult SwitchController::ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) +ControllerResult SwitchController::ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) { - uint8_t input_bytes[CONTROLLER_INPUT_BUFFER_SIZE]; - size_t size = sizeof(input_bytes); - - static_assert(sizeof(input_bytes) == 64, "Input byte for switch as to be 64 bytes long"); + (void)input_idx; + SwitchButtonData *buttonData = reinterpret_cast(buffer); - ControllerResult result = m_inPipe[0]->Read(input_bytes, &size, timeout_us); - if (result != CONTROLLER_STATUS_SUCCESS) - return result; - - SwitchButtonData *buttonData = reinterpret_cast(input_bytes); - - *input_idx = 0; + if (size < sizeof(SwitchButtonData)) + return CONTROLLER_STATUS_UNEXPECTED_DATA; if (buttonData->report_id != 0x30) return CONTROLLER_STATUS_NOTHING_TODO; @@ -69,12 +66,33 @@ ControllerResult SwitchController::ReadRawInput(RawInputData *rawData, uint16_t rawData->buttons[18] = buttonData->button18; rawData->buttons[19] = buttonData->button19; - /* - rawData->X = BaseController::Normalize(buttonData->stick_left_x, -32768, 32767); - rawData->Y = BaseController::Normalize(-buttonData->stick_left_y, -32768, 32767); - rawData->Z = BaseController::Normalize(buttonData->stick_right_x, -32768, 32767); - rawData->Rz = BaseController::Normalize(-buttonData->stick_right_y, -32768, 32767); - */ + uint16_t left_x = BaseController::ReadBitsLE(buttonData->stick_left, 0, 12); + uint16_t left_y = BaseController::ReadBitsLE(buttonData->stick_left, 12, 12); + uint16_t right_x = BaseController::ReadBitsLE(buttonData->stick_right, 0, 12); + uint16_t right_y = BaseController::ReadBitsLE(buttonData->stick_right, 12, 12); + + cal_left_x.max = std::max(left_x, cal_left_x.max); + cal_left_x.min = std::min(left_x, cal_left_x.min); + cal_left_y.max = std::max(left_y, cal_left_y.max); + cal_left_y.min = std::min(left_y, cal_left_y.min); + cal_right_x.max = std::max(right_x, cal_right_x.max); + cal_right_x.min = std::min(right_x, cal_right_x.min); + cal_right_y.max = std::max(right_y, cal_right_y.max); + cal_right_y.min = std::min(right_y, cal_right_y.min); + + Log(LogLevelTrace, "X=%u, Y=%u, Z=%u, Rz=%u (Calib: X=[%u,%u], Y=[%u,%u], Z=[%u,%u], Rz=[%u,%u])", + left_x, left_y, right_x, right_y, + cal_left_x.min, cal_left_x.max, cal_left_y.min, cal_left_y.max, + cal_right_x.min, cal_right_x.max, cal_right_y.min, cal_right_y.max); + + rawData->X = BaseController::Normalize(left_x, cal_left_x.min, cal_left_x.max, 2000); + rawData->Y = BaseController::Normalize(left_y, cal_left_y.min, cal_left_y.max, 2000); + rawData->Z = BaseController::Normalize(right_x, cal_right_x.min, cal_right_x.max, 2000); + rawData->Rz = BaseController::Normalize(right_y, cal_right_y.min, cal_right_y.max, 2000); + + rawData->Y = -rawData->Y; + rawData->Rz = -rawData->Rz; + rawData->dpad_up = buttonData->dpad_up; rawData->dpad_right = buttonData->dpad_right; rawData->dpad_down = buttonData->dpad_down; diff --git a/source/ControllerLib/Controllers/SwitchController.h b/source/ControllerLib/Controllers/SwitchController.h index 3901858..31703ad 100644 --- a/source/ControllerLib/Controllers/SwitchController.h +++ b/source/ControllerLib/Controllers/SwitchController.h @@ -40,21 +40,31 @@ struct SwitchButtonData bool button18 : 1; bool button19 : 1; - int16_t stick_left_x; - int16_t stick_left_y; - int16_t stick_right_x; - int16_t stick_right_y; + uint8_t stick_left[3]; + uint8_t stick_right[3]; +}; + +struct SwitchCalibration +{ + uint16_t min; + uint16_t max; }; class SwitchController : public BaseController { +private: + SwitchCalibration cal_left_x; + SwitchCalibration cal_left_y; + SwitchCalibration cal_right_x; + SwitchCalibration cal_right_y; + public: SwitchController(std::unique_ptr &&device, const ControllerConfig &config, std::unique_ptr &&logger); virtual ~SwitchController() override; ControllerResult Initialize() override; - virtual ControllerResult ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) override; + virtual ControllerResult ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) override; bool Support(ControllerFeature feature) override; }; \ No newline at end of file diff --git a/source/ControllerLib/Controllers/Xbox360Controller.cpp b/source/ControllerLib/Controllers/Xbox360Controller.cpp index 53dafcc..6dab792 100644 --- a/source/ControllerLib/Controllers/Xbox360Controller.cpp +++ b/source/ControllerLib/Controllers/Xbox360Controller.cpp @@ -22,22 +22,16 @@ ControllerResult Xbox360Controller::Initialize() return CONTROLLER_STATUS_SUCCESS; } -ControllerResult Xbox360Controller::ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) +ControllerResult Xbox360Controller::ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) { - uint8_t input_bytes[CONTROLLER_INPUT_BUFFER_SIZE]; - size_t size = sizeof(input_bytes); + (void)input_idx; + Xbox360ButtonData *buttonData = reinterpret_cast(buffer); - ControllerResult result = m_inPipe[0]->Read(input_bytes, &size, timeout_us); - if (result != CONTROLLER_STATUS_SUCCESS) - return result; - - Xbox360ButtonData *buttonData = reinterpret_cast(input_bytes); - - *input_idx = 0; + if (size < sizeof(Xbox360ButtonData)) + return CONTROLLER_STATUS_UNEXPECTED_DATA; if (buttonData->type == XBOX360INPUT_BUTTON) // Button data { - rawData->buttons[1] = buttonData->button1; rawData->buttons[2] = buttonData->button2; rawData->buttons[3] = buttonData->button3; diff --git a/source/ControllerLib/Controllers/Xbox360Controller.h b/source/ControllerLib/Controllers/Xbox360Controller.h index 604c65c..7a80533 100644 --- a/source/ControllerLib/Controllers/Xbox360Controller.h +++ b/source/ControllerLib/Controllers/Xbox360Controller.h @@ -77,7 +77,7 @@ class Xbox360Controller : public BaseController ControllerResult Initialize() override; - ControllerResult ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) override; + virtual ControllerResult ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) override; bool Support(ControllerFeature feature) override; diff --git a/source/ControllerLib/Controllers/Xbox360WirelessController.cpp b/source/ControllerLib/Controllers/Xbox360WirelessController.cpp index 57d6031..ca0b153 100644 --- a/source/ControllerLib/Controllers/Xbox360WirelessController.cpp +++ b/source/ControllerLib/Controllers/Xbox360WirelessController.cpp @@ -43,44 +43,34 @@ void Xbox360WirelessController::CloseInterfaces() BaseController::CloseInterfaces(); } -ControllerResult Xbox360WirelessController::ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) +ControllerResult Xbox360WirelessController::ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) { - uint8_t input_bytes[CONTROLLER_INPUT_BUFFER_SIZE]; - size_t size = sizeof(input_bytes); + Xbox360ButtonData *buttonData = reinterpret_cast(buffer); - uint16_t controller_idx = m_current_controller_idx; - m_current_controller_idx = (m_current_controller_idx + 1) % XBOX360_MAX_INPUTS; - - ControllerResult result = m_inPipe[controller_idx]->Read(input_bytes, &size, timeout_us); - if (result != CONTROLLER_STATUS_SUCCESS) - return result; - - Xbox360ButtonData *buttonData = reinterpret_cast(input_bytes); - - *input_idx = controller_idx; + if (size < sizeof(Xbox360ButtonData)) + return CONTROLLER_STATUS_UNEXPECTED_DATA; // https://github.com/xboxdrv/xboxdrv/blob/stable/src/xbox360_controller.cpp // https://github.com/felis/USB_Host_Shield_2.0/blob/master/XBOXRECV.cpp - if (input_bytes[0] & 0x08) // Connect/Disconnect + if (buffer[0] & 0x08) // Connect/Disconnect { - bool is_connected = (input_bytes[1] & 0x80) != 0; + bool is_connected = (buffer[1] & 0x80) != 0; - if (m_is_connected[controller_idx] != is_connected) + if (m_is_connected[*input_idx] != is_connected) { if (is_connected) - OnControllerConnect(controller_idx); + OnControllerConnect(*input_idx); else - OnControllerDisconnect(controller_idx); + OnControllerDisconnect(*input_idx); } } - else if (input_bytes[0] == 0x00 && input_bytes[1] == 0x01 && input_bytes[2] == 0x00 && input_bytes[3] == 0xf0) + else if (buffer[0] == 0x00 && buffer[1] == 0x01 && buffer[2] == 0x00 && buffer[3] == 0xf0) { - buttonData = reinterpret_cast(input_bytes + 4); + buttonData = reinterpret_cast(buffer + 4); if (buttonData->type == XBOX360INPUT_BUTTON) // Button data { - rawData->buttons[1] = buttonData->button1; rawData->buttons[2] = buttonData->button2; rawData->buttons[3] = buttonData->button3; diff --git a/source/ControllerLib/Controllers/Xbox360WirelessController.h b/source/ControllerLib/Controllers/Xbox360WirelessController.h index 75034f9..49f8aef 100644 --- a/source/ControllerLib/Controllers/Xbox360WirelessController.h +++ b/source/ControllerLib/Controllers/Xbox360WirelessController.h @@ -9,7 +9,6 @@ class Xbox360WirelessController : public BaseController { private: bool m_is_connected[XBOX360_MAX_INPUTS]; - uint8_t m_current_controller_idx = 0; ControllerResult SetLED(uint16_t input_idx, Xbox360LEDValue value); @@ -23,7 +22,7 @@ class Xbox360WirelessController : public BaseController ControllerResult OpenInterfaces() override; void CloseInterfaces() override; - ControllerResult ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) override; + virtual ControllerResult ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) override; bool Support(ControllerFeature feature) override; diff --git a/source/ControllerLib/Controllers/XboxController.cpp b/source/ControllerLib/Controllers/XboxController.cpp index ca4c60f..71ced8d 100644 --- a/source/ControllerLib/Controllers/XboxController.cpp +++ b/source/ControllerLib/Controllers/XboxController.cpp @@ -9,18 +9,14 @@ XboxController::~XboxController() { } -ControllerResult XboxController::ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) +ControllerResult XboxController::ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) { - uint8_t input_bytes[CONTROLLER_INPUT_BUFFER_SIZE]; - size_t size = sizeof(input_bytes); + (void)input_idx; - ControllerResult result = m_inPipe[0]->Read(input_bytes, &size, timeout_us); - if (result != CONTROLLER_STATUS_SUCCESS) - return result; + XboxButtonData *buttonData = reinterpret_cast(buffer); - XboxButtonData *buttonData = reinterpret_cast(input_bytes); - - *input_idx = 0; + if (size < sizeof(XboxButtonData)) + return CONTROLLER_STATUS_UNEXPECTED_DATA; rawData->buttons[1] = buttonData->button1 > 0; rawData->buttons[2] = buttonData->button2 > 0; diff --git a/source/ControllerLib/Controllers/XboxController.h b/source/ControllerLib/Controllers/XboxController.h index 1563216..1e95bcd 100644 --- a/source/ControllerLib/Controllers/XboxController.h +++ b/source/ControllerLib/Controllers/XboxController.h @@ -56,7 +56,7 @@ class XboxController : public BaseController XboxController(std::unique_ptr &&device, const ControllerConfig &config, std::unique_ptr &&logger); virtual ~XboxController() override; - virtual ControllerResult ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) override; + virtual ControllerResult ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) override; bool Support(ControllerFeature feature) override; ControllerResult SetRumble(uint16_t input_idx, float amp_high, float amp_low) override; diff --git a/source/ControllerLib/Controllers/XboxOneController.cpp b/source/ControllerLib/Controllers/XboxOneController.cpp index f001166..d3df035 100644 --- a/source/ControllerLib/Controllers/XboxOneController.cpp +++ b/source/ControllerLib/Controllers/XboxOneController.cpp @@ -119,23 +119,16 @@ ControllerResult XboxOneController::Initialize() return CONTROLLER_STATUS_SUCCESS; } -ControllerResult XboxOneController::ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) +ControllerResult XboxOneController::ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) { - uint8_t input_bytes[CONTROLLER_INPUT_BUFFER_SIZE]; - size_t size = sizeof(input_bytes); - - ControllerResult result = m_inPipe[0]->Read(input_bytes, &size, timeout_us); - if (result != CONTROLLER_STATUS_SUCCESS) - return result; - - uint8_t type = input_bytes[0]; + (void)input_idx; + XboxOneButtonData *buttonData = reinterpret_cast(buffer); - *input_idx = 0; + if (size < sizeof(XboxOneButtonData)) + return CONTROLLER_STATUS_UNEXPECTED_DATA; - if (type == GIP_CMD_INPUT) // Button data + if (buttonData->type == GIP_CMD_INPUT) // Button data { - XboxOneButtonData *buttonData = reinterpret_cast(input_bytes); - m_rawInput.buttons[1] = buttonData->button1; m_rawInput.buttons[2] = buttonData->button2; m_rawInput.buttons[3] = buttonData->button3; @@ -165,13 +158,13 @@ ControllerResult XboxOneController::ReadRawInput(RawInputData *rawData, uint16_t return CONTROLLER_STATUS_SUCCESS; } - else if (type == GIP_CMD_VIRTUAL_KEY) // Mode button (XBOX center button) + else if (buttonData->type == GIP_CMD_VIRTUAL_KEY) // Mode button (XBOX center button) { - m_rawInput.buttons[12] = input_bytes[4]; + m_rawInput.buttons[12] = buffer[4]; - if (input_bytes[1] == (GIP_OPT_ACK | GIP_OPT_INTERNAL)) + if (buffer[1] == (GIP_OPT_ACK | GIP_OPT_INTERNAL)) { - ControllerResult result = WriteAckModeReport(*input_idx, input_bytes[2]); + ControllerResult result = WriteAckModeReport(*input_idx, buffer[2]); if (result != CONTROLLER_STATUS_SUCCESS) return result; } diff --git a/source/ControllerLib/Controllers/XboxOneController.h b/source/ControllerLib/Controllers/XboxOneController.h index 9d5b1fa..dab05b3 100644 --- a/source/ControllerLib/Controllers/XboxOneController.h +++ b/source/ControllerLib/Controllers/XboxOneController.h @@ -54,7 +54,7 @@ class XboxOneController : public BaseController virtual ControllerResult Initialize() override; - virtual ControllerResult ReadRawInput(RawInputData *rawData, uint16_t *input_idx, uint32_t timeout_us) override; + virtual ControllerResult ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) override; bool Support(ControllerFeature feature) override; diff --git a/tests/test_controller_switch.cpp b/tests/test_controller_switch.cpp new file mode 100644 index 0000000..28fd7b4 --- /dev/null +++ b/tests/test_controller_switch.cpp @@ -0,0 +1,48 @@ +#include +#include "Controllers/SwitchController.h" + +/* --------------------------- Test setup --------------------------- */ + +class MockDevice : public IUSBDevice +{ +public: + ControllerResult Open() override { return CONTROLLER_STATUS_SUCCESS; } + void Close() override {} + void Reset() override {} +}; + +class MockLogger : public ILogger +{ +public: + void Log(LogLevel aLogLevel, const char *format, ::std::va_list vl) override {} + void LogBuffer(LogLevel aLogLevel, const uint8_t *buffer, size_t size) override {} +}; + +/* --------------------------- Tests --------------------------- */ + +TEST(Controller, test_switch_button1) +{ + ControllerConfig config; + RawInputData rawData; + uint16_t input_idx = 0; + SwitchController controller(std::make_unique(), config, std::make_unique()); + + uint8_t buffer[64] = {0x30, 0x0D, 0x91, 0x01, 0x80, 0x00, 0xB9, 0x77, 0x7D, 0xDB, 0xF7, 0x7B, 0x09, 0x00, 0x00, 0x00}; + EXPECT_EQ(controller.ParseData(buffer, sizeof(buffer), &rawData, &input_idx), CONTROLLER_STATUS_SUCCESS); + + EXPECT_TRUE(rawData.buttons[1]); +} + +TEST(Controller, test_switch_lstick_left) +{ + ControllerConfig config; + RawInputData rawData; + uint16_t input_idx = 0; + + SwitchController controller(std::make_unique(), config, std::make_unique()); + + uint8_t buffer[64] = {0x30, 0xFC, 0x91, 0x00, 0x80, 0x00, 0xC6, 0xB1, 0x72, 0xE5, 0xE7, 0x79, 0x03, 0x00, 0x00, 0x00}; + EXPECT_EQ(controller.ParseData(buffer, sizeof(buffer), &rawData, &input_idx), CONTROLLER_STATUS_SUCCESS); + + EXPECT_FLOAT_EQ(rawData.X, -1.0f); +} diff --git a/tests/test_input_binding.cpp b/tests/test_input_binding.cpp index 06b521f..5341bc2 100644 --- a/tests/test_input_binding.cpp +++ b/tests/test_input_binding.cpp @@ -6,9 +6,9 @@ class MockDevice : public IUSBDevice { public: - MOCK_METHOD(ControllerResult, Open, (), (override)); - MOCK_METHOD(void, Close, (), (override)); - MOCK_METHOD(void, Reset, (), (override)); + ControllerResult Open() override { return CONTROLLER_STATUS_SUCCESS; } + void Close() override {} + void Reset() override {} }; class MockLogger : public ILogger @@ -22,33 +22,16 @@ class MockBaseController : public BaseController { public: MockBaseController(std::unique_ptr &&device, const ControllerConfig &config, std::unique_ptr &&logger) : BaseController(std::move(device), config, std::move(logger)) {} - virtual ~MockBaseController() override {} - - MOCK_METHOD(ControllerResult, ReadRawInput, (RawInputData * rawData, uint16_t *input_idx, uint32_t timeout_us), (override)); + ControllerResult ParseData(uint8_t *buffer, size_t size, RawInputData *rawData, uint16_t *input_idx) override { return CONTROLLER_STATUS_SUCCESS; } + using BaseController::MapRawInputToNormalized; // Move protected method to public for testing }; -NormalizedButtonData TestReadInput(ControllerConfig config, RawInputData inputData) -{ - uint16_t input_idx; - NormalizedButtonData normalizedData = {0}; - - MockBaseController controller(std::make_unique(), config, std::make_unique()); - - EXPECT_CALL(controller, ReadRawInput(::testing::_, ::testing::_, ::testing::_)) - .WillOnce(::testing::DoAll( - ::testing::SetArgPointee<0>(inputData), // Set the first argument (rawData) to expectedData - ::testing::SetArgPointee<1>(0), // Set the second argument (input_idx) to 0 - ::testing::Return(ControllerResult::CONTROLLER_STATUS_SUCCESS))); - - controller.ReadInput(&normalizedData, &input_idx, 0); - - return normalizedData; -} - /* --------------------------- Tests --------------------------- */ TEST(BaseController, test_input_binding_basis) { + NormalizedButtonData normalizedData = {0}; + ControllerConfig config; config.buttons_pin[ControllerButton::X][0] = 1; config.buttons_pin[ControllerButton::Y][0] = 2; @@ -65,7 +48,8 @@ TEST(BaseController, test_input_binding_basis) inputData.X = 0.5f; inputData.Y = -0.5f; - NormalizedButtonData normalizedData = TestReadInput(config, inputData); + MockBaseController controller(std::make_unique(), config, std::make_unique()); + controller.MapRawInputToNormalized(inputData, &normalizedData); EXPECT_TRUE(normalizedData.buttons[ControllerButton::X]); EXPECT_FALSE(normalizedData.buttons[ControllerButton::Y]); @@ -77,6 +61,8 @@ TEST(BaseController, test_input_binding_basis) TEST(BaseController, test_input_deadzone) { + NormalizedButtonData normalizedData = {0}; + ControllerConfig config; config.stickDeadzonePercent[0] = 10; config.stickDeadzonePercent[1] = 5; @@ -85,7 +71,8 @@ TEST(BaseController, test_input_deadzone) inputData.X = 0.1f; inputData.Y = 0.1f; - NormalizedButtonData normalizedData = TestReadInput(config, inputData); + MockBaseController controller(std::make_unique(), config, std::make_unique()); + controller.MapRawInputToNormalized(inputData, &normalizedData); EXPECT_FLOAT_EQ(normalizedData.sticks[0].axis_x, 0.0f); EXPECT_FLOAT_EQ(normalizedData.sticks[0].axis_y, 0.0f); @@ -93,6 +80,8 @@ TEST(BaseController, test_input_deadzone) TEST(BaseController, test_input_simulate_buttons) { + NormalizedButtonData normalizedData = {0}; + ControllerConfig config; config.buttons_pin[ControllerButton::X][0] = 1; config.buttons_pin[ControllerButton::Y][0] = 2; @@ -109,7 +98,8 @@ TEST(BaseController, test_input_simulate_buttons) inputData.buttons[3] = true; inputData.buttons[4] = true; - NormalizedButtonData normalizedData = TestReadInput(config, inputData); + MockBaseController controller(std::make_unique(), config, std::make_unique()); + controller.MapRawInputToNormalized(inputData, &normalizedData); EXPECT_FALSE(normalizedData.buttons[ControllerButton::X]); EXPECT_FALSE(normalizedData.buttons[ControllerButton::A]); @@ -121,6 +111,8 @@ TEST(BaseController, test_input_simulate_buttons) TEST(BaseController, test_input_stick_by_buttons) { + NormalizedButtonData normalizedData = {0}; + ControllerConfig config; config.buttons_pin[ControllerButton::LSTICK_LEFT][0] = 1; config.buttons_pin[ControllerButton::LSTICK_DOWN][0] = 2; @@ -135,7 +127,8 @@ TEST(BaseController, test_input_stick_by_buttons) inputData.X = 0.0f; inputData.Y = 0.0f; - NormalizedButtonData normalizedData = TestReadInput(config, inputData); + MockBaseController controller(std::make_unique(), config, std::make_unique()); + controller.MapRawInputToNormalized(inputData, &normalizedData); EXPECT_TRUE(normalizedData.buttons[ControllerButton::LSTICK_LEFT]); EXPECT_TRUE(normalizedData.buttons[ControllerButton::LSTICK_DOWN]); @@ -151,13 +144,16 @@ TEST(BaseController, test_input_stick_by_buttons) TEST(BaseController, test_input_alias) { + NormalizedButtonData normalizedData = {0}; + ControllerConfig config; config.buttons_alias[ControllerButton::X] = ControllerButton::DPAD_UP; RawInputData inputData; inputData.dpad_up = true; - NormalizedButtonData normalizedData = TestReadInput(config, inputData); + MockBaseController controller(std::make_unique(), config, std::make_unique()); + controller.MapRawInputToNormalized(inputData, &normalizedData); EXPECT_TRUE(normalizedData.buttons[ControllerButton::X]); EXPECT_FALSE(normalizedData.buttons[ControllerButton::A]); @@ -170,6 +166,8 @@ TEST(BaseController, test_input_alias) TEST(BaseController, test_input_multiple_pin) { + NormalizedButtonData normalizedData = {0}; + ControllerConfig config; config.buttons_pin[ControllerButton::X][0] = 1; config.buttons_pin[ControllerButton::X][1] = 2; @@ -177,13 +175,16 @@ TEST(BaseController, test_input_multiple_pin) RawInputData inputData; inputData.buttons[2] = true; - NormalizedButtonData normalizedData = TestReadInput(config, inputData); + MockBaseController controller(std::make_unique(), config, std::make_unique()); + controller.MapRawInputToNormalized(inputData, &normalizedData); EXPECT_TRUE(normalizedData.buttons[ControllerButton::X]); } TEST(BaseController, test_input_complex_combination) { + NormalizedButtonData normalizedData = {0}; + ControllerConfig config; config.stickActivationThreshold = 10; config.buttons_pin[ControllerButton::L][0] = 1; @@ -216,7 +217,8 @@ TEST(BaseController, test_input_complex_combination) // LSTICK_LEFT will enable Y // X+Y will simulate HOME - NormalizedButtonData normalizedData = TestReadInput(config, inputData); + MockBaseController controller(std::make_unique(), config, std::make_unique()); + controller.MapRawInputToNormalized(inputData, &normalizedData); EXPECT_FALSE(normalizedData.buttons[ControllerButton::X]); EXPECT_FALSE(normalizedData.buttons[ControllerButton::Y]); diff --git a/tests/test_normalize.cpp b/tests/test_normalize.cpp index 338a009..b81d440 100644 --- a/tests/test_normalize.cpp +++ b/tests/test_normalize.cpp @@ -17,3 +17,12 @@ TEST(BaseController, test_normalize) EXPECT_NEAR(BaseController::Normalize(109, 0, 255), -0.14f, 0.01f); EXPECT_FLOAT_EQ(BaseController::Normalize(0, 0, 255), -1.0f); } + +TEST(BaseController, test_normalize_center) +{ + EXPECT_FLOAT_EQ(BaseController::Normalize(2000, 400, 3500, 2000), 0.0f); + EXPECT_FLOAT_EQ(BaseController::Normalize(400, 400, 3500, 2000), -1.0f); + EXPECT_FLOAT_EQ(BaseController::Normalize(3500, 400, 3500, 2000), 1.0f); + EXPECT_FLOAT_EQ(BaseController::Normalize(1200, 400, 3500, 2000), -0.5f); + EXPECT_FLOAT_EQ(BaseController::Normalize(2750, 400, 3500, 2000), 0.5f); +}