我试图通过Microsoft
与XBOX控制器对话,而不使用
XINPUT。我目前能够控制所有隆隆电机(包括力反馈触发器)发送数据包使用
HidD_SetOutputReport(HANDLE, VOID*, ULONG)
。但是,我无法使用
HidD_GetInputReport(HANDLE, VOID*, ULONG)
或
ReadFile() / ReadFileEx()
读取按钮值,不管是否使用
FILE_FLAG_OVERLAPPED
创建句柄,以及使用
OVERLAPPED
和Windows。
我已经反向设计了URB协议,并借助下面的文章 https://github.com/quantus/xbox-one-controller-protocol 。主要目标是克服XINPUT开销,并编写一个灵活的框架,以便我也可以集成其他游戏垫。
这就是我所完成的:
SetupDiGetClassDevs(...)
、
SetupDiEnumDeviceInfo(...)
、
SetupDiEnumDeviceInterfaces(...)
和
SetupDiGetDeviceInterfaceDetail(...)
找到了控制器的路径
HANDLE gamePad = CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL)
为设备创建了一个句柄
HidP_GetCaps(HANDLE, HIDP_CAPS*)
似乎不会返回有效数据,因为它报告的OutputReportByteLength为0,但我能够发送大小为5(打开)和9(设置隆隆马达)的输出报告。
byte 0: Package type
byte 1: was always 0x00
byte 2: Package number (always incrementing every package)
byte 3: Size of following data
byte 4+: <Data>
例如:我的两个输出隆隆包看起来像这样(用脉冲长度脏地打开和关闭马达):
这开启了所有发动机的隆隆电机速度在0xFF和触发器的速度0xF0。我就是这样做的:
struct RumbleContinous{
BYTE mode;
BYTE mask; // Motor Mask 0b 0 0 0 0 LT RT L R
BYTE lTForce;
BYTE rTForce;
BYTE lForce;
BYTE rForce;
BYTE pulseLength;
BYTE offTime;
BYTE terminator; // Terminator / Dummy / ?? (XINPUT sends that as 0xEB!) / Changing seems to not make any changes
RumbleContinous rc = {0x00, 0x0F, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF 0x00, 0xEB};
HidD_SetOutputReport(gamePad, (PVOID)&rc, sizeof(RumbleContinous ));
to my problem
查看来自控制器的输入包,看起来您需要创建一个大小为0x0E = 14的缓冲区,ZeroMemory it (或者像MSDN建议的那样将第一个字节写入0),只需调用
HidD_GetInputReport(HANDLE, buffer, 14)
因此,我所做的就是调用
HidD_FlushQueue()
,以确保下一个包是输入包。然后,我插入一个小延迟,以便我能够改变一些控制器的值。之后,我尝试用
HidD_GetInputReport(HANDLE, cmd_in, 14)
读取字节数组,但是函数在
GetLastError() == 0x00000057 // ERROR_INVALID_PARAMETER
中总是失败。
由于HID能够过滤包,所以可能需要分配比预期大一个字节的缓冲区,并将所需的报告id传递到位置0的缓冲区。我就是这样做的:
BYTE cmd_in[15];
ZeroMemory(cmd_in, 15);
cmd_in[0] = 0x20;
HidD_GetInputReport(gamePad, cmd_in, 15);
还是没有成功。由于
HidP_GetCaps(...)
函数报告了16的输入报告(但我不相信这一点,因为它已经愚弄了我,输出报告大小为0),所以我尝试扫过许多缓冲区大小:
BYTE cmd_in[30];
for (UINT bs = 0; bs < 30; bs++) {
ZeroMemory(cmd_in, 30);
HidD_FlushQueue(gamePad); // Flushing works
Sleep(500);
if (HidD_GetInputReport(gamePad, cmd_in, bs)) {
// Output result (ommited)
else {
// Print error (ommited)
}
和
BYTE cmd_in[30];
for (UINT bs = 0; bs < 30; bs++) {
ZeroMemory(cmd_in, 30);
cmd_in[0] = 0x20;
HidD_FlushQueue(gamePad); // Flushing works
Sleep(500);
if (HidD_GetInputReport(gamePad, cmd_in, bs)) {
// Output result (ommited)