WM_INPUT
WM_INPUT — это системное сообщение операционной системы Microsoft Windows, которое отправляется окну приложения для передачи необработанных (raw) данных от устройств ввода, таких как клавиатура, мышь, джойстик или сенсорная панель. В отличие от стандартных сообщений (например, WM_KEYDOWN или WM_MOUSEMOVE), WM_INPUT предоставляет программе прямой доступ к данным с устройства до их обработки драйвером мыши или клавиатуры, что позволяет реализовать более точное и гибкое управление вводом, особенно в играх, симуляторах и профессиональных приложениях.
История и предпосылки появления
До появления Windows XP (2001 год) разработчики, нуждавшиеся в высокоточной обработке ввода, были ограничены стандартными сообщениями Windows, которые уже проходили через системную очередь сообщений и подвергались обработке драйверами. Это приводило к задержкам, потере точности (например, при работе с мышью — к эффекту «ускорения» или «сглаживания») и невозможности получать данные от нескольких устройств одного типа одновременно.
С ростом популярности трёхмерных игр и профессиональных приложений (CAD, 3D-моделирование) возникла потребность в прямом доступе к данным устройств ввода. В ответ на это корпорация Microsoft ввела в Windows XP механизм Raw Input, частью которого и является сообщение WM_INPUT. Этот механизм был разработан для замены устаревших API DirectInput (из состава DirectX) в задачах, не связанных с геймпадами, и для обеспечения более низкой задержки и точности.
Механизм работы
Регистрация устройств
Для получения сообщения WM_INPUT приложение должно сначала зарегистрировать интересующие его устройства с помощью функции RegisterRawInputDevices. Эта функция принимает массив структур RAWINPUTDEVICE, в которых указываются:
- usUsagePage и usUsage — идентификаторы типа устройства (например, HID-класс мыши, клавиатуры или джойстика);
- dwFlags — флаги, определяющие режим работы (например,
RIDEV_INPUTSINK— получать данные даже когда окно не в фокусе); - hwndTarget — дескриптор окна, которое будет получать сообщения.
Только после успешной регистрации окно начинает получать WM_INPUT при каждом событии от зарегистрированного устройства.
Формат сообщения
Сообщение WM_INPUT передаётся через параметр wParam (содержит флаг RIM_INPUT или RIM_INPUTSINK) и lParam (содержит дескриптор структуры RAWINPUT). Для извлечения данных приложение вызывает функцию GetRawInputData, которая возвращает структуру RAWINPUT, содержащую:
- header — заголовок с типом устройства (
RIM_TYPEMOUSE,RIM_TYPEKEYBOARD,RIM_TYPEHID); - data — объединение (union) с детальными данными:
- для мыши: смещение по осям X/Y, флаги кнопок, колёсико прокрутки;
- для клавиатуры: скан-код, виртуальный код, флаги нажатия/отпускания;
- для HID-устройств: необработанные байты отчёта.
Обработка
После получения данных приложение должно вызвать DefWindowProc или самостоятельно освободить память, связанную с сообщением, с помощью функции GetRawInputData с флагом RID_INPUT. Невыполнение этого требования может привести к утечкам памяти.
Преимущества перед стандартными сообщениями
- Точность и отсутствие сглаживания: WM_INPUT передаёт сырые данные от мыши до любых системных фильтров (таких как ускорение указателя). Это критически важно для шутеров от первого лица (FPS) и киберспорта.
- Низкая задержка: сообщение минует очередь сообщений Windows и обрабатывается напрямую, что снижает задержку ввода.
- Поддержка нескольких устройств: приложение может различать данные от разных мышей или клавиатур, подключённых к одному компьютеру.
- Доступ к данным HID-устройств: позволяет работать с нестандартными устройствами (графические планшеты, трекболы, джойстики), которые не имеют стандартных драйверов мыши или клавиатуры.
Недостатки и ограничения
- Сложность реализации: требуется явная регистрация устройств и ручное извлечение данных, что увеличивает объём кода по сравнению с обработкой стандартных сообщений.
- Отсутствие автоматической обработки: приложение должно самостоятельно интерпретировать сырые данные, включая расчёт ускорения, фильтрацию дребезга и т.д.
- Совместимость: не все старые приложения и библиотеки поддерживают Raw Input; для обратной совместимости может потребоваться использование DirectInput (особенно для геймпадов).
Применение
Игры и симуляторы
- Шутеры от первого лица (Counter-Strike, Overwatch, Valorant) — для исключения ускорения мыши и обеспечения максимальной точности прицеливания.
- Авиасимуляторы (Microsoft Flight Simulator, X-Plane) — для обработки данных от джойстиков и рулей с высокой частотой опроса.
- Гоночные симуляторы (Assetto Corsa, iRacing) — для работы с рулевыми колёсами и педалями.
Профессиональные приложения
- CAD-системы (AutoCAD, SolidWorks) — для точного управления курсором при черчении.
- Графические редакторы (Adobe Photoshop, Krita) — для работы с графическими планшетами, где требуется передача давления и наклона пера.
- Медицинское оборудование — для обработки данных от специализированных манипуляторов.
Системные утилиты
- Программы для записи макросов — для захвата событий от клавиатуры и мыши на низком уровне.
- Античит-системы — для анализа ввода на предмет автоматизации или ботов.
Пример использования (псевдокод)
```cpp // Регистрация мыши для получения WM_INPUT RAWINPUTDEVICE rid; rid.usUsagePage = 0x01; // HID-класс rid.usUsage = 0x02; // Мышь rid.dwFlags = RIDEV_INPUTSINK; // Получать данные даже в фоне rid.hwndTarget = hWnd;
RegisterRawInputDevices(&rid, 1, sizeof(rid));
// Обработка сообщения case WM_INPUT: { UINT dwSize; GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); BYTE buffer = new BYTE[dwSize]; GetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &dwSize, sizeof(RAWINPUTHEADER)); RAWINPUT raw = (RAWINPUT*)buffer; if (raw->header.dwType == RIM_TYPEMOUSE) { int dx = raw->data.mouse.lLastX; int dy = raw->data.mouse.lLastY; // Использовать dx, dy для управления камерой } delete[] buffer; break; } ```
Сравнение с альтернативами
| Характеристика | WM_INPUT (Raw Input) | DirectInput | Стандартные сообщения (WM_MOUSEMOVE) |
|---|---|---|---|
| Задержка | Низкая | Средняя | Высокая (через очередь) |
| Точность | Максимальная (сырые данные) | Высокая | Низкая (сглажена) |
| Поддержка нескольких устройств | Да | Да | Нет |
| Сложность реализации | Высокая | Средняя | Низкая |
| Совместимость с геймпадами | Ограниченная | Полная | Нет |
| Поддержка в современных версиях Windows | Полная | Устаревшая (рекомендуется XInput) | Полная |
Интересные факты
- В Windows 8 и более поздних версиях Microsoft рекомендует использовать Raw Input для всех новых приложений, требующих точного ввода, а DirectInput — только для обратной совместимости со старыми геймпадами.
- Сообщение WM_INPUT может быть отправлено с флагом
RIM_INPUTSINK, что позволяет приложению получать данные даже когда оно не активно (например, для программ-кейлоггеров, что требует особых мер безопасности). - В некоторых играх (например, Quake III Arena) использование Raw Input стало стандартом де-факто для профессиональных игроков, так как исключает влияние системного ускорения мыши.
Источники
- Microsoft Docs: Raw Input (Windows App Development).
- MSDN Magazine: "Raw Input in Windows XP" (2002).
- "Programming Windows" by Charles Petzold (5th edition, 1998) — глава о вводе.
- DirectX SDK Documentation (раздел о DirectInput и Raw Input).
- Статья "Raw Input vs DirectInput" на сайте GameDev.net (2005).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →