XInput手柄输入封装

news2024/12/23 7:43:28

功能全面地封装了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;                 // A
        DWORD    B : 1;                 // B
        DWORD    X : 1;                 // X
        DWORD    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;
#endif

void 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);
}

测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2119397.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信支付开发避坑指南

1 微信支付的坑 1.1 不能用前端传递过来的金额 订单的商品金额要从数据库获取&#xff0c;前端只传商品 id。 1.2 交易类型trade type字段不要传错 v2版API&#xff0c;不同交易类型&#xff0c;要调用的支付方式也不同。 1.3 二次签名 下单时&#xff0c;在拿到预支付交…

哈希表-数据结构

一、哈希表基本概念 哈希表&#xff08;也称为散列表&#xff09;是根据键而直接访问在内存存储位置的数据结构&#xff0c;也就是说实际上是经过哈希函数进行映射&#xff0c;映射道表中一个位置来访问记录&#xff0c;这个存放记录的数组称为散列表。 哈希函数&#xff1a;就…

计组基础知识

操作系统的特征 并发 共享 虚拟 异步 操作系统的功能 1、资源分配&#xff0c;资源回收 硬件资源 CPU、内存、硬盘、I/O设备。 2、为应⽤程序提供服务 操作系统将硬件资源的操作封装起来&#xff0c;提供相对统⼀的接⼝&#xff08;系统调⽤&#xff09;供开发者调⽤。 3、管…

Redis 集群会有写操作丢失吗?为什么?

大家好&#xff0c;我是锋哥。今天分享关于 【Redis 集群会有写操作丢失吗&#xff1f;为什么&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; Redis 集群会有写操作丢失吗&#xff1f;为什么&#xff1f; Redis 并不能保证数据的强一致性&#xff0c;这意味这在…

Qt_概述

目录 1、图形用户界面 2、客户端开发 3、什么是界面 4、Qt的发展史 5、Qt支持的平台 6、Qt的版本 7、Qt的优点 8、Qt的应用场景 小结 前言&#xff1a; Qt是一个应用程序开发框架&#xff0c;他具有跨平台性质&#xff0c;主要使用C语言进行编程&#xff0c;Qt的开发…

透视表支持自定义聚合公式,新增字体管理功能,DataEase开源BI工具v2.10 LTS版本发布

2024年9月9日&#xff0c;人人可用的开源BI工具DataEase正式发布v2.10 LTS&#xff08;Long Term Support&#xff09;版本。DataEase开源项目组将对v2.10 LTS版本提供长期支持&#xff0c;定期迭代发布小版本&#xff0c;持续进行问题修复更新并针对部分功能进行优化。欢迎广大…

数据结构第二周做题总结_顺序表

id:17 A. DS顺序表–类实现 题目描述 用C语言和类实现顺序表 属性包括&#xff1a;数组、实际长度、最大长度&#xff08;设定为1000&#xff09; 操作包括&#xff1a;创建、插入、删除、查找 类定义参考 输入 第1行先输入n表示有n个数据&#xff0c;即n是实际长度&am…

【软件测试】盒木进销存管理系统 需求说明书

目录 1 引言 2 项目概述 3 平台、角色和权限 3.1 Web端 4 Web端需求 4.1 登录/注册页面 4.1.1 业务描述 4.1.2 需求描述 4.1.3 行为人 4.1.4 UI页面 4.1.5 业务规则 4.2 首页 4.2.1 业务描述 4.2.2 需求描述 4.2.3 行为人 4.2.4 UI界面 4.2.5 业务规则 4.3报…

软件测试工程师面试题大全(附答案)

1、什么是兼容性测试? 答&#xff1a;兼容性测试是检查软件在不同软件平台&#xff0c;硬件平台上是否可以正常运行的测试。主要查看软件在不同操作系统、浏览器、数据库中运行是否正常。 2、你能不能说下你3-5年的职业规划? 答&#xff1a;首先&#xff0c;要巩固自己的测…

电脑怎么切换IP地址 手机如何更改ip地址

深度IP转换器是一款专业的网络工具&#xff0c;旨在帮助用户轻松切换IP地址&#xff0c;保护个人隐私和网络安全。以下是使用深度IP转换器切换IP地址的详细步骤&#xff1a; ‌下载与安装‌&#xff1a; 首先&#xff0c;您需要在官方网站或正规下载渠道下载深度IP转换器的最新…

自选择问题和处理效应模型

自选择问题和处理效应模型 DGP 注意&#xff1a; 这里的概率密度超过了1&#xff0c;这是正常的。概率密度的三原则&#xff0c;1是大于等于0&#xff1b;2是积分等于1&#xff1b;对于连续型随机变量&#xff0c;给定一个具体的x值&#xff0c;f(x)并不是该事件发生的概率。而…

感谢关注 Thanks for your attention

后端技术栈 前端技术栈 DevOps 运维技术栈 测试技术栈 开发工具 其他 汇总 一个基于websocket协议的分布式推送服务 ( https://github.com/webVueBlog/springboot-cloud-push )Mall-system-Java-Vue-Uni-app商城JAVA版&#xff0c;SpringBoot Maven Swagger Mybatis Plus R…

yarn create vite时报错error Error: EPERM: operation not permitted, mkdir

在构建项目的前端脚手架时&#xff0c;窗口出现了该错误&#xff0c;搜索了大量解决方案后&#xff0c;以下是我的步骤 &#xff1a; 再cd到我的D盘项目路径位置 再次运行yarn create vite 算了&#xff0c;换npm搞&#xff1a;npm create vitelatest 出现以下报错 我的解…

网络编程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;可以处…

【C++ 面试 - 新特性】每日 3 题(三)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

RPKI应急管控网络拓扑搭建

应急管控网络拓扑搭建 一、网络拓扑图 二、拓扑配置 1.资源库批量导入roas 在rpki.qcl.edu.cn服务器上的/usr/local/rpki/目录下执行脚本 sh roa_get.sh add#!/bin/dash# TODO Aadd Rremove start10000 sum254 run(){for i in seq 1 20dofor j in seq 1 250doas_numberexpr…

正点原子阿尔法ARM开发板-IMX6ULL(三)——汇编LED驱动实验-上

文章目录 一、原理分析1.1 对于IMX6ULL的IO初始化1.2 IO的复用&#xff08;MUX&#xff09;1.3 电气属性寄存器&#xff08;PAD&#xff09;1.3.1 SRE(bit0)1.3.2 DSE(bit5:3)1.3.3 SPEED(bit7:6)1.3.4 ODE(bit11)1.3.5 PKE(bit12)1.3.6 PUE(bit13)1.3.7 PUS(bit15:14)1.3.8 HY…

6.5椒盐噪声

在OpenCV中联合C给一张图片加上椒盐噪声&#xff08;Salt and Pepper Noise&#xff09;可以通过随机选择像素点并将其置为黑色&#xff08;0&#xff09;或白色&#xff08;255&#xff09;来实现。椒盐噪声是一种随机噪声&#xff0c;通常表现为图像中的孤立黑点&#xff08;…

Windows环境下 VS2022 编译 LAME 源码

LAME LAME 是一个非常流行的开源 MP3 编码器库&#xff0c;它的全称是 “LAME Ain’t an MP3 Encoder”&#xff0c;这是一个带有讽刺意味的名字&#xff0c;因为 LAME 实际上是一个功能强大的 MP3 编码器。LAME 的开发始于 1998 年&#xff0c;目的是创建一个开放源代码的库&a…

AIPaperGPT写论文靠谱吗?

AIPaperGPT&#xff0c;论文写作神器~ https://www.aipapergpt.com/ 在信息爆炸的今天&#xff0c;学术写作的挑战日益增加&#xff0c;而AIPaperGPT作为一款旨在提升写作效率的工具&#xff0c;其可靠性自然成为了用户关注的焦点。本文将从多个维度对AIPaperGPT进行全面评估&…