如何使用Microsoft的IDirectInputDevice8在C++中实现游戏手柄代码?

3

我需要使用IDirectInputDevice8接口对Direct Input设备进行阻塞和非阻塞调用

在Linux中,我们使用select来进行阻塞操作,例如:

while( ::select(_jdev+1, &set, NULL, NULL, &tv) > 0) 
{
    if( ::read( _jdev, &js, sizeof( js_event ) ) != sizeof( js_event ) )
    {
        perror( "Joystick : read error" );
        return;
    }

    _handleEvent( js );
}

我该如何使用idirectinputdevice8接口完成同样的事情: https://msdn.microsoft.com/en-us/library/windows/desktop/ee417816(v=vs.85).aspx 即使我设置了IDirectInputDevice8 :: SetEventNotification(),我仍然需要每次调用poll()以获取新数据,这不是可行的解决方案,因为它会导致CPU旋转。
我该如何实现这一点?
***目前,我可以找到,迭代,连接和从游戏杆设备中获取数据。 我只是没有实现阻塞调用。
...这是我的实验/测试代码...请忽略语法错误
#include <windows.h>
#include <dinput.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include "joystick.h"


LPDIRECTINPUT8 di;
HRESULT hr;
LPDIRECTINPUTDEVICE8 joystick;
DIDEVCAPS capabilities;

BOOL CALLBACK
enumCallback(const DIDEVICEINSTANCE* instance, VOID* context)
{
    HRESULT hr;
    hr = di->CreateDevice(instance->guidInstance, &joystick, NULL);
    if (FAILED(hr)) {
        return DIENUM_CONTINUE;
    }
    return DIENUM_STOP;
}

int JoyStickProp()
{
    if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick2))) {
        return hr;
    }
    if (FAILED(hr = joystick->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE |
        DISCL_FOREGROUND))) {
        return hr;
    }
    capabilities.dwSize = sizeof(DIDEVCAPS);
    if (FAILED(hr = joystick->GetCapabilities(&capabilities))) {
        return hr;
    }
}

HRESULT JoyStickPoll(DIJOYSTATE2 *js)
{
    HRESULT     hr;

    if (joystick == NULL) {
        return S_OK;
    }

    // Poll the device to read the current state
    hr = joystick->Poll();
    if (FAILED(hr)) {
        hr = joystick->Acquire();
        while (hr == DIERR_INPUTLOST) {
            hr = joystick->Acquire();
        }

        if ((hr == DIERR_INVALIDPARAM) || (hr == DIERR_NOTINITIALIZED)) {
            return E_FAIL;
        }

        // If another application has control of this device, return successfully.
        // We'll just have to wait our turn to use the joystick.
        if (hr == DIERR_OTHERAPPHASPRIO) {
            return S_OK;
        }
    }

    // Get the input's device state
    if (FAILED(hr = joystick->GetDeviceState(sizeof(DIJOYSTATE2), js))) {
        return hr; // The device should have been acquired during the Poll()
    }

    return S_OK;
}

int main()
{


    // Create a DirectInput device
    if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
        IID_IDirectInput8, (VOID**)&di, NULL))) {
        return hr;
    }

    // Look for the first simple joystick we can find.
    if (FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback,
        NULL, DIEDFL_ATTACHEDONLY))) {
        return hr;
    }
    // Make sure we got a joystick
    if (joystick == NULL) {
        printf("Joystick not found.\n");
        return E_FAIL;
    }

    JoyStickProp();
    DIJOYSTATE2 diState;

    HANDLE ghWriteEvent;
    // Create joystick event stuff here
    ghWriteEvent = CreateEvent(
        NULL,               // default security attributes
        FALSE,               // manual-reset event
        FALSE,              // initial state is nonsignaled
        TEXT("WriteEvent")  // object name
        );

    //makesure we can read fromt the joystick before waiting for an event
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
    JoyStickPoll(&diState);
    printf("x axis %d", diState.lX);
    printf("y axis %d", diState.lY);
joystick->SetEventNotification(ghWriteEvent);
    while (TRUE) {

        DWORD dwResult = WaitForSingleObject(
            ghWriteEvent, // event handle
            INFINITE);

        switch (dwResult) {
        case WAIT_OBJECT_0:
            // Event 1 has been set. If the event was created as
            // autoreset, it has also been reset. 
            int x = 0;
            //ProcessInputEvent1();
            break;
        }
    }
}

这是Linux代码的一个例子,展示我想在Windows中实现的内容...它与Windows代码或当前问题无关。 - luke signh
1
不相关。在C++中,下划线具有特殊含义,这是由C++标准规定的。无论代码是为Linux还是Windows编写,规则都是相同的。只有结果会有所不同。请注意如何使用它们。 - user4581301
1
只有在状态改变时,您才需要处理通知事件并进行轮询。除非状态发生改变,否则不需要进行轮询和“自旋CPU”。 - Roman R.
@Roman 是的!但我该怎么做呢?只有在调用directInputdevice8::poll之后才会生成通知事件: https://msdn.microsoft.com/zh-cn/library/windows/desktop/microsoft.directx_sdk.idirectinputdevice8.idirectinputdevice8.poll(v=vs.85).aspx我已经在我的代码中添加了通知事件,但是我发现只有在调用poll之后才会触发该事件。 - luke signh
不,你需要在收到API生成的通知后进行轮询。请注意,这是由API触发的。 - Roman R.
显示剩余2条评论
1个回答

2

MSDN文档中关于IDirectInputDevice8::SetEventNotification()提供了相关的代码片段。

您需要调用CreateEvent()并将返回的HANDLE作为参数传递给SetEventNotification()。当发生状态更改时,事件将被信号化。您可以等待事件,然后一旦收到通知,就可以使用IDirectInputDevice8::GetDeviceState()获取新的状态数据。

所有标准的等待函数都适用于等待事件被信号化。以下代码片段展示了一种方法,使用MsgWaitForMultipleObjects()

while (TRUE) { 

    dwResult = MsgWaitForMultipleObjects(2, ah, FALSE, INFINITE, QS_ALLINPUT); 
    switch (dwResult) { 
        case WAIT_OBJECT_0: 
            // Event 1 has been set. If the event was created as
            // autoreset, it has also been reset. 
            ProcessInputEvent1();
            break; 
IDirectInputDevice8::Poll()方法用于无法生成事件通知的设备。您需要轮询它们,因为它们无法按照您希望的方式运行。

某些游戏杆和其他游戏设备或其中的特定对象不会生成硬件中断,也不会返回任何数据或信号任何事件,直到您调用IDirectInputDevice8::Poll方法

只有在设备的通知不起作用时,才需要进行轮询。上面的链接解释了如何确定是否需要轮询。

我已经完成了这个任务,除非我做错了,否则事件不会被调用,直到我调用poll。我想要做的/应该发生的是等待状态改变事件,然后我才能调用poll。 - luke signh
我刚意识到我创建了一个事件和一个单独的线程来运行它...所以也许这会导致一些问题...让我尝试并回复你。 - luke signh
2
@lukesignh:使用CreateEvent()创建的事件不与任何特定线程绑定。一个或多个线程可以等待事件,而另一个线程则发出信号。所以这不是你的问题。请[编辑]你的问题,展示实际上对你不起作用的代码。 - Remy Lebeau
@lukesignh,你有没有看罗曼给你链接的轮询和事件通知文档?"要确定每次检索数据是否需要调用IDirectInputDevice8::Poll,请先为设备设置数据格式,然后调用IDirectInputDevice8::GetCapabilities方法并检查DIDEVCAPS结构中的DIDC_POLLEDDATAFORMAT标志。"你的代码没有检查GetCapabilities()的输出以确定是使用Poll()还是SetEventNotification() - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接