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);
117 CFIndex count = CFArrayGetCount(elements);
118 for (CFIndex i = 0; i < count; ++i) {
119 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
120 parse_element(element);
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()));
132 if (_pointer_x !=
nullptr && _pointer_y !=
nullptr) {
133 enable_feature(Feature::pointer);
134 add_pointer(PointerType::unknown, 0);
137 _is_connected =
true;
138 IOHIDDeviceRegisterRemovalCallback(device, removal_callback,
this);
145 ~IOKitInputDevice() {
151 void IOKitInputDevice::
155 if (!_is_connected) {
158 _is_connected =
false;
161 if (device_cat.is_debug()) {
163 <<
"Removed input device " << *
this <<
"\n";
166 IOHIDDeviceClose(_device, kIOHIDOptionsTypeNone);
169 nassertv(mgr !=
nullptr);
176 void IOKitInputDevice::
177 parse_element(IOHIDElementRef element) {
179 Axis axis = Axis::none;
180 uint32_t page = IOHIDElementGetUsagePage(element);
181 uint32_t usage = IOHIDElementGetUsage(element);
183 switch (IOHIDElementGetType(element)) {
184 case kIOHIDElementTypeInput_Misc:
186 case kHIDPage_GenericDesktop:
189 if (_device_class == DeviceClass::gamepad) {
191 }
else if (_device_class == DeviceClass::flight_stick) {
193 }
else if (_device_class == DeviceClass::mouse) {
194 _pointer_x = element;
201 if (_device_class == DeviceClass::gamepad) {
203 }
else if (_device_class == DeviceClass::flight_stick) {
205 }
else if (_device_class == DeviceClass::mouse) {
206 _pointer_y = element;
213 if (_device_class == DeviceClass::gamepad) {
214 axis = Axis::left_trigger;
215 }
else if (_device_class == DeviceClass::flight_stick) {
216 axis = Axis::throttle;
221 case kHIDUsage_GD_Rx:
222 if (_device_class == DeviceClass::gamepad) {
223 axis = Axis::right_x;
228 case kHIDUsage_GD_Ry:
229 if (_device_class == DeviceClass::gamepad) {
230 axis = Axis::right_y;
235 case kHIDUsage_GD_Rz:
236 if (_device_class == DeviceClass::gamepad) {
237 axis = Axis::right_trigger;
242 case kHIDUsage_GD_Slider:
245 case kHIDUsage_GD_Dial:
247 case kHIDUsage_GD_Wheel:
248 _scroll_wheel = element;
250 case kHIDUsage_GD_Hatswitch:
251 _hat_element = element;
253 case kHIDUsage_GD_DPadUp:
254 handle = GamepadButton::dpad_up();
256 case kHIDUsage_GD_DPadDown:
257 handle = GamepadButton::dpad_down();
259 case kHIDUsage_GD_DPadRight:
260 handle = GamepadButton::dpad_right();
262 case kHIDUsage_GD_DPadLeft:
263 handle = GamepadButton::dpad_left();
272 case kHIDPage_Simulation:
274 case kHIDUsage_Sim_Rudder:
277 case kHIDUsage_Sim_Throttle:
278 axis = Axis::throttle;
280 case kHIDUsage_Sim_Accelerator:
281 axis = Axis::accelerator;
283 case kHIDUsage_Sim_Brake:
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) {
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))) {
299 add_axis(axis, max, min);
301 add_axis(axis, min, max);
304 _analog_elements.push_back(element);
308 case kIOHIDElementTypeInput_Button:
310 case kHIDPage_GenericDesktop:
312 case kHIDUsage_GD_DPadUp:
313 handle = GamepadButton::dpad_up();
315 case kHIDUsage_GD_DPadDown:
316 handle = GamepadButton::dpad_down();
318 case kHIDUsage_GD_DPadRight:
319 handle = GamepadButton::dpad_right();
321 case kHIDUsage_GD_DPadLeft:
322 handle = GamepadButton::dpad_left();
329 case kHIDPage_KeyboardOrKeypad:
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:
359 case kHIDUsage_Keyboard1:
362 case kHIDUsage_Keyboard2:
365 case kHIDUsage_Keyboard3:
368 case kHIDUsage_Keyboard4:
371 case kHIDUsage_Keyboard5:
374 case kHIDUsage_Keyboard6:
377 case kHIDUsage_Keyboard7:
380 case kHIDUsage_Keyboard8:
383 case kHIDUsage_Keyboard9:
386 case kHIDUsage_Keyboard0:
389 case kHIDUsage_KeyboardReturnOrEnter:
390 handle = KeyboardButton::enter();
392 case kHIDUsage_KeyboardEscape:
393 handle = KeyboardButton::escape();
395 case kHIDUsage_KeyboardDeleteOrBackspace:
396 handle = KeyboardButton::backspace();
398 case kHIDUsage_KeyboardTab:
399 handle = KeyboardButton::tab();
401 case kHIDUsage_KeyboardSpacebar:
404 case kHIDUsage_KeyboardHyphen:
407 case kHIDUsage_KeyboardEqualSign:
410 case kHIDUsage_KeyboardOpenBracket:
413 case kHIDUsage_KeyboardCloseBracket:
416 case kHIDUsage_KeyboardBackslash:
419 case kHIDUsage_KeyboardNonUSPound:
422 case kHIDUsage_KeyboardSemicolon:
425 case kHIDUsage_KeyboardQuote:
428 case kHIDUsage_KeyboardGraveAccentAndTilde:
431 case kHIDUsage_KeyboardComma:
434 case kHIDUsage_KeyboardPeriod:
437 case kHIDUsage_KeyboardSlash:
440 case kHIDUsage_KeyboardCapsLock:
441 handle = KeyboardButton::caps_lock();
443 case kHIDUsage_KeyboardF1:
444 handle = KeyboardButton::f1();
446 case kHIDUsage_KeyboardF2:
447 handle = KeyboardButton::f2();
449 case kHIDUsage_KeyboardF3:
450 handle = KeyboardButton::f3();
452 case kHIDUsage_KeyboardF4:
453 handle = KeyboardButton::f4();
455 case kHIDUsage_KeyboardF5:
456 handle = KeyboardButton::f5();
458 case kHIDUsage_KeyboardF6:
459 handle = KeyboardButton::f6();
461 case kHIDUsage_KeyboardF7:
462 handle = KeyboardButton::f7();
464 case kHIDUsage_KeyboardF8:
465 handle = KeyboardButton::f8();
467 case kHIDUsage_KeyboardF9:
468 handle = KeyboardButton::f9();
470 case kHIDUsage_KeyboardF10:
471 handle = KeyboardButton::f10();
473 case kHIDUsage_KeyboardF11:
474 handle = KeyboardButton::f11();
476 case kHIDUsage_KeyboardF12:
477 handle = KeyboardButton::f12();
479 case kHIDUsage_KeyboardPrintScreen:
480 handle = KeyboardButton::print_screen();
482 case kHIDUsage_KeyboardScrollLock:
483 handle = KeyboardButton::scroll_lock();
485 case kHIDUsage_KeyboardPause:
486 handle = KeyboardButton::pause();
488 case kHIDUsage_KeyboardInsert:
489 handle = KeyboardButton::insert();
491 case kHIDUsage_KeyboardHome:
492 handle = KeyboardButton::home();
494 case kHIDUsage_KeyboardPageUp:
495 handle = KeyboardButton::page_up();
497 case kHIDUsage_KeyboardDeleteForward:
498 handle = KeyboardButton::del();
500 case kHIDUsage_KeyboardEnd:
501 handle = KeyboardButton::end();
503 case kHIDUsage_KeyboardPageDown:
504 handle = KeyboardButton::page_down();
506 case kHIDUsage_KeyboardRightArrow:
507 handle = KeyboardButton::right();
509 case kHIDUsage_KeyboardLeftArrow:
510 handle = KeyboardButton::left();
512 case kHIDUsage_KeyboardDownArrow:
513 handle = KeyboardButton::down();
515 case kHIDUsage_KeyboardUpArrow:
516 handle = KeyboardButton::up();
518 case kHIDUsage_KeypadNumLock:
519 handle = KeyboardButton::num_lock();
521 case kHIDUsage_KeypadSlash:
524 case kHIDUsage_KeypadAsterisk:
527 case kHIDUsage_KeypadHyphen:
530 case kHIDUsage_KeypadPlus:
533 case kHIDUsage_KeypadEnter:
534 handle = KeyboardButton::enter();
536 case kHIDUsage_Keypad1:
539 case kHIDUsage_Keypad2:
542 case kHIDUsage_Keypad3:
545 case kHIDUsage_Keypad4:
548 case kHIDUsage_Keypad5:
551 case kHIDUsage_Keypad6:
554 case kHIDUsage_Keypad7:
557 case kHIDUsage_Keypad8:
560 case kHIDUsage_Keypad9:
563 case kHIDUsage_Keypad0:
566 case kHIDUsage_KeypadPeriod:
569 case kHIDUsage_KeyboardNonUSBackslash:
572 case kHIDUsage_KeypadEqualSign:
575 case kHIDUsage_KeyboardF13:
576 handle = KeyboardButton::f13();
578 case kHIDUsage_KeyboardF14:
579 handle = KeyboardButton::f14();
581 case kHIDUsage_KeyboardF15:
582 handle = KeyboardButton::f15();
584 case kHIDUsage_KeyboardF16:
585 handle = KeyboardButton::f16();
587 case kHIDUsage_KeyboardExecute:
589 case kHIDUsage_KeyboardHelp:
590 handle = KeyboardButton::help();
592 case kHIDUsage_KeyboardMenu:
593 handle = KeyboardButton::menu();
595 case kHIDUsage_KeypadComma:
598 case kHIDUsage_KeypadEqualSignAS400:
601 case kHIDUsage_KeyboardReturn:
602 handle = KeyboardButton::enter();
604 case kHIDUsage_KeyboardLeftControl:
605 handle = KeyboardButton::lcontrol();
607 case kHIDUsage_KeyboardLeftShift:
608 handle = KeyboardButton::lshift();
610 case kHIDUsage_KeyboardLeftAlt:
611 handle = KeyboardButton::lalt();
613 case kHIDUsage_KeyboardLeftGUI:
614 handle = KeyboardButton::lmeta();
616 case kHIDUsage_KeyboardRightControl:
617 handle = KeyboardButton::rcontrol();
619 case kHIDUsage_KeyboardRightShift:
620 handle = KeyboardButton::rshift();
622 case kHIDUsage_KeyboardRightAlt:
623 handle = KeyboardButton::ralt();
625 case kHIDUsage_KeyboardRightGUI:
626 handle = KeyboardButton::rmeta();
633 case kHIDPage_Button:
634 if (_device_class == DeviceClass::gamepad) {
635 if (_vendor_id == 0x0810 && _product_id == 0xe501) {
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(),
650 if (usage <
sizeof(gamepad_buttons) /
sizeof(
ButtonHandle)) {
651 handle = gamepad_buttons[usage];
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(),
674 if (usage <
sizeof(gamepad_buttons) /
sizeof(
ButtonHandle)) {
675 handle = gamepad_buttons[usage];
678 }
else if (_device_class == DeviceClass::flight_stick) {
682 }
else if (_device_class == DeviceClass::mouse) {
684 int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
689 _buttons.push_back(ButtonState(handle));
690 _button_elements.push_back(element);
693 case kIOHIDElementTypeInput_Axis:
696 case kIOHIDElementTypeInput_ScanCodes:
699 case kIOHIDElementTypeOutput:
702 case kIOHIDElementTypeFeature:
705 case kIOHIDElementTypeCollection:
726 void IOKitInputDevice::
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);
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);
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);
750 button_changed(_hat_left_button + 1, value >= 1 && value <= 3);
751 button_changed(_hat_left_button + 2, value >= 3 && value <= 5);
752 button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1);
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;
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;
777 if (x != 0 || y != 0) {
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);
792 _button_events->add_event(
ButtonEvent(handle, ButtonEvent::T_down, time));
793 _button_events->add_event(
ButtonEvent(handle, ButtonEvent::T_up, time));
795 _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.