Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом разделе приведен пример кода для следующих целей:
- регистрация необработанных входных
- выполнение стандартного чтения необработанных данных
- Выполнение буферизованного чтения необработанных данных
Регистрация необработанных входных данных
Пример 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 содержит дескриптор HRAWINPUT в lParam , ссылающийся на текущее событие ввода, необходимо считывать с помощью GetRawInputData перед вызовом DefWindowProc.
Для высокочастотных устройств, таких как мыши на 1000Гц, несколько событий могут накапливаться между итерациями цикла сообщений. В этом случае следуйте инструкциям 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();
}