16 #if defined(__APPLE__) && !defined(CPPPARSER)
18 #include <IOKit/hid/IOHIDElement.h>
24 static void removal_callback(
void *ctx, IOReturn result,
void *sender) {
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();
37 IOKitInputDevice(IOHIDDeviceRef device) :
39 _hat_element(nullptr),
42 _scroll_wheel(nullptr),
43 _pointer_x_timestamp(0),
44 _pointer_y_timestamp(0),
45 _scroll_wheel_timestamp(0) {
50 CFStringRef name = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
53 CFStringGetCString(name, buffer,
sizeof(buffer), kCFStringEncodingUTF8);
56 size_t len = strlen(buffer);
57 while (isspace(buffer[len - 1])) {
60 _name.assign(buffer, len);
63 CFStringRef mfg = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey));
65 CFStringGetCString(mfg, buffer,
sizeof(buffer), kCFStringEncodingUTF8);
66 _manufacturer = buffer;
69 CFStringRef serial = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDSerialNumberKey));
71 CFStringGetCString(serial, buffer,
sizeof(buffer), kCFStringEncodingUTF8);
72 _serial_number = buffer;
75 CFNumberRef vendor = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
78 CFNumberGetValue(vendor, kCFNumberSInt32Type, &value);
79 _vendor_id = (
unsigned short)value;
82 CFNumberRef product = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
85 CFNumberGetValue(product, kCFNumberSInt32Type, &value);
86 _product_id = (
unsigned short)value;
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) {
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)) {
111 _device_class = DeviceClass::spatial_mouse;
112 }
else if (_name ==
"usb gamepad") {
113 _device_class = DeviceClass::gamepad;
116 CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device,
nullptr, 0);
118 CFIndex count = CFArrayGetCount(elements);
119 for (CFIndex i = 0; i < count; ++i) {
120 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
121 parse_element(element);
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()));
134 if (_pointer_x !=
nullptr && _pointer_y !=
nullptr) {
135 enable_feature(Feature::pointer);
136 add_pointer(PointerType::unknown, 0);
139 _is_connected =
true;
140 IOHIDDeviceRegisterRemovalCallback(device, removal_callback,
this);
147 ~IOKitInputDevice() {
153 void IOKitInputDevice::
157 if (!_is_connected) {
160 _is_connected =
false;
163 if (device_cat.is_debug()) {
165 <<
"Removed input device " << *
this <<
"\n";
168 IOHIDDeviceClose(_device, kIOHIDOptionsTypeNone);
171 nassertv(mgr !=
nullptr);
178 void IOKitInputDevice::
179 parse_element(IOHIDElementRef element) {
181 Axis axis = Axis::none;
182 uint32_t page = IOHIDElementGetUsagePage(element);
183 uint32_t usage = IOHIDElementGetUsage(element);
185 switch (IOHIDElementGetType(element)) {
186 case kIOHIDElementTypeInput_Misc:
188 case kHIDPage_GenericDesktop:
191 if (_device_class == DeviceClass::gamepad) {
193 }
else if (_device_class == DeviceClass::flight_stick) {
195 }
else if (_device_class == DeviceClass::mouse) {
196 _pointer_x = element;
203 if (_device_class == DeviceClass::gamepad) {
205 }
else if (_device_class == DeviceClass::flight_stick) {
207 }
else if (_device_class == DeviceClass::mouse) {
208 _pointer_y = element;
215 if (_device_class == DeviceClass::gamepad) {
216 axis = Axis::left_trigger;
217 }
else if (_device_class == DeviceClass::flight_stick) {
218 axis = Axis::throttle;
223 case kHIDUsage_GD_Rx:
224 if (_device_class == DeviceClass::gamepad) {
225 axis = Axis::right_x;
230 case kHIDUsage_GD_Ry:
231 if (_device_class == DeviceClass::gamepad) {
232 axis = Axis::right_y;
237 case kHIDUsage_GD_Rz:
238 if (_device_class == DeviceClass::gamepad) {
239 axis = Axis::right_trigger;
244 case kHIDUsage_GD_Slider:
247 case kHIDUsage_GD_Dial:
249 case kHIDUsage_GD_Wheel:
250 _scroll_wheel = element;
252 case kHIDUsage_GD_Hatswitch:
253 _hat_element = element;
255 case kHIDUsage_GD_DPadUp:
256 handle = GamepadButton::dpad_up();
258 case kHIDUsage_GD_DPadDown:
259 handle = GamepadButton::dpad_down();
261 case kHIDUsage_GD_DPadRight:
262 handle = GamepadButton::dpad_right();
264 case kHIDUsage_GD_DPadLeft:
265 handle = GamepadButton::dpad_left();
274 case kHIDPage_Simulation:
276 case kHIDUsage_Sim_Rudder:
279 case kHIDUsage_Sim_Throttle:
280 axis = Axis::throttle;
282 case kHIDUsage_Sim_Accelerator:
283 axis = Axis::accelerator;
285 case kHIDUsage_Sim_Brake:
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) {
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))) {
301 add_axis(axis, max, min);
303 add_axis(axis, min, max);
306 _analog_elements.push_back(element);
310 case kIOHIDElementTypeInput_Button:
312 case kHIDPage_GenericDesktop:
314 case kHIDUsage_GD_DPadUp:
315 handle = GamepadButton::dpad_up();
317 case kHIDUsage_GD_DPadDown:
318 handle = GamepadButton::dpad_down();
320 case kHIDUsage_GD_DPadRight:
321 handle = GamepadButton::dpad_right();
323 case kHIDUsage_GD_DPadLeft:
324 handle = GamepadButton::dpad_left();
331 case kHIDPage_KeyboardOrKeypad:
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:
361 case kHIDUsage_Keyboard1:
364 case kHIDUsage_Keyboard2:
367 case kHIDUsage_Keyboard3:
370 case kHIDUsage_Keyboard4:
373 case kHIDUsage_Keyboard5:
376 case kHIDUsage_Keyboard6:
379 case kHIDUsage_Keyboard7:
382 case kHIDUsage_Keyboard8:
385 case kHIDUsage_Keyboard9:
388 case kHIDUsage_Keyboard0:
391 case kHIDUsage_KeyboardReturnOrEnter:
392 handle = KeyboardButton::enter();
394 case kHIDUsage_KeyboardEscape:
395 handle = KeyboardButton::escape();
397 case kHIDUsage_KeyboardDeleteOrBackspace:
398 handle = KeyboardButton::backspace();
400 case kHIDUsage_KeyboardTab:
401 handle = KeyboardButton::tab();
403 case kHIDUsage_KeyboardSpacebar:
406 case kHIDUsage_KeyboardHyphen:
409 case kHIDUsage_KeyboardEqualSign:
412 case kHIDUsage_KeyboardOpenBracket:
415 case kHIDUsage_KeyboardCloseBracket:
418 case kHIDUsage_KeyboardBackslash:
421 case kHIDUsage_KeyboardNonUSPound:
424 case kHIDUsage_KeyboardSemicolon:
427 case kHIDUsage_KeyboardQuote:
430 case kHIDUsage_KeyboardGraveAccentAndTilde:
433 case kHIDUsage_KeyboardComma:
436 case kHIDUsage_KeyboardPeriod:
439 case kHIDUsage_KeyboardSlash:
442 case kHIDUsage_KeyboardCapsLock:
443 handle = KeyboardButton::caps_lock();
445 case kHIDUsage_KeyboardF1:
446 handle = KeyboardButton::f1();
448 case kHIDUsage_KeyboardF2:
449 handle = KeyboardButton::f2();
451 case kHIDUsage_KeyboardF3:
452 handle = KeyboardButton::f3();
454 case kHIDUsage_KeyboardF4:
455 handle = KeyboardButton::f4();
457 case kHIDUsage_KeyboardF5:
458 handle = KeyboardButton::f5();
460 case kHIDUsage_KeyboardF6:
461 handle = KeyboardButton::f6();
463 case kHIDUsage_KeyboardF7:
464 handle = KeyboardButton::f7();
466 case kHIDUsage_KeyboardF8:
467 handle = KeyboardButton::f8();
469 case kHIDUsage_KeyboardF9:
470 handle = KeyboardButton::f9();
472 case kHIDUsage_KeyboardF10:
473 handle = KeyboardButton::f10();
475 case kHIDUsage_KeyboardF11:
476 handle = KeyboardButton::f11();
478 case kHIDUsage_KeyboardF12:
479 handle = KeyboardButton::f12();
481 case kHIDUsage_KeyboardPrintScreen:
482 handle = KeyboardButton::print_screen();
484 case kHIDUsage_KeyboardScrollLock:
485 handle = KeyboardButton::scroll_lock();
487 case kHIDUsage_KeyboardPause:
488 handle = KeyboardButton::pause();
490 case kHIDUsage_KeyboardInsert:
491 handle = KeyboardButton::insert();
493 case kHIDUsage_KeyboardHome:
494 handle = KeyboardButton::home();
496 case kHIDUsage_KeyboardPageUp:
497 handle = KeyboardButton::page_up();
499 case kHIDUsage_KeyboardDeleteForward:
500 handle = KeyboardButton::del();
502 case kHIDUsage_KeyboardEnd:
503 handle = KeyboardButton::end();
505 case kHIDUsage_KeyboardPageDown:
506 handle = KeyboardButton::page_down();
508 case kHIDUsage_KeyboardRightArrow:
509 handle = KeyboardButton::right();
511 case kHIDUsage_KeyboardLeftArrow:
512 handle = KeyboardButton::left();
514 case kHIDUsage_KeyboardDownArrow:
515 handle = KeyboardButton::down();
517 case kHIDUsage_KeyboardUpArrow:
518 handle = KeyboardButton::up();
520 case kHIDUsage_KeypadNumLock:
521 handle = KeyboardButton::num_lock();
523 case kHIDUsage_KeypadSlash:
526 case kHIDUsage_KeypadAsterisk:
529 case kHIDUsage_KeypadHyphen:
532 case kHIDUsage_KeypadPlus:
535 case kHIDUsage_KeypadEnter:
536 handle = KeyboardButton::enter();
538 case kHIDUsage_Keypad1:
541 case kHIDUsage_Keypad2:
544 case kHIDUsage_Keypad3:
547 case kHIDUsage_Keypad4:
550 case kHIDUsage_Keypad5:
553 case kHIDUsage_Keypad6:
556 case kHIDUsage_Keypad7:
559 case kHIDUsage_Keypad8:
562 case kHIDUsage_Keypad9:
565 case kHIDUsage_Keypad0:
568 case kHIDUsage_KeypadPeriod:
571 case kHIDUsage_KeyboardNonUSBackslash:
574 case kHIDUsage_KeypadEqualSign:
577 case kHIDUsage_KeyboardF13:
578 handle = KeyboardButton::f13();
580 case kHIDUsage_KeyboardF14:
581 handle = KeyboardButton::f14();
583 case kHIDUsage_KeyboardF15:
584 handle = KeyboardButton::f15();
586 case kHIDUsage_KeyboardF16:
587 handle = KeyboardButton::f16();
589 case kHIDUsage_KeyboardExecute:
591 case kHIDUsage_KeyboardHelp:
592 handle = KeyboardButton::help();
594 case kHIDUsage_KeyboardMenu:
595 handle = KeyboardButton::menu();
597 case kHIDUsage_KeypadComma:
600 case kHIDUsage_KeypadEqualSignAS400:
603 case kHIDUsage_KeyboardReturn:
604 handle = KeyboardButton::enter();
606 case kHIDUsage_KeyboardLeftControl:
607 handle = KeyboardButton::lcontrol();
609 case kHIDUsage_KeyboardLeftShift:
610 handle = KeyboardButton::lshift();
612 case kHIDUsage_KeyboardLeftAlt:
613 handle = KeyboardButton::lalt();
615 case kHIDUsage_KeyboardLeftGUI:
616 handle = KeyboardButton::lmeta();
618 case kHIDUsage_KeyboardRightControl:
619 handle = KeyboardButton::rcontrol();
621 case kHIDUsage_KeyboardRightShift:
622 handle = KeyboardButton::rshift();
624 case kHIDUsage_KeyboardRightAlt:
625 handle = KeyboardButton::ralt();
627 case kHIDUsage_KeyboardRightGUI:
628 handle = KeyboardButton::rmeta();
635 case kHIDPage_Button:
636 if (_device_class == DeviceClass::gamepad) {
637 if (_vendor_id == 0x0810 && _product_id == 0xe501) {
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(),
652 if (usage <
sizeof(gamepad_buttons) /
sizeof(
ButtonHandle)) {
653 handle = gamepad_buttons[usage];
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(),
676 if (usage <
sizeof(gamepad_buttons) /
sizeof(
ButtonHandle)) {
677 handle = gamepad_buttons[usage];
680 }
else if (_device_class == DeviceClass::flight_stick) {
684 }
else if (_device_class == DeviceClass::mouse) {
686 int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
691 _buttons.push_back(ButtonState(handle));
692 _button_elements.push_back(element);
695 case kIOHIDElementTypeInput_Axis:
698 case kIOHIDElementTypeInput_ScanCodes:
701 case kIOHIDElementTypeOutput:
704 case kIOHIDElementTypeFeature:
707 case kIOHIDElementTypeCollection:
728 void IOKitInputDevice::
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);
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);
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);
752 button_changed(_hat_left_button + 1, value >= 1 && value <= 3);
753 button_changed(_hat_left_button + 2, value >= 3 && value <= 5);
754 button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1);
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;
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;
779 if (x != 0 || y != 0) {
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);
794 _button_events->add_event(
ButtonEvent(handle, ButtonEvent::T_down, time));
795 _button_events->add_event(
ButtonEvent(handle, ButtonEvent::T_up, time));
797 _scroll_wheel_timestamp = timestamp;