18 #if defined(_WIN32) && !defined(CPPPARSER) 24 typedef USHORT USAGE, *PUSAGE;
26 #define HID_USAGE_PAGE_UNDEFINED ((USAGE) 0x00) 27 #define HID_USAGE_PAGE_GENERIC ((USAGE) 0x01) 28 #define HID_USAGE_PAGE_SIMULATION ((USAGE) 0x02) 29 #define HID_USAGE_PAGE_VR ((USAGE) 0x03) 30 #define HID_USAGE_PAGE_SPORT ((USAGE) 0x04) 31 #define HID_USAGE_PAGE_GAME ((USAGE) 0x05) 32 #define HID_USAGE_PAGE_KEYBOARD ((USAGE) 0x07) 33 #define HID_USAGE_PAGE_LED ((USAGE) 0x08) 34 #define HID_USAGE_PAGE_BUTTON ((USAGE) 0x09) 36 #define HID_USAGE_GENERIC_POINTER ((USAGE) 0x01) 37 #define HID_USAGE_GENERIC_MOUSE ((USAGE) 0x02) 38 #define HID_USAGE_GENERIC_JOYSTICK ((USAGE) 0x04) 39 #define HID_USAGE_GENERIC_GAMEPAD ((USAGE) 0x05) 40 #define HID_USAGE_GENERIC_KEYBOARD ((USAGE) 0x06) 41 #define HID_USAGE_GENERIC_KEYPAD ((USAGE) 0x07) 42 #define HID_USAGE_GENERIC_SYSTEM_CTL ((USAGE) 0x80) 44 #define HID_USAGE_GENERIC_X ((USAGE) 0x30) 45 #define HID_USAGE_GENERIC_Y ((USAGE) 0x31) 46 #define HID_USAGE_GENERIC_Z ((USAGE) 0x32) 47 #define HID_USAGE_GENERIC_RX ((USAGE) 0x33) 48 #define HID_USAGE_GENERIC_RY ((USAGE) 0x34) 49 #define HID_USAGE_GENERIC_RZ ((USAGE) 0x35) 50 #define HID_USAGE_GENERIC_SLIDER ((USAGE) 0x36) 51 #define HID_USAGE_GENERIC_DIAL ((USAGE) 0x37) 52 #define HID_USAGE_GENERIC_WHEEL ((USAGE) 0x38) 53 #define HID_USAGE_GENERIC_HATSWITCH ((USAGE) 0x39) 56 #define HIDP_STATUS_SUCCESS ((NTSTATUS)(0x11 << 16)) 58 typedef enum _HIDP_REPORT_TYPE {
64 typedef struct _HIDP_BUTTON_CAPS {
69 USHORT LinkCollection;
73 BOOLEAN IsStringRange;
74 BOOLEAN IsDesignatorRange;
79 USAGE UsageMin, UsageMax;
80 USHORT StringMin, StringMax;
81 USHORT DesignatorMin, DesignatorMax;
82 USHORT DataIndexMin, DataIndexMax;
85 USAGE Usage, Reserved1;
86 USHORT StringIndex, Reserved2;
87 USHORT DesignatorIndex, Reserved3;
88 USHORT DataIndex, Reserved4;
91 } HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS;
93 typedef struct _HIDP_VALUE_CAPS {
98 USHORT LinkCollection;
102 BOOLEAN IsStringRange;
103 BOOLEAN IsDesignatorRange;
112 LONG LogicalMin, LogicalMax;
113 LONG PhysicalMin, PhysicalMax;
116 USAGE UsageMin, UsageMax;
117 USHORT StringMin, StringMax;
118 USHORT DesignatorMin, DesignatorMax;
119 USHORT DataIndexMin, DataIndexMax;
122 USAGE Usage, Reserved1;
123 USHORT StringIndex, Reserved2;
124 USHORT DesignatorIndex, Reserved3;
125 USHORT DataIndex, Reserved4;
128 } HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
130 typedef PUCHAR PHIDP_REPORT_DESCRIPTOR;
131 typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA;
133 typedef struct _HIDP_CAPS {
136 USHORT InputReportByteLength;
137 USHORT OutputReportByteLength;
138 USHORT FeatureReportByteLength;
140 USHORT NumberLinkCollectionNodes;
141 USHORT NumberInputButtonCaps;
142 USHORT NumberInputValueCaps;
143 USHORT NumberInputDataIndices;
144 USHORT NumberOutputButtonCaps;
145 USHORT NumberOutputValueCaps;
146 USHORT NumberOutputDataIndices;
147 USHORT NumberFeatureButtonCaps;
148 USHORT NumberFeatureValueCaps;
149 USHORT NumberFeatureDataIndices;
150 } HIDP_CAPS, *PHIDP_CAPS;
152 typedef struct _HIDP_DATA {
159 } HIDP_DATA, *PHIDP_DATA;
161 typedef LONG NTSTATUS;
162 typedef NTSTATUS (*pHidP_GetCaps)(PHIDP_PREPARSED_DATA, PHIDP_CAPS);
163 typedef NTSTATUS (*pHidP_GetButtonCaps)(HIDP_REPORT_TYPE, PHIDP_BUTTON_CAPS, PUSHORT, PHIDP_PREPARSED_DATA);
164 typedef NTSTATUS (*pHidP_GetValueCaps)(HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PUSHORT, PHIDP_PREPARSED_DATA);
165 typedef NTSTATUS (*pHidP_GetData)(HIDP_REPORT_TYPE, PHIDP_DATA, PULONG, PHIDP_PREPARSED_DATA, PCHAR, ULONG);
166 typedef ULONG (*pHidP_MaxDataListLength)(HIDP_REPORT_TYPE, PHIDP_PREPARSED_DATA);
168 static pHidP_GetCaps _HidP_GetCaps =
nullptr;
169 static pHidP_GetButtonCaps _HidP_GetButtonCaps =
nullptr;
170 static pHidP_GetValueCaps _HidP_GetValueCaps =
nullptr;
171 static pHidP_GetData _HidP_GetData =
nullptr;
172 static pHidP_MaxDataListLength _HidP_MaxDataListLength =
nullptr;
178 static bool init_hidp() {
179 HMODULE module = LoadLibraryA(
"hid.dll");
181 if (device_cat.is_debug()) {
183 <<
"Successfully loaded hid.dll\n";
186 _HidP_GetCaps = (pHidP_GetCaps)GetProcAddress(module,
"HidP_GetCaps");
187 _HidP_GetButtonCaps = (pHidP_GetButtonCaps)GetProcAddress(module,
"HidP_GetButtonCaps");
188 _HidP_GetValueCaps = (pHidP_GetValueCaps)GetProcAddress(module,
"HidP_GetValueCaps");
189 _HidP_GetData = (pHidP_GetData)GetProcAddress(module,
"HidP_GetData");
190 _HidP_MaxDataListLength = (pHidP_MaxDataListLength)GetProcAddress(module,
"HidP_MaxDataListLength");
192 if (_HidP_GetCaps ==
nullptr || _HidP_GetButtonCaps ==
nullptr ||
193 _HidP_GetValueCaps ==
nullptr || _HidP_GetData ==
nullptr ||
194 _HidP_MaxDataListLength ==
nullptr) {
196 <<
"Failed to locate function pointers in hid.dll\n";
204 <<
"Failed to load hid.dll.\n";
212 WinRawInputDevice(WinInputDeviceManager *manager,
const char *path) :
216 _preparsed(nullptr) {
223 ~WinRawInputDevice() {
226 if (_manager !=
nullptr) {
227 _manager->device_destroyed(
this);
229 if (_preparsed !=
nullptr) {
231 _preparsed =
nullptr;
239 bool WinRawInputDevice::
240 on_arrival(HANDLE handle,
const RID_DEVICE_INFO &info, std::string name) {
247 _name = std::move(name);
249 switch (info.dwType) {
251 _device_class = DeviceClass::mouse;
254 case RIM_TYPEKEYBOARD:
255 _device_class = DeviceClass::keyboard;
259 _vendor_id = info.hid.dwVendorId;
260 _product_id = info.hid.dwProductId;
263 if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
264 info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) {
265 _device_class = DeviceClass::gamepad;
268 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
269 info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) {
270 _device_class = DeviceClass::flight_stick;
272 if (_name ==
"usb gamepad") {
274 _device_class = DeviceClass::gamepad;
277 if (_vendor_id == 0x0079 && _product_id == 0x0006) {
279 _device_class = DeviceClass::gamepad;
283 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
284 info.hid.usUsage == HID_USAGE_GENERIC_MOUSE) {
285 _device_class = DeviceClass::mouse;
288 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
289 info.hid.usUsage == HID_USAGE_GENERIC_KEYBOARD) {
290 _device_class = DeviceClass::keyboard;
293 }
else if (_vendor_id == 0x046d &&
294 (_product_id == 0xc623 ||
295 _product_id == 0xc625 ||
296 _product_id == 0xc626 ||
297 _product_id == 0xc627 ||
298 _product_id == 0xc628 ||
299 _product_id == 0xc629 ||
300 _product_id == 0xc62b)) {
301 _device_class = DeviceClass::spatial_mouse;
310 static bool hid_initialized =
false;
311 if (!hid_initialized) {
315 hid_initialized =
true;
320 if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA,
nullptr, &size) < 0) {
324 PHIDP_PREPARSED_DATA buffer = (PHIDP_PREPARSED_DATA)malloc(size);
325 if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, buffer, &size) <= 0) {
331 if (_HidP_GetCaps(buffer, &caps) != HIDP_STATUS_SUCCESS) {
333 <<
"Failed to get capabilities from HID preparsed data.\n";
339 ButtonHandle::none(),
340 GamepadButton::face_a(),
341 GamepadButton::face_b(),
342 GamepadButton::face_x(),
343 GamepadButton::face_y(),
344 GamepadButton::lshoulder(),
345 GamepadButton::rshoulder(),
346 GamepadButton::start(),
347 GamepadButton::back(),
348 GamepadButton::lstick(),
349 GamepadButton::rstick(),
351 const ButtonHandle *gamepad_buttons = gamepad_buttons_common;
352 if (_vendor_id == 0x0810 && _product_id == 0xe501) {
355 ButtonHandle::none(),
356 GamepadButton::face_x(),
357 GamepadButton::face_a(),
358 GamepadButton::face_b(),
359 GamepadButton::face_y(),
360 GamepadButton::lshoulder(),
361 GamepadButton::rshoulder(),
362 ButtonHandle::none(),
363 ButtonHandle::none(),
364 GamepadButton::back(),
365 GamepadButton::start(),
367 gamepad_buttons = gamepad_buttons_snes;
371 _indices.resize(caps.NumberInputDataIndices);
376 USHORT num_button_caps = caps.NumberInputButtonCaps;
377 PHIDP_BUTTON_CAPS button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps *
sizeof(HIDP_BUTTON_CAPS));
378 _HidP_GetButtonCaps(HidP_Input, button_caps, &num_button_caps, buffer);
380 for (USHORT i = 0; i < num_button_caps; ++i) {
381 HIDP_BUTTON_CAPS &cap = button_caps[i];
384 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
386 if (device_cat.is_debug()) {
388 <<
"Found button range: DataIndex=" << dec
389 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
390 <<
", ReportID=" << (int)cap.ReportID
391 <<
", UsagePage=0x" << hex << cap.UsagePage
392 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
396 if (device_cat.is_debug()) {
398 <<
"Found button: DataIndex=" << dec << cap.NotRange.DataIndex
399 <<
", ReportID=" << dec << (int)cap.ReportID
400 <<
", UsagePage=0x" << cap.UsagePage
401 <<
", Usage=0x" << cap.NotRange.Usage
409 if (cap.ReportID >= _report_buttons.size()) {
410 _report_buttons.resize(cap.ReportID + 1);
412 for (
int j = 0; j <= upper; ++j) {
413 USAGE usage = j + cap.Range.UsageMin;
414 USHORT data_index = j + cap.Range.DataIndexMin;
416 switch (cap.UsagePage) {
417 case HID_USAGE_PAGE_BUTTON:
418 if (_device_class == DeviceClass::gamepad) {
419 if (usage <
sizeof(gamepad_buttons_common) /
sizeof(
ButtonHandle)) {
420 handle = gamepad_buttons[usage];
422 }
else if (_device_class == DeviceClass::flight_stick) {
426 }
else if (_device_class == DeviceClass::mouse) {
428 int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
434 int button_index = _buttons.size();
435 _report_buttons[cap.ReportID].set_bit(button_index);
436 _indices[data_index] = Index::button(button_index);
437 _buttons.push_back(ButtonState(handle));
441 USHORT num_value_caps = caps.NumberInputValueCaps;
442 PHIDP_VALUE_CAPS value_caps = (PHIDP_VALUE_CAPS)alloca(num_value_caps *
sizeof(HIDP_VALUE_CAPS));
443 _HidP_GetValueCaps(HidP_Input, value_caps, &num_value_caps, buffer);
445 _hat_data_index = -1;
447 for (USHORT i = 0; i < num_value_caps; ++i) {
448 HIDP_VALUE_CAPS &cap = value_caps[i];
451 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
453 if (device_cat.is_debug()) {
455 <<
"Found value range: DataIndex=" << dec
456 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
457 <<
", ReportID=" << (int)cap.ReportID
458 <<
", UsagePage=0x" << hex << cap.UsagePage
459 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
460 << dec <<
", LogicalMin=" << cap.LogicalMin
461 <<
", LogicalMax=" << cap.LogicalMax <<
"\n";
464 if (device_cat.is_debug()) {
466 <<
"Found value: DataIndex=" << dec << cap.NotRange.DataIndex
467 <<
", ReportID=" << dec << (int)cap.ReportID
468 <<
", UsagePage=0x" << hex << cap.UsagePage
469 <<
", Usage=0x" << cap.NotRange.Usage
470 << dec <<
", LogicalMin=" << cap.LogicalMin
471 <<
", LogicalMax=" << cap.LogicalMax <<
"\n";
475 for (
int j = 0; j <= upper; ++j) {
476 USAGE usage = j + cap.Range.UsageMin;
477 USHORT data_index = j + cap.Range.DataIndexMin;
478 bool is_signed =
true;
481 if (cap.LogicalMin == 0 && cap.LogicalMax == -1) {
482 cap.LogicalMax = 65535;
486 Axis axis = Axis::none;
487 switch (cap.UsagePage) {
488 case HID_USAGE_PAGE_GENERIC:
490 case HID_USAGE_GENERIC_X:
491 if (_device_class == DeviceClass::gamepad) {
493 }
else if (_device_class == DeviceClass::flight_stick) {
499 case HID_USAGE_GENERIC_Y:
500 if (_device_class == DeviceClass::gamepad) {
502 swap(cap.LogicalMin, cap.LogicalMax);
503 }
else if (_device_class == DeviceClass::flight_stick) {
507 swap(cap.LogicalMin, cap.LogicalMax);
510 case HID_USAGE_GENERIC_Z:
511 if (_device_class == DeviceClass::gamepad) {
512 axis = Axis::left_trigger;
513 }
else if (_device_class == DeviceClass::flight_stick) {
514 axis = Axis::throttle;
517 swap(cap.LogicalMin, cap.LogicalMax);
520 case HID_USAGE_GENERIC_RX:
521 if (_device_class == DeviceClass::gamepad) {
522 axis = Axis::right_x;
527 case HID_USAGE_GENERIC_RY:
528 if (_device_class == DeviceClass::gamepad) {
529 axis = Axis::right_y;
533 swap(cap.LogicalMin, cap.LogicalMax);
535 case HID_USAGE_GENERIC_RZ:
536 if (_device_class == DeviceClass::gamepad) {
537 axis = Axis::right_trigger;
541 swap(cap.LogicalMin, cap.LogicalMax);
544 case HID_USAGE_GENERIC_SLIDER:
547 swap(cap.LogicalMin, cap.LogicalMax);
549 case HID_USAGE_GENERIC_WHEEL:
552 case HID_USAGE_GENERIC_HATSWITCH:
554 _hat_data_index = data_index;
555 _hat_data_minimum = cap.LogicalMin;
562 if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) {
564 axis_index = add_axis(axis, cap.LogicalMax, cap.LogicalMin,
true);
565 }
else if (!is_signed) {
567 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax,
true);
569 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax);
571 _indices[data_index] = Index::axis(axis_index, is_signed);
576 if (_hat_data_index != -1) {
577 _hat_left_button = (int)_buttons.size();
578 if (_device_class == DeviceClass::gamepad) {
579 _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
580 _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
581 _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
582 _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
584 _buttons.push_back(ButtonState(GamepadButton::hat_left()));
585 _buttons.push_back(ButtonState(GamepadButton::hat_right()));
586 _buttons.push_back(ButtonState(GamepadButton::hat_down()));
587 _buttons.push_back(ButtonState(GamepadButton::hat_up()));
591 _max_data_count = _HidP_MaxDataListLength(HidP_Input, buffer);
594 _is_connected =
true;
601 void WinRawInputDevice::
604 _is_connected =
false;
606 if (_preparsed !=
nullptr) {
608 _preparsed =
nullptr;
611 _report_buttons.clear();
617 void WinRawInputDevice::
618 on_input(PRAWINPUT input) {
619 nassertv(input !=
nullptr);
620 nassertv(_preparsed !=
nullptr);
622 BYTE *ptr = input->data.hid.bRawData;
623 if (input->data.hid.dwSizeHid == 0) {
629 for (DWORD i = 0; i < input->data.hid.dwCount; ++i) {
630 process_report((PCHAR)ptr, input->data.hid.dwSizeHid);
631 ptr += input->data.hid.dwSizeHid;
638 void WinRawInputDevice::
639 process_report(PCHAR ptr,
size_t size) {
642 UCHAR report_id = ptr[0];
645 if (report_id < _report_buttons.size()) {
646 unset_buttons = _report_buttons[report_id];
649 PHIDP_DATA data = (PHIDP_DATA)alloca(
sizeof(HIDP_DATA) * _max_data_count);
650 nassertv(data !=
nullptr);
652 ULONG count = _max_data_count;
653 NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, ptr, size);
654 if (status == HIDP_STATUS_SUCCESS) {
655 for (ULONG di = 0; di < count; ++di) {
656 if (data[di].DataIndex != _hat_data_index) {
657 const Index &idx = _indices[data[di].DataIndex];
658 if (idx._axis >= 0) {
660 axis_changed(idx._axis, (SHORT)data[di].RawValue);
662 axis_changed(idx._axis, data[di].RawValue);
665 if (idx._button >= 0) {
667 button_changed(idx._button, (data[di].On != FALSE));
670 int value = (int)data[di].RawValue - _hat_data_minimum;
671 button_changed(_hat_left_button + 0, value >= 5 && value <= 7);
672 button_changed(_hat_left_button + 1, value >= 1 && value <= 3);
673 button_changed(_hat_left_button + 2, value >= 3 && value <= 5);
674 button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1);
680 while (button_index >= 0) {
681 button_changed(button_index,
false);
685 }
else if (device_cat.is_spam()) {
687 <<
"Failed to get data from raw device " << _path
688 <<
" (error 0x" << std::hex << (status & 0xffffffffu) << std::dec <<
")\n";
697 void WinRawInputDevice::
void clear_bit(int index)
Sets the nth bit off.
A dynamic array with an unlimited number of bits.
Similar to MutexHolder, but for a light mutex.
int get_lowest_on_bit() const
Returns the index of the lowest 1 bit in the array.