RawInput: HidP_GetUsages возвращает HIDP_STATUS_INCOMPATIBLE_REPORT_ID (контроллер Bluetooth PS4)
Я использую RawInput и HID для обработки геймпадов. Я тестировал контроллер PS4 (Dual Shock 4) в проводном и Bluetooth-режимах. Проводной режим работает на 100% правильно, но режим Bluetooth не работает с HIDP_STATUS_INCOMPATIBLE_REPORT_ID
после звонка HidP_GetUsages
, Мой код в основном такой же, как https://www.codeproject.com/Articles/185522/Using-the-Raw-Input-API-to-Process-Joystick-Input, который имеет ту же проблему.
Вот описание Microsoft для этой проблемы:
HIDP_STATUS_INCOMPATIBLE_REPORT_ID Запрошенное использование содержится в отчете, поддерживаемом коллекцией HID, но не в отчете, указанном приложением или драйвером.
К сожалению, у меня нет других геймпадов Bluetooth, поэтому я не могу исключить, если это проблема с Bluetooth или проблема с DS4. Кто-то в комментариях к статье имел похожую проблему ( https://www.codeproject.com/Messages/3866406/Re-Great-article-but-something-isnt-right.aspx) и говорит, что может быть несколько вызовов WM_INPUT с разные куски данных, что верно, но ни один из них не избежит ошибки.
Примечание: когда у меня подключен контроллер и включен Bluetooth, контроллер отправляет вход под двумя разными именами устройств (RIDI_DEVICENAME
). Тем не менее, проводное устройство работает, а Bluetooth нет.
Вот код из связанной статьи с моим комментарием: /********** HIDP_STATUS_INCOMPATIBLE_REPORT_ID **********/
///////////////////////////////////////////////////////////////////////////////
//
// Raw Input API sample showing joystick support
//
// Author: Alexander Böcken
// Date: 04/22/2011
//
// Copyright 2011 Alexander Böcken
//
///////////////////////////////////////////////////////////////////////////////
#include <Windows.h>
#include <tchar.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <hidsdi.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define WC_MAINFRAME TEXT("MainFrame")
#define MAX_BUTTONS 128
#define CHECK(exp) { if(!(exp)) goto Error; }
#define SAFE_FREE(p) { if(p) { HeapFree(hHeap, 0, p); (p) = NULL; } }
//
// Global variables
//
BOOL bButtonStates[MAX_BUTTONS];
LONG lAxisX;
LONG lAxisY;
LONG lAxisZ;
LONG lAxisRz;
LONG lHat;
INT g_NumberOfButtons;
void ParseRawInput(PRAWINPUT pRawInput)
{
PHIDP_PREPARSED_DATA pPreparsedData;
HIDP_CAPS Caps;
PHIDP_BUTTON_CAPS pButtonCaps;
PHIDP_VALUE_CAPS pValueCaps;
USHORT capsLength;
UINT bufferSize;
HANDLE hHeap;
USAGE usage[MAX_BUTTONS];
ULONG i, usageLength, value;
pPreparsedData = NULL;
pButtonCaps = NULL;
pValueCaps = NULL;
hHeap = GetProcessHeap();
//
// Get the preparsed data block
//
CHECK( GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize) == 0 );
CHECK( pPreparsedData = (PHIDP_PREPARSED_DATA)HeapAlloc(hHeap, 0, bufferSize) );
CHECK( (int)GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, &bufferSize) >= 0 );
//
// Get the joystick's capabilities
//
// Button caps
CHECK( HidP_GetCaps(pPreparsedData, &Caps) == HIDP_STATUS_SUCCESS )
CHECK( pButtonCaps = (PHIDP_BUTTON_CAPS)HeapAlloc(hHeap, 0, sizeof(HIDP_BUTTON_CAPS) * Caps.NumberInputButtonCaps) );
capsLength = Caps.NumberInputButtonCaps;
CHECK( HidP_GetButtonCaps(HidP_Input, pButtonCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS )
g_NumberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1;
// Value caps
CHECK( pValueCaps = (PHIDP_VALUE_CAPS)HeapAlloc(hHeap, 0, sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps) );
capsLength = Caps.NumberInputValueCaps;
CHECK( HidP_GetValueCaps(HidP_Input, pValueCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS )
//
// Get the pressed buttons
//
/********** HIDP_STATUS_INCOMPATIBLE_REPORT_ID **********/
usageLength = g_NumberOfButtons;
CHECK(
HidP_GetUsages(
HidP_Input, pButtonCaps->UsagePage, 0, usage, &usageLength, pPreparsedData,
(PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid
) == HIDP_STATUS_SUCCESS );
ZeroMemory(bButtonStates, sizeof(bButtonStates));
for(i = 0; i < usageLength; i++)
bButtonStates[usage[i] - pButtonCaps->Range.UsageMin] = TRUE;
//
// Get the state of discrete-valued-controls
//
for(i = 0; i < Caps.NumberInputValueCaps; i++)
{
CHECK(
HidP_GetUsageValue(
HidP_Input, pValueCaps[i].UsagePage, 0, pValueCaps[i].Range.UsageMin, &value, pPreparsedData,
(PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid
) == HIDP_STATUS_SUCCESS );
switch(pValueCaps[i].Range.UsageMin)
{
case 0x30: // X-axis
lAxisX = (LONG)value - 128;
break;
case 0x31: // Y-axis
lAxisY = (LONG)value - 128;
break;
case 0x32: // Z-axis
lAxisZ = (LONG)value - 128;
break;
case 0x35: // Rotate-Z
lAxisRz = (LONG)value - 128;
break;
case 0x39: // Hat Switch
lHat = value;
break;
}
}
//
// Clean up
//
Error:
SAFE_FREE(pPreparsedData);
SAFE_FREE(pButtonCaps);
SAFE_FREE(pValueCaps);
}
void DrawButton(HDC hDC, int i, int x, int y, BOOL bPressed)
{
HBRUSH hOldBrush, hBr;
TCHAR sz[4];
RECT rc;
if(bPressed)
{
hBr = CreateSolidBrush(RGB(192, 0, 0));
hOldBrush = (HBRUSH)SelectObject(hDC, hBr);
}
rc.left = x;
rc.top = y;
rc.right = x + 30;
rc.bottom = y + 30;
Ellipse(hDC, rc.left, rc.top, rc.right, rc.bottom);
_stprintf_s(sz, ARRAY_SIZE(sz), TEXT("%d"), i);
DrawText(hDC, sz, -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
if(bPressed)
{
SelectObject(hDC, hOldBrush);
DeleteObject(hBr);
}
}
void DrawCrosshair(HDC hDC, int x, int y, LONG xVal, LONG yVal)
{
Rectangle(hDC, x, y, x + 256, y + 256);
MoveToEx(hDC, x + xVal - 5 + 128, y + yVal + 128, NULL);
LineTo(hDC, x + xVal + 5 + 128, y + yVal + 128);
MoveToEx(hDC, x + xVal + 128, y + yVal - 5 + 128, NULL);
LineTo(hDC, x + xVal + 128, y + yVal + 5 + 128);
}
void DrawDPad(HDC hDC, int x, int y, LONG value)
{
LONG i;
for(i = 0; i < 8; i++)
{
HBRUSH hOldBrush;
HBRUSH hBr;
int xPos = (int)(sin(-2 * M_PI * i / 8 + M_PI) * 80.0) + 80;
int yPos = (int)(cos(2 * M_PI * i / 8 + M_PI) * 80.0) + 80;
if(value == i)
{
hBr = CreateSolidBrush(RGB(192, 0, 0));
hOldBrush = (HBRUSH)SelectObject(hDC, hBr);
}
Ellipse(hDC, x + xPos, y + yPos, x + xPos + 20, y + yPos + 20);
if(value == i)
{
SelectObject(hDC, hOldBrush);
DeleteObject(hBr);
}
}
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
//
// Register for joystick devices
//
RAWINPUTDEVICE rid;
rid.usUsagePage = 1;
rid.usUsage = 4; // Joystick
rid.dwFlags = 0;
rid.hwndTarget = hWnd;
if(!RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE)))
return -1;
}
return 0;
case WM_INPUT:
{
//
// Get the pointer to the raw device data, process it and update the window
//
PRAWINPUT pRawInput;
UINT bufferSize;
HANDLE hHeap;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &bufferSize, sizeof(RAWINPUTHEADER));
hHeap = GetProcessHeap();
pRawInput = (PRAWINPUT)HeapAlloc(hHeap, 0, bufferSize);
if(!pRawInput)
return 0;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, pRawInput, &bufferSize, sizeof(RAWINPUTHEADER));
ParseRawInput(pRawInput);
HeapFree(hHeap, 0, pRawInput);
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
}
return 0;
case WM_PAINT:
{
//
// Draw the buttons and axis-values
//
PAINTSTRUCT ps;
HDC hDC;
int i;
hDC = BeginPaint(hWnd, &ps);
SetBkMode(hDC, TRANSPARENT);
for(i = 0; i < g_NumberOfButtons; i++)
DrawButton(hDC, i+1, 20 + i * 40, 20, bButtonStates[i]);
DrawCrosshair(hDC, 20, 100, lAxisX, lAxisY);
DrawCrosshair(hDC, 296, 100, lAxisZ, lAxisRz);
DrawDPad(hDC, 600, 140, lHat);
EndPaint(hWnd, &ps);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
HWND hWnd;
MSG msg;
WNDCLASSEX wcex;
//
// Register window class
//
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wcex.hInstance = hInstance;
wcex.lpfnWndProc = WindowProc;
wcex.lpszClassName = WC_MAINFRAME;
wcex.lpszMenuName = NULL;
wcex.style = CS_HREDRAW | CS_VREDRAW;
if(!RegisterClassEx(&wcex))
return -1;
//
// Create window
//
hWnd = CreateWindow(WC_MAINFRAME, TEXT("Joystick using Raw Input API"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
//
// Message loop
//
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
Изменить: я проверил все с GLFW joysticks
тест, и у него точно такая же проблема. Нечувствительный блютуз PS4. GLFW использует XInput + DirectInput в качестве бэкенда, поэтому PS4 будет идти по маршруту DirectInput, который, как я полагаю, написан поверх RawInput.