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}
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"}
92 static const char *default_gamepad_mapping[16] = {
93 "face_a",
"face_b",
"face_x",
"face_y",
"lshoulder",
"rshoulder",
"back",
"start",
"lstick",
"rstick"
96 static pHidP_GetCaps _HidP_GetCaps =
nullptr;
97 static pHidP_GetButtonCaps _HidP_GetButtonCaps =
nullptr;
98 static pHidP_GetValueCaps _HidP_GetValueCaps =
nullptr;
99 static pHidP_GetData _HidP_GetData =
nullptr;
100 static pHidP_MaxDataListLength _HidP_MaxDataListLength =
nullptr;
106 static bool init_hidp() {
107 HMODULE module = LoadLibraryA(
"hid.dll");
109 if (device_cat.is_debug()) {
111 <<
"Successfully loaded hid.dll\n";
114 _HidP_GetCaps = (pHidP_GetCaps)GetProcAddress(module,
"HidP_GetCaps");
115 _HidP_GetButtonCaps = (pHidP_GetButtonCaps)GetProcAddress(module,
"HidP_GetButtonCaps");
116 _HidP_GetValueCaps = (pHidP_GetValueCaps)GetProcAddress(module,
"HidP_GetValueCaps");
117 _HidP_GetData = (pHidP_GetData)GetProcAddress(module,
"HidP_GetData");
118 _HidP_MaxDataListLength = (pHidP_MaxDataListLength)GetProcAddress(module,
"HidP_MaxDataListLength");
120 if (_HidP_GetCaps ==
nullptr || _HidP_GetButtonCaps ==
nullptr ||
121 _HidP_GetValueCaps ==
nullptr || _HidP_GetData ==
nullptr ||
122 _HidP_MaxDataListLength ==
nullptr) {
124 <<
"Failed to locate function pointers in hid.dll\n";
132 <<
"Failed to load hid.dll.\n";
140 WinRawInputDevice(WinInputDeviceManager *manager,
const char *path) :
144 _preparsed(nullptr) {
151 ~WinRawInputDevice() {
154 if (_manager !=
nullptr) {
155 _manager->device_destroyed(
this);
157 if (_preparsed !=
nullptr) {
159 _preparsed =
nullptr;
167 bool WinRawInputDevice::
168 on_arrival(HANDLE handle,
const RID_DEVICE_INFO &info, std::string name) {
175 _name = std::move(name);
178 const char *
const *gamepad_buttons = default_gamepad_mapping;
180 switch (info.dwType) {
182 _device_class = DeviceClass::mouse;
185 case RIM_TYPEKEYBOARD:
186 _device_class = DeviceClass::keyboard;
190 _vendor_id = info.hid.dwVendorId;
191 _product_id = info.hid.dwProductId;
194 if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
195 info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) {
196 _device_class = DeviceClass::gamepad;
199 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
200 info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) {
201 _device_class = DeviceClass::flight_stick;
203 if (_name ==
"usb gamepad") {
205 _device_class = DeviceClass::gamepad;
209 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
210 info.hid.usUsage == HID_USAGE_GENERIC_MOUSE) {
211 _device_class = DeviceClass::mouse;
214 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
215 info.hid.usUsage == HID_USAGE_GENERIC_KEYBOARD) {
216 _device_class = DeviceClass::keyboard;
219 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_DIGITIZER &&
220 info.hid.usUsage == 1) {
221 _device_class = DeviceClass::digitizer;
224 }
else if (_vendor_id == 0x046d &&
225 (_product_id == 0xc623 ||
226 _product_id == 0xc625 ||
227 _product_id == 0xc626 ||
228 _product_id == 0xc627 ||
229 _product_id == 0xc628 ||
230 _product_id == 0xc629 ||
231 _product_id == 0xc62b)) {
232 _device_class = DeviceClass::spatial_mouse;
240 if (_device_class == DeviceClass::gamepad ||
241 _device_class == DeviceClass::flight_stick) {
243 const DeviceMapping *mapping = mapping_presets;
244 while (mapping->vendor != 0) {
245 if (info.hid.dwVendorId == mapping->vendor &&
246 info.hid.dwProductId == mapping->product) {
247 _device_class = mapping->device_class;
248 gamepad_buttons = mapping->buttons;
249 quirks = mapping->quirks;
250 if (device_cat.is_debug()) {
252 <<
"Using preset mapping for " << mapping->device_class
253 <<
" with VID=" << std::hex << mapping->vendor
254 <<
" PID=" << mapping->product << std::dec <<
"\n";
263 static bool hid_initialized =
false;
264 if (!hid_initialized) {
268 hid_initialized =
true;
273 if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA,
nullptr, &size) < 0) {
277 PHIDP_PREPARSED_DATA buffer = (PHIDP_PREPARSED_DATA)malloc(size);
278 if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, buffer, &size) <= 0) {
284 if (_HidP_GetCaps(buffer, &caps) != HIDP_STATUS_SUCCESS) {
286 <<
"Failed to get capabilities from HID preparsed data.\n";
290 if (device_cat.is_debug()) {
292 <<
"Found " << _device_class <<
" device \"" << _name <<
"\" with "
293 << caps.NumberInputDataIndices <<
" data indices, "
294 << caps.NumberInputButtonCaps <<
" button caps, "
295 << caps.NumberInputValueCaps <<
" value caps\n";
301 _indices.resize(caps.NumberInputDataIndices);
306 USHORT num_button_caps = caps.NumberInputButtonCaps;
307 PHIDP_BUTTON_CAPS button_caps;
308 if (num_button_caps > 0u) {
309 button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps *
sizeof(HIDP_BUTTON_CAPS));
310 _HidP_GetButtonCaps(HidP_Input, button_caps, &num_button_caps, buffer);
313 for (USHORT i = 0; i < num_button_caps; ++i) {
314 HIDP_BUTTON_CAPS &cap = button_caps[i];
317 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
319 if (device_cat.is_debug()) {
321 <<
"Found button range: DataIndex=" << dec
322 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
323 <<
", ReportID=" << (int)cap.ReportID
324 <<
", UsagePage=0x" << hex << cap.UsagePage
325 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
329 if (device_cat.is_debug()) {
331 <<
"Found button: DataIndex=" << dec << cap.NotRange.DataIndex
332 <<
", ReportID=" << (int)cap.ReportID
333 <<
", UsagePage=0x" << hex << cap.UsagePage
334 <<
", Usage=0x" << cap.NotRange.Usage
339 nassertd(cap.Range.DataIndexMin + upper < (
int)_indices.size()) continue;
344 if (cap.ReportID >= _report_buttons.size()) {
345 _report_buttons.resize(cap.ReportID + 1);
347 for (
int j = 0; j <= upper; ++j) {
348 USAGE usage = j + cap.Range.UsageMin;
349 USHORT data_index = j + cap.Range.DataIndexMin;
351 switch (cap.UsagePage) {
352 case HID_USAGE_PAGE_BUTTON:
353 if (_device_class == DeviceClass::gamepad) {
354 if (usage > 0 && usage - 1 < _countof(default_gamepad_mapping)) {
355 if (gamepad_buttons[usage - 1] !=
nullptr) {
356 handle = registry->
find_button(gamepad_buttons[usage - 1]);
359 }
else if (_device_class == DeviceClass::flight_stick) {
363 }
else if (_device_class == DeviceClass::mouse) {
365 int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
374 int button_index = _buttons.size();
375 _report_buttons[cap.ReportID].set_bit(button_index);
376 _indices[data_index] = Index::button(button_index);
377 _buttons.push_back(ButtonState(handle));
381 USHORT num_value_caps = caps.NumberInputValueCaps;
382 PHIDP_VALUE_CAPS value_caps;
383 if (num_value_caps > 0u) {
384 value_caps = (PHIDP_VALUE_CAPS)alloca(num_value_caps *
sizeof(HIDP_VALUE_CAPS));
385 _HidP_GetValueCaps(HidP_Input, value_caps, &num_value_caps, buffer);
388 _hat_data_index = -1;
390 for (USHORT i = 0; i < num_value_caps; ++i) {
391 HIDP_VALUE_CAPS &cap = value_caps[i];
394 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
396 if (device_cat.is_debug()) {
398 <<
"Found value range: DataIndex=" << dec
399 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
400 <<
", ReportID=" << (int)cap.ReportID
401 <<
", UsagePage=0x" << hex << cap.UsagePage
402 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
403 << dec <<
", LogicalMin=" << cap.LogicalMin
404 <<
", LogicalMax=" << cap.LogicalMax <<
"\n";
407 if (device_cat.is_debug()) {
409 <<
"Found value: DataIndex=" << dec << cap.NotRange.DataIndex
410 <<
", ReportID=" << (int)cap.ReportID
411 <<
", UsagePage=0x" << hex << cap.UsagePage
412 <<
", Usage=0x" << cap.NotRange.Usage
413 << dec <<
", LogicalMin=" << cap.LogicalMin
414 <<
", LogicalMax=" << cap.LogicalMax <<
"\n";
418 nassertd(cap.Range.DataIndexMin + upper < (
int)_indices.size()) continue;
420 for (
int j = 0; j <= upper; ++j) {
421 USAGE usage = j + cap.Range.UsageMin;
422 USHORT data_index = j + cap.Range.DataIndexMin;
423 bool is_signed =
true;
426 if (cap.LogicalMin == 0 && cap.LogicalMax == -1) {
427 cap.LogicalMax = 65535;
431 Axis axis = Axis::none;
432 switch (cap.UsagePage) {
433 case HID_USAGE_PAGE_GENERIC:
435 case HID_USAGE_GENERIC_X:
436 if (_device_class == DeviceClass::gamepad) {
438 }
else if (_device_class == DeviceClass::flight_stick) {
444 case HID_USAGE_GENERIC_Y:
445 if (_device_class == DeviceClass::gamepad) {
447 swap(cap.LogicalMin, cap.LogicalMax);
448 }
else if (_device_class == DeviceClass::flight_stick) {
452 swap(cap.LogicalMin, cap.LogicalMax);
455 case HID_USAGE_GENERIC_Z:
456 if (_device_class == DeviceClass::gamepad) {
457 if (quirks & QB_rstick_from_z) {
458 if (quirks & QB_right_axes_swapped) {
459 axis = InputDevice::Axis::right_y;
460 swap(cap.LogicalMin, cap.LogicalMax);
462 axis = InputDevice::Axis::right_x;
464 }
else if ((quirks & QB_no_analog_triggers) == 0) {
465 axis = Axis::left_trigger;
467 }
else if (_device_class == DeviceClass::flight_stick) {
468 axis = Axis::throttle;
469 if ((quirks & QB_reversed_throttle) != 0) {
470 std::swap(cap.LogicalMin, cap.LogicalMax);
472 if ((quirks & QB_centered_throttle) != 0) {
477 swap(cap.LogicalMin, cap.LogicalMax);
480 case HID_USAGE_GENERIC_RX:
481 if (_device_class == DeviceClass::gamepad) {
482 if (quirks & QB_rstick_from_z) {
483 if ((quirks & QB_no_analog_triggers) == 0) {
484 axis = Axis::left_trigger;
487 axis = Axis::right_x;
493 case HID_USAGE_GENERIC_RY:
494 if (_device_class == DeviceClass::gamepad) {
495 if (quirks & QB_rstick_from_z) {
496 if ((quirks & QB_no_analog_triggers) == 0) {
497 axis = Axis::right_trigger;
500 axis = Axis::right_y;
501 swap(cap.LogicalMin, cap.LogicalMax);
505 swap(cap.LogicalMin, cap.LogicalMax);
508 case HID_USAGE_GENERIC_RZ:
509 if (_device_class == DeviceClass::gamepad) {
510 if (quirks & QB_rstick_from_z) {
511 if (quirks & QB_right_axes_swapped) {
512 axis = InputDevice::Axis::right_x;
514 axis = InputDevice::Axis::right_y;
515 swap(cap.LogicalMin, cap.LogicalMax);
517 }
else if ((quirks & QB_no_analog_triggers) == 0) {
518 axis = Axis::right_trigger;
523 swap(cap.LogicalMin, cap.LogicalMax);
526 case HID_USAGE_GENERIC_SLIDER:
529 swap(cap.LogicalMin, cap.LogicalMax);
531 case HID_USAGE_GENERIC_WHEEL:
534 case HID_USAGE_GENERIC_HATSWITCH:
536 _hat_data_index = data_index;
537 _hat_data_minimum = cap.LogicalMin;
542 case HID_USAGE_PAGE_DIGITIZER:
545 axis = Axis::pressure;
554 for (
size_t i = 0; i < _axes.size(); ++i) {
555 if (_axes[i].axis == axis) {
564 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax,
true);
566 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax);
568 _indices[data_index] = Index::axis(axis_index, is_signed);
573 if (_hat_data_index != -1) {
574 _hat_left_button = (int)_buttons.size();
575 if (_device_class == DeviceClass::gamepad) {
576 _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
577 _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
578 _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
579 _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
581 _buttons.push_back(ButtonState(GamepadButton::hat_left()));
582 _buttons.push_back(ButtonState(GamepadButton::hat_right()));
583 _buttons.push_back(ButtonState(GamepadButton::hat_down()));
584 _buttons.push_back(ButtonState(GamepadButton::hat_up()));
588 _max_data_count = _HidP_MaxDataListLength(HidP_Input, buffer);
590 nassertr_always(_max_data_count >= 0,
false);
593 _is_connected =
true;
600 void WinRawInputDevice::
603 _is_connected =
false;
605 if (_preparsed !=
nullptr) {
607 _preparsed =
nullptr;
610 _report_buttons.clear();
616 void WinRawInputDevice::
617 on_input(PRAWINPUT input) {
618 nassertv(input !=
nullptr);
619 nassertv(_preparsed !=
nullptr);
621 if (_max_data_count == 0) {
625 BYTE *ptr = input->data.hid.bRawData;
626 if (input->data.hid.dwSizeHid == 0) {
632 if (device_cat.is_spam()) {
634 << _name <<
" received " << input->data.hid.dwCount <<
" reports of size "
635 << input->data.hid.dwSizeHid <<
"\n";
638 for (DWORD i = 0; i < input->data.hid.dwCount; ++i) {
639 process_report((PCHAR)ptr, input->data.hid.dwSizeHid);
640 ptr += input->data.hid.dwSizeHid;
647 void WinRawInputDevice::
648 process_report(PCHAR ptr,
size_t size) {
651 UCHAR report_id = ptr[0];
654 if (report_id < _report_buttons.size()) {
655 unset_buttons = _report_buttons[report_id];
658 PHIDP_DATA data = (PHIDP_DATA)alloca(
sizeof(HIDP_DATA) * _max_data_count);
659 nassertv(data !=
nullptr);
661 ULONG count = _max_data_count;
662 NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, ptr, size);
663 if (status == HIDP_STATUS_SUCCESS) {
664 for (ULONG di = 0; di < count; ++di) {
665 if (data[di].DataIndex != _hat_data_index) {
666 nassertd(data[di].DataIndex < _indices.size()) continue;
667 const Index &idx = _indices[data[di].DataIndex];
668 if (idx._axis >= 0) {
670 axis_changed(idx._axis, (SHORT)data[di].RawValue);
672 axis_changed(idx._axis, data[di].RawValue);
675 if (idx._button >= 0) {
677 button_changed(idx._button, (data[di].On != FALSE));
680 int value = (int)data[di].RawValue - _hat_data_minimum;
681 button_changed(_hat_left_button + 0, value >= 5 && value <= 7);
682 button_changed(_hat_left_button + 1, value >= 1 && value <= 3);
683 button_changed(_hat_left_button + 2, value >= 3 && value <= 5);
684 button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1);
690 while (button_index >= 0) {
691 button_changed(button_index,
false);
695 }
else if (device_cat.is_spam()) {
697 <<
"Failed to get data from raw device " << _path
698 <<
" (error 0x" << std::hex << (status & 0xffffffffu) << std::dec <<
")\n";
707 void 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.