如何使用 Microsoft 的 IDirectInputDevice8 在 C++ 中使用 Joystick 代码?

How to implement Joystick code using in C++ using Microsoft's IDirectInputDevice8?

我需要进行阻塞和非 阻塞 调用 以使用 IDirectInputDevice8[= 轮询直接输入设备34=]界面。

在 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 旋转。

我怎样才能做到这一点?

*** 目前,我可以从操纵杆设备中查找、迭代、连接和获取数据。我只是没有实现阻塞调用。

...这是我的 experimental/test 代码...请忽略语法错误

#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;
        }
    }
}

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

您需要致电CreateEvent() and provide the returned HANDLE as an argument to SetEventNotification(). The event will be signaled for you when a state change occurs. You can wait on the event, and then once you have the notification, you get new state data using IDirectInputDevice8::GetDeviceState()

所有标准Wait Functions are applicable to waiting for the event to be signaled. The following code snippet shows you one way, using 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()方法用于不生成事件通知的设备。您应该对它们进行轮询,因为它们无法按照您希望的方式运行。

Some joysticks and other game devices, or particular objects on them, do not generate hardware interrupts and do not return any data or signal any events until you call the IDirectInputDevice8::Poll method.

只有当通知对设备不起作用时,您才会在需要时进行轮询。上面的link说明了如何判断是否需要轮询