0

我试图从我的 Xbox 360 控制器中读取而不进行轮询。(准确地说,我实际上使用的是 Logitech F310,但我的 Windows 10 PC 将其视为 XBox 360 控制器。)我编写了一些相当讨厌的 HID 代码,它使用重叠 I/O 在两个线程中阻塞事件,一个表示准备好从 HID 设备读取报告,另一个表示 UI 线程已请求 HID 线程退出。这很好用,但是 HID 驱动程序的行为与 XInput 的行为有些不同。特别是,它将两个触发器合并为一个值,仅传递它们的差异(奇怪的是,当玩家的手指离开控件时,游戏期望 HID 值为 0x80)。XInput 将它们视为两个不同的值,这是一个很大的改进。此外,XInput 将帽子开关报告为四位,

对我来说,XInput 的缺点是,在控制器更改其值或按钮之一之前,似乎无法阻止读取请求。作为 HID 设备,ReadFile 调用将阻塞(更准确地说,WaitForMultipleEvents 阻塞直到有可用数据)。XInput 似乎预料到了轮询。对于一个自然而然地编写为轮询控制器的游戏,它更新游戏状态的频率(例如,可能为每个显示的新视频帧一次),这是有道理的。但是,如果您想将控制器用于其他目的(我正在开发一个戏剧应用程序),您可能需要一个纯粹的异步系统,如 HID API 提供的。但是,同样,HID API 结合了两个值触发器。

现在,当您使用 XInput 读取设备时,您不仅可以获得所有控件的状态,还可以获得数据包编号。MSDN 说数据包编号仅在控件状态更改时才会更改。这样,如果连续的数据包编号相同,您就不必在第一个数据包之后进行任何处理,因为您知道控制器状态没有改变。但你仍在投票,对我来说,这有点粗俗。

然而,让我感兴趣的是,当我在轮询之间设置一个很大的延迟(100 毫秒)时,我可以看到当值控件(触发器或摇杆)被移动时,数据包数量增加了不止一个。我认为,这表明设备正在发送数据包而无需等待轮询,并且每次轮询时我只会获取最新的数据包。如果是这种情况,似乎我应该能够在发送数据包之前进行阻塞,并且仅在发生这种情况时做出反应,而不必进行轮询。但我找不到任何迹象表明这是一种选择。因为我可以使用 HID API 进行阻止,所以我不想不尝试就放弃(包括在这里寻求建议)。

没有为控制器编写自己的驱动程序(我不确定它是否是没有专有文档的选项),有谁知道我如何使用重叠 I/O(或任何其他阻塞方法)来读取 Xbox 360 控制器XInput 的方式,触发器作为单独的值,帽子作为四个按钮?

下面是我编写的一些代码,用于读取控制器并显示数据包编号在两次读取之间可以跳跃不止一个:

#include <Windows.h>
#include <Xinput.h>
#include <stdio.h>

#define MAX_CONTROLLERS 4

int main()
{
    DWORD userIndex;

    XINPUT_STATE xs;
    XINPUT_VIBRATION v;

    XInputEnable(TRUE);

    // Which one are we?

    for (userIndex = 0; userIndex < XUSER_MAX_COUNT; ++userIndex)
        if (XInputGetState(userIndex, &xs) == ERROR_SUCCESS)
            break;

    if (userIndex == XUSER_MAX_COUNT)
    {
        printf("Couldn't find an Xbox 360 controller.\n");
        getchar();
        return -1;
    }

    printf("Using controller #%1d.\n", userIndex);

    while (TRUE)
    {
        DWORD res = XInputGetState(userIndex, &xs);

        printf("%5d %6d: %3d %3d %3d %3d %3d %3d 0x%04X\n",
                res,
            xs.dwPacketNumber,
            xs.Gamepad.bLeftTrigger & 0xFF,
            xs.Gamepad.bRightTrigger & 0xFF,
            xs.Gamepad.sThumbLX & 0xFF,
            xs.Gamepad.sThumbLY & 0xFF,
            xs.Gamepad.sThumbRX & 0xFF,
            xs.Gamepad.sThumbRY & 0xFF,
            xs.Gamepad.wButtons);

        if (xs.Gamepad.wButtons == 0x000F) // mash down the hat
            break;

        Sleep(100);
    }

    getchar();
    return 0;
}

请注意 DirectInput 没有多大帮助,因为它还将触发器组合成一个值。

谢谢!

4

1 回答 1

1

不确定这样做有什么好处,但是您能否编写一个定期轮询的线程,然后在状态更改时设置一个信号量(或其他信号)。然后你的主线程可能会阻塞等待来自轮询线程的信号。但是这个系统可能没有任何优势,因为在某些控制器上,无论您是否移动它们,拇指杆的值都会随着帧的变化而略有变化。(噪音)你当然可以忽略小的变化,只在发生大的变化时才发出信号。

于 2016-08-24T00:31:36.577 回答