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";
337 if (device_cat.is_debug()) {
339 <<
"Found " << _device_class <<
" device \"" << _name <<
"\" with " 340 << caps.NumberInputDataIndices <<
" data indices, " 341 << caps.NumberInputButtonCaps <<
" button caps, " 342 << caps.NumberInputValueCaps <<
" value caps\n";
347 ButtonHandle::none(),
348 GamepadButton::face_a(),
349 GamepadButton::face_b(),
350 GamepadButton::face_x(),
351 GamepadButton::face_y(),
352 GamepadButton::lshoulder(),
353 GamepadButton::rshoulder(),
354 GamepadButton::start(),
355 GamepadButton::back(),
356 GamepadButton::lstick(),
357 GamepadButton::rstick(),
359 const ButtonHandle *gamepad_buttons = gamepad_buttons_common;
360 if (_vendor_id == 0x0810 && _product_id == 0xe501) {
363 ButtonHandle::none(),
364 GamepadButton::face_x(),
365 GamepadButton::face_a(),
366 GamepadButton::face_b(),
367 GamepadButton::face_y(),
368 GamepadButton::lshoulder(),
369 GamepadButton::rshoulder(),
370 ButtonHandle::none(),
371 ButtonHandle::none(),
372 GamepadButton::back(),
373 GamepadButton::start(),
375 gamepad_buttons = gamepad_buttons_snes;
379 _indices.resize(caps.NumberInputDataIndices);
384 USHORT num_button_caps = caps.NumberInputButtonCaps;
385 PHIDP_BUTTON_CAPS button_caps;
386 if (num_button_caps > 0u) {
387 button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps *
sizeof(HIDP_BUTTON_CAPS));
388 _HidP_GetButtonCaps(HidP_Input, button_caps, &num_button_caps, buffer);
391 for (USHORT i = 0; i < num_button_caps; ++i) {
392 HIDP_BUTTON_CAPS &cap = button_caps[i];
395 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
397 if (device_cat.is_debug()) {
399 <<
"Found button range: DataIndex=" << dec
400 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
401 <<
", ReportID=" << (int)cap.ReportID
402 <<
", UsagePage=0x" << hex << cap.UsagePage
403 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
407 if (device_cat.is_debug()) {
409 <<
"Found button: DataIndex=" << dec << cap.NotRange.DataIndex
410 <<
", ReportID=" << (int)cap.ReportID
411 <<
", UsagePage=0x" << hex << cap.UsagePage
412 <<
", Usage=0x" << cap.NotRange.Usage
417 nassertd(cap.Range.DataIndexMin + upper < _indices.size())
continue;
422 if (cap.ReportID >= _report_buttons.size()) {
423 _report_buttons.resize(cap.ReportID + 1);
425 for (
int j = 0; j <= upper; ++j) {
426 USAGE usage = j + cap.Range.UsageMin;
427 USHORT data_index = j + cap.Range.DataIndexMin;
429 switch (cap.UsagePage) {
430 case HID_USAGE_PAGE_BUTTON:
431 if (_device_class == DeviceClass::gamepad) {
432 if (usage <
sizeof(gamepad_buttons_common) /
sizeof(
ButtonHandle)) {
433 handle = gamepad_buttons[usage];
435 }
else if (_device_class == DeviceClass::flight_stick) {
439 }
else if (_device_class == DeviceClass::mouse) {
441 int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
450 int button_index = _buttons.size();
451 _report_buttons[cap.ReportID].set_bit(button_index);
452 _indices[data_index] = Index::button(button_index);
453 _buttons.push_back(ButtonState(handle));
457 USHORT num_value_caps = caps.NumberInputValueCaps;
458 PHIDP_VALUE_CAPS value_caps;
459 if (num_value_caps > 0u) {
460 value_caps = (PHIDP_VALUE_CAPS)alloca(num_value_caps *
sizeof(HIDP_VALUE_CAPS));
461 _HidP_GetValueCaps(HidP_Input, value_caps, &num_value_caps, buffer);
464 _hat_data_index = -1;
466 for (USHORT i = 0; i < num_value_caps; ++i) {
467 HIDP_VALUE_CAPS &cap = value_caps[i];
470 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
472 if (device_cat.is_debug()) {
474 <<
"Found value range: DataIndex=" << dec
475 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
476 <<
", ReportID=" << (int)cap.ReportID
477 <<
", UsagePage=0x" << hex << cap.UsagePage
478 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
479 << dec <<
", LogicalMin=" << cap.LogicalMin
480 <<
", LogicalMax=" << cap.LogicalMax <<
"\n";
483 if (device_cat.is_debug()) {
485 <<
"Found value: DataIndex=" << dec << cap.NotRange.DataIndex
486 <<
", ReportID=" << (int)cap.ReportID
487 <<
", UsagePage=0x" << hex << cap.UsagePage
488 <<
", Usage=0x" << cap.NotRange.Usage
489 << dec <<
", LogicalMin=" << cap.LogicalMin
490 <<
", LogicalMax=" << cap.LogicalMax <<
"\n";
494 nassertd(cap.Range.DataIndexMin + upper < _indices.size())
continue;
496 for (
int j = 0; j <= upper; ++j) {
497 USAGE usage = j + cap.Range.UsageMin;
498 USHORT data_index = j + cap.Range.DataIndexMin;
499 bool is_signed =
true;
502 if (cap.LogicalMin == 0 && cap.LogicalMax == -1) {
503 cap.LogicalMax = 65535;
507 Axis axis = Axis::none;
508 switch (cap.UsagePage) {
509 case HID_USAGE_PAGE_GENERIC:
511 case HID_USAGE_GENERIC_X:
512 if (_device_class == DeviceClass::gamepad) {
514 }
else if (_device_class == DeviceClass::flight_stick) {
520 case HID_USAGE_GENERIC_Y:
521 if (_device_class == DeviceClass::gamepad) {
523 swap(cap.LogicalMin, cap.LogicalMax);
524 }
else if (_device_class == DeviceClass::flight_stick) {
528 swap(cap.LogicalMin, cap.LogicalMax);
531 case HID_USAGE_GENERIC_Z:
532 if (_device_class == DeviceClass::gamepad) {
533 axis = Axis::left_trigger;
534 }
else if (_device_class == DeviceClass::flight_stick) {
535 axis = Axis::throttle;
538 swap(cap.LogicalMin, cap.LogicalMax);
541 case HID_USAGE_GENERIC_RX:
542 if (_device_class == DeviceClass::gamepad) {
543 axis = Axis::right_x;
548 case HID_USAGE_GENERIC_RY:
549 if (_device_class == DeviceClass::gamepad) {
550 axis = Axis::right_y;
554 swap(cap.LogicalMin, cap.LogicalMax);
556 case HID_USAGE_GENERIC_RZ:
557 if (_device_class == DeviceClass::gamepad) {
558 axis = Axis::right_trigger;
562 swap(cap.LogicalMin, cap.LogicalMax);
565 case HID_USAGE_GENERIC_SLIDER:
568 swap(cap.LogicalMin, cap.LogicalMax);
570 case HID_USAGE_GENERIC_WHEEL:
573 case HID_USAGE_GENERIC_HATSWITCH:
575 _hat_data_index = data_index;
576 _hat_data_minimum = cap.LogicalMin;
583 if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) {
585 axis_index = add_axis(axis, cap.LogicalMax, cap.LogicalMin,
true);
586 }
else if (!is_signed) {
588 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax,
true);
590 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax);
592 _indices[data_index] = Index::axis(axis_index, is_signed);
597 if (_hat_data_index != -1) {
598 _hat_left_button = (int)_buttons.size();
599 if (_device_class == DeviceClass::gamepad) {
600 _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
601 _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
602 _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
603 _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
605 _buttons.push_back(ButtonState(GamepadButton::hat_left()));
606 _buttons.push_back(ButtonState(GamepadButton::hat_right()));
607 _buttons.push_back(ButtonState(GamepadButton::hat_down()));
608 _buttons.push_back(ButtonState(GamepadButton::hat_up()));
612 _max_data_count = _HidP_MaxDataListLength(HidP_Input, buffer);
614 nassertr_always(_max_data_count >= 0,
false);
617 _is_connected =
true;
624 void WinRawInputDevice::
627 _is_connected =
false;
629 if (_preparsed !=
nullptr) {
631 _preparsed =
nullptr;
634 _report_buttons.clear();
640 void WinRawInputDevice::
641 on_input(PRAWINPUT input) {
642 nassertv(input !=
nullptr);
643 nassertv(_preparsed !=
nullptr);
645 if (_max_data_count == 0) {
649 BYTE *ptr = input->data.hid.bRawData;
650 if (input->data.hid.dwSizeHid == 0) {
656 if (device_cat.is_spam()) {
658 << _name <<
" received " << input->data.hid.dwCount <<
" reports of size " 659 << input->data.hid.dwSizeHid <<
"\n";
662 for (DWORD i = 0; i < input->data.hid.dwCount; ++i) {
663 process_report((PCHAR)ptr, input->data.hid.dwSizeHid);
664 ptr += input->data.hid.dwSizeHid;
671 void WinRawInputDevice::
672 process_report(PCHAR ptr,
size_t size) {
675 UCHAR report_id = ptr[0];
678 if (report_id < _report_buttons.size()) {
679 unset_buttons = _report_buttons[report_id];
682 PHIDP_DATA data = (PHIDP_DATA)alloca(
sizeof(HIDP_DATA) * _max_data_count);
683 nassertv(data !=
nullptr);
685 ULONG count = _max_data_count;
686 NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, ptr, size);
687 if (status == HIDP_STATUS_SUCCESS) {
688 for (ULONG di = 0; di < count; ++di) {
689 if (data[di].DataIndex != _hat_data_index) {
690 nassertd(data[di].DataIndex < _indices.size())
continue;
691 const Index &idx = _indices[data[di].DataIndex];
692 if (idx._axis >= 0) {
694 axis_changed(idx._axis, (SHORT)data[di].RawValue);
696 axis_changed(idx._axis, data[di].RawValue);
699 if (idx._button >= 0) {
701 button_changed(idx._button, (data[di].On != FALSE));
704 int value = (int)data[di].RawValue - _hat_data_minimum;
705 button_changed(_hat_left_button + 0, value >= 5 && value <= 7);
706 button_changed(_hat_left_button + 1, value >= 1 && value <= 3);
707 button_changed(_hat_left_button + 2, value >= 3 && value <= 5);
708 button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1);
714 while (button_index >= 0) {
715 button_changed(button_index,
false);
719 }
else if (device_cat.is_spam()) {
721 <<
"Failed to get data from raw device " << _path
722 <<
" (error 0x" << std::hex << (status & 0xffffffffu) << std::dec <<
")\n";
731 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.