#pragma once #include "Emu/Io/PadHandler.h" #include "Utilities/Thread.h" #include "Utilities/CRC.h" #include "hidapi.h" #include #include const u32 MAX_GAMEPADS = 7; class ds4_pad_handler final : public PadHandlerBase { enum DS4CalibIndex { // gyro PITCH = 0, YAW, ROLL, // accel X, Y, Z, COUNT }; struct DS4CalibData { s16 bias; s32 sensNumer; s32 sensDenom; }; struct DS4Device { hid_device* hidDevice{ nullptr }; std::string path{ "" }; bool btCon{ false }; std::array calibData; bool newVibrateData{ true }; u8 largeVibrate{ 0 }; u8 smallVibrate{ 0 }; std::array padData; }; const u16 DS4_VID = 0x054C; // pid's of connected ds4 const std::array ds4Pids = { { 0xBA0, 0x5C4, 0x09CC } }; // pseudo 'controller id' to keep track of unique controllers std::unordered_map> controllers; CRCPP::CRC::Table crcTable{ CRCPP::CRC::CRC_32() }; public: ds4_pad_handler(); ~ds4_pad_handler(); bool Init() override; std::vector ListDevices() override; bool bindPadToDevice(std::shared_ptr pad, const std::string& device) override; void ThreadProc() override; private: bool is_init; // holds internal controller state change std::array last_connection_status = {}; std::vector, std::shared_ptr>> bindings; private: void ProcessData(); void UpdateRumble(); bool GetCalibrationData(std::shared_ptr ds4Device); void CheckAddDevice(hid_device* hidDevice, hid_device_info* hidDevInfo); void SendVibrateData(const std::shared_ptr device); inline s16 ApplyCalibration(s32 rawValue, const DS4CalibData& calibData) { const s32 biased = rawValue - calibData.bias; const s32 quot = calibData.sensNumer / calibData.sensDenom; const s32 rem = calibData.sensNumer % calibData.sensDenom; const s32 output = (quot * biased) + ((rem * biased) / calibData.sensDenom); if (output > std::numeric_limits::max()) return std::numeric_limits::max(); else if (output < std::numeric_limits::min()) return std::numeric_limits::min(); else return static_cast(output); } };