Panda3D
winRawInputDevice.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file winRawInputDevice.cxx
10  * @author rdb
11  * @date 2018-01-19
12  */
13 
14 #include "winRawInputDevice.h"
15 #include "gamepadButton.h"
16 #include "mouseButton.h"
17 #include "buttonRegistry.h"
18 #include "winInputDeviceManager.h"
19 
20 #if defined(_WIN32) && !defined(CPPPARSER)
21 
22 #include <CfgMgr32.h>
23 #include <devpkey.h>
24 #include "phidsdi.h"
25 
26 enum QuirkBits : int {
27  // Has no trigger axes.
28  QB_no_analog_triggers = 1,
29 
30  // Throttle goes from -1 to 1 rather than from 0 to 1.
31  QB_centered_throttle = 2,
32 
33  // Throttle is reversed.
34  QB_reversed_throttle = 4,
35 
36  // Right stick uses Z and Rz inputs.
37  QB_rstick_from_z = 8,
38 
39  // Axes on the right stick are swapped, using x for y and vice versa.
40  QB_right_axes_swapped = 64,
41 };
42 
43 // Some nonstandard gamepads have different button mappings.
44 static const struct DeviceMapping {
45  unsigned short vendor;
46  unsigned short product;
47  InputDevice::DeviceClass device_class;
48  int quirks;
49  const char *buttons[16];
50 } mapping_presets[] = {
51  // SNES-style USB gamepad, or cheap unbranded USB gamepad with no sticks
52  // ABXY are mapped based on their position, not based on their label.
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"}
55  },
56  // Unbranded generic cheap USB gamepad
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"}
59  },
60  // Trust GXT 24 / SPEED Link SL-6535-SBK-01
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"}
63  },
64  // T.Flight Hotas X
65  {0x044f, 0xb108, InputDevice::DeviceClass::flight_stick, QB_centered_throttle | QB_reversed_throttle,
66  {0}
67  },
68  // NVIDIA Shield Controller
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}
71  },
72  {0},
73 };
74 
75 // This is our fallback button mapping, used with Xbox 360 and other devices.
76 static const char *default_gamepad_mapping[16] = {
77  "face_a", "face_b", "face_x", "face_y", "lshoulder", "rshoulder", "back", "start", "lstick", "rstick"
78 };
79 
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;
85 
86 /**
87  * Static method to initialize the HID parser library. We load it dynamically
88  * because the Windows 7.1 SDK doesn't ship hid.lib.
89  */
90 static bool init_hidp() {
91  HMODULE module = LoadLibraryA("hid.dll");
92  if (module) {
93  if (device_cat.is_debug()) {
94  device_cat.debug()
95  << "Successfully loaded hid.dll\n";
96  }
97 
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");
103 
104  if (_HidP_GetCaps == nullptr || _HidP_GetButtonCaps == nullptr ||
105  _HidP_GetValueCaps == nullptr || _HidP_GetData == nullptr ||
106  _HidP_MaxDataListLength == nullptr) {
107  device_cat.error()
108  << "Failed to locate function pointers in hid.dll\n";
109  return false;
110  }
111 
112  return true;
113  }
114 
115  device_cat.error()
116  << "Failed to load hid.dll.\n";
117  return false;
118 }
119 
120 /**
121  * Protected constructor. Given a raw device handle.
122  */
123 WinRawInputDevice::
124 WinRawInputDevice(WinInputDeviceManager *manager, const char *path) :
125  _manager(manager),
126  _path(path),
127  _max_data_count(0),
128  _preparsed(nullptr) {
129 }
130 
131 /**
132  *
133  */
134 WinRawInputDevice::
135 ~WinRawInputDevice() {
136  // Unregister the device from the manager.
137  LightMutexHolder holder(_lock);
138  if (_manager != nullptr) {
139  _manager->device_destroyed(this);
140  }
141  if (_preparsed != nullptr) {
142  free(_preparsed);
143  _preparsed = nullptr;
144  }
145 }
146 
147 /**
148  * Called by InputDeviceManager when this device is connected. Returns true
149  * if the device was connected successfully.
150  */
151 bool WinRawInputDevice::
152 on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
153  using std::hex;
154  using std::dec;
155  using std::swap;
156 
157  LightMutexHolder holder(_lock);
158 
159  _name = std::move(name);
160 
161  int quirks = 0;
162  const char *const *gamepad_buttons = default_gamepad_mapping;
163 
164  switch (info.dwType) {
165  case RIM_TYPEMOUSE:
166  _device_class = DeviceClass::mouse;
167  break;
168 
169  case RIM_TYPEKEYBOARD:
170  _device_class = DeviceClass::keyboard;
171  break;
172 
173  case RIM_TYPEHID:
174  _vendor_id = info.hid.dwVendorId;
175  _product_id = info.hid.dwProductId;
176 
177  // Gamepads
178  if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
179  info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) {
180  _device_class = DeviceClass::gamepad;
181 
182  // Various game controllers, incl. flight sticks and some gamepads
183  } else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
184  info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) {
185  _device_class = DeviceClass::flight_stick;
186 
187  if (_name == "usb gamepad") {
188  // Well, it claims to be a gamepad...
189  _device_class = DeviceClass::gamepad;
190  }
191 
192  // Mice
193  } else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
194  info.hid.usUsage == HID_USAGE_GENERIC_MOUSE) {
195  _device_class = DeviceClass::mouse;
196 
197  // Keyboards
198  } else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
199  info.hid.usUsage == HID_USAGE_GENERIC_KEYBOARD) {
200  _device_class = DeviceClass::keyboard;
201 
202  // 3Dconnexion SpaceNavigator and friends.
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;
212  }
213  break;
214 
215  default:
216  return false;
217  }
218 
219  if (_device_class == DeviceClass::gamepad ||
220  _device_class == DeviceClass::flight_stick) {
221  // Do we have a built-in mapping?
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()) {
230  device_cat.debug()
231  << "Using preset mapping for " << mapping->device_class
232  << " with VID=" << std::hex << mapping->vendor
233  << " PID=" << mapping->product << std::dec << "\n";
234  }
235  break;
236  }
237  ++mapping;
238  }
239  }
240 
241  // Initialize hid.dll, which provides the HID parser functions.
242  static bool hid_initialized = false;
243  if (!hid_initialized) {
244  if (!init_hidp()) {
245  return false;
246  }
247  hid_initialized = true;
248  }
249 
250  // Get the "preparsed data", which we can parse with the HID parser API.
251  UINT size = 0;
252  if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, nullptr, &size) < 0) {
253  return false;
254  }
255 
256  PHIDP_PREPARSED_DATA buffer = (PHIDP_PREPARSED_DATA)malloc(size);
257  if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, buffer, &size) <= 0) {
258  return false;
259  }
260  _preparsed = buffer;
261 
262  HIDP_CAPS caps;
263  if (_HidP_GetCaps(buffer, &caps) != HIDP_STATUS_SUCCESS) {
264  device_cat.warning()
265  << "Failed to get capabilities from HID preparsed data.\n";
266  return false;
267  }
268 
269  if (device_cat.is_debug()) {
270  device_cat.debug()
271  << "Found " << _device_class << " device \"" << _name << "\" with "
272  << caps.NumberInputDataIndices << " data indices, "
273  << caps.NumberInputButtonCaps << " button caps, "
274  << caps.NumberInputValueCaps << " value caps\n";
275  }
276 
277  ButtonRegistry *registry = ButtonRegistry::ptr();
278 
279  // Prepare a mapping of data indices to button/axis indices.
280  _indices.resize(caps.NumberInputDataIndices);
281 
282  _buttons.clear();
283  _axes.clear();
284 
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);
290  }
291 
292  for (USHORT i = 0; i < num_button_caps; ++i) {
293  HIDP_BUTTON_CAPS &cap = button_caps[i];
294  int upper = 0;
295  if (cap.IsRange) {
296  upper = (cap.Range.UsageMax - cap.Range.UsageMin);
297 
298  if (device_cat.is_debug()) {
299  device_cat.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
305  << dec << "\n";
306  }
307  } else {
308  if (device_cat.is_debug()) {
309  device_cat.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
314  << dec << "\n";
315  }
316  }
317 
318  nassertd(cap.Range.DataIndexMin + upper < (int)_indices.size()) continue;
319 
320  // Windows will only tell us which buttons in a report are "on", so we
321  // need to keep track of which buttons exist in which report so that we
322  // can figure out which ones are off.
323  if (cap.ReportID >= _report_buttons.size()) {
324  _report_buttons.resize(cap.ReportID + 1);
325  }
326  for (int j = 0; j <= upper; ++j) {
327  USAGE usage = j + cap.Range.UsageMin;
328  USHORT data_index = j + cap.Range.DataIndexMin;
329  ButtonHandle handle = ButtonHandle::none();
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]);
336  }
337  }
338  } else if (_device_class == DeviceClass::flight_stick) {
339  if (usage > 0) {
340  handle = GamepadButton::joystick(usage - 1);
341  }
342  } else if (_device_class == DeviceClass::mouse) {
343  // In Panda, wheel and right button are flipped around...
344  int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
345  handle = MouseButton::button(button);
346  }
347  break;
348 
349  default:
350  continue;
351  }
352 
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));
357  }
358  }
359 
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);
365  }
366 
367  _hat_data_index = -1;
368 
369  for (USHORT i = 0; i < num_value_caps; ++i) {
370  HIDP_VALUE_CAPS &cap = value_caps[i];
371  int upper = 0;
372  if (cap.IsRange) {
373  upper = (cap.Range.UsageMax - cap.Range.UsageMin);
374 
375  if (device_cat.is_debug()) {
376  device_cat.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";
384  }
385  } else {
386  if (device_cat.is_debug()) {
387  device_cat.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";
394  }
395  }
396 
397  nassertd(cap.Range.DataIndexMin + upper < (int)_indices.size()) continue;
398 
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;
403 
404  // My gamepads give this odd invalid range.
405  if (cap.LogicalMin == 0 && cap.LogicalMax == -1) {
406  cap.LogicalMax = 65535;
407  is_signed = false;
408  }
409 
410  Axis axis = Axis::none;
411  switch (cap.UsagePage) {
412  case HID_USAGE_PAGE_GENERIC:
413  switch (usage) {
414  case HID_USAGE_GENERIC_X:
415  if (_device_class == DeviceClass::gamepad) {
416  axis = Axis::left_x;
417  } else if (_device_class == DeviceClass::flight_stick) {
418  axis = Axis::roll;
419  } else {
420  axis = Axis::x;
421  }
422  break;
423  case HID_USAGE_GENERIC_Y:
424  if (_device_class == DeviceClass::gamepad) {
425  axis = Axis::left_y;
426  swap(cap.LogicalMin, cap.LogicalMax);
427  } else if (_device_class == DeviceClass::flight_stick) {
428  axis = Axis::pitch;
429  } else {
430  axis = Axis::y;
431  swap(cap.LogicalMin, cap.LogicalMax);
432  }
433  break;
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);
440  } else {
441  axis = InputDevice::Axis::right_x;
442  }
443  } else if ((quirks & QB_no_analog_triggers) == 0) {
444  axis = Axis::left_trigger;
445  }
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);
450  }
451  if ((quirks & QB_centered_throttle) != 0) {
452  is_signed = false;
453  }
454  } else {
455  axis = Axis::z;
456  swap(cap.LogicalMin, cap.LogicalMax);
457  }
458  break;
459  case HID_USAGE_GENERIC_RX:
460  if (_device_class == DeviceClass::gamepad) {
461  axis = Axis::right_x;
462  } else {
463  axis = Axis::pitch;
464  }
465  break;
466  case HID_USAGE_GENERIC_RY:
467  if (_device_class == DeviceClass::gamepad) {
468  axis = Axis::right_y;
469  } else {
470  axis = Axis::roll;
471  }
472  swap(cap.LogicalMin, cap.LogicalMax);
473  break;
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;
479  } else {
480  axis = InputDevice::Axis::right_y;
481  swap(cap.LogicalMin, cap.LogicalMax);
482  }
483  } else if ((quirks & QB_no_analog_triggers) == 0) {
484  axis = Axis::right_trigger;
485  }
486  } else {
487  // Flip to match Panda's convention for heading.
488  axis = Axis::yaw;
489  swap(cap.LogicalMin, cap.LogicalMax);
490  }
491  break;
492  case HID_USAGE_GENERIC_SLIDER:
493  // Flip to match Panda's convention for heading.
494  axis = Axis::rudder;
495  swap(cap.LogicalMin, cap.LogicalMax);
496  break;
497  case HID_USAGE_GENERIC_WHEEL:
498  axis = Axis::wheel;
499  break;
500  case HID_USAGE_GENERIC_HATSWITCH:
501  // This is handled specially.
502  _hat_data_index = data_index;
503  _hat_data_minimum = cap.LogicalMin;
504  continue;
505  }
506  break;
507  }
508 
509  // If this axis already exists, don't double-map it, but take the first
510  // one. This is important for the Trust GXT 24 / SL-6535-SBK-01 which
511  // have a weird extra Z axis with DataIndex 2 that should be ignored.
512  for (size_t i = 0; i < _axes.size(); ++i) {
513  if (_axes[i].axis == axis) {
514  axis = Axis::none;
515  break;
516  }
517  }
518 
519  int axis_index;
520  if (!is_signed) {
521  // All axes on the weird XInput-style mappings go from -1 to 1
522  axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax, true);
523  } else {
524  axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax);
525  }
526  _indices[data_index] = Index::axis(axis_index, is_signed);
527  }
528  }
529 
530  // Do we need to emulate a hat switch or directional pad?
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()));
538  } else {
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()));
543  }
544  }
545 
546  _max_data_count = _HidP_MaxDataListLength(HidP_Input, buffer);
547 
548  nassertr_always(_max_data_count >= 0, false);
549 
550  _handle = handle;
551  _is_connected = true;
552  return true;
553 }
554 
555 /**
556  * Called by InputDeviceManager when this device is removed.
557  */
558 void WinRawInputDevice::
559 on_removal() {
560  LightMutexHolder holder(_lock);
561  _is_connected = false;
562  _handle = nullptr;
563  if (_preparsed != nullptr) {
564  free(_preparsed);
565  _preparsed = nullptr;
566  }
567  _indices.clear();
568  _report_buttons.clear();
569 }
570 
571 /**
572  * Called by InputDeviceManager when raw input is received for this device.
573  */
574 void WinRawInputDevice::
575 on_input(PRAWINPUT input) {
576  nassertv(input != nullptr);
577  nassertv(_preparsed != nullptr);
578 
579  if (_max_data_count == 0) {
580  return;
581  }
582 
583  BYTE *ptr = input->data.hid.bRawData;
584  if (input->data.hid.dwSizeHid == 0) {
585  return;
586  }
587 
588  LightMutexHolder holder(_lock);
589 
590  if (device_cat.is_spam()) {
591  device_cat.spam()
592  << _name << " received " << input->data.hid.dwCount << " reports of size "
593  << input->data.hid.dwSizeHid << "\n";
594  }
595 
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;
599  }
600 }
601 
602 /**
603  * Processes a single HID report. Assumes the lock is held.
604  */
605 void WinRawInputDevice::
606 process_report(PCHAR ptr, size_t size) {
607  // The first byte is the report identifier. We need it to figure out which
608  // buttons are off, since each report only contains the "on" buttons.
609  UCHAR report_id = ptr[0];
610  BitArray unset_buttons;
611 
612  if (report_id < _report_buttons.size()) {
613  unset_buttons = _report_buttons[report_id];
614  }
615 
616  PHIDP_DATA data = (PHIDP_DATA)alloca(sizeof(HIDP_DATA) * _max_data_count);
617  nassertv(data != nullptr);
618 
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) {
627  if (idx._signed) {
628  axis_changed(idx._axis, (SHORT)data[di].RawValue);
629  } else {
630  axis_changed(idx._axis, data[di].RawValue);
631  }
632  }
633  if (idx._button >= 0) {
634  unset_buttons.clear_bit(idx._button);
635  button_changed(idx._button, (data[di].On != FALSE));
636  }
637  } else {
638  int value = (int)data[di].RawValue - _hat_data_minimum;
639  button_changed(_hat_left_button + 0, value >= 5 && value <= 7); // left
640  button_changed(_hat_left_button + 1, value >= 1 && value <= 3); // right
641  button_changed(_hat_left_button + 2, value >= 3 && value <= 5); // down
642  button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1); // up
643  }
644  }
645 
646  // Now unset the buttons in this report that aren't pressed.
647  int button_index = unset_buttons.get_lowest_on_bit();
648  while (button_index >= 0) {
649  button_changed(button_index, false);
650  unset_buttons.clear_bit(button_index);
651  button_index = unset_buttons.get_lowest_on_bit();
652  }
653  } else if (device_cat.is_spam()) {
654  device_cat.spam()
655  << "Failed to get data from raw device " << _path
656  << " (error 0x" << std::hex << (status & 0xffffffffu) << std::dec << ")\n";
657  }
658 }
659 
660 /**
661  * Polls the input device for new activity, to ensure it contains the latest
662  * events. This will only have any effect for some types of input devices;
663  * others may be updated automatically, and this method will be a no-op.
664  */
665 void WinRawInputDevice::
666 do_poll() {
667 }
668 
669 #endif // _WIN32
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ButtonHandle find_button(const std::string &name)
Finds a ButtonHandle in the registry matching the indicated name.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The ButtonRegistry class maintains all the assigned ButtonHandles in a given system.
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
void clear_bit(int index)
Sets the nth bit off.
Definition: bitArray.I:133
A dynamic array with an unlimited number of bits.
Definition: bitArray.h:39
static ButtonRegistry * ptr()
Returns the pointer to the global ButtonRegistry object.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static ButtonHandle button(int button_number)
Returns the ButtonHandle associated with the particular numbered mouse button (zero-based),...
Definition: mouseButton.cxx:32
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.
Definition: bitArray.cxx:332
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static ButtonHandle joystick(int button_number)
Returns the ButtonHandle associated with the particular numbered joystick button (zero-based),...