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}
76 static const char *default_gamepad_mapping[16] = {
77 "face_a",
"face_b",
"face_x",
"face_y",
"lshoulder",
"rshoulder",
"back",
"start",
"lstick",
"rstick" 80 static pHidP_GetCaps _HidP_GetCaps =
nullptr;
81 static pHidP_GetButtonCaps _HidP_GetButtonCaps =
nullptr;
82 static pHidP_GetValueCaps _HidP_GetValueCaps =
nullptr;
83 static pHidP_GetData _HidP_GetData =
nullptr;
84 static pHidP_MaxDataListLength _HidP_MaxDataListLength =
nullptr;
90 static bool init_hidp() {
91 HMODULE module = LoadLibraryA(
"hid.dll");
93 if (device_cat.is_debug()) {
95 <<
"Successfully loaded hid.dll\n";
98 _HidP_GetCaps = (pHidP_GetCaps)GetProcAddress(module,
"HidP_GetCaps");
99 _HidP_GetButtonCaps = (pHidP_GetButtonCaps)GetProcAddress(module,
"HidP_GetButtonCaps");
100 _HidP_GetValueCaps = (pHidP_GetValueCaps)GetProcAddress(module,
"HidP_GetValueCaps");
101 _HidP_GetData = (pHidP_GetData)GetProcAddress(module,
"HidP_GetData");
102 _HidP_MaxDataListLength = (pHidP_MaxDataListLength)GetProcAddress(module,
"HidP_MaxDataListLength");
104 if (_HidP_GetCaps ==
nullptr || _HidP_GetButtonCaps ==
nullptr ||
105 _HidP_GetValueCaps ==
nullptr || _HidP_GetData ==
nullptr ||
106 _HidP_MaxDataListLength ==
nullptr) {
108 <<
"Failed to locate function pointers in hid.dll\n";
116 <<
"Failed to load hid.dll.\n";
124 WinRawInputDevice(WinInputDeviceManager *manager,
const char *path) :
128 _preparsed(nullptr) {
135 ~WinRawInputDevice() {
138 if (_manager !=
nullptr) {
139 _manager->device_destroyed(
this);
141 if (_preparsed !=
nullptr) {
143 _preparsed =
nullptr;
151 bool WinRawInputDevice::
152 on_arrival(HANDLE handle,
const RID_DEVICE_INFO &info, std::string name) {
159 _name = std::move(name);
162 const char *
const *gamepad_buttons = default_gamepad_mapping;
164 switch (info.dwType) {
166 _device_class = DeviceClass::mouse;
169 case RIM_TYPEKEYBOARD:
170 _device_class = DeviceClass::keyboard;
174 _vendor_id = info.hid.dwVendorId;
175 _product_id = info.hid.dwProductId;
178 if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
179 info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) {
180 _device_class = DeviceClass::gamepad;
183 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
184 info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) {
185 _device_class = DeviceClass::flight_stick;
187 if (_name ==
"usb gamepad") {
189 _device_class = DeviceClass::gamepad;
193 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
194 info.hid.usUsage == HID_USAGE_GENERIC_MOUSE) {
195 _device_class = DeviceClass::mouse;
198 }
else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
199 info.hid.usUsage == HID_USAGE_GENERIC_KEYBOARD) {
200 _device_class = DeviceClass::keyboard;
203 }
else if (_vendor_id == 0x046d &&
204 (_product_id == 0xc623 ||
205 _product_id == 0xc625 ||
206 _product_id == 0xc626 ||
207 _product_id == 0xc627 ||
208 _product_id == 0xc628 ||
209 _product_id == 0xc629 ||
210 _product_id == 0xc62b)) {
211 _device_class = DeviceClass::spatial_mouse;
219 if (_device_class == DeviceClass::gamepad ||
220 _device_class == DeviceClass::flight_stick) {
222 const DeviceMapping *mapping = mapping_presets;
223 while (mapping->vendor != 0) {
224 if (info.hid.dwVendorId == mapping->vendor &&
225 info.hid.dwProductId == mapping->product) {
226 _device_class = mapping->device_class;
227 gamepad_buttons = mapping->buttons;
228 quirks = mapping->quirks;
229 if (device_cat.is_debug()) {
231 <<
"Using preset mapping for " << mapping->device_class
232 <<
" with VID=" << std::hex << mapping->vendor
233 <<
" PID=" << mapping->product << std::dec <<
"\n";
242 static bool hid_initialized =
false;
243 if (!hid_initialized) {
247 hid_initialized =
true;
252 if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA,
nullptr, &size) < 0) {
256 PHIDP_PREPARSED_DATA buffer = (PHIDP_PREPARSED_DATA)malloc(size);
257 if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, buffer, &size) <= 0) {
263 if (_HidP_GetCaps(buffer, &caps) != HIDP_STATUS_SUCCESS) {
265 <<
"Failed to get capabilities from HID preparsed data.\n";
269 if (device_cat.is_debug()) {
271 <<
"Found " << _device_class <<
" device \"" << _name <<
"\" with " 272 << caps.NumberInputDataIndices <<
" data indices, " 273 << caps.NumberInputButtonCaps <<
" button caps, " 274 << caps.NumberInputValueCaps <<
" value caps\n";
280 _indices.resize(caps.NumberInputDataIndices);
285 USHORT num_button_caps = caps.NumberInputButtonCaps;
286 PHIDP_BUTTON_CAPS button_caps;
287 if (num_button_caps > 0u) {
288 button_caps = (PHIDP_BUTTON_CAPS)alloca(num_button_caps *
sizeof(HIDP_BUTTON_CAPS));
289 _HidP_GetButtonCaps(HidP_Input, button_caps, &num_button_caps, buffer);
292 for (USHORT i = 0; i < num_button_caps; ++i) {
293 HIDP_BUTTON_CAPS &cap = button_caps[i];
296 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
298 if (device_cat.is_debug()) {
300 <<
"Found button range: DataIndex=" << dec
301 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
302 <<
", ReportID=" << (int)cap.ReportID
303 <<
", UsagePage=0x" << hex << cap.UsagePage
304 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
308 if (device_cat.is_debug()) {
310 <<
"Found button: DataIndex=" << dec << cap.NotRange.DataIndex
311 <<
", ReportID=" << (int)cap.ReportID
312 <<
", UsagePage=0x" << hex << cap.UsagePage
313 <<
", Usage=0x" << cap.NotRange.Usage
318 nassertd(cap.Range.DataIndexMin + upper < (
int)_indices.size())
continue;
323 if (cap.ReportID >= _report_buttons.size()) {
324 _report_buttons.resize(cap.ReportID + 1);
326 for (
int j = 0; j <= upper; ++j) {
327 USAGE usage = j + cap.Range.UsageMin;
328 USHORT data_index = j + cap.Range.DataIndexMin;
330 switch (cap.UsagePage) {
331 case HID_USAGE_PAGE_BUTTON:
332 if (_device_class == DeviceClass::gamepad) {
333 if (usage > 0 && usage - 1 < _countof(default_gamepad_mapping)) {
334 if (gamepad_buttons[usage - 1] !=
nullptr) {
335 handle = registry->
find_button(gamepad_buttons[usage - 1]);
338 }
else if (_device_class == DeviceClass::flight_stick) {
342 }
else if (_device_class == DeviceClass::mouse) {
344 int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
353 int button_index = _buttons.size();
354 _report_buttons[cap.ReportID].set_bit(button_index);
355 _indices[data_index] = Index::button(button_index);
356 _buttons.push_back(ButtonState(handle));
360 USHORT num_value_caps = caps.NumberInputValueCaps;
361 PHIDP_VALUE_CAPS value_caps;
362 if (num_value_caps > 0u) {
363 value_caps = (PHIDP_VALUE_CAPS)alloca(num_value_caps *
sizeof(HIDP_VALUE_CAPS));
364 _HidP_GetValueCaps(HidP_Input, value_caps, &num_value_caps, buffer);
367 _hat_data_index = -1;
369 for (USHORT i = 0; i < num_value_caps; ++i) {
370 HIDP_VALUE_CAPS &cap = value_caps[i];
373 upper = (cap.Range.UsageMax - cap.Range.UsageMin);
375 if (device_cat.is_debug()) {
377 <<
"Found value range: DataIndex=" << dec
378 << cap.Range.DataIndexMin <<
".." << cap.Range.DataIndexMax
379 <<
", ReportID=" << (int)cap.ReportID
380 <<
", UsagePage=0x" << hex << cap.UsagePage
381 <<
", Usage=0x" << cap.Range.UsageMin <<
"..0x" << cap.Range.UsageMax
382 << dec <<
", LogicalMin=" << cap.LogicalMin
383 <<
", LogicalMax=" << cap.LogicalMax <<
"\n";
386 if (device_cat.is_debug()) {
388 <<
"Found value: DataIndex=" << dec << cap.NotRange.DataIndex
389 <<
", ReportID=" << (int)cap.ReportID
390 <<
", UsagePage=0x" << hex << cap.UsagePage
391 <<
", Usage=0x" << cap.NotRange.Usage
392 << dec <<
", LogicalMin=" << cap.LogicalMin
393 <<
", LogicalMax=" << cap.LogicalMax <<
"\n";
397 nassertd(cap.Range.DataIndexMin + upper < (
int)_indices.size())
continue;
399 for (
int j = 0; j <= upper; ++j) {
400 USAGE usage = j + cap.Range.UsageMin;
401 USHORT data_index = j + cap.Range.DataIndexMin;
402 bool is_signed =
true;
405 if (cap.LogicalMin == 0 && cap.LogicalMax == -1) {
406 cap.LogicalMax = 65535;
410 Axis axis = Axis::none;
411 switch (cap.UsagePage) {
412 case HID_USAGE_PAGE_GENERIC:
414 case HID_USAGE_GENERIC_X:
415 if (_device_class == DeviceClass::gamepad) {
417 }
else if (_device_class == DeviceClass::flight_stick) {
423 case HID_USAGE_GENERIC_Y:
424 if (_device_class == DeviceClass::gamepad) {
426 swap(cap.LogicalMin, cap.LogicalMax);
427 }
else if (_device_class == DeviceClass::flight_stick) {
431 swap(cap.LogicalMin, cap.LogicalMax);
434 case HID_USAGE_GENERIC_Z:
435 if (_device_class == DeviceClass::gamepad) {
436 if (quirks & QB_rstick_from_z) {
437 if (quirks & QB_right_axes_swapped) {
438 axis = InputDevice::Axis::right_y;
439 swap(cap.LogicalMin, cap.LogicalMax);
441 axis = InputDevice::Axis::right_x;
443 }
else if ((quirks & QB_no_analog_triggers) == 0) {
444 axis = Axis::left_trigger;
446 }
else if (_device_class == DeviceClass::flight_stick) {
447 axis = Axis::throttle;
448 if ((quirks & QB_reversed_throttle) != 0) {
449 std::swap(cap.LogicalMin, cap.LogicalMax);
451 if ((quirks & QB_centered_throttle) != 0) {
456 swap(cap.LogicalMin, cap.LogicalMax);
459 case HID_USAGE_GENERIC_RX:
460 if (_device_class == DeviceClass::gamepad) {
461 axis = Axis::right_x;
466 case HID_USAGE_GENERIC_RY:
467 if (_device_class == DeviceClass::gamepad) {
468 axis = Axis::right_y;
472 swap(cap.LogicalMin, cap.LogicalMax);
474 case HID_USAGE_GENERIC_RZ:
475 if (_device_class == DeviceClass::gamepad) {
476 if (quirks & QB_rstick_from_z) {
477 if (quirks & QB_right_axes_swapped) {
478 axis = InputDevice::Axis::right_x;
480 axis = InputDevice::Axis::right_y;
481 swap(cap.LogicalMin, cap.LogicalMax);
483 }
else if ((quirks & QB_no_analog_triggers) == 0) {
484 axis = Axis::right_trigger;
489 swap(cap.LogicalMin, cap.LogicalMax);
492 case HID_USAGE_GENERIC_SLIDER:
495 swap(cap.LogicalMin, cap.LogicalMax);
497 case HID_USAGE_GENERIC_WHEEL:
500 case HID_USAGE_GENERIC_HATSWITCH:
502 _hat_data_index = data_index;
503 _hat_data_minimum = cap.LogicalMin;
512 for (
size_t i = 0; i < _axes.size(); ++i) {
513 if (_axes[i].axis == axis) {
522 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax,
true);
524 axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax);
526 _indices[data_index] = Index::axis(axis_index, is_signed);
531 if (_hat_data_index != -1) {
532 _hat_left_button = (int)_buttons.size();
533 if (_device_class == DeviceClass::gamepad) {
534 _buttons.push_back(ButtonState(GamepadButton::dpad_left()));
535 _buttons.push_back(ButtonState(GamepadButton::dpad_right()));
536 _buttons.push_back(ButtonState(GamepadButton::dpad_down()));
537 _buttons.push_back(ButtonState(GamepadButton::dpad_up()));
539 _buttons.push_back(ButtonState(GamepadButton::hat_left()));
540 _buttons.push_back(ButtonState(GamepadButton::hat_right()));
541 _buttons.push_back(ButtonState(GamepadButton::hat_down()));
542 _buttons.push_back(ButtonState(GamepadButton::hat_up()));
546 _max_data_count = _HidP_MaxDataListLength(HidP_Input, buffer);
548 nassertr_always(_max_data_count >= 0,
false);
551 _is_connected =
true;
558 void WinRawInputDevice::
561 _is_connected =
false;
563 if (_preparsed !=
nullptr) {
565 _preparsed =
nullptr;
568 _report_buttons.clear();
574 void WinRawInputDevice::
575 on_input(PRAWINPUT input) {
576 nassertv(input !=
nullptr);
577 nassertv(_preparsed !=
nullptr);
579 if (_max_data_count == 0) {
583 BYTE *ptr = input->data.hid.bRawData;
584 if (input->data.hid.dwSizeHid == 0) {
590 if (device_cat.is_spam()) {
592 << _name <<
" received " << input->data.hid.dwCount <<
" reports of size " 593 << input->data.hid.dwSizeHid <<
"\n";
596 for (DWORD i = 0; i < input->data.hid.dwCount; ++i) {
597 process_report((PCHAR)ptr, input->data.hid.dwSizeHid);
598 ptr += input->data.hid.dwSizeHid;
605 void WinRawInputDevice::
606 process_report(PCHAR ptr,
size_t size) {
609 UCHAR report_id = ptr[0];
612 if (report_id < _report_buttons.size()) {
613 unset_buttons = _report_buttons[report_id];
616 PHIDP_DATA data = (PHIDP_DATA)alloca(
sizeof(HIDP_DATA) * _max_data_count);
617 nassertv(data !=
nullptr);
619 ULONG count = _max_data_count;
620 NTSTATUS status = _HidP_GetData(HidP_Input, data, &count, (PHIDP_PREPARSED_DATA)_preparsed, ptr, size);
621 if (status == HIDP_STATUS_SUCCESS) {
622 for (ULONG di = 0; di < count; ++di) {
623 if (data[di].DataIndex != _hat_data_index) {
624 nassertd(data[di].DataIndex < _indices.size())
continue;
625 const Index &idx = _indices[data[di].DataIndex];
626 if (idx._axis >= 0) {
628 axis_changed(idx._axis, (SHORT)data[di].RawValue);
630 axis_changed(idx._axis, data[di].RawValue);
633 if (idx._button >= 0) {
635 button_changed(idx._button, (data[di].On != FALSE));
638 int value = (int)data[di].RawValue - _hat_data_minimum;
639 button_changed(_hat_left_button + 0, value >= 5 && value <= 7);
640 button_changed(_hat_left_button + 1, value >= 1 && value <= 3);
641 button_changed(_hat_left_button + 2, value >= 3 && value <= 5);
642 button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1);
648 while (button_index >= 0) {
649 button_changed(button_index,
false);
653 }
else if (device_cat.is_spam()) {
655 <<
"Failed to get data from raw device " << _path
656 <<
" (error 0x" << std::hex << (status & 0xffffffffu) << std::dec <<
")\n";
665 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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.