16 #if defined(__APPLE__) && !defined(CPPPARSER) 18 #include <IOKit/hid/IOHIDElement.h> 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();
34 IOKitInputDevice(IOHIDDeviceRef device) :
36 _hat_element(nullptr),
39 _scroll_wheel(nullptr),
40 _pointer_x_timestamp(0),
41 _pointer_y_timestamp(0),
42 _scroll_wheel_timestamp(0) {
47 CFStringRef name = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
50 CFStringGetCString(name, buffer,
sizeof(buffer), kCFStringEncodingUTF8);
53 size_t len = strlen(buffer);
54 while (isspace(buffer[len - 1])) {
57 _name.assign(buffer, len);
60 CFStringRef mfg = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey));
62 CFStringGetCString(mfg, buffer,
sizeof(buffer), kCFStringEncodingUTF8);
63 _manufacturer = buffer;
66 CFStringRef serial = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDSerialNumberKey));
68 CFStringGetCString(serial, buffer,
sizeof(buffer), kCFStringEncodingUTF8);
69 _serial_number = buffer;
72 CFNumberRef vendor = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
75 CFNumberGetValue(vendor, kCFNumberSInt32Type, &value);
76 _vendor_id = (
unsigned short)value;
79 CFNumberRef product = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
82 CFNumberGetValue(product, kCFNumberSInt32Type, &value);
83 _product_id = (
unsigned short)value;
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) {
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)) {
108 _device_class = DeviceClass::spatial_mouse;
109 }
else if (_name ==
"usb gamepad") {
110 _device_class = DeviceClass::gamepad;
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);
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()));
129 if (_pointer_x !=
nullptr && _pointer_y !=
nullptr) {
130 enable_feature(Feature::pointer);
131 add_pointer(PointerType::unknown, 0);
134 _is_connected =
true;
135 IOHIDDeviceRegisterRemovalCallback(device, removal_callback,
this);
142 ~IOKitInputDevice() {
148 void IOKitInputDevice::
152 if (!_is_connected) {
155 _is_connected =
false;
158 if (device_cat.is_debug()) {
160 <<
"Removed input device " << *
this <<
"\n";
163 IOHIDDeviceClose(_device, kIOHIDOptionsTypeNone);
166 nassertv(mgr !=
nullptr);
173 void IOKitInputDevice::
174 parse_element(IOHIDElementRef element) {
176 Axis axis = Axis::none;
177 uint32_t page = IOHIDElementGetUsagePage(element);
178 uint32_t usage = IOHIDElementGetUsage(element);
180 switch (IOHIDElementGetType(element)) {
181 case kIOHIDElementTypeInput_Misc:
183 case kHIDPage_GenericDesktop:
186 if (_device_class == DeviceClass::gamepad) {
188 }
else if (_device_class == DeviceClass::flight_stick) {
190 }
else if (_device_class == DeviceClass::mouse) {
191 _pointer_x = element;
198 if (_device_class == DeviceClass::gamepad) {
200 }
else if (_device_class == DeviceClass::flight_stick) {
202 }
else if (_device_class == DeviceClass::mouse) {
203 _pointer_y = element;
210 if (_device_class == DeviceClass::gamepad) {
211 axis = Axis::left_trigger;
212 }
else if (_device_class == DeviceClass::flight_stick) {
213 axis = Axis::throttle;
218 case kHIDUsage_GD_Rx:
219 if (_device_class == DeviceClass::gamepad) {
220 axis = Axis::right_x;
225 case kHIDUsage_GD_Ry:
226 if (_device_class == DeviceClass::gamepad) {
227 axis = Axis::right_y;
232 case kHIDUsage_GD_Rz:
233 if (_device_class == DeviceClass::gamepad) {
234 axis = Axis::right_trigger;
239 case kHIDUsage_GD_Slider:
242 case kHIDUsage_GD_Dial:
244 case kHIDUsage_GD_Wheel:
245 _scroll_wheel = element;
247 case kHIDUsage_GD_Hatswitch:
248 _hat_element = element;
250 case kHIDUsage_GD_DPadUp:
251 handle = GamepadButton::dpad_up();
253 case kHIDUsage_GD_DPadDown:
254 handle = GamepadButton::dpad_down();
256 case kHIDUsage_GD_DPadRight:
257 handle = GamepadButton::dpad_right();
259 case kHIDUsage_GD_DPadLeft:
260 handle = GamepadButton::dpad_left();
269 case kHIDPage_Simulation:
271 case kHIDUsage_Sim_Rudder:
274 case kHIDUsage_Sim_Throttle:
275 axis = Axis::throttle;
277 case kHIDUsage_Sim_Accelerator:
278 axis = Axis::accelerator;
280 case kHIDUsage_Sim_Brake:
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) {
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))) {
296 add_axis(axis, max, min);
298 add_axis(axis, min, max);
301 _analog_elements.push_back(element);
305 case kIOHIDElementTypeInput_Button:
307 case kHIDPage_GenericDesktop:
309 case kHIDUsage_GD_DPadUp:
310 handle = GamepadButton::dpad_up();
312 case kHIDUsage_GD_DPadDown:
313 handle = GamepadButton::dpad_down();
315 case kHIDUsage_GD_DPadRight:
316 handle = GamepadButton::dpad_right();
318 case kHIDUsage_GD_DPadLeft:
319 handle = GamepadButton::dpad_left();
326 case kHIDPage_KeyboardOrKeypad:
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:
356 case kHIDUsage_Keyboard1:
359 case kHIDUsage_Keyboard2:
362 case kHIDUsage_Keyboard3:
365 case kHIDUsage_Keyboard4:
368 case kHIDUsage_Keyboard5:
371 case kHIDUsage_Keyboard6:
374 case kHIDUsage_Keyboard7:
377 case kHIDUsage_Keyboard8:
380 case kHIDUsage_Keyboard9:
383 case kHIDUsage_Keyboard0:
386 case kHIDUsage_KeyboardReturnOrEnter:
387 handle = KeyboardButton::enter();
389 case kHIDUsage_KeyboardEscape:
390 handle = KeyboardButton::escape();
392 case kHIDUsage_KeyboardDeleteOrBackspace:
393 handle = KeyboardButton::backspace();
395 case kHIDUsage_KeyboardTab:
396 handle = KeyboardButton::tab();
398 case kHIDUsage_KeyboardSpacebar:
401 case kHIDUsage_KeyboardHyphen:
404 case kHIDUsage_KeyboardEqualSign:
407 case kHIDUsage_KeyboardOpenBracket:
410 case kHIDUsage_KeyboardCloseBracket:
413 case kHIDUsage_KeyboardBackslash:
416 case kHIDUsage_KeyboardNonUSPound:
419 case kHIDUsage_KeyboardSemicolon:
422 case kHIDUsage_KeyboardQuote:
425 case kHIDUsage_KeyboardGraveAccentAndTilde:
428 case kHIDUsage_KeyboardComma:
431 case kHIDUsage_KeyboardPeriod:
434 case kHIDUsage_KeyboardSlash:
437 case kHIDUsage_KeyboardCapsLock:
438 handle = KeyboardButton::caps_lock();
440 case kHIDUsage_KeyboardF1:
441 handle = KeyboardButton::f1();
443 case kHIDUsage_KeyboardF2:
444 handle = KeyboardButton::f2();
446 case kHIDUsage_KeyboardF3:
447 handle = KeyboardButton::f3();
449 case kHIDUsage_KeyboardF4:
450 handle = KeyboardButton::f4();
452 case kHIDUsage_KeyboardF5:
453 handle = KeyboardButton::f5();
455 case kHIDUsage_KeyboardF6:
456 handle = KeyboardButton::f6();
458 case kHIDUsage_KeyboardF7:
459 handle = KeyboardButton::f7();
461 case kHIDUsage_KeyboardF8:
462 handle = KeyboardButton::f8();
464 case kHIDUsage_KeyboardF9:
465 handle = KeyboardButton::f9();
467 case kHIDUsage_KeyboardF10:
468 handle = KeyboardButton::f10();
470 case kHIDUsage_KeyboardF11:
471 handle = KeyboardButton::f11();
473 case kHIDUsage_KeyboardF12:
474 handle = KeyboardButton::f12();
476 case kHIDUsage_KeyboardPrintScreen:
477 handle = KeyboardButton::print_screen();
479 case kHIDUsage_KeyboardScrollLock:
480 handle = KeyboardButton::scroll_lock();
482 case kHIDUsage_KeyboardPause:
483 handle = KeyboardButton::pause();
485 case kHIDUsage_KeyboardInsert:
486 handle = KeyboardButton::insert();
488 case kHIDUsage_KeyboardHome:
489 handle = KeyboardButton::home();
491 case kHIDUsage_KeyboardPageUp:
492 handle = KeyboardButton::page_up();
494 case kHIDUsage_KeyboardDeleteForward:
495 handle = KeyboardButton::del();
497 case kHIDUsage_KeyboardEnd:
498 handle = KeyboardButton::end();
500 case kHIDUsage_KeyboardPageDown:
501 handle = KeyboardButton::page_down();
503 case kHIDUsage_KeyboardRightArrow:
504 handle = KeyboardButton::right();
506 case kHIDUsage_KeyboardLeftArrow:
507 handle = KeyboardButton::left();
509 case kHIDUsage_KeyboardDownArrow:
510 handle = KeyboardButton::down();
512 case kHIDUsage_KeyboardUpArrow:
513 handle = KeyboardButton::up();
515 case kHIDUsage_KeypadNumLock:
516 handle = KeyboardButton::num_lock();
518 case kHIDUsage_KeypadSlash:
521 case kHIDUsage_KeypadAsterisk:
524 case kHIDUsage_KeypadHyphen:
527 case kHIDUsage_KeypadPlus:
530 case kHIDUsage_KeypadEnter:
531 handle = KeyboardButton::enter();
533 case kHIDUsage_Keypad1:
536 case kHIDUsage_Keypad2:
539 case kHIDUsage_Keypad3:
542 case kHIDUsage_Keypad4:
545 case kHIDUsage_Keypad5:
548 case kHIDUsage_Keypad6:
551 case kHIDUsage_Keypad7:
554 case kHIDUsage_Keypad8:
557 case kHIDUsage_Keypad9:
560 case kHIDUsage_Keypad0:
563 case kHIDUsage_KeypadPeriod:
566 case kHIDUsage_KeyboardNonUSBackslash:
569 case kHIDUsage_KeypadEqualSign:
572 case kHIDUsage_KeyboardF13:
573 handle = KeyboardButton::f13();
575 case kHIDUsage_KeyboardF14:
576 handle = KeyboardButton::f14();
578 case kHIDUsage_KeyboardF15:
579 handle = KeyboardButton::f15();
581 case kHIDUsage_KeyboardF16:
582 handle = KeyboardButton::f16();
584 case kHIDUsage_KeyboardExecute:
586 case kHIDUsage_KeyboardHelp:
587 handle = KeyboardButton::help();
589 case kHIDUsage_KeyboardMenu:
590 handle = KeyboardButton::menu();
592 case kHIDUsage_KeypadComma:
595 case kHIDUsage_KeypadEqualSignAS400:
598 case kHIDUsage_KeyboardReturn:
599 handle = KeyboardButton::enter();
601 case kHIDUsage_KeyboardLeftControl:
602 handle = KeyboardButton::lcontrol();
604 case kHIDUsage_KeyboardLeftShift:
605 handle = KeyboardButton::lshift();
607 case kHIDUsage_KeyboardLeftAlt:
608 handle = KeyboardButton::lalt();
610 case kHIDUsage_KeyboardLeftGUI:
611 handle = KeyboardButton::lmeta();
613 case kHIDUsage_KeyboardRightControl:
614 handle = KeyboardButton::rcontrol();
616 case kHIDUsage_KeyboardRightShift:
617 handle = KeyboardButton::rshift();
619 case kHIDUsage_KeyboardRightAlt:
620 handle = KeyboardButton::ralt();
622 case kHIDUsage_KeyboardRightGUI:
623 handle = KeyboardButton::rmeta();
630 case kHIDPage_Button:
631 if (_device_class == DeviceClass::gamepad) {
632 if (_vendor_id == 0x0810 && _product_id == 0xe501) {
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(),
647 if (usage <
sizeof(gamepad_buttons) /
sizeof(
ButtonHandle)) {
648 handle = gamepad_buttons[usage];
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(),
671 if (usage <
sizeof(gamepad_buttons) /
sizeof(
ButtonHandle)) {
672 handle = gamepad_buttons[usage];
675 }
else if (_device_class == DeviceClass::flight_stick) {
679 }
else if (_device_class == DeviceClass::mouse) {
681 int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
686 _buttons.push_back(ButtonState(handle));
687 _button_elements.push_back(element);
690 case kIOHIDElementTypeInput_Axis:
693 case kIOHIDElementTypeInput_ScanCodes:
696 case kIOHIDElementTypeOutput:
699 case kIOHIDElementTypeFeature:
702 case kIOHIDElementTypeCollection:
723 void IOKitInputDevice::
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);
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);
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);
747 button_changed(_hat_left_button + 1, value >= 1 && value <= 3);
748 button_changed(_hat_left_button + 2, value >= 3 && value <= 5);
749 button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1);
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;
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;
774 if (x != 0 || y != 0) {
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);
789 _button_events->add_event(
ButtonEvent(handle, ButtonEvent::T_down, time));
790 _button_events->add_event(
ButtonEvent(handle, ButtonEvent::T_up, time));
792 _scroll_wheel_timestamp = timestamp;
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Similar to MutexHolder, but for a light mutex.