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