/** This function assumes that there are 4 buttons that can be read from the HID device which represent the button's usages values. This will not be true for all HID Devices. If you want to use this function, you must modify it for the correct number of buttons. Furthermore, you need to update nesmapping.h, or include a different mapping file, which holds constants for the button mapping. (You'll have to figure out the button mapping experimentally using log statements or breakpoints) **/ int Controller::getButtonInput(bool * pValues) { DWORD readbytecount = 0; _succ = ReadFile( _controllerHandle, _pInputReport, _pCaps->InputReportByteLength, &readbytecount, nullptr ); CHECK_FAILURE(_succ, BUTTON_INPUT_FAILURE); ULONG numButtonUsages = _pButtonCaps->Range.UsageMax - _pButtonCaps->Range.UsageMin + 1; //use the input report to generate button usage data HidP_GetUsages(HidP_Input, _pButtonCaps[0].UsagePage, _pButtonCaps[0].LinkCollection, _pButtonUsages, &numButtonUsages, _pPreparsedData, _pInputReport, _pCaps->InputReportByteLength); //reset button values to 0 for (unsigned int i = 0; i < NUM_BUTTONS; i++) { pValues[i] = false; } //every button that is currently pressed will have it's ID in the _pButtonUsages array for (unsigned int i = 0; i < numButtonUsages; i++) { USAGE dat = _pButtonUsages[i]; switch (dat) { case BUTTON_A: pValues[BUTTON_A_INDEX] = true; break; case BUTTON_B: pValues[BUTTON_B_INDEX] = true; break; case BUTTON_SELECT: pValues[BUTTON_SELECT_INDEX] = true; break; case BUTTON_START: pValues[BUTTON_START_INDEX] = true; break; default: break; } } return numButtonUsages; BUTTON_INPUT_FAILURE: return -1; }
HIDAPI NTSTATUS NTAPI HidP_GetUsagesEx( IN HIDP_REPORT_TYPE ReportType, IN USHORT LinkCollection, OUT PUSAGE_AND_PAGE ButtonList, IN OUT ULONG *UsageLength, IN PHIDP_PREPARSED_DATA PreparsedData, IN PCHAR Report, IN ULONG ReportLength) { return HidP_GetUsages(ReportType, HID_USAGE_PAGE_UNDEFINED, LinkCollection, &ButtonList->Usage, UsageLength, PreparsedData, Report, ReportLength); }
static void ParseRawInputHID(PRAWINPUT pRawInput) { PHIDP_PREPARSED_DATA pPreparsedData = NULL; HIDP_CAPS Caps; PHIDP_BUTTON_CAPS pButtonCaps = NULL; PHIDP_VALUE_CAPS pValueCaps = NULL; UINT bufferSize; ULONG usageLength, value; TCHAR name[1024] = {0}; UINT nameSize = 1024; RID_DEVICE_INFO devInfo = {0}; std::wstring devName; USHORT capsLength = 0; USAGE usage[MAX_BUTTONS] = {0}; Mappings *mapping = NULL; MapVector::iterator it; GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, name, &nameSize); devName = name; std::transform(devName.begin(), devName.end(), devName.begin(), ::toupper); for(it = mapVector.begin(); it != mapVector.end(); it++) { if((*it).hidPath == devName) { mapping = &(*it); break; } } if(mapping == NULL) return; CHECK( GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize) == 0 ); CHECK( pPreparsedData = (PHIDP_PREPARSED_DATA)malloc(bufferSize) ); CHECK( (int)GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, &bufferSize) >= 0 ); CHECK( HidP_GetCaps(pPreparsedData, &Caps) == HIDP_STATUS_SUCCESS ); //pSize = sizeof(devInfo); //GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICEINFO, &devInfo, &pSize); //Unset buttons/axes mapped to this device //ResetData(&mapping->data[0]); //ResetData(&mapping->data[1]); memset(&mapping->data[0], 0xFF, sizeof(wheel_data_t)); memset(&mapping->data[1], 0xFF, sizeof(wheel_data_t)); mapping->data[0].buttons = 0; mapping->data[1].buttons = 0; mapping->data[0].hatswitch = 0x8; mapping->data[1].hatswitch = 0x8; //Get pressed buttons CHECK( pButtonCaps = (PHIDP_BUTTON_CAPS)malloc(sizeof(HIDP_BUTTON_CAPS) * Caps.NumberInputButtonCaps) ); //If fails, maybe wheel only has axes capsLength = Caps.NumberInputButtonCaps; HidP_GetButtonCaps(HidP_Input, pButtonCaps, &capsLength, pPreparsedData); int numberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1; usageLength = numberOfButtons; NTSTATUS stat; if((stat = HidP_GetUsages( HidP_Input, pButtonCaps->UsagePage, 0, usage, &usageLength, pPreparsedData, (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid)) == HIDP_STATUS_SUCCESS ) { for(uint32_t i = 0; i < usageLength; i++) { uint16_t btn = mapping->btnMap[usage[i] - pButtonCaps->Range.UsageMin]; for(int j=0; j<2; j++) { PS2WheelTypes wt = (PS2WheelTypes)conf.WheelType[j]; if(PLY_IS_MAPPED(j, btn)) { uint32_t wtbtn = (1 << convert_wt_btn(wt, PLY_GET_VALUE(j, btn))) & 0xFFF; //12bit mask mapping->data[j].buttons |= wtbtn; } } } } /// Get axes' values CHECK( pValueCaps = (PHIDP_VALUE_CAPS)malloc(sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps) ); capsLength = Caps.NumberInputValueCaps; if(HidP_GetValueCaps(HidP_Input, pValueCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS ) { for(USHORT i = 0; i < capsLength; i++) { if(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 ) { continue; // if here then maybe something is up with HIDP_CAPS.NumberInputValueCaps } //fprintf(stderr, "Min/max %d/%d\t", pValueCaps[i].LogicalMin, pValueCaps[i].LogicalMax); //TODO can be simpler? //Get mapped axis for physical axis uint16_t v = 0; switch(pValueCaps[i].Range.UsageMin) { // X-axis 0x30 case HID_USAGE_GENERIC_X: v = mapping->axisMap[0]; break; // Y-axis case HID_USAGE_GENERIC_Y: v = mapping->axisMap[1]; break; // Z-axis case HID_USAGE_GENERIC_Z: v = mapping->axisMap[2]; break; // Rotate-X case HID_USAGE_GENERIC_RX: v = mapping->axisMap[3]; break; // Rotate-Y case HID_USAGE_GENERIC_RY: v = mapping->axisMap[4]; break; // Rotate-Z 0x35 case HID_USAGE_GENERIC_RZ: v = mapping->axisMap[5]; break; case HID_USAGE_GENERIC_HATSWITCH: //fprintf(stderr, "Hat: %02X\n", value); v = mapping->axisMap[6]; break; } int type = 0; for(int j=0; j<2; j++) { if(!PLY_IS_MAPPED(j, v)) continue; type = conf.WheelType[j]; switch(PLY_GET_VALUE(j, v)) { case PAD_AXIS_X: // X-axis //fprintf(stderr, "X: %d\n", value); // Need for logical min too? //generic_data.axis_x = ((value - pValueCaps[i].LogicalMin) * 0x3FF) / (pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin); if(type == WT_DRIVING_FORCE_PRO) mapping->data[j].steering = (value * 0x3FFF) / pValueCaps[i].LogicalMax; else //XXX Limit value range to 0..1023 if using 'generic' wheel descriptor mapping->data[j].steering = (value * 0x3FF) / pValueCaps[i].LogicalMax; break; case PAD_AXIS_Y: // Y-axis if(!(devInfo.hid.dwVendorId == 0x046D && devInfo.hid.dwProductId == 0xCA03)) //XXX Limit value range to 0..255 mapping->data[j].clutch = (value * 0xFF) / pValueCaps[i].LogicalMax; break; case PAD_AXIS_Z: // Z-axis //fprintf(stderr, "Z: %d\n", value); //XXX Limit value range to 0..255 mapping->data[j].throttle = (value * 0xFF) / pValueCaps[i].LogicalMax; break; case PAD_AXIS_RZ: // Rotate-Z //fprintf(stderr, "Rz: %d\n", value); //XXX Limit value range to 0..255 mapping->data[j].brake = (value * 0xFF) / pValueCaps[i].LogicalMax; break; case PAD_AXIS_HAT: // TODO Hat Switch //fprintf(stderr, "Hat: %02X\n", value); //TODO 4 vs 8 direction hat switch if(pValueCaps[i].LogicalMax == 4 && value < 4) mapping->data[j].hatswitch = HATS_8TO4[value]; else mapping->data[j].hatswitch = value; break; } } } } Error: SAFE_FREE(pPreparsedData); SAFE_FREE(pButtonCaps); SAFE_FREE(pValueCaps); }
static void ParseRawInputHID(PRAWINPUT pRawInput, HIDState *hs) { PHIDP_PREPARSED_DATA pPreparsedData = NULL; HIDP_CAPS Caps; PHIDP_BUTTON_CAPS pButtonCaps = NULL; PHIDP_VALUE_CAPS pValueCaps = NULL; UINT bufferSize = 0; ULONG usageLength, value; TCHAR name[1024] = {0}; UINT nameSize = 1024; RID_DEVICE_INFO devInfo = {0}; std::wstring devName; USHORT capsLength = 0; USAGE usage[32] = {0}; int numberOfButtons; GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, name, &nameSize); devName = name; std::transform(devName.begin(), devName.end(), devName.begin(), ::toupper); CHECK( GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize) == 0 ); CHECK( pPreparsedData = (PHIDP_PREPARSED_DATA)malloc(bufferSize) ); CHECK( (int)GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, &bufferSize) >= 0 ); CHECK( HidP_GetCaps(pPreparsedData, &Caps) == HIDP_STATUS_SUCCESS ); //Get pressed buttons CHECK( pButtonCaps = (PHIDP_BUTTON_CAPS)malloc(sizeof(HIDP_BUTTON_CAPS) * Caps.NumberInputButtonCaps) ); //If fails, maybe wheel only has axes capsLength = Caps.NumberInputButtonCaps; HidP_GetButtonCaps(HidP_Input, pButtonCaps, &capsLength, pPreparsedData); numberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1; usageLength = countof(usage);//numberOfButtons; NTSTATUS stat; if((stat = HidP_GetUsages( HidP_Input, pButtonCaps->UsagePage, 0, usage, &usageLength, pPreparsedData, (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid)) == HIDP_STATUS_SUCCESS ) { for(uint32_t i = 0; i < usageLength; i++) { uint16_t btn = usage[i] - pButtonCaps->Range.UsageMin; } } /// Get axes' values CHECK( pValueCaps = (PHIDP_VALUE_CAPS)malloc(sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps) ); capsLength = Caps.NumberInputValueCaps; if(HidP_GetValueCaps(HidP_Input, pValueCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS ) { for(USHORT i = 0; i < capsLength; i++) { if(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 ) { continue; // if here then maybe something is up with HIDP_CAPS.NumberInputValueCaps } switch(pValueCaps[i].Range.UsageMin) { case HID_USAGE_GENERIC_X: //0x30 case HID_USAGE_GENERIC_Y: case HID_USAGE_GENERIC_Z: case HID_USAGE_GENERIC_RX: case HID_USAGE_GENERIC_RY: case HID_USAGE_GENERIC_RZ: //0x35 //int axis = (value * 0x3FFF) / pValueCaps[i].LogicalMax; break; case HID_USAGE_GENERIC_HATSWITCH: //fprintf(stderr, "Hat: %02X\n", value); break; } } } Error: SAFE_FREE(pPreparsedData); SAFE_FREE(pButtonCaps); SAFE_FREE(pValueCaps); }
NTSTATUS Hid::GetUsages(REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE *UsageList, ULONG *UsageLength, void *PreparsedData, void *Report, ULONG ReportLength) { return HidP_GetUsages(ReportType, UsagePage, LinkCollection, UsageList, UsageLength, PreparsedData, Report, ReportLength); }
bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, long* result) { MSG *msg = static_cast<MSG *>(message); if (msg->message == WM_INPUT && false) { UINT dwSize; GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); LPBYTE lpb = new BYTE[dwSize]; if (lpb == NULL) { return 0; } if (GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) OutputDebugString(TEXT("GetRawInputData does not return correct size !\n")); RAWINPUT* raw = (RAWINPUT*)lpb; if (raw->header.dwType == RIM_TYPEKEYBOARD) { MessageBox(NULL, L"Test", L"Test", MB_OK); } if (raw->header.dwType == RIM_TYPEHID) { UINT bufferSize; GetRawInputDeviceInfo(raw->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize); LPBYTE deviceInfo = new BYTE[bufferSize]; //Add check for allocation //Get the HID preparsed data GetRawInputDeviceInfo(raw->header.hDevice, RIDI_PREPARSEDDATA, deviceInfo, &bufferSize); PHIDP_PREPARSED_DATA deviceInfoData = (PHIDP_PREPARSED_DATA)deviceInfo; HIDP_CAPS caps; HidP_GetCaps(deviceInfoData, &caps); LPBYTE ButtonCapsBuf = new BYTE[sizeof(HIDP_BUTTON_CAPS) * caps.NumberInputButtonCaps]; //Add check for allocation //Get the button capabilities from HID PHIDP_BUTTON_CAPS ButtonCaps = (PHIDP_BUTTON_CAPS)ButtonCapsBuf; USHORT capsLength = caps.NumberInputButtonCaps; HidP_GetButtonCaps(HidP_Input, ButtonCaps, &capsLength, deviceInfoData); LPBYTE ValCapsBuf = new BYTE[sizeof(HIDP_BUTTON_CAPS) * caps.NumberInputValueCaps]; //Find the current button usage data from HID device USAGE usage[128]; ULONG usageLength = ButtonCaps->Range.UsageMax - ButtonCaps->Range.UsageMin + 1; HidP_GetUsages(HidP_Input, ButtonCaps->UsagePage, 0, usage, &usageLength, deviceInfoData, (PCHAR)raw->data.hid.bRawData, raw->data.hid.dwSizeHid); GetRawInputDeviceInfo(raw->header.hDevice, RIDI_DEVICENAME, NULL, &bufferSize); LPBYTE deviceNameInfoBuf = new BYTE[sizeof(wchar_t) * bufferSize]; //Add check for allocation //Get the HID Name GetRawInputDeviceInfo(raw->header.hDevice, RIDI_DEVICENAME, deviceNameInfoBuf, &bufferSize); wchar_t DeviceName[127]; HANDLE HIDHandle = CreateFile((LPCWSTR)deviceNameInfoBuf, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL); if (HIDHandle) { HidD_GetProductString(HIDHandle, DeviceName, sizeof(wchar_t) * 126); CloseHandle(HIDHandle); for (int i = 0; i < usageLength; i++) { MessageBox(NULL, boost::lexical_cast<std::wstring>(usage[i] - ButtonCaps->Range.UsageMin).c_str(), std::wstring(DeviceName).c_str(), MB_OK); } } //Clean up byte arrays; delete[] deviceNameInfoBuf; delete[] ValCapsBuf; delete[] ButtonCapsBuf; delete[] deviceInfo; } //Clean up byte arrays; delete[] lpb; return true; } return false; }
static void ParseRawInput(PRAWINPUT pRawInput) { //IwDebugTraceLinePrintf("%s(%d): Parsing raw input\n", __FUNCTION__, __LINE__); 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 // 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 // //#define VALUES_DEBUG #ifdef VALUES_DEBUG char str[512] = ""; #endif 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 ); // NOTE: These values are only correct for the Xbox 360 controller // and for the PS3 Controller (via MotioninJoy '360 Emulation' mode) switch(pValueCaps[i].Range.UsageMin) { case 0x30: // X-axis (L-Stick Horizontal) lAxisX = (LONG)value; break; case 0x31: // Y-axis (L-Stick Vertical) lAxisY = (LONG)value; break; case 0x32: // Z-axis (L-Trigger: 32K-65K, R-Trigger: 0K-32K) lAxisZ = (LONG)value; break; case 0x33: // U-axis (R-Stick Horizontal) lAxisU = (LONG)value; break; case 0x34: // R-axis (R-Stick Vertical) lAxisR = (LONG)value; break; // POV Hat Switch (DPAD: // 1: Up // 2: UP/RIGHT // 3: RIGHT // 4: RIGHT/DOWN // 5: DOWN // 6: DOWN/LEFT // 7: LEFT // 8: LEFT/UP case 0x39: lDPad = (LONG)value; break; } #ifdef VALUES_DEBUG char s[128]; _snprintf(s, 128, "[Usage=0x%2X, Val=%4u] ", pValueCaps[i].Range.UsageMin, value); strcat(str, s); #endif } // // Clean up // Error: SAFE_FREE(pPreparsedData); SAFE_FREE(pButtonCaps); SAFE_FREE(pValueCaps); }