次の方法で共有


生入力の使用

このセクションには、次の目的のサンプル コードが含まれています。

未加工入力の登録

例 1

このサンプルでは、アプリケーションはゲーム コントローラー (ゲーム パッドとジョイスティックの両方) からの生入力と、応答マシンを除くテレフォニー使用ページからのすべてのデバイスを指定します。

RAWINPUTDEVICE Rid[4];
        
Rid[0].usUsagePage = 0x01;          // HID_USAGE_PAGE_GENERIC
Rid[0].usUsage = 0x05;              // HID_USAGE_GENERIC_GAMEPAD
Rid[0].dwFlags = 0;                 // adds game pad
Rid[0].hwndTarget = 0;

Rid[1].usUsagePage = 0x01;          // HID_USAGE_PAGE_GENERIC
Rid[1].usUsage = 0x04;              // HID_USAGE_GENERIC_JOYSTICK
Rid[1].dwFlags = 0;                 // adds joystick
Rid[1].hwndTarget = 0;

Rid[2].usUsagePage = 0x0B;          // HID_USAGE_PAGE_TELEPHONY
Rid[2].usUsage = 0x00; 
Rid[2].dwFlags = RIDEV_PAGEONLY;    // adds all devices from telephony page
Rid[2].hwndTarget = 0;

Rid[3].usUsagePage = 0x0B;          // HID_USAGE_PAGE_TELEPHONY
Rid[3].usUsage = 0x02;              // HID_USAGE_TELEPHONY_ANSWERING_MACHINE
Rid[3].dwFlags = RIDEV_EXCLUDE;     // excludes answering machines
Rid[3].hwndTarget = 0;

if (RegisterRawInputDevices(Rid, 4, sizeof(Rid[0])) == FALSE)
{
    //registration failed. Call GetLastError for the cause of the error.
}

例 2

このサンプルでは、アプリケーションはキーボードとマウスからの生の入力を必要としますが、 従来のキーボードマウス ウィンドウのメッセージ (同じキーボードとマウスから送信されるメッセージ) を無視したいと考えています。

RAWINPUTDEVICE Rid[2];
        
Rid[0].usUsagePage = 0x01;          // HID_USAGE_PAGE_GENERIC
Rid[0].usUsage = 0x02;              // HID_USAGE_GENERIC_MOUSE
Rid[0].dwFlags = RIDEV_NOLEGACY;    // adds mouse and also ignores legacy mouse messages
Rid[0].hwndTarget = 0;

Rid[1].usUsagePage = 0x01;          // HID_USAGE_PAGE_GENERIC
Rid[1].usUsage = 0x06;              // HID_USAGE_GENERIC_KEYBOARD
Rid[1].dwFlags = RIDEV_NOLEGACY;    // adds keyboard and also ignores legacy keyboard messages
Rid[1].hwndTarget = 0;

if (RegisterRawInputDevices(Rid, 2, sizeof(Rid[0])) == FALSE)
{
    //registration failed. Call GetLastError for the cause of the error
}

生入力の標準読み取りの実行

このサンプルでは、 WM_INPUT メッセージ ハンドラーから生の入力を標準で読み取るための最小パターンを示します。 各WM_INPUTメッセージには、現在の入力イベントを参照する lParamHRAWINPUT ハンドルが含まれています。DefWindowProc を呼び出す前に、GetRawInputData を介して読み取る必要があります。

1000Hz のマウスなどの高周波デバイスの場合、メッセージ ループイテレーション間で複数のイベントが蓄積される可能性があります。 その場合は、 GetRawInputBuffer に従って残りのキューをドレインします。以下のオプションのフェーズ 2 を参照してください。

/* Initialized once at startup */
UINT  g_bufferSize = 64 * sizeof(RAWINPUT);
void* g_pBuffer    = NULL;

/* Call once before entering the message loop: */
/* g_pBuffer = malloc(g_bufferSize); */

void ProcessInput(const RAWINPUT* input)
{
    if (input->header.dwType == RIM_TYPEKEYBOARD)
    {
        const RAWKEYBOARD* kb = &input->data.keyboard;
        const char* transition = (kb->Flags & RI_KEY_BREAK) ? "up" : "down";
        const char* extended   = (kb->Flags & RI_KEY_E0)    ? " e0" :
                                 (kb->Flags & RI_KEY_E1)    ? " e1" : "";
        printf("keyboard: vk=0x%02x scan=0x%02x %s%s msg=0x%04x extra=0x%08x\n",
            kb->VKey, kb->MakeCode, transition, extended,
            kb->Message, kb->ExtraInformation);
    }
    else if (input->header.dwType == RIM_TYPEMOUSE)
    {
        const RAWMOUSE* mouse = &input->data.mouse;
        const char* moveMode  = (mouse->usFlags & MOUSE_MOVE_ABSOLUTE) ? "abs" : "rel";

        printf("mouse: move %s dx=%d dy=%d\n", moveMode, mouse->lLastX, mouse->lLastY);

        if (mouse->usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)   printf("  left down\n");
        if (mouse->usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP)     printf("  left up\n");
        if (mouse->usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN)  printf("  right down\n");
        if (mouse->usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP)    printf("  right up\n");
        if (mouse->usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN) printf("  middle down\n");
        if (mouse->usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_UP)   printf("  middle up\n");
        if (mouse->usButtonFlags & RI_MOUSE_BUTTON_4_DOWN)      printf("  x1 down\n");
        if (mouse->usButtonFlags & RI_MOUSE_BUTTON_4_UP)        printf("  x1 up\n");
        if (mouse->usButtonFlags & RI_MOUSE_BUTTON_5_DOWN)      printf("  x2 down\n");
        if (mouse->usButtonFlags & RI_MOUSE_BUTTON_5_UP)        printf("  x2 up\n");

        if (mouse->usButtonFlags & RI_MOUSE_WHEEL)
            printf("  wheel delta=%d\n",  (int)(short)mouse->usButtonData);
        if (mouse->usButtonFlags & RI_MOUSE_HWHEEL)
            printf("  hwheel delta=%d\n", (int)(short)mouse->usButtonData);
    }
    else if (input->header.dwType == RIM_TYPEHID)
    {
        const RAWHID* hid = &input->data.hid;
        printf("hid: count=%u size=%u\n", hid->dwCount, hid->dwSizeHid);
    }
}

void DrainRawInputQueue(void)
{
    for (;;)
    {
        UINT bufferSize = g_bufferSize;
        UINT count = GetRawInputBuffer((RAWINPUT*)g_pBuffer, &bufferSize, sizeof(RAWINPUTHEADER));

        if (count == 0)
            break;

        if (count == (UINT)-1)
        {
            /* Buffer too small — grow and retry. */
            g_bufferSize = max(bufferSize, g_bufferSize * 2);
            g_pBuffer = realloc(g_pBuffer, g_bufferSize);
            if (g_pBuffer == NULL)
                break;
            continue;
        }

        {
            RAWINPUT* ri = (RAWINPUT*)g_pBuffer;
            UINT i;
            for (i = 0; i < count; ++i, ri = NEXTRAWINPUTBLOCK(ri))
                ProcessInput(ri);
        }
        /* Do not break — there may be more events in the queue. */
    }
}

/* ... */

case WM_INPUT:
{
    /* Phase 1: read the event carried by this WM_INPUT message. */
    UINT bufferSize = g_bufferSize;
    if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, g_pBuffer, &bufferSize, sizeof(RAWINPUTHEADER)) != (UINT)-1)
    {
        ProcessInput((RAWINPUT*)g_pBuffer);
    }

    /* Phase 2 (optional): drain any additional events that accumulated in the queue since this message was posted.
     * Recommended for high-frequency devices such as mice at 1000Hz. */
    DrainRawInputQueue();

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

生入力のバッファー読み取りの実行

このサンプルでは、定期的なタイマーを使用して、固定レート バッチで生の入力を読み取る方法を示します。 WM_INPUT メッセージは DispatchMessage を通じて意図的にディスパッチされません。 GetMessage は、返される前に未加工の入力キューからメッセージを削除するため、明示的なメッセージ範囲フィルターを持つ PeekMessage のみが使用され、 WM_INPUT は完全にスキップされます。 他のすべてのメッセージは、 DispatchMessage を介して通常ディスパッチされます。 これにより、 GetRawInputBuffer がタイマー ティックごとに一度にすべてをドレインできるキュー内のすべての生の入力イベントが保持されます。 このアプローチは、各イベントに個別に反応するのではなく、一定のレートで入力を処理するゲーム ループやその他のアプリケーションに適しています。

MSG msg;
BOOL running = TRUE;

HWND hWnd = CreateWindowExW(0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);

RAWINPUTDEVICE rid[2] = {
    { 0x01, 0x02, RIDEV_INPUTSINK, hWnd }, /* mouse */
    { 0x01, 0x06, RIDEV_INPUTSINK, hWnd }, /* keyboard */
};
RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE));

/* Drain raw input queue every 16ms (~60Hz) */
SetTimer(hWnd, 1, 16, NULL);

/* Message loop — WM_INPUT is skipped via range filters so it
 * accumulates in the raw input queue for DrainRawInputQueue to drain. */
while (running)
{
    while (PeekMessageW(&msg, NULL, 0, WM_INPUT - 1, PM_REMOVE) ||
           PeekMessageW(&msg, NULL, WM_INPUT + 1, 0xFFFF, PM_REMOVE))
    {
        if (msg.message == WM_QUIT)
        {
            running = FALSE;
            break;
        }

        if (msg.message == WM_TIMER)
        {
            DrainRawInputQueue();
        }

        DispatchMessageW(&msg);
    }

    if (running)
        WaitMessage();
}