CRawInputUtils.h
#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
#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;
}