Panda3D
ioKitInputDevice.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 ioKitInputDevice.cxx
10  * @author rdb
11  * @date 2017-12-21
12  */
13 
14 #include "ioKitInputDevice.h"
15 
16 #if defined(__APPLE__) && !defined(CPPPARSER)
17 
18 #include <IOKit/hid/IOHIDElement.h>
19 
20 #include "keyboardButton.h"
21 #include "gamepadButton.h"
22 #include "mouseButton.h"
23 
24 static void removal_callback(void *ctx, IOReturn result, void *sender) {
25  // We need to hold a reference to this because it may otherwise be destroyed
26  // during the call to on_remove().
27  PT(IOKitInputDevice) input_device = (IOKitInputDevice *)ctx;
28  nassertv(input_device != nullptr);
29  nassertv(input_device->test_ref_count_integrity());
30  input_device->on_remove();
31 }
32 
33 /**
34  * Protected constructor.
35  */
36 IOKitInputDevice::
37 IOKitInputDevice(IOHIDDeviceRef device) :
38  _device(device),
39  _hat_element(nullptr),
40  _pointer_x(nullptr),
41  _pointer_y(nullptr),
42  _scroll_wheel(nullptr),
43  _pointer_x_timestamp(0),
44  _pointer_y_timestamp(0),
45  _scroll_wheel_timestamp(0) {
46  nassertv(device);
47 
48  char buffer[4096];
49 
50  CFStringRef name = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
51  if (name) {
52  buffer[0] = 0;
53  CFStringGetCString(name, buffer, sizeof(buffer), kCFStringEncodingUTF8);
54 
55  // Strip trailing spaces.
56  size_t len = strlen(buffer);
57  while (isspace(buffer[len - 1])) {
58  --len;
59  }
60  _name.assign(buffer, len);
61  }
62 
63  CFStringRef mfg = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey));
64  if (mfg) {
65  CFStringGetCString(mfg, buffer, sizeof(buffer), kCFStringEncodingUTF8);
66  _manufacturer = buffer;
67  }
68 
69  CFStringRef serial = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDSerialNumberKey));
70  if (serial) {
71  CFStringGetCString(serial, buffer, sizeof(buffer), kCFStringEncodingUTF8);
72  _serial_number = buffer;
73  }
74 
75  CFNumberRef vendor = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
76  if (vendor) {
77  int32_t value = 0;
78  CFNumberGetValue(vendor, kCFNumberSInt32Type, &value);
79  _vendor_id = (unsigned short)value;
80  }
81 
82  CFNumberRef product = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
83  if (product) {
84  int32_t value = 0;
85  CFNumberGetValue(product, kCFNumberSInt32Type, &value);
86  _product_id = (unsigned short)value;
87  }
88 
89  if (IOHIDDeviceConformsTo(device, kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse)) {
90  _device_class = DeviceClass::mouse;
91  } else if (IOHIDDeviceConformsTo(device, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard)) {
92  _device_class = DeviceClass::keyboard;
93  } else if (IOHIDDeviceConformsTo(device, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad)) {
94  _device_class = DeviceClass::gamepad;
95  } else if (IOHIDDeviceConformsTo(device, kHIDPage_Simulation, kHIDUsage_Sim_FlightStick)) {
96  _device_class = DeviceClass::flight_stick;
97  } else if (IOHIDDeviceConformsTo(device, kHIDPage_Simulation, kHIDUsage_GD_Joystick)) {
98  _device_class = DeviceClass::flight_stick;
99  } else if (_vendor_id == 0x044f && _product_id == 0xb108) {
100  // T.Flight Hotas X
101  _device_class = DeviceClass::flight_stick;
102  } else if (_vendor_id == 0x046d &&
103  (_product_id == 0xc623 ||
104  _product_id == 0xc625 ||
105  _product_id == 0xc626 ||
106  _product_id == 0xc627 ||
107  _product_id == 0xc628 ||
108  _product_id == 0xc629 ||
109  _product_id == 0xc62b)) {
110  // 3Dconnexion SpaceNavigator and friends.
111  _device_class = DeviceClass::spatial_mouse;
112  } else if (_name == "usb gamepad") {
113  _device_class = DeviceClass::gamepad;
114  }
115 
116  CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, nullptr, 0);
117  CFIndex count = CFArrayGetCount(elements);
118  for (CFIndex i = 0; i < count; ++i) {
119  IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
120  parse_element(element);
121  }
122  CFRelease(elements);
123 
124  if (_hat_element != nullptr) {
125  _hat_left_button = (int)_buttons.size();
126  _buttons.push_back(ButtonState(GamepadButton::hat_left()));
127  _buttons.push_back(ButtonState(GamepadButton::hat_right()));
128  _buttons.push_back(ButtonState(GamepadButton::hat_down()));
129  _buttons.push_back(ButtonState(GamepadButton::hat_up()));
130  }
131 
132  if (_pointer_x != nullptr && _pointer_y != nullptr) {
133  enable_feature(Feature::pointer);
134  add_pointer(PointerType::unknown, 0);
135  }
136 
137  _is_connected = true;
138  IOHIDDeviceRegisterRemovalCallback(device, removal_callback, this);
139 }
140 
141 /**
142  *
143  */
144 IOKitInputDevice::
145 ~IOKitInputDevice() {
146 }
147 
148 /**
149  * The nonstatic version of on_remove_device.
150  */
151 void IOKitInputDevice::
152 on_remove() {
153  {
154  LightMutexHolder holder(_lock);
155  if (!_is_connected) {
156  return;
157  }
158  _is_connected = false;
159  }
160 
161  if (device_cat.is_debug()) {
162  device_cat.debug()
163  << "Removed input device " << *this << "\n";
164  }
165 
166  IOHIDDeviceClose(_device, kIOHIDOptionsTypeNone);
167 
169  nassertv(mgr != nullptr);
170  mgr->remove_device(this);
171 }
172 
173 /**
174  *
175  */
176 void IOKitInputDevice::
177 parse_element(IOHIDElementRef element) {
178  ButtonHandle handle = ButtonHandle::none();
179  Axis axis = Axis::none;
180  uint32_t page = IOHIDElementGetUsagePage(element);
181  uint32_t usage = IOHIDElementGetUsage(element);
182 
183  switch (IOHIDElementGetType(element)) {
184  case kIOHIDElementTypeInput_Misc:
185  switch (page) {
186  case kHIDPage_GenericDesktop:
187  switch (usage) {
188  case kHIDUsage_GD_X:
189  if (_device_class == DeviceClass::gamepad) {
190  axis = Axis::left_x;
191  } else if (_device_class == DeviceClass::flight_stick) {
192  axis = Axis::roll;
193  } else if (_device_class == DeviceClass::mouse) {
194  _pointer_x = element;
195  return;
196  } else {
197  axis = Axis::x;
198  }
199  break;
200  case kHIDUsage_GD_Y:
201  if (_device_class == DeviceClass::gamepad) {
202  axis = Axis::left_y;
203  } else if (_device_class == DeviceClass::flight_stick) {
204  axis = Axis::pitch;
205  } else if (_device_class == DeviceClass::mouse) {
206  _pointer_y = element;
207  return;
208  } else {
209  axis = Axis::y;
210  }
211  break;
212  case kHIDUsage_GD_Z:
213  if (_device_class == DeviceClass::gamepad) {
214  axis = Axis::left_trigger;
215  } else if (_device_class == DeviceClass::flight_stick) {
216  axis = Axis::throttle;
217  } else {
218  axis = Axis::z;
219  }
220  break;
221  case kHIDUsage_GD_Rx:
222  if (_device_class == DeviceClass::gamepad) {
223  axis = Axis::right_x;
224  } else {
225  axis = Axis::pitch;
226  }
227  break;
228  case kHIDUsage_GD_Ry:
229  if (_device_class == DeviceClass::gamepad) {
230  axis = Axis::right_y;
231  } else {
232  axis = Axis::roll;
233  }
234  break;
235  case kHIDUsage_GD_Rz:
236  if (_device_class == DeviceClass::gamepad) {
237  axis = Axis::right_trigger;
238  } else {
239  axis = Axis::yaw;
240  }
241  break;
242  case kHIDUsage_GD_Slider:
243  axis = Axis::rudder;
244  break;
245  case kHIDUsage_GD_Dial:
246  break;
247  case kHIDUsage_GD_Wheel:
248  _scroll_wheel = element;
249  return;
250  case kHIDUsage_GD_Hatswitch:
251  _hat_element = element;
252  return;
253  case kHIDUsage_GD_DPadUp:
254  handle = GamepadButton::dpad_up();
255  break;
256  case kHIDUsage_GD_DPadDown:
257  handle = GamepadButton::dpad_down();
258  break;
259  case kHIDUsage_GD_DPadRight:
260  handle = GamepadButton::dpad_right();
261  break;
262  case kHIDUsage_GD_DPadLeft:
263  handle = GamepadButton::dpad_left();
264  break;
265  case 0xffffffffu:
266  return;
267  default:
268  break;
269  }
270  break;
271 
272  case kHIDPage_Simulation:
273  switch (usage) {
274  case kHIDUsage_Sim_Rudder:
275  axis = Axis::rudder;
276  break;
277  case kHIDUsage_Sim_Throttle:
278  axis = Axis::throttle;
279  break;
280  case kHIDUsage_Sim_Accelerator:
281  axis = Axis::accelerator;
282  break;
283  case kHIDUsage_Sim_Brake:
284  axis = Axis::brake;
285  break;
286  }
287  break;
288  }
289  if (axis != Axis::none) {
290  int min = IOHIDElementGetLogicalMin(element);
291  int max = IOHIDElementGetLogicalMax(element);
292  if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) {
293  // T.Flight Hotas X throttle is reversed and can go backwards.
294  add_axis(axis, max, min, true);
295  } else if (axis == Axis::yaw || axis == Axis::rudder || axis == Axis::left_y || axis == Axis::right_y ||
296  (_device_class == DeviceClass::spatial_mouse && (axis == Axis::y || axis == Axis::z || axis == Axis::roll))) {
297  // We'd like to reverse the Y axis to match the XInput behavior.
298  // We also reverse yaw to obey the right-hand rule.
299  add_axis(axis, max, min);
300  } else {
301  add_axis(axis, min, max);
302  }
303 
304  _analog_elements.push_back(element);
305  }
306  break;
307 
308  case kIOHIDElementTypeInput_Button:
309  switch (page) {
310  case kHIDPage_GenericDesktop:
311  switch (usage) {
312  case kHIDUsage_GD_DPadUp:
313  handle = GamepadButton::dpad_up();
314  break;
315  case kHIDUsage_GD_DPadDown:
316  handle = GamepadButton::dpad_down();
317  break;
318  case kHIDUsage_GD_DPadRight:
319  handle = GamepadButton::dpad_right();
320  break;
321  case kHIDUsage_GD_DPadLeft:
322  handle = GamepadButton::dpad_left();
323  break;
324  default:
325  break;
326  }
327  break;
328 
329  case kHIDPage_KeyboardOrKeypad:
330  switch (usage) {
331  case kHIDUsage_KeyboardA:
332  case kHIDUsage_KeyboardB:
333  case kHIDUsage_KeyboardC:
334  case kHIDUsage_KeyboardD:
335  case kHIDUsage_KeyboardE:
336  case kHIDUsage_KeyboardF:
337  case kHIDUsage_KeyboardG:
338  case kHIDUsage_KeyboardH:
339  case kHIDUsage_KeyboardI:
340  case kHIDUsage_KeyboardJ:
341  case kHIDUsage_KeyboardK:
342  case kHIDUsage_KeyboardL:
343  case kHIDUsage_KeyboardM:
344  case kHIDUsage_KeyboardN:
345  case kHIDUsage_KeyboardO:
346  case kHIDUsage_KeyboardP:
347  case kHIDUsage_KeyboardQ:
348  case kHIDUsage_KeyboardR:
349  case kHIDUsage_KeyboardS:
350  case kHIDUsage_KeyboardT:
351  case kHIDUsage_KeyboardU:
352  case kHIDUsage_KeyboardV:
353  case kHIDUsage_KeyboardW:
354  case kHIDUsage_KeyboardX:
355  case kHIDUsage_KeyboardY:
356  case kHIDUsage_KeyboardZ:
357  handle = KeyboardButton::ascii_key('a' + (usage - kHIDUsage_KeyboardA));
358  break;
359  case kHIDUsage_Keyboard1:
360  handle = KeyboardButton::ascii_key('1');
361  break;
362  case kHIDUsage_Keyboard2:
363  handle = KeyboardButton::ascii_key('2');
364  break;
365  case kHIDUsage_Keyboard3:
366  handle = KeyboardButton::ascii_key('3');
367  break;
368  case kHIDUsage_Keyboard4:
369  handle = KeyboardButton::ascii_key('4');
370  break;
371  case kHIDUsage_Keyboard5:
372  handle = KeyboardButton::ascii_key('5');
373  break;
374  case kHIDUsage_Keyboard6:
375  handle = KeyboardButton::ascii_key('6');
376  break;
377  case kHIDUsage_Keyboard7:
378  handle = KeyboardButton::ascii_key('7');
379  break;
380  case kHIDUsage_Keyboard8:
381  handle = KeyboardButton::ascii_key('8');
382  break;
383  case kHIDUsage_Keyboard9:
384  handle = KeyboardButton::ascii_key('9');
385  break;
386  case kHIDUsage_Keyboard0:
387  handle = KeyboardButton::ascii_key('0');
388  break;
389  case kHIDUsage_KeyboardReturnOrEnter:
390  handle = KeyboardButton::enter();
391  break;
392  case kHIDUsage_KeyboardEscape:
393  handle = KeyboardButton::escape();
394  break;
395  case kHIDUsage_KeyboardDeleteOrBackspace:
396  handle = KeyboardButton::backspace();
397  break;
398  case kHIDUsage_KeyboardTab:
399  handle = KeyboardButton::tab();
400  break;
401  case kHIDUsage_KeyboardSpacebar:
402  handle = KeyboardButton::ascii_key(' ');
403  break;
404  case kHIDUsage_KeyboardHyphen:
405  handle = KeyboardButton::ascii_key('-');
406  break;
407  case kHIDUsage_KeyboardEqualSign:
408  handle = KeyboardButton::ascii_key('=');
409  break;
410  case kHIDUsage_KeyboardOpenBracket:
411  handle = KeyboardButton::ascii_key('[');
412  break;
413  case kHIDUsage_KeyboardCloseBracket:
414  handle = KeyboardButton::ascii_key(']');
415  break;
416  case kHIDUsage_KeyboardBackslash:
417  handle = KeyboardButton::ascii_key('\\');
418  break;
419  case kHIDUsage_KeyboardNonUSPound:
420  handle = KeyboardButton::ascii_key('$');
421  break;
422  case kHIDUsage_KeyboardSemicolon:
423  handle = KeyboardButton::ascii_key(';');
424  break;
425  case kHIDUsage_KeyboardQuote:
426  handle = KeyboardButton::ascii_key('\'');
427  break;
428  case kHIDUsage_KeyboardGraveAccentAndTilde:
429  handle = KeyboardButton::ascii_key('`');
430  break;
431  case kHIDUsage_KeyboardComma:
432  handle = KeyboardButton::ascii_key(',');
433  break;
434  case kHIDUsage_KeyboardPeriod:
435  handle = KeyboardButton::ascii_key('.');
436  break;
437  case kHIDUsage_KeyboardSlash:
438  handle = KeyboardButton::ascii_key('/');
439  break;
440  case kHIDUsage_KeyboardCapsLock:
441  handle = KeyboardButton::caps_lock();
442  break;
443  case kHIDUsage_KeyboardF1:
444  handle = KeyboardButton::f1();
445  break;
446  case kHIDUsage_KeyboardF2:
447  handle = KeyboardButton::f2();
448  break;
449  case kHIDUsage_KeyboardF3:
450  handle = KeyboardButton::f3();
451  break;
452  case kHIDUsage_KeyboardF4:
453  handle = KeyboardButton::f4();
454  break;
455  case kHIDUsage_KeyboardF5:
456  handle = KeyboardButton::f5();
457  break;
458  case kHIDUsage_KeyboardF6:
459  handle = KeyboardButton::f6();
460  break;
461  case kHIDUsage_KeyboardF7:
462  handle = KeyboardButton::f7();
463  break;
464  case kHIDUsage_KeyboardF8:
465  handle = KeyboardButton::f8();
466  break;
467  case kHIDUsage_KeyboardF9:
468  handle = KeyboardButton::f9();
469  break;
470  case kHIDUsage_KeyboardF10:
471  handle = KeyboardButton::f10();
472  break;
473  case kHIDUsage_KeyboardF11:
474  handle = KeyboardButton::f11();
475  break;
476  case kHIDUsage_KeyboardF12:
477  handle = KeyboardButton::f12();
478  break;
479  case kHIDUsage_KeyboardPrintScreen:
480  handle = KeyboardButton::print_screen();
481  break;
482  case kHIDUsage_KeyboardScrollLock:
483  handle = KeyboardButton::scroll_lock();
484  break;
485  case kHIDUsage_KeyboardPause:
486  handle = KeyboardButton::pause();
487  break;
488  case kHIDUsage_KeyboardInsert:
489  handle = KeyboardButton::insert();
490  break;
491  case kHIDUsage_KeyboardHome:
492  handle = KeyboardButton::home();
493  break;
494  case kHIDUsage_KeyboardPageUp:
495  handle = KeyboardButton::page_up();
496  break;
497  case kHIDUsage_KeyboardDeleteForward:
498  handle = KeyboardButton::del();
499  break;
500  case kHIDUsage_KeyboardEnd:
501  handle = KeyboardButton::end();
502  break;
503  case kHIDUsage_KeyboardPageDown:
504  handle = KeyboardButton::page_down();
505  break;
506  case kHIDUsage_KeyboardRightArrow:
507  handle = KeyboardButton::right();
508  break;
509  case kHIDUsage_KeyboardLeftArrow:
510  handle = KeyboardButton::left();
511  break;
512  case kHIDUsage_KeyboardDownArrow:
513  handle = KeyboardButton::down();
514  break;
515  case kHIDUsage_KeyboardUpArrow:
516  handle = KeyboardButton::up();
517  break;
518  case kHIDUsage_KeypadNumLock:
519  handle = KeyboardButton::num_lock();
520  break;
521  case kHIDUsage_KeypadSlash:
522  handle = KeyboardButton::ascii_key('/');
523  break;
524  case kHIDUsage_KeypadAsterisk:
525  handle = KeyboardButton::ascii_key('*');
526  break;
527  case kHIDUsage_KeypadHyphen:
528  handle = KeyboardButton::ascii_key('-');
529  break;
530  case kHIDUsage_KeypadPlus:
531  handle = KeyboardButton::ascii_key('+');
532  break;
533  case kHIDUsage_KeypadEnter:
534  handle = KeyboardButton::enter();
535  break;
536  case kHIDUsage_Keypad1:
537  handle = KeyboardButton::ascii_key('1');
538  break;
539  case kHIDUsage_Keypad2:
540  handle = KeyboardButton::ascii_key('2');
541  break;
542  case kHIDUsage_Keypad3:
543  handle = KeyboardButton::ascii_key('3');
544  break;
545  case kHIDUsage_Keypad4:
546  handle = KeyboardButton::ascii_key('4');
547  break;
548  case kHIDUsage_Keypad5:
549  handle = KeyboardButton::ascii_key('5');
550  break;
551  case kHIDUsage_Keypad6:
552  handle = KeyboardButton::ascii_key('6');
553  break;
554  case kHIDUsage_Keypad7:
555  handle = KeyboardButton::ascii_key('7');
556  break;
557  case kHIDUsage_Keypad8:
558  handle = KeyboardButton::ascii_key('8');
559  break;
560  case kHIDUsage_Keypad9:
561  handle = KeyboardButton::ascii_key('9');
562  break;
563  case kHIDUsage_Keypad0:
564  handle = KeyboardButton::ascii_key('0');
565  break;
566  case kHIDUsage_KeypadPeriod:
567  handle = KeyboardButton::ascii_key('.');
568  break;
569  case kHIDUsage_KeyboardNonUSBackslash:
570  handle = KeyboardButton::ascii_key('\\');
571  break;
572  case kHIDUsage_KeypadEqualSign:
573  handle = KeyboardButton::ascii_key('=');
574  break;
575  case kHIDUsage_KeyboardF13:
576  handle = KeyboardButton::f13();
577  break;
578  case kHIDUsage_KeyboardF14:
579  handle = KeyboardButton::f14();
580  break;
581  case kHIDUsage_KeyboardF15:
582  handle = KeyboardButton::f15();
583  break;
584  case kHIDUsage_KeyboardF16:
585  handle = KeyboardButton::f16();
586  break;
587  case kHIDUsage_KeyboardExecute:
588  break;
589  case kHIDUsage_KeyboardHelp:
590  handle = KeyboardButton::help();
591  break;
592  case kHIDUsage_KeyboardMenu:
593  handle = KeyboardButton::menu();
594  break;
595  case kHIDUsage_KeypadComma:
596  handle = KeyboardButton::ascii_key(',');
597  break;
598  case kHIDUsage_KeypadEqualSignAS400:
599  handle = KeyboardButton::ascii_key('=');
600  break;
601  case kHIDUsage_KeyboardReturn:
602  handle = KeyboardButton::enter();
603  break;
604  case kHIDUsage_KeyboardLeftControl:
605  handle = KeyboardButton::lcontrol();
606  break;
607  case kHIDUsage_KeyboardLeftShift:
608  handle = KeyboardButton::lshift();
609  break;
610  case kHIDUsage_KeyboardLeftAlt:
611  handle = KeyboardButton::lalt();
612  break;
613  case kHIDUsage_KeyboardLeftGUI:
614  handle = KeyboardButton::lmeta();
615  break;
616  case kHIDUsage_KeyboardRightControl:
617  handle = KeyboardButton::rcontrol();
618  break;
619  case kHIDUsage_KeyboardRightShift:
620  handle = KeyboardButton::rshift();
621  break;
622  case kHIDUsage_KeyboardRightAlt:
623  handle = KeyboardButton::ralt();
624  break;
625  case kHIDUsage_KeyboardRightGUI:
626  handle = KeyboardButton::rmeta();
627  break;
628  default:
629  break;
630  }
631  break;
632 
633  case kHIDPage_Button:
634  if (_device_class == DeviceClass::gamepad) {
635  if (_vendor_id == 0x0810 && _product_id == 0xe501) {
636  // SNES-style USB gamepad
637  static const ButtonHandle gamepad_buttons[] = {
638  ButtonHandle::none(),
639  GamepadButton::face_x(),
640  GamepadButton::face_a(),
641  GamepadButton::face_b(),
642  GamepadButton::face_y(),
643  GamepadButton::lshoulder(),
644  GamepadButton::rshoulder(),
645  ButtonHandle::none(),
646  ButtonHandle::none(),
647  GamepadButton::back(),
648  GamepadButton::start(),
649  };
650  if (usage < sizeof(gamepad_buttons) / sizeof(ButtonHandle)) {
651  handle = gamepad_buttons[usage];
652  }
653  } else {
654  // These seem to be the button mappings exposed by the 360Controller
655  // driver. I don't know if other drivers do the same thing at all.
656  static const ButtonHandle gamepad_buttons[] = {
657  ButtonHandle::none(),
658  GamepadButton::face_a(),
659  GamepadButton::face_b(),
660  GamepadButton::face_x(),
661  GamepadButton::face_y(),
662  GamepadButton::lshoulder(),
663  GamepadButton::rshoulder(),
664  GamepadButton::lstick(),
665  GamepadButton::rstick(),
666  GamepadButton::start(),
667  GamepadButton::back(),
668  GamepadButton::guide(),
669  GamepadButton::dpad_up(),
670  GamepadButton::dpad_down(),
671  GamepadButton::dpad_left(),
672  GamepadButton::dpad_right(),
673  };
674  if (usage < sizeof(gamepad_buttons) / sizeof(ButtonHandle)) {
675  handle = gamepad_buttons[usage];
676  }
677  }
678  } else if (_device_class == DeviceClass::flight_stick) {
679  if (usage > 0) {
680  handle = GamepadButton::joystick(usage - 1);
681  }
682  } else if (_device_class == DeviceClass::mouse) {
683  // In Panda, wheel and right button are flipped around...
684  int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
685  handle = MouseButton::button(button);
686  }
687  break;
688  }
689  _buttons.push_back(ButtonState(handle));
690  _button_elements.push_back(element);
691  break;
692 
693  case kIOHIDElementTypeInput_Axis:
694  break;
695 
696  case kIOHIDElementTypeInput_ScanCodes:
697  break;
698 
699  case kIOHIDElementTypeOutput:
700  break;
701 
702  case kIOHIDElementTypeFeature:
703  break;
704 
705  case kIOHIDElementTypeCollection:
706  {
707  // This doesn't seem to be necessary and instead leads to duplication of
708  // axes and buttons.
709  /*
710  CFArrayRef children = IOHIDElementGetChildren(element);
711  CFIndex count = CFArrayGetCount(children);
712  for (CFIndex i = 0; i < count; ++i) {
713  IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(children, i);
714  parse_element(element, depth + 2);
715  }*/
716  }
717  break;
718  }
719 }
720 
721 /**
722  * Polls the input device for new activity, to ensure it contains the latest
723  * events. This will only have any effect for some types of input devices;
724  * others may be updated automatically, and this method will be a no-op.
725  */
726 void IOKitInputDevice::
727 do_poll() {
728  for (size_t i = 0; i < _button_elements.size(); ++i) {
729  IOHIDValueRef value_ref;
730  if (!_button_elements[i]) continue;
731  if (IOHIDDeviceGetValue(_device, _button_elements[i], &value_ref) == kIOReturnSuccess) {
732  int value = IOHIDValueGetIntegerValue(value_ref);
733  button_changed(i, value != 0);
734  }
735  }
736 
737  for (size_t i = 0; i < _analog_elements.size(); ++i) {
738  IOHIDValueRef value_ref;
739  if (IOHIDDeviceGetValue(_device, _analog_elements[i], &value_ref) == kIOReturnSuccess) {
740  int value = IOHIDValueGetIntegerValue(value_ref);
741  axis_changed(i, value);
742  }
743  }
744 
745  if (_hat_element != nullptr) {
746  IOHIDValueRef value_ref;
747  if (IOHIDDeviceGetValue(_device, _hat_element, &value_ref) == kIOReturnSuccess) {
748  int value = IOHIDValueGetIntegerValue(value_ref);
749  button_changed(_hat_left_button + 0, value >= 5 && value <= 7); // left
750  button_changed(_hat_left_button + 1, value >= 1 && value <= 3); // right
751  button_changed(_hat_left_button + 2, value >= 3 && value <= 5); // down
752  button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1); // up
753  }
754  }
755 
756  int x = 0, y = 0;
757  if (_pointer_x != nullptr) {
758  IOHIDValueRef value_ref;
759  if (IOHIDDeviceGetValue(_device, _pointer_x, &value_ref) == kIOReturnSuccess) {
760  uint64_t timestamp = IOHIDValueGetTimeStamp(value_ref);
761  if (timestamp != _pointer_x_timestamp) {
762  x = IOHIDValueGetIntegerValue(value_ref);
763  _pointer_x_timestamp = timestamp;
764  }
765  }
766  }
767  if (_pointer_y != nullptr) {
768  IOHIDValueRef value_ref;
769  if (IOHIDDeviceGetValue(_device, _pointer_y, &value_ref) == kIOReturnSuccess) {
770  uint64_t timestamp = IOHIDValueGetTimeStamp(value_ref);
771  if (timestamp != _pointer_y_timestamp) {
772  y = IOHIDValueGetIntegerValue(value_ref);
773  _pointer_y_timestamp = timestamp;
774  }
775  }
776  }
777  if (x != 0 || y != 0) {
778  pointer_moved(0, x, y, ClockObject::get_global_clock()->get_frame_time());
779  }
780 
781  // Do we have a scroll wheel axis?
782  if (_scroll_wheel != nullptr) {
783  IOHIDValueRef value_ref;
784  if (IOHIDDeviceGetValue(_device, _scroll_wheel, &value_ref) == kIOReturnSuccess) {
785  uint64_t timestamp = IOHIDValueGetTimeStamp(value_ref);
786  if (timestamp != _scroll_wheel_timestamp) {
787  int value = IOHIDValueGetIntegerValue(value_ref);
788  if (value != 0) {
789  // Just fire off a rapid down/up event.
791  ButtonHandle handle = (value > 0) ? MouseButton::wheel_up() : MouseButton::wheel_down();
792  _button_events->add_event(ButtonEvent(handle, ButtonEvent::T_down, time));
793  _button_events->add_event(ButtonEvent(handle, ButtonEvent::T_up, time));
794  }
795  _scroll_wheel_timestamp = timestamp;
796  }
797  }
798  }
799 }
800 
801 #endif // __APPLE__
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class keeps track of all the devices on a system, and sends out events when a device has been ho...
Records a button event of some kind.
Definition: buttonEvent.h:46
static InputDeviceManager * get_global_ptr()
Returns the singleton InputDeviceManager instance.
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
void remove_device(InputDevice *device)
Called when a device has been removed, or when a device should otherwise no longer be tracked.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.h:91
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.
static ButtonHandle wheel_down()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch downwards.
Definition: mouseButton.cxx:93
static ButtonHandle joystick(int button_number)
Returns the ButtonHandle associated with the particular numbered joystick button (zero-based),...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static ButtonHandle ascii_key(char ascii_equivalent)
Returns the ButtonHandle associated with the particular ASCII character, if there is one,...
static ButtonHandle wheel_up()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch upwards.
Definition: mouseButton.cxx:84