20#if defined(_WIN32) && !defined(CPPPARSER)
28 QB_no_analog_triggers = 1,
31 QB_centered_throttle = 2,
34 QB_reversed_throttle = 4,
40 QB_right_axes_swapped = 64,
44static 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}
85 {0x2563, 0x0523, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_no_analog_triggers,
86 {
"face_y",
"face_b",
"face_a",
"face_x",
"lshoulder",
"rshoulder",
"ltrigger",
"rtrigger",
"back",
"start",
"lstick",
"rstick"}
89 {0x0483, 0x5720, InputDevice::DeviceClass::flight_stick, 0,
96static const char *default_gamepad_mapping[16] = {
97 "face_a",
"face_b",
"face_x",
"face_y",
"lshoulder",
"rshoulder",
"back",
"start",
"lstick",
"rstick"
100static pHidP_GetCaps _HidP_GetCaps =
nullptr;
101static pHidP_GetButtonCaps _HidP_GetButtonCaps =
nullptr;
102static pHidP_GetValueCaps _HidP_GetValueCaps =
nullptr;
103static pHidP_GetData _HidP_GetData =
nullptr;
104static pHidP_MaxDataListLength _HidP_MaxDataListLength =
nullptr;
110static bool init_hidp() {
111 HMODULE
module = LoadLibraryA("hid.dll");
113 if (device_cat.is_debug()) {
115 <<
"Successfully loaded hid.dll\n";
118 _HidP_GetCaps = (pHidP_GetCaps)GetProcAddress(module,
"HidP_GetCaps");
119 _HidP_GetButtonCaps = (pHidP_GetButtonCaps)GetProcAddress(module,
"HidP_GetButtonCaps");
120 _HidP_GetValueCaps = (pHidP_GetValueCaps)GetProcAddress(module,
"HidP_GetValueCaps");
121 _HidP_GetData = (pHidP_GetData)GetProcAddress(module,
"HidP_GetData");
122 _HidP_MaxDataListLength = (pHidP_MaxDataListLength)GetProcAddress(module,
"HidP_MaxDataListLength");
124 if (_HidP_GetCaps ==
nullptr || _HidP_GetButtonCaps ==
nullptr ||
125 _HidP_GetValueCaps ==
nullptr || _HidP_GetData ==
nullptr ||
126 _HidP_MaxDataListLength ==
nullptr) {
128 <<
"Failed to locate function pointers in hid.dll\n";
136 <<
"Failed to load hid.dll.\n";
144WinRawInputDevice(WinInputDeviceManager *manager,
const char *path) :
148 _preparsed(nullptr) {
155~WinRawInputDevice() {
158 if (_manager !=
nullptr) {
159 _manager->device_destroyed(
this);
161 if (_preparsed !=
nullptr) {
163 _preparsed =
nullptr;
171bool WinRawInputDevice::
172on_arrival(HANDLE handle,
const RID_DEVICE_INFO &info, std::string name) {
179 _name = std::move(name);
182 const char *
const *gamepad_buttons = default_gamepad_mapping;
184 switch (info.dwType) {
186 _device_class = DeviceClass::mouse;
189 case RIM_TYPEKEYBOARD:
190 _device_class = DeviceClass::keyboard;
194 _vendor_id = info.hid.dwVendorId;
195 _product_id = info.hid.dwProductId;
198 if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
199 info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) {
200 _device_class = DeviceClass::gamepad;
203 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
204 info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) {
205 _device_class = DeviceClass::flight_stick;
207 if (_name ==
"usb gamepad") {
209 _device_class = DeviceClass::gamepad;
213 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
214 info.hid.usUsage == HID_USAGE_GENERIC_MOUSE) {
215 _device_class = DeviceClass::mouse;
218 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
219 info.hid.usUsage == HID_USAGE_GENERIC_KEYBOARD) {
220 _device_class = DeviceClass::keyboard;
223 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_DIGITIZER &&
224 info.hid.usUsage == 1) {
225 _device_class = DeviceClass::digitizer;
228 }
else if (_vendor_id == 0x046d &&
229 (_product_id == 0xc623 ||
230 _product_id == 0xc625 ||
231 _product_id == 0xc626 ||
232 _product_id == 0xc627 ||
233 _product_id == 0xc628 ||
234 _product_id == 0xc629 ||
235 _product_id == 0xc62b)) {
236 _device_class = DeviceClass::spatial_mouse;
244 if (_device_class == DeviceClass::gamepad ||
245 _device_class == DeviceClass::flight_stick) {
247 const DeviceMapping *mapping = mapping_presets;
248 while (mapping->vendor != 0) {
249 if (info.hid.dwVendorId == mapping->vendor &&
250 info.hid.dwProductId == mapping->product) {
251 _device_class = mapping->device_class;
252 gamepad_buttons = mapping->buttons;
253 quirks = mapping->quirks;
254 if (device_cat.is_debug()) {
256 <<
"Using preset mapping for " << mapping->device_class
257 <<
" with VID=" << std::hex << mapping->vendor
258 <<
" PID=" << mapping->product << std::dec <<
"\n";
267 static bool hid_initialized =
false;
268 if (!hid_initialized) {
272 hid_initialized =
true;
277 if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA,
nullptr, &size) < 0) {
281 PHIDP_PREPARSED_DATA buffer = (PHIDP_PREPARSED_DATA)malloc(size);
282 if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, buffer, &size) <= 0) {
288 if (_HidP_GetCaps(buffer, &caps) != HIDP_STATUS_SUCCESS) {
290 <<
"Failed to get capabilities from HID preparsed data.\n";
294 if (device_cat.is_debug()) {
296 <<
"Found " << _device_class <<
" device \"" << _name <<
"\" with "
297 << caps.NumberInputDataIndices <<
" data indices, "
298 << caps.NumberInputButtonCaps <<
" button caps, "
299 << caps.NumberInputValueCaps <<
" value caps\n";
305 _indices.resize(caps.NumberInputDataIndices);
310 USHORT num_button_caps = caps.NumberInputButtonCaps;
311 PHIDP_BUTTON_CAPS button_caps;
312 if (num_button_caps > 0u) {
313 button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps *
sizeof(HIDP_BUTTON_CAPS));
314 _HidP_GetButtonCaps(HidP_Input, button_caps, &num_button_caps, buffer);
317 for (USHORT i = 0; i < num_button_caps; ++i) {
318 HIDP_BUTTON_CAPS &cap = button_caps[i];
321 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
323 if (device_cat.is_debug()) {
325 <<
"Found button range: DataIndex=" << dec
326 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
327 <<
", ReportID=" << (int)cap.ReportID
328 <<
", UsagePage=0x" << hex << cap.UsagePage
329 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
333 if (device_cat.is_debug()) {
335 <<
"Found button: DataIndex=" << dec << cap.NotRange.DataIndex
336 <<
", ReportID=" << (int)cap.ReportID
337 <<
", UsagePage=0x" << hex << cap.UsagePage
338 <<
", Usage=0x" << cap.NotRange.Usage
343 nassertd(cap.Range.DataIndexMin + upper < (
int)_indices.size()) continue;
348 if (cap.ReportID >= _report_buttons.size()) {
349 _report_buttons.resize(cap.ReportID + 1);
351 for (
int j = 0; j <= upper; ++j) {
352 USAGE usage = j + cap.Range.UsageMin;
353 USHORT data_index = j + cap.Range.DataIndexMin;
355 switch (cap.UsagePage) {
356 case HID_USAGE_PAGE_BUTTON:
357 if (_device_class == DeviceClass::gamepad) {
358 if (usage > 0 && usage - 1 < _countof(default_gamepad_mapping)) {
359 if (gamepad_buttons[usage - 1] !=
nullptr) {
360 handle = registry->
find_button(gamepad_buttons[usage - 1]);
363 }
else if (_device_class == DeviceClass::flight_stick) {
367 }
else if (_device_class == DeviceClass::mouse) {
369 int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
378 int button_index = _buttons.size();
379 _report_buttons[cap.ReportID].set_bit(button_index);
380 _indices[data_index] = Index::button(button_index);
381 _buttons.push_back(ButtonState(handle));
385 USHORT num_value_caps = caps.NumberInputValueCaps;
386 PHIDP_VALUE_CAPS value_caps;
387 if (num_value_caps > 0u) {
388 value_caps = (PHIDP_VALUE_CAPS)alloca(num_value_caps *
sizeof(HIDP_VALUE_CAPS));
389 _HidP_GetValueCaps(HidP_Input, value_caps, &num_value_caps, buffer);
392 _hat_data_index = -1;
394 for (USHORT i = 0; i < num_value_caps; ++i) {
395 HIDP_VALUE_CAPS &cap = value_caps[i];
398 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
400 if (device_cat.is_debug()) {
402 <<
"Found value range: DataIndex=" << dec
403 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
404 <<
", ReportID=" << (int)cap.ReportID
405 <<
", UsagePage=0x" << hex << cap.UsagePage
406 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
407 << dec <<
", LogicalMin=" << cap.LogicalMin
408 <<
", LogicalMax=" << cap.LogicalMax
409 <<
", BitSize=" << cap.BitSize <<
"\n";
412 if (device_cat.is_debug()) {
414 <<
"Found value: DataIndex=" << dec << cap.NotRange.DataIndex
415 <<
", ReportID=" << (int)cap.ReportID
416 <<
", UsagePage=0x" << hex << cap.UsagePage
417 <<
", Usage=0x" << cap.NotRange.Usage
418 << dec <<
", LogicalMin=" << cap.LogicalMin
419 <<
", LogicalMax=" << cap.LogicalMax
420 <<
", BitSize=" << cap.BitSize <<
"\n";
424 nassertd(cap.Range.DataIndexMin + upper < (
int)_indices.size()) continue;
426 for (
int j = 0; j <= upper; ++j) {
427 USAGE usage = j + cap.Range.UsageMin;
428 USHORT data_index = j + cap.Range.DataIndexMin;
429 bool is_signed =
true;
432 if (cap.LogicalMin == 0 && cap.LogicalMax == -1) {
433 cap.LogicalMax = (1 << cap.BitSize) - 1;
437 Axis axis = Axis::none;
438 switch (cap.UsagePage) {
439 case HID_USAGE_PAGE_GENERIC:
441 case HID_USAGE_GENERIC_X:
442 if (_device_class == DeviceClass::gamepad) {
444 }
else if (_device_class == DeviceClass::flight_stick) {
450 case HID_USAGE_GENERIC_Y:
451 if (_device_class == DeviceClass::gamepad) {
453 swap(cap.LogicalMin, cap.LogicalMax);
454 }
else if (_device_class == DeviceClass::flight_stick) {
458 swap(cap.LogicalMin, cap.LogicalMax);
461 case HID_USAGE_GENERIC_Z:
462 if (_device_class == DeviceClass::gamepad) {
463 if (quirks & QB_rstick_from_z) {
464 if (quirks & QB_right_axes_swapped) {
465 axis = InputDevice::Axis::right_y;
466 swap(cap.LogicalMin, cap.LogicalMax);
468 axis = InputDevice::Axis::right_x;
470 }
else if ((quirks & QB_no_analog_triggers) == 0) {
471 axis = Axis::left_trigger;
473 }
else if (_device_class == DeviceClass::flight_stick) {
474 axis = Axis::throttle;
475 if ((quirks & QB_reversed_throttle) != 0) {
476 std::swap(cap.LogicalMin, cap.LogicalMax);
478 if ((quirks & QB_centered_throttle) != 0) {
483 swap(cap.LogicalMin, cap.LogicalMax);
486 case HID_USAGE_GENERIC_RX:
487 if (_device_class == DeviceClass::gamepad) {
488 if (quirks & QB_rstick_from_z) {
489 if ((quirks & QB_no_analog_triggers) == 0) {
490 axis = Axis::left_trigger;
493 axis = Axis::right_x;
499 case HID_USAGE_GENERIC_RY:
500 if (_device_class == DeviceClass::gamepad) {
501 if (quirks & QB_rstick_from_z) {
502 if ((quirks & QB_no_analog_triggers) == 0) {
503 axis = Axis::right_trigger;
506 axis = Axis::right_y;
507 swap(cap.LogicalMin, cap.LogicalMax);
511 swap(cap.LogicalMin, cap.LogicalMax);
514 case HID_USAGE_GENERIC_RZ:
515 if (_device_class == DeviceClass::gamepad) {
516 if (quirks & QB_rstick_from_z) {
517 if (quirks & QB_right_axes_swapped) {
518 axis = InputDevice::Axis::right_x;
520 axis = InputDevice::Axis::right_y;
521 swap(cap.LogicalMin, cap.LogicalMax);
523 }
else if ((quirks & QB_no_analog_triggers) == 0) {
524 axis = Axis::right_trigger;
529 swap(cap.LogicalMin, cap.LogicalMax);
532 case HID_USAGE_GENERIC_SLIDER:
535 swap(cap.LogicalMin, cap.LogicalMax);
537 case HID_USAGE_GENERIC_WHEEL:
540 case HID_USAGE_GENERIC_HATSWITCH:
542 _hat_data_index = data_index;
543 _hat_data_minimum = cap.LogicalMin;
548 case HID_USAGE_PAGE_DIGITIZER:
551 axis = Axis::pressure;
560 for (
size_t i = 0; i < _axes.size(); ++i) {
561 if (_axes[i].axis == axis) {
568 if (cap.BitSize < 32) {
569 if (cap.LogicalMin < 0) {
570 sign_bit = 1 << (cap.BitSize - 1);
572 else if (is_signed) {
574 sign_bit = (1 << 15);
581 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax,
true);
583 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax);
585 _indices[data_index] = Index::axis(axis_index, sign_bit);
590 if (_hat_data_index != -1) {
591 _hat_left_button = (int)_buttons.size();
592 if (_device_class == DeviceClass::gamepad) {
593 _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
594 _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
595 _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
596 _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
598 _buttons.push_back(ButtonState(GamepadButton::hat_left()));
599 _buttons.push_back(ButtonState(GamepadButton::hat_right()));
600 _buttons.push_back(ButtonState(GamepadButton::hat_down()));
601 _buttons.push_back(ButtonState(GamepadButton::hat_up()));
605 _max_data_count = _HidP_MaxDataListLength(HidP_Input, buffer);
607 nassertr_always(_max_data_count >= 0,
false);
610 _is_connected =
true;
617void WinRawInputDevice::
620 _is_connected =
false;
622 if (_preparsed !=
nullptr) {
624 _preparsed =
nullptr;
627 _report_buttons.clear();
633void WinRawInputDevice::
634on_input(PRAWINPUT input) {
635 nassertv(input !=
nullptr);
636 nassertv(_preparsed !=
nullptr);
638 if (_max_data_count == 0) {
642 BYTE *ptr = input->data.hid.bRawData;
643 if (input->data.hid.dwSizeHid == 0) {
649 if (device_cat.is_spam()) {
651 << _name <<
" received " << input->data.hid.dwCount <<
" reports of size "
652 << input->data.hid.dwSizeHid <<
"\n";
655 for (DWORD i = 0; i < input->data.hid.dwCount; ++i) {
656 process_report((PCHAR)ptr, input->data.hid.dwSizeHid);
657 ptr += input->data.hid.dwSizeHid;
664void WinRawInputDevice::
665process_report(PCHAR ptr,
size_t size) {
668 UCHAR report_id = ptr[0];
671 if (report_id < _report_buttons.size()) {
672 unset_buttons = _report_buttons[report_id];
675 PHIDP_DATA data = (PHIDP_DATA)alloca(
sizeof(HIDP_DATA) * _max_data_count);
676 nassertv(data !=
nullptr);
678 ULONG count = _max_data_count;
679 NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, ptr, size);
680 if (status == HIDP_STATUS_SUCCESS) {
681 for (ULONG di = 0; di < count; ++di) {
682 if (data[di].DataIndex != _hat_data_index) {
683 if (device_cat.is_spam()) {
685 <<
"Read RawValue " << data[di].RawValue
686 <<
" for DataIndex " << data[di].DataIndex
687 <<
" from raw device " << _path <<
"\n";
690 if (data[di].DataIndex >= _indices.size()) {
691 if (device_cat.is_debug()) {
693 <<
"Ignoring out of range DataIndex " << data[di].DataIndex
694 <<
"from raw device " << _path <<
"\n";
699 const Index &idx = _indices[data[di].DataIndex];
700 if (idx._axis >= 0) {
701 if (idx._sign_bit != 0) {
703 int value = data[di].RawValue;
704 if (value & idx._sign_bit) {
705 value -= (idx._sign_bit << 1);
707 axis_changed(idx._axis, value);
709 axis_changed(idx._axis, data[di].RawValue);
712 if (idx._button >= 0) {
714 button_changed(idx._button, (data[di].On != FALSE));
717 int value = (int)data[di].RawValue - _hat_data_minimum;
718 button_changed(_hat_left_button + 0, value >= 5 && value <= 7);
719 button_changed(_hat_left_button + 1, value >= 1 && value <= 3);
720 button_changed(_hat_left_button + 2, value >= 3 && value <= 5);
721 button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1);
727 while (button_index >= 0) {
728 button_changed(button_index,
false);
732 }
else if (device_cat.is_spam()) {
734 <<
"Failed to get data from raw device " << _path
735 <<
" (error 0x" << std::hex << (status & 0xffffffffu) << std::dec <<
")\n";
744void WinRawInputDevice::
A dynamic array with an unlimited number of bits.
void clear_bit(int index)
Sets the nth bit off.
int get_lowest_on_bit() const
Returns the index of the lowest 1 bit in the array.
Similar to MutexHolder, but for a light mutex.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.