原始输入解析XBOX手柄

CRawInputUtils.h

cpp 复制代码
#pragma once
#include <windows.h>
#include <hidsdi.h>
#include <stdint.h>
#include <hidusage.h>

#pragma comment(lib, "Hid.lib")

#define PI                                  (3.141592653589793f)
#define ANGLE_RANGE                         (22.5f)

typedef union _GAMEPAD_STICK_AXIS_STATE
{
    uint8_t    Data;                   // 状态
    // 按钮比特位
    struct _Flags
    {
        uint8_t    Up : 1;             // 摇杆 上
        uint8_t    Down : 1;           // 摇杆 下
        uint8_t    Left : 1;           // 摇杆 左
        uint8_t    Right : 1;          // 摇杆 右
    }Mask;
}GAMEPAD_STICK_AXIS_STATE;

typedef struct _GAMEPAD_INPUT
{
    // 手柄按键状态位
    union _GAME_PAD_BUTTON_MASK
    {
        // 按钮比特位
        struct _Flags
        {
            DWORD    DpadUp : 1;            // 十字键 上 Up
            DWORD    DpadDown : 1;          // 十字键 下 Down
            DWORD    DpadLeft : 1;          // 十字键 左 Left
            DWORD    DpadRight : 1;         // 十字键 右 Right
            DWORD    Start : 1;             // 菜单键(开始键) Start
            DWORD    Back : 1;              // 返回键(选择键) Back
            DWORD    LSB : 1;               // 左摇杆键 Left stick button
            DWORD    RSB : 1;               // 右摇杆键 Right stick button
            DWORD    LB : 1;                // 左肩键 Left bumper
            DWORD    RB : 1;                // 右肩键 Right bumper
            DWORD    Guide : 1;             // 西瓜键(导航键) Guide
            DWORD    Share : 1;             // 分享键(假设, 未验证) Share
            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    LT : 1;                // 左扳机 Left Trigger
            DWORD    RT : 1;                // 右扳机 Right Trigger

        }Mask;

        DWORD    Data;                      // 按钮键值

    }Buttons;
    short LX;
    short LY;
    short RX;
    short RY;
    short bLeftTrigger;
    short bRightTrigger;
}GAMEPAD_INPUT, *PGAMEPAD_INPUT;

class CRawInputUtils
{
public:

    static void ParseRawInput(
        PRAWINPUT pRawInput,                // 原始输入
        PGAMEPAD_INPUT pGamepadInput        // 控制器解析结果
    );

private:

    static GAMEPAD_STICK_AXIS_STATE _ParseStickAxis(short x, short y, int nThreshold);
};

CRawInputUtils.cpp

cpp 复制代码
#include "CRawInputUtils.h"
#include <math.h>

void CRawInputUtils::ParseRawInput(
    PRAWINPUT pRawInput,
    PGAMEPAD_INPUT pGamepadInput
)
{
    HANDLE               hHeap = ::GetProcessHeap();
    HIDP_CAPS            Caps = { 0 };
    PHIDP_PREPARSED_DATA pPreparsedData = NULL;
    PHIDP_BUTTON_CAPS    pButtonCaps = NULL;
    PHIDP_VALUE_CAPS     pValueCaps = NULL;
    USHORT               capsLength = 0;
    UINT                 bufferSize = sizeof(RAWINPUT);
    ULONG                usageLength = 0; 
    ULONG                value = 0;
    PUSAGE               usages = NULL;
    int nNumberOfButtons = 0;

    do
    {
        if( 0 != GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize))
        {
            break;
        }

        pPreparsedData = (PHIDP_PREPARSED_DATA)HeapAlloc(hHeap, 0, bufferSize);
        if (NULL == pPreparsedData)
        {
            break;
        }

        if (GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, &bufferSize) <= 0)
        {
            break;
        }

        if (HIDP_STATUS_SUCCESS != HidP_GetCaps(pPreparsedData, &Caps))
        {
            break;
        }

        pButtonCaps = (PHIDP_BUTTON_CAPS)HeapAlloc(hHeap, 0, sizeof(HIDP_BUTTON_CAPS) * Caps.NumberInputButtonCaps);
        if (NULL == pButtonCaps)
        {
            break;
        }

        capsLength = Caps.NumberInputButtonCaps;
        if (HIDP_STATUS_SUCCESS != HidP_GetButtonCaps(HidP_Input, pButtonCaps, &capsLength, pPreparsedData))
        {
            break;
        }

        nNumberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1;

        pValueCaps = (PHIDP_VALUE_CAPS)HeapAlloc(hHeap, 0, sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps);
        if (NULL == pValueCaps)
        {
            break;
        }

        capsLength = Caps.NumberInputValueCaps;
        if (HIDP_STATUS_SUCCESS != HidP_GetValueCaps(HidP_Input, pValueCaps, &capsLength, pPreparsedData))
        {
            break;
        }

        usageLength = HidP_MaxUsageListLength(HidP_Input, pButtonCaps->UsagePage, pPreparsedData);
        usages = (PUSAGE)HeapAlloc(hHeap, 0, usageLength * sizeof(USAGE));

        if (HIDP_STATUS_SUCCESS != HidP_GetUsages(
            HidP_Input, pButtonCaps->UsagePage, 0, usages, &usageLength, pPreparsedData,
            (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid
        ))
        {
            break;
        }

        pGamepadInput->Buttons.Data = 0;

        // 按钮解析
        for (ULONG i = 0; i < usageLength; i++)
        {
            switch (usages[i])
            {
            case 0x01:  // A
                pGamepadInput->Buttons.Mask.A = 1;
                break;
            case 0x02:  // B
                pGamepadInput->Buttons.Mask.B = 1;
                break;
            case 0x03:  // X
                pGamepadInput->Buttons.Mask.X = 1;
                break;
            case 0x04:  // Y
                pGamepadInput->Buttons.Mask.Y = 1;
                break;
            case 0x05:  // LB
                pGamepadInput->Buttons.Mask.LB = 1;
                break;
            case 0x06:  // RB
                pGamepadInput->Buttons.Mask.RB = 1;
                break;
            case 0x07:  // Back
                pGamepadInput->Buttons.Mask.Back = 1;
                break;
            case 0x08:  // Start
                pGamepadInput->Buttons.Mask.Start = 1;
                break;
            case 0x09:  // LSB
                pGamepadInput->Buttons.Mask.LSB = 1;
                break;
            case 0x0A:  // RSB
                pGamepadInput->Buttons.Mask.RSB = 1;
                break;
            }
        }

        // 摇杆, 十字键, 扳机解析
        for (ULONG i = 0; i < Caps.NumberInputValueCaps; i++)
        {
            if (HIDP_STATUS_SUCCESS != HidP_GetUsageValue(
                HidP_Input, pValueCaps[i].UsagePage, 0, pValueCaps[i].Range.UsageMin, &value, pPreparsedData,
                (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid
            ))
            {
                break;
            }

            switch (pValueCaps[i].Range.UsageMin)
            {
            case HID_USAGE_GENERIC_X:  // 0x30 左摇杆 Left Stick X
                pGamepadInput->LX = (LONG)value + 32768;
                break;

            case HID_USAGE_GENERIC_Y:  // 0x31 左摇杆 Left Stick Y
                pGamepadInput->LY = (LONG)32767 - value;
                break;
            case HID_USAGE_GENERIC_Z:  // 0x32 左/右扳机 Left Trigger + Right Trigger
                if ((SHORT)value < 0)
                {
                    pGamepadInput->bLeftTrigger = 0 - (value - 127);
                }
                else if ((SHORT)value > 0)
                {
                    pGamepadInput->bRightTrigger = value + 127;
                }

                if (INT16_MIN == (SHORT)value)
                {
                    pGamepadInput->bLeftTrigger = 0;
                    pGamepadInput->bRightTrigger = 0;
                }
                break;

            case HID_USAGE_GENERIC_RX:  // 0x33 右摇杆 Right Stick X
                pGamepadInput->RX = (LONG)value + 32768;
                break;

            case HID_USAGE_GENERIC_RY:  // 0x34 右摇杆 Right Stick Y
                pGamepadInput->RY = (LONG)32767 - value;
                break;

            case HID_USAGE_GENERIC_HATSWITCH:  // 0x39 十字键 Dpad
                switch (value)
                {
                case 0x01:  // 上
                    pGamepadInput->Buttons.Mask.DpadUp = 1;
                    break;
                case 0x02:  // 上右
                    pGamepadInput->Buttons.Mask.DpadUp = 1;
                    pGamepadInput->Buttons.Mask.DpadRight = 1;
                    break;
                case 0x03:  // 右
                    pGamepadInput->Buttons.Mask.DpadRight = 1;
                    break;
                case 0x04:  // 右下
                    pGamepadInput->Buttons.Mask.DpadRight = 1;
                    pGamepadInput->Buttons.Mask.DpadDown = 1;
                    break;
                case 0x05:  // 下
                    pGamepadInput->Buttons.Mask.DpadDown = 1;
                    break;
                case 0x06:  // 左下
                    pGamepadInput->Buttons.Mask.DpadDown = 1;
                    pGamepadInput->Buttons.Mask.DpadLeft = 1;
                    break;
                case 0x07:  // 左
                    pGamepadInput->Buttons.Mask.DpadLeft = 1;
                    break;
                case 0x08:  // 左上
                    pGamepadInput->Buttons.Mask.DpadLeft = 1;
                    pGamepadInput->Buttons.Mask.DpadUp = 1;
                    break;
                }
                break;
            }
        }

        //计算左摇杆方向
        {
            GAMEPAD_STICK_AXIS_STATE axis = _ParseStickAxis(pGamepadInput->LX, pGamepadInput->LY, 4096);
            pGamepadInput->Buttons.Mask.LeftStickUp = axis.Mask.Up;
            pGamepadInput->Buttons.Mask.LeftStickDown = axis.Mask.Down;
            pGamepadInput->Buttons.Mask.LeftStickLeft = axis.Mask.Left;
            pGamepadInput->Buttons.Mask.LeftStickRight = axis.Mask.Right;
        }

        //计算右摇杆方向
        {

            GAMEPAD_STICK_AXIS_STATE axis = _ParseStickAxis(pGamepadInput->RX, pGamepadInput->RY, 4096);
            pGamepadInput->Buttons.Mask.RightStickUp = axis.Mask.Up;
            pGamepadInput->Buttons.Mask.RightStickDown = axis.Mask.Down;
            pGamepadInput->Buttons.Mask.RightStickLeft = axis.Mask.Left;
            pGamepadInput->Buttons.Mask.RightStickRight = axis.Mask.Right;
        }

    } while (false);

    if (pPreparsedData)
    {
        HeapFree(hHeap, 0, pPreparsedData);
    }

    if (pButtonCaps)
    {
        HeapFree(hHeap, 0, pButtonCaps);
    }

    if (pValueCaps)
    {
        HeapFree(hHeap, 0, pValueCaps);
    }

    if (usages)
    {
        HeapFree(hHeap, 0, usages);
    }
}

GAMEPAD_STICK_AXIS_STATE CRawInputUtils::_ParseStickAxis(short x, short y, int nThreshold)
{
    GAMEPAD_STICK_AXIS_STATE axisResult = { 0 };
    double fCurAngle = atan2(y, x) * 180.0f / PI;

    if (fCurAngle < 0)
    {
        fCurAngle = 360.0f + fCurAngle;
    }

    // 上
    if (y > nThreshold)
    {
        if (fCurAngle >= (90.0f - ANGLE_RANGE) && fCurAngle <= (90.0f + ANGLE_RANGE))
        {
            axisResult.Mask.Up = 1;
        }
    }

    // 下
    if (y < -nThreshold)
    {
        if (fCurAngle >= (270.0f - ANGLE_RANGE) && fCurAngle <= (270.0f + ANGLE_RANGE))
        {
            axisResult.Mask.Down = 1;
        }
    }

    // 左
    if (x < -nThreshold)
    {
        if (fCurAngle >= (180.0f - ANGLE_RANGE) || fCurAngle <= (180.0f + ANGLE_RANGE))
        {
            axisResult.Mask.Left = 1;
        }
    }

    // 右
    if (x > nThreshold)
    {
        if (fCurAngle >= (360.0f - ANGLE_RANGE) || fCurAngle <= (0.0f + ANGLE_RANGE))
        {
            axisResult.Mask.Right = 1;
        }
    }

    // 左上
    if (x < -nThreshold && y > nThreshold)
    {
        if (fCurAngle > (90.0f + 45.0f - ANGLE_RANGE) && fCurAngle < (90.0f + 45.0f + ANGLE_RANGE))
        {
            axisResult.Mask.Left = 1;
            axisResult.Mask.Up = 1;
        }
    }

    // 右上
    if (x > nThreshold && y > nThreshold)
    {
        if (fCurAngle > (90.0f - 45.0f - ANGLE_RANGE) && fCurAngle < (90.0f - 45.0f + ANGLE_RANGE))
        {
            axisResult.Mask.Right = 1;
            axisResult.Mask.Up = 1;
        }
    }

    // 左下
    if (y < -nThreshold && x < -nThreshold)
    {
        if (fCurAngle > (270.0f - 45.0f - ANGLE_RANGE) && fCurAngle < (270.0f - 45.0f + ANGLE_RANGE))
        {
            axisResult.Mask.Left = 1;
            axisResult.Mask.Down = 1;
        }
    }

    // 右下
    if (y < -nThreshold && x > nThreshold)
    {
        if (fCurAngle > (270.0f + 45.0f - ANGLE_RANGE) && fCurAngle < (270.0f + 45.0f + ANGLE_RANGE))
        {
            axisResult.Mask.Right = 1;
            axisResult.Mask.Down = 1;
        }
    }

    return axisResult;
}
相关推荐
梓仁沐白1 小时前
ubuntu+windows双系统切换后蓝牙设备无法连接
windows·ubuntu
‘’林花谢了春红‘’4 小时前
C++ list (链表)容器
c++·链表·list
九鼎科技-Leo5 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
机器视觉知识推荐、就业指导6 小时前
C++设计模式:建造者模式(Builder) 房屋建造案例
c++
Yang.998 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
熬夜学编程的小王8 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
zz40_8 小时前
C++自己写类 和 运算符重载函数
c++
我不瘦但很逗8 小时前
Windows下使用DBeaver连接云数据库(MySQL)
数据库·windows
六月的翅膀8 小时前
C++:实例访问静态成员函数和类访问静态成员函数有什么区别
开发语言·c++