20 #if defined(_WIN32) && !defined(CPPPARSER)
26 enum QuirkBits :
int {
28 QB_no_analog_triggers = 1,
31 QB_centered_throttle = 2,
34 QB_reversed_throttle = 4,
40 QB_right_axes_swapped = 64,
44 static const struct DeviceMapping {
45 unsigned short vendor;
46 unsigned short product;
47 InputDevice::DeviceClass device_class;
49 const char *buttons[16];
50 } mapping_presets[] = {
53 {0x0810, 0xe501, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers,
54 {
"face_y",
"face_b",
"face_a",
"face_x",
"lshoulder",
"rshoulder",
"ltrigger",
"rtrigger",
"back",
"start"}
57 {0x0810, 0x0001, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_no_analog_triggers | QB_right_axes_swapped,
58 {
"face_y",
"face_b",
"face_a",
"face_x",
"lshoulder",
"rshoulder",
"ltrigger",
"rtrigger",
"back",
"start",
"lstick",
"rstick"}
61 {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_no_analog_triggers,
62 {
"face_y",
"face_b",
"face_a",
"face_x",
"lshoulder",
"rshoulder",
"ltrigger",
"rtrigger",
"back",
"start",
"lstick",
"rstick"}
65 {0x044f, 0xb108, InputDevice::DeviceClass::flight_stick, QB_centered_throttle | QB_reversed_throttle,
69 {0x0955, 0x7214, InputDevice::DeviceClass::gamepad, 0,
70 {
"face_a",
"face_b", 0,
"face_x",
"face_y",
"rshoulder",
"lshoulder",
"rshoulder", 0, 0, 0,
"start", 0,
"lstick",
"rstick", 0}
73 {0x054c, 0x05c4, InputDevice::DeviceClass::gamepad, QB_rstick_from_z,
74 {
"face_x",
"face_a",
"face_b",
"face_y",
"lshoulder",
"rshoulder", 0, 0,
"back",
"start",
"lstick",
"rstick",
"guide", 0}
77 {0x054c, 0x09cc, InputDevice::DeviceClass::gamepad, QB_rstick_from_z,
78 {
"face_x",
"face_a",
"face_b",
"face_y",
"lshoulder",
"rshoulder", 0, 0,
"back",
"start",
"lstick",
"rstick",
"guide", 0}
81 {0x054c, 0x0ba0, InputDevice::DeviceClass::gamepad, QB_rstick_from_z,
82 {
"face_x",
"face_a",
"face_b",
"face_y",
"lshoulder",
"rshoulder", 0, 0,
"back",
"start",
"lstick",
"rstick",
"guide", 0}
88 static const char *default_gamepad_mapping[16] = {
89 "face_a",
"face_b",
"face_x",
"face_y",
"lshoulder",
"rshoulder",
"back",
"start",
"lstick",
"rstick"
92 static pHidP_GetCaps _HidP_GetCaps =
nullptr;
93 static pHidP_GetButtonCaps _HidP_GetButtonCaps =
nullptr;
94 static pHidP_GetValueCaps _HidP_GetValueCaps =
nullptr;
95 static pHidP_GetData _HidP_GetData =
nullptr;
96 static pHidP_MaxDataListLength _HidP_MaxDataListLength =
nullptr;
102 static bool init_hidp() {
103 HMODULE module = LoadLibraryA(
"hid.dll");
105 if (device_cat.is_debug()) {
107 <<
"Successfully loaded hid.dll\n";
110 _HidP_GetCaps = (pHidP_GetCaps)GetProcAddress(module,
"HidP_GetCaps");
111 _HidP_GetButtonCaps = (pHidP_GetButtonCaps)GetProcAddress(module,
"HidP_GetButtonCaps");
112 _HidP_GetValueCaps = (pHidP_GetValueCaps)GetProcAddress(module,
"HidP_GetValueCaps");
113 _HidP_GetData = (pHidP_GetData)GetProcAddress(module,
"HidP_GetData");
114 _HidP_MaxDataListLength = (pHidP_MaxDataListLength)GetProcAddress(module,
"HidP_MaxDataListLength");
116 if (_HidP_GetCaps ==
nullptr || _HidP_GetButtonCaps ==
nullptr ||
117 _HidP_GetValueCaps ==
nullptr || _HidP_GetData ==
nullptr ||
118 _HidP_MaxDataListLength ==
nullptr) {
120 <<
"Failed to locate function pointers in hid.dll\n";
128 <<
"Failed to load hid.dll.\n";
136 WinRawInputDevice(WinInputDeviceManager *manager,
const char *path) :
140 _preparsed(nullptr) {
147 ~WinRawInputDevice() {
150 if (_manager !=
nullptr) {
151 _manager->device_destroyed(
this);
153 if (_preparsed !=
nullptr) {
155 _preparsed =
nullptr;
163 bool WinRawInputDevice::
164 on_arrival(HANDLE handle,
const RID_DEVICE_INFO &info, std::string name) {
171 _name = std::move(name);
174 const char *
const *gamepad_buttons = default_gamepad_mapping;
176 switch (info.dwType) {
178 _device_class = DeviceClass::mouse;
181 case RIM_TYPEKEYBOARD:
182 _device_class = DeviceClass::keyboard;
186 _vendor_id = info.hid.dwVendorId;
187 _product_id = info.hid.dwProductId;
190 if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
191 info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) {
192 _device_class = DeviceClass::gamepad;
195 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
196 info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) {
197 _device_class = DeviceClass::flight_stick;
199 if (_name ==
"usb gamepad") {
201 _device_class = DeviceClass::gamepad;
205 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
206 info.hid.usUsage == HID_USAGE_GENERIC_MOUSE) {
207 _device_class = DeviceClass::mouse;
210 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
211 info.hid.usUsage == HID_USAGE_GENERIC_KEYBOARD) {
212 _device_class = DeviceClass::keyboard;
215 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_DIGITIZER &&
216 info.hid.usUsage == 1) {
217 _device_class = DeviceClass::digitizer;
220 }
else if (_vendor_id == 0x046d &&
221 (_product_id == 0xc623 ||
222 _product_id == 0xc625 ||
223 _product_id == 0xc626 ||
224 _product_id == 0xc627 ||
225 _product_id == 0xc628 ||
226 _product_id == 0xc629 ||
227 _product_id == 0xc62b)) {
228 _device_class = DeviceClass::spatial_mouse;
236 if (_device_class == DeviceClass::gamepad ||
237 _device_class == DeviceClass::flight_stick) {
239 const DeviceMapping *mapping = mapping_presets;
240 while (mapping->vendor != 0) {
241 if (info.hid.dwVendorId == mapping->vendor &&
242 info.hid.dwProductId == mapping->product) {
243 _device_class = mapping->device_class;
244 gamepad_buttons = mapping->buttons;
245 quirks = mapping->quirks;
246 if (device_cat.is_debug()) {
248 <<
"Using preset mapping for " << mapping->device_class
249 <<
" with VID=" << std::hex << mapping->vendor
250 <<
" PID=" << mapping->product << std::dec <<
"\n";
259 static bool hid_initialized =
false;
260 if (!hid_initialized) {
264 hid_initialized =
true;
269 if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA,
nullptr, &size) < 0) {
273 PHIDP_PREPARSED_DATA buffer = (PHIDP_PREPARSED_DATA)malloc(size);
274 if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, buffer, &size) <= 0) {
280 if (_HidP_GetCaps(buffer, &caps) != HIDP_STATUS_SUCCESS) {
282 <<
"Failed to get capabilities from HID preparsed data.\n";
286 if (device_cat.is_debug()) {
288 <<
"Found " << _device_class <<
" device \"" << _name <<
"\" with "
289 << caps.NumberInputDataIndices <<
" data indices, "
290 << caps.NumberInputButtonCaps <<
" button caps, "
291 << caps.NumberInputValueCaps <<
" value caps\n";
297 _indices.resize(caps.NumberInputDataIndices);
302 USHORT num_button_caps = caps.NumberInputButtonCaps;
303 PHIDP_BUTTON_CAPS button_caps;
304 if (num_button_caps > 0u) {
305 button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps *
sizeof(HIDP_BUTTON_CAPS));
306 _HidP_GetButtonCaps(HidP_Input, button_caps, &num_button_caps, buffer);
309 for (USHORT i = 0; i < num_button_caps; ++i) {
310 HIDP_BUTTON_CAPS &cap = button_caps[i];
313 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
315 if (device_cat.is_debug()) {
317 <<
"Found button range: DataIndex=" << dec
318 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
319 <<
", ReportID=" << (int)cap.ReportID
320 <<
", UsagePage=0x" << hex << cap.UsagePage
321 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
325 if (device_cat.is_debug()) {
327 <<
"Found button: DataIndex=" << dec << cap.NotRange.DataIndex
328 <<
", ReportID=" << (int)cap.ReportID
329 <<
", UsagePage=0x" << hex << cap.UsagePage
330 <<
", Usage=0x" << cap.NotRange.Usage
335 nassertd(cap.Range.DataIndexMin + upper < (
int)_indices.size()) continue;
340 if (cap.ReportID >= _report_buttons.size()) {
341 _report_buttons.resize(cap.ReportID + 1);
343 for (
int j = 0; j <= upper; ++j) {
344 USAGE usage = j + cap.Range.UsageMin;
345 USHORT data_index = j + cap.Range.DataIndexMin;
347 switch (cap.UsagePage) {
348 case HID_USAGE_PAGE_BUTTON:
349 if (_device_class == DeviceClass::gamepad) {
350 if (usage > 0 && usage - 1 < _countof(default_gamepad_mapping)) {
351 if (gamepad_buttons[usage - 1] !=
nullptr) {
352 handle = registry->
find_button(gamepad_buttons[usage - 1]);
355 }
else if (_device_class == DeviceClass::flight_stick) {
359 }
else if (_device_class == DeviceClass::mouse) {
361 int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
370 int button_index = _buttons.size();
371 _report_buttons[cap.ReportID].set_bit(button_index);
372 _indices[data_index] = Index::button(button_index);
373 _buttons.push_back(ButtonState(handle));
377 USHORT num_value_caps = caps.NumberInputValueCaps;
378 PHIDP_VALUE_CAPS value_caps;
379 if (num_value_caps > 0u) {
380 value_caps = (PHIDP_VALUE_CAPS)alloca(num_value_caps *
sizeof(HIDP_VALUE_CAPS));
381 _HidP_GetValueCaps(HidP_Input, value_caps, &num_value_caps, buffer);
384 _hat_data_index = -1;
386 for (USHORT i = 0; i < num_value_caps; ++i) {
387 HIDP_VALUE_CAPS &cap = value_caps[i];
390 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
392 if (device_cat.is_debug()) {
394 <<
"Found value range: DataIndex=" << dec
395 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
396 <<
", ReportID=" << (int)cap.ReportID
397 <<
", UsagePage=0x" << hex << cap.UsagePage
398 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
399 << dec <<
", LogicalMin=" << cap.LogicalMin
400 <<
", LogicalMax=" << cap.LogicalMax <<
"\n";
403 if (device_cat.is_debug()) {
405 <<
"Found value: DataIndex=" << dec << cap.NotRange.DataIndex
406 <<
", ReportID=" << (int)cap.ReportID
407 <<
", UsagePage=0x" << hex << cap.UsagePage
408 <<
", Usage=0x" << cap.NotRange.Usage
409 << dec <<
", LogicalMin=" << cap.LogicalMin
410 <<
", LogicalMax=" << cap.LogicalMax <<
"\n";
414 nassertd(cap.Range.DataIndexMin + upper < (
int)_indices.size()) continue;
416 for (
int j = 0; j <= upper; ++j) {
417 USAGE usage = j + cap.Range.UsageMin;
418 USHORT data_index = j + cap.Range.DataIndexMin;
419 bool is_signed =
true;
422 if (cap.LogicalMin == 0 && cap.LogicalMax == -1) {
423 cap.LogicalMax = 65535;
427 Axis axis = Axis::none;
428 switch (cap.UsagePage) {
429 case HID_USAGE_PAGE_GENERIC:
431 case HID_USAGE_GENERIC_X:
432 if (_device_class == DeviceClass::gamepad) {
434 }
else if (_device_class == DeviceClass::flight_stick) {
440 case HID_USAGE_GENERIC_Y:
441 if (_device_class == DeviceClass::gamepad) {
443 swap(cap.LogicalMin, cap.LogicalMax);
444 }
else if (_device_class == DeviceClass::flight_stick) {
448 swap(cap.LogicalMin, cap.LogicalMax);
451 case HID_USAGE_GENERIC_Z:
452 if (_device_class == DeviceClass::gamepad) {
453 if (quirks & QB_rstick_from_z) {
454 if (quirks & QB_right_axes_swapped) {
455 axis = InputDevice::Axis::right_y;
456 swap(cap.LogicalMin, cap.LogicalMax);
458 axis = InputDevice::Axis::right_x;
460 }
else if ((quirks & QB_no_analog_triggers) == 0) {
461 axis = Axis::left_trigger;
463 }
else if (_device_class == DeviceClass::flight_stick) {
464 axis = Axis::throttle;
465 if ((quirks & QB_reversed_throttle) != 0) {
466 std::swap(cap.LogicalMin, cap.LogicalMax);
468 if ((quirks & QB_centered_throttle) != 0) {
473 swap(cap.LogicalMin, cap.LogicalMax);
476 case HID_USAGE_GENERIC_RX:
477 if (_device_class == DeviceClass::gamepad) {
478 if (quirks & QB_rstick_from_z) {
479 if ((quirks & QB_no_analog_triggers) == 0) {
480 axis = Axis::left_trigger;
483 axis = Axis::right_x;
489 case HID_USAGE_GENERIC_RY:
490 if (_device_class == DeviceClass::gamepad) {
491 if (quirks & QB_rstick_from_z) {
492 if ((quirks & QB_no_analog_triggers) == 0) {
493 axis = Axis::right_trigger;
496 axis = Axis::right_y;
497 swap(cap.LogicalMin, cap.LogicalMax);
501 swap(cap.LogicalMin, cap.LogicalMax);
504 case HID_USAGE_GENERIC_RZ:
505 if (_device_class == DeviceClass::gamepad) {
506 if (quirks & QB_rstick_from_z) {
507 if (quirks & QB_right_axes_swapped) {
508 axis = InputDevice::Axis::right_x;
510 axis = InputDevice::Axis::right_y;
511 swap(cap.LogicalMin, cap.LogicalMax);
513 }
else if ((quirks & QB_no_analog_triggers) == 0) {
514 axis = Axis::right_trigger;
519 swap(cap.LogicalMin, cap.LogicalMax);
522 case HID_USAGE_GENERIC_SLIDER:
525 swap(cap.LogicalMin, cap.LogicalMax);
527 case HID_USAGE_GENERIC_WHEEL:
530 case HID_USAGE_GENERIC_HATSWITCH:
532 _hat_data_index = data_index;
533 _hat_data_minimum = cap.LogicalMin;
538 case HID_USAGE_PAGE_DIGITIZER:
541 axis = Axis::pressure;
550 for (
size_t i = 0; i < _axes.size(); ++i) {
551 if (_axes[i].axis == axis) {
560 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax,
true);
562 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax);
564 _indices[data_index] = Index::axis(axis_index, is_signed);
569 if (_hat_data_index != -1) {
570 _hat_left_button = (int)_buttons.size();
571 if (_device_class == DeviceClass::gamepad) {
572 _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
573 _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
574 _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
575 _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
577 _buttons.push_back(ButtonState(GamepadButton::hat_left()));
578 _buttons.push_back(ButtonState(GamepadButton::hat_right()));
579 _buttons.push_back(ButtonState(GamepadButton::hat_down()));
580 _buttons.push_back(ButtonState(GamepadButton::hat_up()));
584 _max_data_count = _HidP_MaxDataListLength(HidP_Input, buffer);
586 nassertr_always(_max_data_count >= 0,
false);
589 _is_connected =
true;
596 void WinRawInputDevice::
599 _is_connected =
false;
601 if (_preparsed !=
nullptr) {
603 _preparsed =
nullptr;
606 _report_buttons.clear();
612 void WinRawInputDevice::
613 on_input(PRAWINPUT input) {
614 nassertv(input !=
nullptr);
615 nassertv(_preparsed !=
nullptr);
617 if (_max_data_count == 0) {
621 BYTE *ptr = input->data.hid.bRawData;
622 if (input->data.hid.dwSizeHid == 0) {
628 if (device_cat.is_spam()) {
630 << _name <<
" received " << input->data.hid.dwCount <<
" reports of size "
631 << input->data.hid.dwSizeHid <<
"\n";
634 for (DWORD i = 0; i < input->data.hid.dwCount; ++i) {
635 process_report((PCHAR)ptr, input->data.hid.dwSizeHid);
636 ptr += input->data.hid.dwSizeHid;
643 void WinRawInputDevice::
644 process_report(PCHAR ptr,
size_t size) {
647 UCHAR report_id = ptr[0];
650 if (report_id < _report_buttons.size()) {
651 unset_buttons = _report_buttons[report_id];
654 PHIDP_DATA data = (PHIDP_DATA)alloca(
sizeof(HIDP_DATA) * _max_data_count);
655 nassertv(data !=
nullptr);
657 ULONG count = _max_data_count;
658 NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, ptr, size);
659 if (status == HIDP_STATUS_SUCCESS) {
660 for (ULONG di = 0; di < count; ++di) {
661 if (data[di].DataIndex != _hat_data_index) {
662 nassertd(data[di].DataIndex < _indices.size()) continue;
663 const Index &idx = _indices[data[di].DataIndex];
664 if (idx._axis >= 0) {
666 axis_changed(idx._axis, (SHORT)data[di].RawValue);
668 axis_changed(idx._axis, data[di].RawValue);
671 if (idx._button >= 0) {
673 button_changed(idx._button, (data[di].On != FALSE));
676 int value = (int)data[di].RawValue - _hat_data_minimum;
677 button_changed(_hat_left_button + 0, value >= 5 && value <= 7);
678 button_changed(_hat_left_button + 1, value >= 1 && value <= 3);
679 button_changed(_hat_left_button + 2, value >= 3 && value <= 5);
680 button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1);
686 while (button_index >= 0) {
687 button_changed(button_index,
false);
691 }
else if (device_cat.is_spam()) {
693 <<
"Failed to get data from raw device " << _path
694 <<
" (error 0x" << std::hex << (status & 0xffffffffu) << std::dec <<
")\n";
703 void WinRawInputDevice::