当前位置: 首页 > news >正文

XInput手柄输入封装

功能全面地封装了XInput的输入,

1. 普通按钮按下, 按住, 弹起状态检查,

2. 摇杆4个方向的按下, 按住, 弹起检查

3. 按键状态变化检测并且记录按下触发时间, 按住保持时间, 方便用来完全自定义的输入功能

4. 多手柄输入合并

CXinputHelper.h

#pragma once
#include <windows.h>
#include <xinput.h>
#include <tchar.h>
#include <thread>
#include <functional>
#include <string>
#include <map>
#pragma comment(lib, "xinput.lib")#define PI                                  (3.141592653589793f)
#define ANGLE_RANGE                         (22.5f)#define XINPUT_GAMEPAD_GUIDE                (0x00000400)
#define XINPUT_GAMEPAD_SHARE                (0x00000800)
#define XINPUT_GAMEPAD_LEFT_TRIGGER         (0x00010000)
#define XINPUT_GAMEPAD_RIGHT_TRIGGER        (0x00020000)
#define XINPUT_GAMEPAD_LEFT_STICK           (0x00040000)
#define XINPUT_GAMEPAD_RIGHT_STICK          (0x00080000)#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif// 手柄按键状态位
typedef union _GAME_PAD_BUTTON_MASK
{DWORD    dwButtons;                 // 标准按钮键值// 按钮比特位struct _Flags{DWORD    DpadUp : 1;            // 十字键 上DWORD    DpadDown : 1;          // 十字键 下DWORD    DpadLeft : 1;          // 十字键 左DWORD    DpadRight : 1;         // 十字键 右DWORD    Start : 1;             // 菜单键(开始键)DWORD    Back : 1;              // 返回键(选择键)DWORD    LeftThumb : 1;         // 左摇杆键DWORD    RightThumb : 1;        // 右摇杆键DWORD    LeftShoulder : 1;      // 左肩键DWORD    RightShoulder : 1;     // 右肩键DWORD    Guide : 1;             // 西瓜键(导航键)DWORD    Share : 1;             // 分享键(假设, 未验证)DWORD    A : 1;                 // ADWORD    B : 1;                 // BDWORD    X : 1;                 // XDWORD    Y : 1;                 // Y// 根据摇杆方向假设的摇杆方向键DWORD    LeftStickUp : 1;       // 左摇杆 上DWORD    LeftStickDown : 1;     // 左摇杆 下DWORD    LeftStickLeft : 1;     // 左摇杆 左DWORD    LeftStickRight : 1;    // 左摇杆 右DWORD    RightStickUp : 1;      // 右摇杆 上DWORD    RightStickDown : 1;    // 右摇杆 下DWORD    RightStickLeft : 1;    // 右摇杆 左DWORD    RightStickRight : 1;   // 右摇杆 右DWORD    LeftTrigger : 1;       // 左扳机DWORD    RightTrigger : 1;      // 右扳机}Mask;}GAME_PAD_BUTTON_MASK;// 摇杆方向状态位
typedef union _GAME_PAD_BUTTON_STATE
{uint8_t    State;                   // 状态// 按钮比特位struct _Flags{uint8_t    Down : 1;            // 按下uint8_t    Hold : 1;            // 按住uint8_t    Up : 1;              // 弹起}Mask;
}GAME_PAD_BUTTON_STATE;typedef union _GAME_PAD_STICK_DIRECTION_STATE
{uint32_t    State;                  // 状态// 按钮比特位struct _Flags{uint32_t    Up : 1;             // 摇杆 上uint32_t    Down : 1;           // 摇杆 下uint32_t    Left : 1;           // 摇杆 左uint32_t    Right : 1;          // 摇杆 右}Mask;
}GAME_PAD_STICK_DIRECTION_STATE;// 手柄按键状态位
typedef struct _GAME_PAD_BUTTON_INFO
{uint8_t Value;                      // 按键值GAME_PAD_BUTTON_STATE Flag;         // 按键标志DWORD dwTime;                       // 触发时间DWORD dwDuration;                   // 持续时长
}GAME_PAD_BUTTON_INFO;// 摇杆方向
typedef struct _GAME_PAD_STICK_DIRECTION
{GAME_PAD_STICK_DIRECTION_STATE Direction;         // 当前方向标志GAME_PAD_BUTTON_INFO Up;GAME_PAD_BUTTON_INFO Down;GAME_PAD_BUTTON_INFO Left;GAME_PAD_BUTTON_INFO Right;
}GAME_PAD_STICK_DIRECTION;// 输入事件状态
typedef struct _INPUT_EVENT_STATE
{DWORD dwIndex;                          // 手柄索引DWORD dwButtonMask;                     // 按钮标识位掩码XINPUT_STATE xinputState;               // 手柄输入DWORD dwTime;                           // 触发时间DWORD dwDuration;                       // 触发时长GAME_PAD_BUTTON_STATE curButtonFlag;        // 触发状态(按下, 按住, 弹起)GAME_PAD_STICK_DIRECTION curLeftStickDirection;       // 左摇杆方向触发状态GAME_PAD_STICK_DIRECTION curRightStickDirection;      // 右摇杆方向触发状态GAME_PAD_STICK_DIRECTION lastLeftStickDirection;      // 上次左摇杆方向触发状态GAME_PAD_STICK_DIRECTION lastRightStickDirection;     // 上次右摇杆方向触发状态GAME_PAD_STICK_DIRECTION stickDirection;    // 摇杆方向触发状态GAME_PAD_BUTTON_MASK buttonBitMask;         // 按键位状态DWORD dwButtons;                            // 触发按键SHORT sThumbX;                              // 当前摇杆X值SHORT sThumbY;                              // 当前摇杆Y值BYTE bTrigger;                              // 扳机数据BYTE fRight;_INPUT_EVENT_STATE(){memset(this, 0, sizeof(*this));}}INPUT_EVENT_STATE;// 输入事件变化回调
class CXInputHelper;
typedef bool (CXInputHelper::*pInputEventFn)(const INPUT_EVENT_STATE& state);typedef struct _INPUT_EVENT_INFO
{pInputEventFn eventCb;              // 输入事件变化回调函数INPUT_EVENT_STATE eventState;        // 输入事件变化状态_INPUT_EVENT_INFO(pInputEventFn cb):eventCb(cb){}}INPUT_EVENT_INFO;class CXInputHelper
{
public:CXInputHelper();virtual ~CXInputHelper();bool Initialize();void Uninitialize();// 打印状态信息virtual void PrintState(const INPUT_EVENT_STATE& state);// 打印void PrintText(LPCTSTR pFormat, ...);void IgnoreLeftThumbEmpty(bool bEnable = true                     // 允许空数据触发回调);void IgnoreRightThumbEmpty(bool bEnable = true                     // 允许空数据触发回调);void SetInterval(int millisecond = 10                    // 数据采集间隔时间(毫秒));void SetThumbThreshold(int nLeftThumbThreshold = 4096,         // 左摇杆触发阈值(死区)int nRightThumbThreshold = 4096         // 右摇杆触发阈值(死区));// 设置检测每个控制器输入数据回调void SetRawStateCallback(std::function<void(DWORD dwIndex,                      // 控制器索引(有效值: 0 - 3)const XINPUT_GAMEPAD& xGamepad      // 控制器数据)> cb);// 设置合并多个控制器输入数据回调void SetMergeStateCallback(std::function<void(const XINPUT_GAMEPAD& xGamepad      // 控制器数据)> cb);// 设置检测每个控制器输入状态变化回调(发生按钮按下, 释放, 按住时触发)void SetRawChangeCallback(std::function<void(const INPUT_EVENT_STATE& state      // 回调信息)> cb);// 设置合并多个控制器输入状态变化回调(发生按钮按下, 释放, 按住时触发)void SetMergeChangeCallback(std::function<void(const INPUT_EVENT_STATE& state      // 回调信息)> cb);// 输入变化virtual bool OnChangeDpadUp(const INPUT_EVENT_STATE& state);virtual bool OnChangeDpadDown(const INPUT_EVENT_STATE& state);virtual bool OnChangeDpadLeft(const INPUT_EVENT_STATE& state);virtual bool OnChangeDpadRight(const INPUT_EVENT_STATE& state);virtual bool OnChangeBack(const INPUT_EVENT_STATE& state);virtual bool OnChangeStart(const INPUT_EVENT_STATE& state);virtual bool OnChangeGuide(const INPUT_EVENT_STATE& state);virtual bool OnChangeShare(const INPUT_EVENT_STATE& state);virtual bool OnChangeA(const INPUT_EVENT_STATE& state);virtual bool OnChangeB(const INPUT_EVENT_STATE& state);virtual bool OnChangeX(const INPUT_EVENT_STATE& state);virtual bool OnChangeY(const INPUT_EVENT_STATE& state);virtual bool OnChangeLeftShoulder(const INPUT_EVENT_STATE& state);virtual bool OnChangeRightShoulder(const INPUT_EVENT_STATE& state);virtual bool OnChangeLeftTrigger(const INPUT_EVENT_STATE& state);virtual bool OnChangeRightTrigger(const INPUT_EVENT_STATE& state);virtual bool OnChangeLeftStick(const INPUT_EVENT_STATE& state);virtual bool OnChangeRightStick(const INPUT_EVENT_STATE& state);virtual bool OnChangeLeftThumb(const INPUT_EVENT_STATE& state);virtual bool OnChangeRightThumb(const INPUT_EVENT_STATE& state);// 合并输入变化virtual bool OnMergeChangeDpadUp(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeDpadDown(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeDpadLeft(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeDpadRight(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeBack(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeStart(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeGuide(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeShare(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeA(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeB(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeX(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeY(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeLeftShoulder(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeRightShoulder(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeLeftTrigger(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeRightTrigger(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeLeftStick(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeRightStick(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeLeftThumb(const INPUT_EVENT_STATE& state);virtual bool OnMergeChangeRightThumb(const INPUT_EVENT_STATE& state);private:// 任务循环void _GamepadLoop();// 睡眠间隔void _Sleep(int millisecond);// 更新扳机状态void _UpdateTriggerState(INPUT_EVENT_STATE& state, BYTE bCurTrigger, BYTE bLastTrigger);// 更新摇杆按钮void _UpdateStickButton(GAME_PAD_BUTTON_INFO& curStick, GAME_PAD_BUTTON_INFO& lastStick);// 更新摇杆方向状态void _UpdateStickDirection(INPUT_EVENT_STATE& state, GAME_PAD_STICK_DIRECTION& curStick, GAME_PAD_STICK_DIRECTION& lastStick, SHORT sCurX, SHORT sCurY);// 按钮状态变化处理void _UpateButtonState(INPUT_EVENT_STATE& state, const XINPUT_GAMEPAD& cur, const XINPUT_GAMEPAD& last, DWORD wButton);// 状态变化输入处理void _InputChangeProcess(DWORD dwIndex);// 混合输入处理void _InputMergeProcess();// 混合输入变化处理void _InputMergeChangeProcess();private:std::function<void(DWORD dwIndex, const XINPUT_GAMEPAD& xGamepad)> m_cbRawState;                        // 检测每个控制器输入数据回调std::function<void(const XINPUT_GAMEPAD& xGamepad)> m_cbMergeState;                                     // 合并多个控制器输入数据回调std::function<void(const INPUT_EVENT_STATE& state)> m_cbRawChange;      // 检测每个控制器输入状态变化回调(发生按钮按下, 释放, 按住时触发)std::function<void(const INPUT_EVENT_STATE& state)> m_cbMergeChange;    // 合并多个控制器输入状态变化回调(发生按钮按下, 释放, 按住时触发)std::map<DWORD, INPUT_EVENT_INFO> m_buttonEvents[XUSER_MAX_COUNT];std::map<DWORD, INPUT_EVENT_INFO> m_mergeEvents;std::thread m_gamepadTask;          // 控制器状态检测任务线程int m_LeftThumbThreshold;           // 左摇杆触发阈值int m_RightThumbThreshold;          // 右摇杆触发阈值int m_DataInterval;                 // 采集间隔时间bool m_bIgnoreLeftThumbEmpty;       // 左摇杆 X,Y 轴数据均为 0 时也触发回调bool m_bIgnoreRightThumbEmpty;      // 右摇杆 X,Y 轴数据均为 0 时也触发回调bool m_fQuit;                       // 退出标志public:XINPUT_STATE m_CurXInputState[XUSER_MAX_COUNT];                 // 当前控制器输入状态XINPUT_STATE m_LastXInputState[XUSER_MAX_COUNT];                // 上次控制器输入状态XINPUT_STATE m_CurXInputMerge;                                  // 当前全部控制器混合输入状态XINPUT_STATE m_LastXInputMerge;                                 // 当前全部控制器混合输入状态GAME_PAD_BUTTON_MASK m_CurButtonState[XUSER_MAX_COUNT];GAME_PAD_BUTTON_MASK m_CurButtonMerge;
};

CXinputHelper.cpp

#include "CXInputHelper.h"
#include <stdint.h>
#include <Winerror.h>
#include <map>
#include <math.h>
//#pragma comment(lib, "Winmm.lib")void CXInputHelper::_Sleep(int millisecond)
{//timeBeginPeriod(1);do{const int span = 1;if (millisecond < span){std::this_thread::sleep_for(std::chrono::milliseconds(millisecond));return;}clock_t tmBegin = clock();while (true){if (clock() - tmBegin > millisecond){break;}if (m_fQuit){break;}std::this_thread::sleep_for(std::chrono::milliseconds(span));}} while (false);//timeEndPeriod(1);
}void CXInputHelper::PrintText(LPCTSTR pFormat, ...)
{size_t nCchCount = MAX_PATH;_tstring strResult(nCchCount, 0);va_list args;va_start(args, pFormat);do{//格式化输出字符串int nSize = _vsntprintf_s(&strResult[0], nCchCount, _TRUNCATE, pFormat, args);if (-1 != nSize){HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);::WriteConsole(console, strResult.c_str(), nSize, NULL, NULL);break;}//缓冲大小超限终止if (nCchCount >= INT32_MAX){break;}//重新分配缓冲nCchCount *= 2;strResult.resize(nCchCount);} while (true);va_end(args);
}CXInputHelper::CXInputHelper():m_bIgnoreLeftThumbEmpty(true),m_bIgnoreRightThumbEmpty(true),m_fQuit(false),m_LeftThumbThreshold(0),m_RightThumbThreshold(0),m_DataInterval(10)
{memset(&m_CurXInputState, 0, sizeof(m_CurXInputState));memset(&m_LastXInputState, 0, sizeof(m_LastXInputState));memset(&m_CurXInputMerge, 0, sizeof(m_CurXInputMerge));memset(&m_LastXInputMerge, 0, sizeof(m_LastXInputMerge));memset(&m_CurButtonState, 0, sizeof(m_CurButtonState));memset(&m_CurButtonMerge, 0, sizeof(m_CurButtonMerge));}CXInputHelper::~CXInputHelper()
{Uninitialize();
}bool CXInputHelper::Initialize()
{Uninitialize();m_gamepadTask = std::move(std::thread([this]() {_GamepadLoop();}));return true;
}void CXInputHelper::Uninitialize()
{if (m_gamepadTask.joinable()){m_fQuit = true;m_gamepadTask.join();}m_fQuit = false;
}void CXInputHelper::IgnoreLeftThumbEmpty(bool bEnable
)
{m_bIgnoreLeftThumbEmpty = bEnable;
}void CXInputHelper::IgnoreRightThumbEmpty(bool bEnable
)
{m_bIgnoreRightThumbEmpty = bEnable;
}void CXInputHelper::SetInterval(int millisecond
)
{m_DataInterval = millisecond;
}void CXInputHelper::SetThumbThreshold(int nLeftThumbThreshold/* = 4096*/,int nRightThumbThreshold/* = 4096*/
)
{m_LeftThumbThreshold = nLeftThumbThreshold;m_RightThumbThreshold = nRightThumbThreshold;
}void CXInputHelper::SetRawStateCallback(std::function<void(DWORD dwIndex,const XINPUT_GAMEPAD& xGamepad)> cb
)
{m_cbRawState = cb;
}void CXInputHelper::SetMergeStateCallback(std::function<void(const XINPUT_GAMEPAD& xGamepad)> cb
)
{m_cbMergeState = cb;
}void CXInputHelper::SetRawChangeCallback(std::function<void(const INPUT_EVENT_STATE& state)> cb
)
{m_cbRawChange = cb;
}void CXInputHelper::SetMergeChangeCallback(std::function<void(const INPUT_EVENT_STATE& state)> cb
)
{m_cbMergeChange = cb;
}// 输入变化
bool CXInputHelper::OnChangeDpadUp(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeDpadDown(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeDpadLeft(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeDpadRight(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeBack(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeStart(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeGuide(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeShare(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeA(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeB(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeX(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeY(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeLeftShoulder(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeRightShoulder(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeLeftTrigger(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeRightTrigger(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeLeftStick(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeRightStick(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeLeftThumb(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnChangeRightThumb(const INPUT_EVENT_STATE& state)
{return true;
}// 合并输入变化
bool CXInputHelper::OnMergeChangeDpadUp(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeDpadDown(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeDpadLeft(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeDpadRight(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeBack(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeStart(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeGuide(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeShare(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeA(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeB(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeX(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeY(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeLeftShoulder(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeRightShoulder(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeLeftTrigger(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeRightTrigger(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeLeftStick(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeRightStick(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeLeftThumb(const INPUT_EVENT_STATE& state)
{return true;
}bool CXInputHelper::OnMergeChangeRightThumb(const INPUT_EVENT_STATE& state)
{return true;
}void CXInputHelper::PrintState(const INPUT_EVENT_STATE& state)
{PrintText(_T("OnRightThumb Index: %d bRight: %d Trigger: %3d %6d,%6d Button: %04X Press: %d Release: %d Holding: %d Time:%08d, Duration: %08d\r\n"),state.dwIndex, state.fRight, state.bTrigger, state.sThumbX, state.sThumbY, state.dwButtons, state.curButtonFlag.Mask.Down, state.curButtonFlag.Mask.Up, state.curButtonFlag.Mask.Hold,state.dwTime,state.dwDuration);
}void CXInputHelper::_GamepadLoop()
{m_buttonEvents[0] = {{XINPUT_GAMEPAD_A,                          INPUT_EVENT_INFO(&CXInputHelper::OnChangeA)},{XINPUT_GAMEPAD_B,                          INPUT_EVENT_INFO(&CXInputHelper::OnChangeB)},{XINPUT_GAMEPAD_X,                          INPUT_EVENT_INFO(&CXInputHelper::OnChangeX)},{XINPUT_GAMEPAD_Y,                          INPUT_EVENT_INFO(&CXInputHelper::OnChangeY)},{XINPUT_GAMEPAD_BACK,                       INPUT_EVENT_INFO(&CXInputHelper::OnChangeBack)},{XINPUT_GAMEPAD_START,                      INPUT_EVENT_INFO(&CXInputHelper::OnChangeStart)},{XINPUT_GAMEPAD_GUIDE,                      INPUT_EVENT_INFO(&CXInputHelper::OnChangeGuide)},{XINPUT_GAMEPAD_SHARE,                      INPUT_EVENT_INFO(&CXInputHelper::OnChangeShare)},{XINPUT_GAMEPAD_DPAD_UP,                    INPUT_EVENT_INFO(&CXInputHelper::OnChangeDpadUp)},{XINPUT_GAMEPAD_DPAD_DOWN,                  INPUT_EVENT_INFO(&CXInputHelper::OnChangeDpadDown)},{XINPUT_GAMEPAD_DPAD_LEFT,                  INPUT_EVENT_INFO(&CXInputHelper::OnChangeDpadLeft)},{XINPUT_GAMEPAD_DPAD_RIGHT,                 INPUT_EVENT_INFO(&CXInputHelper::OnChangeDpadRight)},{XINPUT_GAMEPAD_LEFT_SHOULDER,              INPUT_EVENT_INFO(&CXInputHelper::OnChangeLeftShoulder)},{XINPUT_GAMEPAD_RIGHT_SHOULDER,             INPUT_EVENT_INFO(&CXInputHelper::OnChangeRightShoulder)},{XINPUT_GAMEPAD_LEFT_THUMB,                 INPUT_EVENT_INFO(&CXInputHelper::OnChangeLeftThumb)},{XINPUT_GAMEPAD_RIGHT_THUMB,                INPUT_EVENT_INFO(&CXInputHelper::OnChangeRightThumb)},{XINPUT_GAMEPAD_LEFT_TRIGGER,               INPUT_EVENT_INFO(&CXInputHelper::OnChangeLeftTrigger)},{XINPUT_GAMEPAD_RIGHT_TRIGGER,              INPUT_EVENT_INFO(&CXInputHelper::OnChangeRightTrigger)},{XINPUT_GAMEPAD_LEFT_STICK,                 INPUT_EVENT_INFO(&CXInputHelper::OnChangeLeftStick)},{XINPUT_GAMEPAD_RIGHT_STICK,                INPUT_EVENT_INFO(&CXInputHelper::OnChangeRightStick)}};m_buttonEvents[1] = m_buttonEvents[0];m_buttonEvents[2] = m_buttonEvents[0];m_buttonEvents[3] = m_buttonEvents[0];m_mergeEvents = {{XINPUT_GAMEPAD_A,                          INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeA)},{XINPUT_GAMEPAD_B,                          INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeB)},{XINPUT_GAMEPAD_X,                          INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeX)},{XINPUT_GAMEPAD_Y,                          INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeY)},{XINPUT_GAMEPAD_BACK,                       INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeBack)},{XINPUT_GAMEPAD_START,                      INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeStart)},{XINPUT_GAMEPAD_GUIDE,                      INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeGuide)},{XINPUT_GAMEPAD_SHARE,                      INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeShare)},{XINPUT_GAMEPAD_DPAD_UP,                    INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeDpadUp)},{XINPUT_GAMEPAD_DPAD_DOWN,                  INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeDpadDown)},{XINPUT_GAMEPAD_DPAD_LEFT,                  INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeDpadLeft)},{XINPUT_GAMEPAD_DPAD_RIGHT,                 INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeDpadRight)},{XINPUT_GAMEPAD_LEFT_SHOULDER,              INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeLeftShoulder)},{XINPUT_GAMEPAD_RIGHT_SHOULDER,             INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeRightShoulder)},{XINPUT_GAMEPAD_LEFT_THUMB,                 INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeLeftThumb)},{XINPUT_GAMEPAD_RIGHT_THUMB,                INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeRightThumb)},{XINPUT_GAMEPAD_LEFT_TRIGGER,               INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeLeftTrigger)},{XINPUT_GAMEPAD_RIGHT_TRIGGER,              INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeRightTrigger)},{XINPUT_GAMEPAD_LEFT_STICK,                 INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeLeftStick)},{XINPUT_GAMEPAD_RIGHT_STICK,                INPUT_EVENT_INFO(&CXInputHelper::OnMergeChangeRightStick)}};// 加载 控制器状态检测 函数HMODULE hModule = LoadLibrary(_T("XInput1_4.dll"));if (NULL == hModule) hModule = LoadLibrary(_T("xinput1_3.dll"));if (NULL == hModule) return;typedef int(__stdcall* _XInputGetStateEx)(DWORD dwUserIndex, XINPUT_STATE* pState);_XInputGetStateEx XInputGetStateEx = (_XInputGetStateEx)::GetProcAddress(hModule, (LPCSTR)100);if (nullptr == XInputGetStateEx){XInputGetStateEx = (_XInputGetStateEx)::GetProcAddress(hModule, "XInputGetState");}clock_t tmCount = ::clock();while (!m_fQuit && XInputGetStateEx){// 遍历控制器输入for (DWORD i = 0; i < XUSER_MAX_COUNT; ++i){XINPUT_STATE xTmpState = { 0 };DWORD dwResult = XInputGetStateEx(i, &xTmpState);// 摇杆触发值限定if (abs(xTmpState.Gamepad.sThumbLX) < m_LeftThumbThreshold) xTmpState.Gamepad.sThumbLX = 0;if (abs(xTmpState.Gamepad.sThumbLY) < m_LeftThumbThreshold) xTmpState.Gamepad.sThumbLY = 0;if (abs(xTmpState.Gamepad.sThumbRX) < m_RightThumbThreshold) xTmpState.Gamepad.sThumbRX = 0;if (abs(xTmpState.Gamepad.sThumbRY) < m_RightThumbThreshold) xTmpState.Gamepad.sThumbRY = 0;m_CurXInputState[i] = xTmpState;// 原始数据回调if (m_cbRawState){m_cbRawState(i, xTmpState.Gamepad);}// 未连接控制器if (ERROR_DEVICE_NOT_CONNECTED == dwResult){m_LastXInputState[i] = m_CurXInputState[i];continue;}// 单控制器输入变化处理_InputChangeProcess(i);}// 混合输入处理_InputMergeProcess();// 混合输入变化处理_InputMergeChangeProcess();// 输入轮询间隔while (clock() - tmCount < m_DataInterval){_Sleep(1);}tmCount = clock();}if (hModule){::FreeLibrary(hModule);}
}void CXInputHelper::_UpdateTriggerState(INPUT_EVENT_STATE& state, BYTE bCurTrigger, BYTE bLastTrigger)
{clock_t curTime = ::clock();if (0 == bLastTrigger && bCurTrigger){state.curButtonFlag.Mask.Down = true;state.dwTime = curTime;state.dwDuration = 0;}if (bLastTrigger && 0 == bCurTrigger){state.curButtonFlag.Mask.Up = true;state.dwDuration = curTime - state.dwTime;}if (bLastTrigger && bCurTrigger){state.curButtonFlag.Mask.Hold = true;state.dwDuration = curTime - state.dwTime;}
}void CXInputHelper::_UpdateStickButton(GAME_PAD_BUTTON_INFO& curStick, GAME_PAD_BUTTON_INFO& lastStick)
{clock_t curTime = ::clock();curStick.Flag.Mask.Down = false;curStick.Flag.Mask.Up = false;curStick.Flag.Mask.Hold = false;// 按下if (curStick.Value && !lastStick.Value){curStick.Flag.Mask.Down = true;curStick.dwTime = curTime;curStick.dwDuration = 0;}// 弹起if (!curStick.Value && lastStick.Value){curStick.Flag.Mask.Up = true;curStick.dwDuration = curTime - curStick.dwTime;}// 按住if (curStick.Value && lastStick.Value){curStick.Flag.Mask.Hold = true;curStick.dwDuration = curTime - curStick.dwTime;}// 无if (!curStick.Value && !lastStick.Value){curStick.dwTime = 0;curStick.dwDuration = 0;}
}void CXInputHelper::_UpdateStickDirection(INPUT_EVENT_STATE& state, GAME_PAD_STICK_DIRECTION& curStick, GAME_PAD_STICK_DIRECTION& lastStick, SHORT sCurX, SHORT sCurY)
{clock_t curTime = ::clock();curStick.Direction.State = 0;curStick.Up.Value = 0;curStick.Down.Value = 0;curStick.Left.Value = 0;curStick.Right.Value = 0;// 获取角度double fCurAngle = atan2(sCurY, sCurX) * 180.0f / PI;// 上if (sCurY > m_LeftThumbThreshold){if (fCurAngle >= (90.0f - ANGLE_RANGE) && fCurAngle <= (90.0f + ANGLE_RANGE)){curStick.Direction.Mask.Up = 1;curStick.Up.Value = 1;}}// 下if (sCurY < -m_LeftThumbThreshold){if (fCurAngle >= -(90.0f + ANGLE_RANGE) && fCurAngle <= -(90.0f - ANGLE_RANGE)){curStick.Direction.Mask.Down = 1;curStick.Down.Value = 1;}}// 左if (sCurX < -m_LeftThumbThreshold){if (fCurAngle >= (180.0f - ANGLE_RANGE) || fCurAngle <= -(180.0f - ANGLE_RANGE)){curStick.Left.Value = 1;curStick.Direction.Mask.Left = 1;}}// 右if (sCurX > m_LeftThumbThreshold){if (fCurAngle >= -ANGLE_RANGE && fCurAngle <= ANGLE_RANGE){curStick.Direction.Mask.Right = 1;curStick.Right.Value = 1;}}// 右上if (sCurX > m_LeftThumbThreshold && sCurY > m_LeftThumbThreshold){if (fCurAngle > (45.0f - ANGLE_RANGE) && fCurAngle < (45.0f + ANGLE_RANGE)){curStick.Direction.Mask.Right = 1;curStick.Direction.Mask.Up = 1;curStick.Right.Value = 1;curStick.Up.Value = 1;}}// 左上if (sCurX < -m_LeftThumbThreshold && sCurY > m_LeftThumbThreshold){if (fCurAngle > (135.0f - ANGLE_RANGE) && fCurAngle < (135.0f + ANGLE_RANGE)){curStick.Direction.Mask.Left = 1;curStick.Direction.Mask.Up = 1;curStick.Left.Value = 1;curStick.Up.Value = 1;}}// 左下if (sCurY < -m_LeftThumbThreshold && sCurX < -m_LeftThumbThreshold){if (fCurAngle > -(135.0f + ANGLE_RANGE) && fCurAngle < -(135.0f - ANGLE_RANGE)){curStick.Direction.Mask.Left = 1;curStick.Direction.Mask.Down = 1;curStick.Left.Value = 1;curStick.Down.Value = 1;}}// 右下if (sCurY < -m_LeftThumbThreshold && sCurX > m_LeftThumbThreshold){if (fCurAngle > -(45.0f + ANGLE_RANGE) && fCurAngle < -(45.0f - ANGLE_RANGE)){curStick.Direction.Mask.Right = 1;curStick.Direction.Mask.Down = 1;curStick.Right.Value = 1;curStick.Down.Value = 1;}}// 按下if (!lastStick.Direction.State && curStick.Direction.State){state.curButtonFlag.Mask.Down = true;state.dwTime = curTime;state.dwDuration = 0;}// 弹起if (lastStick.Direction.State && !curStick.Direction.State){state.curButtonFlag.Mask.Up = true;state.dwDuration = curTime - state.dwTime;}// 按住if (lastStick.Direction.State && curStick.Direction.State){state.curButtonFlag.Mask.Hold = true;state.dwDuration = curTime - state.dwTime;}// 状态转换_UpdateStickButton(curStick.Up, lastStick.Up);_UpdateStickButton(curStick.Down, lastStick.Down);_UpdateStickButton(curStick.Left, lastStick.Left);_UpdateStickButton(curStick.Right, lastStick.Right);
}void CXInputHelper::_UpateButtonState(INPUT_EVENT_STATE& state, const XINPUT_GAMEPAD& cur, const XINPUT_GAMEPAD& last, DWORD wButton) {state.curButtonFlag.Mask.Down = false;state.curButtonFlag.Mask.Up = false;state.curButtonFlag.Mask.Hold = false;// 左扳机if (XINPUT_GAMEPAD_LEFT_TRIGGER == wButton){_UpdateTriggerState(state, cur.bLeftTrigger, last.bLeftTrigger);}// 右扳机else if (XINPUT_GAMEPAD_RIGHT_TRIGGER == wButton){_UpdateTriggerState(state, cur.bRightTrigger, last.bRightTrigger);}// 左摇杆else if (XINPUT_GAMEPAD_LEFT_STICK == wButton){_UpdateStickDirection(state, state.curLeftStickDirection, state.lastLeftStickDirection, cur.sThumbLX, cur.sThumbLY);state.buttonBitMask.Mask.LeftStickUp = state.curLeftStickDirection.Up.Value;state.buttonBitMask.Mask.LeftStickDown = state.curLeftStickDirection.Down.Value;state.buttonBitMask.Mask.LeftStickLeft = state.curLeftStickDirection.Left.Value;state.buttonBitMask.Mask.LeftStickRight = state.curLeftStickDirection.Right.Value;state.lastLeftStickDirection = state.curLeftStickDirection;}// 右摇杆else if (XINPUT_GAMEPAD_RIGHT_STICK == wButton){_UpdateStickDirection(state, state.curRightStickDirection, state.lastRightStickDirection, cur.sThumbRX, cur.sThumbRY);state.buttonBitMask.Mask.RightStickUp = state.curRightStickDirection.Up.Value;state.buttonBitMask.Mask.RightStickDown = state.curRightStickDirection.Down.Value;state.buttonBitMask.Mask.RightStickLeft = state.curRightStickDirection.Left.Value;state.buttonBitMask.Mask.RightStickRight = state.curRightStickDirection.Right.Value;state.lastRightStickDirection = state.curRightStickDirection;}else{clock_t curTime = ::clock();// 按下if ((0 == (last.wButtons & wButton)) && (cur.wButtons & wButton)){state.curButtonFlag.Mask.Down = true;state.dwTime = curTime;state.dwDuration = 0;}// 弹起if ((last.wButtons & wButton) && (0 == (cur.wButtons & wButton))){state.curButtonFlag.Mask.Up = true;state.dwDuration = curTime - state.dwTime;}// 按住if ((last.wButtons & wButton) && (cur.wButtons & wButton)){state.curButtonFlag.Mask.Hold = true;state.dwDuration = curTime - state.dwTime;}}
}void CXInputHelper::_InputChangeProcess(DWORD dwIndex)
{// 状态处理for (auto& item : m_buttonEvents[dwIndex]){INPUT_EVENT_INFO& eventInfo = item.second;// 按钮状态变化处理_UpateButtonState(eventInfo.eventState, m_CurXInputState[dwIndex].Gamepad, m_LastXInputState[dwIndex].Gamepad, item.first);}// 得到全部输入状态m_CurButtonState[dwIndex].dwButtons = 0;for (auto& item : m_buttonEvents[dwIndex]){INPUT_EVENT_INFO& eventInfo = item.second;if (XINPUT_GAMEPAD_LEFT_TRIGGER == item.first){m_CurButtonState[dwIndex].Mask.LeftTrigger = eventInfo.eventState.curButtonFlag.Mask.Down || eventInfo.eventState.curButtonFlag.Mask.Hold;}else if (XINPUT_GAMEPAD_RIGHT_TRIGGER == item.first){eventInfo.eventState.fRight = true;m_CurButtonState[dwIndex].Mask.RightTrigger = eventInfo.eventState.curButtonFlag.Mask.Down || eventInfo.eventState.curButtonFlag.Mask.Hold;}else if (XINPUT_GAMEPAD_LEFT_STICK == item.first){m_CurButtonState[dwIndex].Mask.LeftStickUp = eventInfo.eventState.buttonBitMask.Mask.LeftStickUp;m_CurButtonState[dwIndex].Mask.LeftStickDown = eventInfo.eventState.buttonBitMask.Mask.LeftStickDown;m_CurButtonState[dwIndex].Mask.LeftStickLeft = eventInfo.eventState.buttonBitMask.Mask.LeftStickLeft;m_CurButtonState[dwIndex].Mask.LeftStickRight = eventInfo.eventState.buttonBitMask.Mask.LeftStickRight;}else if (XINPUT_GAMEPAD_RIGHT_STICK == item.first){eventInfo.eventState.fRight = true;m_CurButtonState[dwIndex].Mask.RightStickUp = eventInfo.eventState.buttonBitMask.Mask.RightStickUp;m_CurButtonState[dwIndex].Mask.RightStickDown = eventInfo.eventState.buttonBitMask.Mask.RightStickDown;m_CurButtonState[dwIndex].Mask.RightStickLeft = eventInfo.eventState.buttonBitMask.Mask.RightStickLeft;m_CurButtonState[dwIndex].Mask.RightStickRight = eventInfo.eventState.buttonBitMask.Mask.RightStickRight;}else{m_CurButtonState[dwIndex].dwButtons |= (eventInfo.eventState.curButtonFlag.Mask.Down || eventInfo.eventState.curButtonFlag.Mask.Hold) ? item.first : 0;}}// 回调处理for (auto& item : m_buttonEvents[dwIndex]){INPUT_EVENT_INFO& eventInfo = item.second;eventInfo.eventState.dwButtonMask = item.first;eventInfo.eventState.dwIndex = dwIndex;eventInfo.eventState.buttonBitMask.dwButtons = m_CurButtonState[dwIndex].dwButtons;eventInfo.eventState.dwButtons = m_CurButtonState[dwIndex].dwButtons;if (eventInfo.eventState.curButtonFlag.State){eventInfo.eventState.xinputState = m_CurXInputState[dwIndex];if (XINPUT_GAMEPAD_LEFT_TRIGGER == item.first){eventInfo.eventState.bTrigger = m_CurXInputState[dwIndex].Gamepad.bLeftTrigger;}if (XINPUT_GAMEPAD_RIGHT_TRIGGER == item.first){eventInfo.eventState.fRight = true;eventInfo.eventState.bTrigger = m_CurXInputState[dwIndex].Gamepad.bRightTrigger;}if (XINPUT_GAMEPAD_LEFT_STICK == item.first){eventInfo.eventState.sThumbX = m_CurXInputState[dwIndex].Gamepad.sThumbLX;eventInfo.eventState.sThumbY = m_CurXInputState[dwIndex].Gamepad.sThumbLY;eventInfo.eventState.stickDirection = eventInfo.eventState.curLeftStickDirection;}if (XINPUT_GAMEPAD_RIGHT_STICK == item.first){eventInfo.eventState.fRight = true;eventInfo.eventState.sThumbX = m_CurXInputState[dwIndex].Gamepad.sThumbRX;eventInfo.eventState.sThumbY = m_CurXInputState[dwIndex].Gamepad.sThumbRY;eventInfo.eventState.stickDirection = eventInfo.eventState.curRightStickDirection;}(this->*(eventInfo.eventCb))(eventInfo.eventState);if (m_cbRawChange){m_cbRawChange(eventInfo.eventState);}}}m_LastXInputState[dwIndex] = m_CurXInputState[dwIndex];
}void CXInputHelper::_InputMergeProcess()
{LONG nThumbLX = 0;LONG nThumbLY = 0;LONG nThumbRX = 0;LONG nThumbRY = 0;LONG nLeftTrigger = 0;LONG nRightTrigger = 0;// 混合输入按钮状态m_CurXInputMerge.Gamepad.wButtons = 0;for (DWORD i = 0; i < XUSER_MAX_COUNT; ++i){m_CurXInputMerge.Gamepad.wButtons |= m_CurXInputState[i].Gamepad.wButtons;nThumbLX += m_CurXInputState[i].Gamepad.sThumbLX;nThumbLY += m_CurXInputState[i].Gamepad.sThumbLY;nThumbRX += m_CurXInputState[i].Gamepad.sThumbRX;nThumbRY += m_CurXInputState[i].Gamepad.sThumbRY;nLeftTrigger += m_CurXInputState[i].Gamepad.bLeftTrigger;nRightTrigger += m_CurXInputState[i].Gamepad.bRightTrigger;}if (nLeftTrigger > UINT8_MAX) nLeftTrigger = UINT8_MAX;if (nRightTrigger > UINT8_MAX) nRightTrigger = UINT8_MAX;if (nThumbLX < INT16_MIN) nThumbLX = INT16_MIN;if (nThumbLY < INT16_MIN) nThumbLY = INT16_MIN;if (nThumbRX < INT16_MIN) nThumbRX = INT16_MIN;if (nThumbRY < INT16_MIN) nThumbRY = INT16_MIN;if (nThumbLX > INT16_MAX) nThumbLX = INT16_MAX;if (nThumbLY > INT16_MAX) nThumbLY = INT16_MAX;if (nThumbRX > INT16_MAX) nThumbRX = INT16_MAX;if (nThumbRY > INT16_MAX) nThumbRY = INT16_MAX;m_CurXInputMerge.Gamepad.bLeftTrigger = nLeftTrigger;m_CurXInputMerge.Gamepad.bRightTrigger = nRightTrigger;m_CurXInputMerge.Gamepad.sThumbLX = nThumbLX;m_CurXInputMerge.Gamepad.sThumbLY = nThumbLY;m_CurXInputMerge.Gamepad.sThumbRX = nThumbRX;m_CurXInputMerge.Gamepad.sThumbRY = nThumbRY;if (m_cbMergeState){m_cbMergeState(m_CurXInputMerge.Gamepad);}
}void CXInputHelper::_InputMergeChangeProcess()
{// 状态处理for (auto& item : m_mergeEvents){INPUT_EVENT_INFO& eventInfo = item.second;// 按钮状态变化处理_UpateButtonState(eventInfo.eventState, m_CurXInputMerge.Gamepad, m_LastXInputMerge.Gamepad, item.first);}// 得到全部输入状态m_CurButtonMerge.dwButtons = 0;for (auto& item : m_mergeEvents){INPUT_EVENT_INFO& eventInfo = item.second;if (XINPUT_GAMEPAD_LEFT_TRIGGER == item.first){m_CurButtonMerge.Mask.LeftTrigger = eventInfo.eventState.curButtonFlag.Mask.Down || eventInfo.eventState.curButtonFlag.Mask.Hold;}else if (XINPUT_GAMEPAD_RIGHT_TRIGGER == item.first){eventInfo.eventState.fRight = true;m_CurButtonMerge.Mask.RightTrigger = eventInfo.eventState.curButtonFlag.Mask.Down || eventInfo.eventState.curButtonFlag.Mask.Hold;}else if (XINPUT_GAMEPAD_LEFT_STICK == item.first){m_CurButtonMerge.Mask.LeftStickUp = eventInfo.eventState.buttonBitMask.Mask.LeftStickUp;m_CurButtonMerge.Mask.LeftStickDown = eventInfo.eventState.buttonBitMask.Mask.LeftStickDown;m_CurButtonMerge.Mask.LeftStickLeft = eventInfo.eventState.buttonBitMask.Mask.LeftStickLeft;m_CurButtonMerge.Mask.LeftStickRight = eventInfo.eventState.buttonBitMask.Mask.LeftStickRight;}else if (XINPUT_GAMEPAD_RIGHT_STICK == item.first){eventInfo.eventState.fRight = true;m_CurButtonMerge.Mask.RightStickUp = eventInfo.eventState.buttonBitMask.Mask.RightStickUp;m_CurButtonMerge.Mask.RightStickDown = eventInfo.eventState.buttonBitMask.Mask.RightStickDown;m_CurButtonMerge.Mask.RightStickLeft = eventInfo.eventState.buttonBitMask.Mask.RightStickLeft;m_CurButtonMerge.Mask.RightStickRight = eventInfo.eventState.buttonBitMask.Mask.RightStickRight;eventInfo.eventState.sThumbX = m_CurXInputMerge.Gamepad.sThumbRX;eventInfo.eventState.sThumbY = m_CurXInputMerge.Gamepad.sThumbRY;}else{m_CurButtonMerge.dwButtons |= eventInfo.eventState.curButtonFlag.Mask.Down || eventInfo.eventState.curButtonFlag.Mask.Hold ? item.first : 0;}}// 回调处理for (auto& item : m_mergeEvents){INPUT_EVENT_INFO& eventInfo = item.second;eventInfo.eventState.dwButtonMask = item.first;eventInfo.eventState.buttonBitMask.dwButtons = m_CurButtonMerge.dwButtons;eventInfo.eventState.dwButtons = m_CurButtonMerge.dwButtons;if (XINPUT_GAMEPAD_LEFT_STICK == item.first){eventInfo.eventState.sThumbX = m_CurXInputMerge.Gamepad.sThumbLX;eventInfo.eventState.sThumbY = m_CurXInputMerge.Gamepad.sThumbLY;}if (XINPUT_GAMEPAD_RIGHT_STICK == item.first){eventInfo.eventState.sThumbX = m_CurXInputMerge.Gamepad.sThumbRX;eventInfo.eventState.sThumbY = m_CurXInputMerge.Gamepad.sThumbRY;}if (eventInfo.eventState.curButtonFlag.State){eventInfo.eventState.xinputState = m_CurXInputMerge;if (XINPUT_GAMEPAD_LEFT_TRIGGER == item.first){eventInfo.eventState.bTrigger = m_CurXInputMerge.Gamepad.bLeftTrigger;}if (XINPUT_GAMEPAD_RIGHT_TRIGGER == item.first){eventInfo.eventState.fRight = true;eventInfo.eventState.bTrigger = m_CurXInputMerge.Gamepad.bRightTrigger;eventInfo.eventState.stickDirection = eventInfo.eventState.curLeftStickDirection;}if (XINPUT_GAMEPAD_LEFT_STICK == item.first){eventInfo.eventState.sThumbX = m_CurXInputMerge.Gamepad.sThumbLX;eventInfo.eventState.sThumbY = m_CurXInputMerge.Gamepad.sThumbLY;eventInfo.eventState.stickDirection = eventInfo.eventState.curRightStickDirection;}if (XINPUT_GAMEPAD_RIGHT_STICK == item.first){eventInfo.eventState.fRight = true;eventInfo.eventState.sThumbX = m_CurXInputMerge.Gamepad.sThumbRX;eventInfo.eventState.sThumbY = m_CurXInputMerge.Gamepad.sThumbRY;}(this->*(eventInfo.eventCb))(eventInfo.eventState);if (m_cbMergeChange){m_cbMergeChange(eventInfo.eventState);}}}m_LastXInputMerge = m_CurXInputMerge;
}

main.cpp

// JoystickAssistant.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <string>
#include <iostream>
#include <windows.h>
#include <tchar.h>
#include "CWin32Utils/CXinputHelper.h"
#include "CWin32Utils/CInputUtils.h"
#include "CMainFrame.h"
#include "resource.h"#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endifvoid LogPrintf(LPCTSTR pFormat, ...);int WINAPI WinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nShowCmd)
{UNREFERENCED_PARAMETER(hInstance);UNREFERENCED_PARAMETER(hPrevInstance);UNREFERENCED_PARAMETER(lpCmdLine);UNREFERENCED_PARAMETER(nShowCmd);CMainFrame dlg;dlg.DoModalEx(IDD_DIALOG_MAIN, NULL, TRUE, true);return 0;
}int main()
{CXInputHelper obj;obj.Initialize();obj.IgnoreLeftThumbEmpty(false);obj.IgnoreRightThumbEmpty(false);obj.SetThumbThreshold(4096);obj.SetMergeStateCallback([](const XINPUT_GAMEPAD& state)-> void {/*LogPrintf(_T("Button: %04X\r\n"),state.wButtons);*/});obj.SetRawChangeCallback([](const INPUT_EVENT_STATE& state)-> void {LogPrintf(_T("Mask: %08X bRight: %d Trigger: %3d Thumb: %6d,%6d State: %d Buttons: %.8X Duration: %6d L: %d U: %d R: %d D: %d\r\n"),state.dwButtonMask,state.fRight, state.bTrigger, state.sThumbX, state.sThumbY,state.curButtonFlag.State,state.dwButtons,state.dwDuration,state.stickDirection.Left.Flag.State,state.stickDirection.Up.Flag.State,state.stickDirection.Right.Flag.State,state.stickDirection.Down.Flag.State);});/*obj.SetMergeChangeCallback([](const XINPUT_GAMEPAD& xGamepad, const INPUT_EVENT_STATE& state)-> void {LogPrintf(_T("Index: %d bRight: %d Trigger: %3d Thumb: %6d,%6d Button: %04X State: %04X Buttons: %.8X\r\n"),state.dwIndex,state.fRight, state.bTrigger, state.sThumbX, state.sThumbY, state.wButton, state.curButtonFlag.State,state.allButtonState.Buttons);});*/system("pause");return 0;
}void LogPrintf(LPCTSTR pFormat, ...)
{size_t nCchCount = MAX_PATH;_tstring strResult(nCchCount, 0);va_list args;va_start(args, pFormat);do{//格式化输出字符串int nSize = _vsntprintf_s(&strResult[0], nCchCount, _TRUNCATE, pFormat, args);if (-1 != nSize){HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);::WriteConsole(console, strResult.c_str(), nSize, NULL, NULL);break;}//缓冲大小超限终止if (nCchCount >= INT32_MAX){break;}//重新分配缓冲nCchCount *= 2;strResult.resize(nCchCount);} while (true);va_end(args);
}

测试

相关文章:

XInput手柄输入封装

功能全面地封装了XInput的输入, 1. 普通按钮按下, 按住, 弹起状态检查, 2. 摇杆4个方向的按下, 按住, 弹起检查 3. 按键状态变化检测并且记录按下触发时间, 按住保持时间, 方便用来完全自定义的输入功能 4. 多手柄输入合并 CXinputHelper.h #pragma once #include <win…...

NodeMCU-ESP8266+flash_download_tool_3.9.7 烧录

USB-TTL 接 NodeMCU的RXD0, TXD0, GND 例程hello_world&#xff1a; Eclipse编译信息&#xff1a; python /d/ESP/ESP8266_RTOS_SDK/ESP8266_RTOS_SDK/components/esptool_py/esptool/esptool.py --chip esp8266 --port COM6 --baud 115200 --before default_reset --after …...

首例开源的自动驾驶混合运动规划框架,手握“规划可解释”和“决策准确”两张王牌!

导读&#xff1a; 本文开发了一种新的混合运动规划方法&#xff0c;将环境和预测信息集成在Frenet坐标系中&#xff0c;提升了运动规划能力。本文将传统运动规划算法的可预测性和稳定性与RL的动态适应性相结合&#xff0c;从而形成了一个能够有效管理复杂情况并适应不断变化的环…...

数据结构之红黑树的 “奥秘“

目录&#xff1a; 一.红黑树概念 二. 红黑树的性质 三.红黑树的实现 四.红黑树验证 五.AVL树和红黑树的比较 一.红黑树概念 1.红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何 一条从根…...

【鸿蒙 HarmonyOS NEXT】使用EventHub进行数据通信

✨本人自己开发的开源项目&#xff1a;土拨鼠充电系统 ✨踩坑不易&#xff0c;还希望各位大佬支持一下&#xff0c;在GitHub给我点个 Start ⭐⭐&#x1f44d;&#x1f44d; ✍GitHub开源项目地址&#x1f449;&#xff1a;https://github.com/cheinlu/groundhog-charging-syst…...

大模型RAG实战|构建知识库:文档和网页的加载、转换、索引与存储

我们要开发一个生产级的系统&#xff0c;还需要对LlamaIndex的各个组件和技术进行深度的理解、运用和调优。本系列将会聚焦在如何让系统实用上&#xff0c;包括&#xff1a;知识库的管理&#xff0c;检索和查询效果的提升&#xff0c;使用本地化部署的模型等主题。我将会讲解相…...

江协科技stm32————11-5 硬件SPI读写W25Q64

一、开启时钟&#xff0c;开启SPI和GPIO的时钟 二、初始化GPIO口&#xff0c;其中SCK和MOSI是由硬件外设控制的输出信号&#xff0c;配置为复用推挽输出 MISO是硬件外设的输入信号&#xff0c;配置为上拉输入&#xff0c;SS是软件控制的输出信号&#xff0c;配置为通用推挽输出…...

网络编程day04(UDP、Linux IO 模型)

目录 【1】UDP 1》通信流程 2》函数接口 1> recvfrom 2> sendto 3》代码展示 1> 服务器代码 2> 客户端代码 【2】Linux IO 模型 场景假设一 1》阻塞式IO&#xff1a;最常见、效率低、不耗费CPU 2》 非阻塞 IO&#xff1a;轮询、耗费CPU&#xff0c;可以处…...

【android10】【binder】【2.servicemanager启动——全源码分析】

系列文章目录 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录 …...

Java实现简易计算器功能(idea)

目的&#xff1a;写一个计算器&#xff0c;要求实现加减乘除功能&#xff0c;并且能够循环接收新的数据&#xff0c;通过用户交互实现。 思路&#xff1a; &#xff08;1&#xff09;写4个方法&#xff1a;加减乘除 &#xff08;2&#xff09;利用循环switch进行用户交互 &…...

Parsec问题解决方案

Parsec目前就是被墙了&#xff0c;有解决方案但治标不治本&#xff0c;如果想稳定串流建议是更换稳定的串流软件&#xff0c;以下是一些解决方案 方案一&#xff1a;在%appdata%/Parsec/config.txt中&#xff0c;添加代理 app_proxy_address 127.0.0.1 app_proxy_scheme http…...

Swift 创建扩展(Extension)

类别(Category) 和 扩展(Extension) 的 用法很多. 常用的 扩展(Extension) 有分离代码和封装模块的功能,例如登陆页面有注册功能,有登陆功能,有找回密码功能,都写在一个页面就太冗余了,可以考虑使用 扩展(Extension) 登陆页面的方法来分离代码 本文介绍Swift 如何创建扩展(Ex…...

九月五日(k8s配置)

一、安装环境 环境准备&#xff1a;&#xff08;有阿里云&#xff09; k8s-master 192.168.1.11 k8s-node1 192.168.1.22 k8s-node2 192.168.1.33 二、前期准备 在k8s-master主机 [rootk8s-master ~]# vim /etc/hosts …...

某极验4.0 -消消乐验证

⚠️前言⚠️ 本文仅用于学术交流。 学习探讨逆向知识&#xff0c;欢迎私信共享学习心得。 如有侵权&#xff0c;联系博主删除。 请勿商用&#xff0c;否则后果自负。 网址 aHR0cHM6Ly93d3cyLmdlZXRlc3QuY29tL2FkYXB0aXZlLWNhcHRjaGE 1. 浅聊一下 验证码样式 验证成功 - …...

洛谷 P10798 「CZOI-R1」消除威胁

题目来源于&#xff1a;洛谷 题目本质&#xff1a;贪心&#xff0c;st表&#xff0c;单调栈 解题思路&#xff1a;由于昨天联练习了平衡树&#xff0c;我就用平衡树STL打了个暴力&#xff0c;超时得了30分 这是暴力代码&#xff1a; #include<bits/stdc.h> using name…...

Pow(x, n)

题目 实现 pow(x, n) &#xff0c;即计算 x 的 n 次幂函数&#xff08;即&#xff0c;xn&#xff09;。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000示例 2&#xff1a; 输入&#xff1a;x 2.10000, n 3 输出&#xff1a;9.26100示…...

一文带你学会使用滑动窗口

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;leetcode刷题 ​ ​ 209.长度最小的子数组 求最短长度之和等于目标值。 方法一&#xff1a; 暴力枚举&#xff08;会超时&#xff09; 从头开始遍历直到之和等于target然后更新结果。这…...

如何从0到1本地搭建whisper语音识别模型

文章目录 环境准备1. 系统要求2. 安装依赖项1:安装 Python 和虚拟环境2:安装 Whisper3:下载 Whisper 模型4:进行语音识别5:提高效率和精度6:开发和集成Whisper 是 OpenAI 发布的一个强大的语音识别模型,它可以将语音转换为文本,支持多语言输入,并且可以处理各种音频类…...

PyTorch 创建数据集

图片数据和标签数据准备 1.本文所用图片数据在同级文件夹中 ,文件路径为train/’ 2.标签数据在同级文件&#xff0c;文件路径为train.csv 3。将标签数据提取 train_csvpd.read_csv(train.csv)创建继承类 第一步&#xff0c;首先创建数据类对象 此时可以想象为单个数据单元的…...

[Java]SpringBoot登录认证流程详解

登录认证 登录接口 1.查看原型 2.查看接口 3.思路分析 登录核心就是根据用户名和密码查询用户信息,存在则登录成功, 不存在则登录失败 4.Controller Slf4j RestController public class LoginController {Autowiredprivate EmpService empService;/*** 登录的方法** param …...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...