2016-08-23 150 views
0

我试图从我的XBox 360控制器中读取而不进行轮询。 (准确地说,我实际上使用的是Logitech F310,但我的Windows 10 PC将它视为XBox 360控制器。)我写了一些相当讨厌的HID代码,它们使用重叠的I/O在两个线程中阻塞事件,一个表明有一个报告准备从HID设备读取,另一个表明UI线程已经请求HID线程退出。这很好,但HID驱动程序的行为与XInput有所不同。特别是,它将两个触发器合并为一个单一的值,只是传递他们的差异(当游戏者的手指离开控制时,游戏期望HID值为0x80)。 XInput将它们视为两个不同的值,这是一个很大的改进。此外,XInput将帽子开关报告为四位,这意味着您实际上可以获得十个状态:未压缩,N,NE,E,SE,S,SW,W,NW和全下(最后可能是很难成功使用,但至少它在那里,如果你想要它,我一直在使用它来退出我的轮询循环)。读取HID API以外的XBox 360控制器时,有什么办法阻止?

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

现在,当您使用XInput读取设备时,您不仅可以获得所有控件的状态,还可以获得包号。 MSDN表示只有当控件的状态发生变化时,数据包号码才会更改。这样,如果连续的数据包编号相同,则不必在第一个数据包之后进行任何处理,因为您知道控制器状态没有改变。但你仍在投票,对我而言,这有点粗俗。然而,当我在我的民意调查(100毫秒)之间放了一个很大的延迟时,我可以看到,当值控制(触发器或棍棒)时,包数量增加了一个以上移动。我认为,这表明设备在不等待轮询的情况下发送数据包,并且每次轮询时我只收到最近的数据包。如果是这样,似乎我应该能够阻止,直到发送一个数据包,并且只有在发生这种情况时才作出反应,而不是必须进行轮询。但我找不到任何迹象表明这是一种选择。因为我可以阻止HID API,所以我不想放弃,而不尝试(包括在此寻求建议)。我不知道如何使用重叠的I/O(或任何其他阻塞方法)来读取控制器(我不知道甚至没有专有文档的选项),没有人知道如何使用重叠的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的是没有太大的帮助,因为它也结合了将触发器合并为一个值。

谢谢!

回答

1

不确定这是否有任何优势,但是您可以编写一个线程轮询定期间隔,然后在状态发生变化时设置信号量(或其他信号)。然后,您的主线程可能会阻止等待来自轮询线程的信号。但是,这个系统可能没有任何优势,因为在某些控制器上,无论您是否移动,拇指指杆的值都会稍有变化。 (噪声)你当然可以忽略小的变化,只在发生大的变化时发出信号。

+0

谢谢,芯片。那就是我期望我会最终结束的地方。微软实际上强烈建议我们将代码称为“死区”,因为价值控制的理想“脱手”价值,正是由于噪音问题(我的实验证实这些棒不总是回到完全相同价值每次我放开他们,但讽刺的是,触发器)。模拟对ReadFile的异步调用的线程可以正常工作,而对XInputGetState的调用在我的机器上只需要20us。非常轻的开销。只是粗俗的;)。 –

相关问题