/** This function assumes that there are 2 values that can be read from the HID device which represent the DPad's state. This will not be true for all HID Devices. If you want to use this function, you must modify it for the correct value count. 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::getDPadInput(bool * pValues) { DWORD readbytecount = 0; _succ = ReadFile( _controllerHandle, _pInputReport, _pCaps->InputReportByteLength, &readbytecount, nullptr ); CHECK_FAILURE(_succ, DPAD_INPUT_FAILURE); //use the input report to generate button usage data LONG value = 0; //reset direction flags for (int i = 0; i < NUM_DIRECTIONS; i++) { pValues[i] = false; } for (int valIt = 0; valIt < _pCaps->NumberInputValueCaps; valIt++) { HidP_GetUsageValue(HidP_Input, _pValueCaps[valIt].UsagePage, _pValueCaps[valIt].LinkCollection, _pValueCaps[valIt].Range.UsageMin, (PULONG)&value, _pPreparsedData, _pInputReport, readbytecount ); if (valIt == 0) { switch (value) { case DPAD_UP: pValues[DPAD_UP_INDEX] = true; break; case DPAD_DOWN: pValues[DPAD_DOWN_INDEX] = true; break; default: break; } } else { switch (value) { case DPAD_RIGHT: pValues[DPAD_RIGHT_INDEX] = true; break; case DPAD_LEFT: pValues[DPAD_LEFT_INDEX] = true; break; default: break; } } } return _pCaps->NumberInputValueCaps; DPAD_INPUT_FAILURE: return -1; }
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); }
NTSTATUS Hid::GetUsageValue(REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, ULONG *UsageValue, void *PreparsedData, void *Report, ULONG ReportLength) { return HidP_GetUsageValue(ReportType, UsagePage, LinkCollection, Usage, UsageValue, PreparsedData, Report, ReportLength); }
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); }
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); }