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