open_toontown_panda3d/panda/src/device/ioKitInputDevice.cxx

778 lines
24 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file ioKitInputDevice.cxx
* @author rdb
* @date 2017-12-21
*/
#include "ioKitInputDevice.h"
#if defined(__APPLE__) && !defined(CPPPARSER)
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDElement.h>
#include "inputDeviceManager.h"
#include "keyboardButton.h"
#include "gamepadButton.h"
#include "mouseButton.h"
/**
* Protected constructor.
*/
IOKitInputDevice::
IOKitInputDevice(IOHIDDeviceRef device) :
_device(device),
_hat_element(nullptr),
_pointer_x(nullptr),
_pointer_y(nullptr),
_scroll_wheel(nullptr),
_pointer_x_timestamp(0),
_pointer_y_timestamp(0),
_scroll_wheel_timestamp(0) {
nassertv(device);
char buffer[4096];
CFStringRef name = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
if (name) {
buffer[0] = 0;
CFStringGetCString(name, buffer, sizeof(buffer), kCFStringEncodingUTF8);
// Strip trailing spaces.
size_t len = strlen(buffer);
while (isspace(buffer[len - 1])) {
--len;
}
_name.assign(buffer, len);
}
CFStringRef mfg = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey));
if (mfg) {
CFStringGetCString(mfg, buffer, sizeof(buffer), kCFStringEncodingUTF8);
_manufacturer = buffer;
}
CFStringRef serial = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDSerialNumberKey));
if (serial) {
CFStringGetCString(serial, buffer, sizeof(buffer), kCFStringEncodingUTF8);
_serial_number = buffer;
}
CFNumberRef vendor = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
if (vendor) {
int32_t value = 0;
CFNumberGetValue(vendor, kCFNumberSInt32Type, &value);
_vendor_id = (unsigned short)value;
}
CFNumberRef product = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
if (product) {
int32_t value = 0;
CFNumberGetValue(product, kCFNumberSInt32Type, &value);
_product_id = (unsigned short)value;
}
if (IOHIDDeviceConformsTo(device, kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse)) {
_device_class = DeviceClass::MOUSE;
}
else if (IOHIDDeviceConformsTo(device, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard)) {
_device_class = DeviceClass::KEYBOARD;
}
else if (IOHIDDeviceConformsTo(device, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad)) {
_device_class = DeviceClass::GAMEPAD;
}
else if (IOHIDDeviceConformsTo(device, kHIDPage_Simulation, kHIDUsage_Sim_FlightStick)) {
_device_class = DeviceClass::FLIGHT_STICK;
}
else if (IOHIDDeviceConformsTo(device, kHIDPage_Simulation, kHIDUsage_GD_Joystick)) {
_device_class = DeviceClass::FLIGHT_STICK;
}
else if (_vendor_id == 0x044f && _product_id == 0xb108) {
// T.Flight Hotas X
_device_class = DeviceClass::FLIGHT_STICK;
}
else if (_vendor_id == 0x046d &&
(_product_id == 0xc623 ||
_product_id == 0xc625 ||
_product_id == 0xc626 ||
_product_id == 0xc627 ||
_product_id == 0xc628 ||
_product_id == 0xc629 ||
_product_id == 0xc62b)) {
// 3Dconnexion SpaceNavigator and friends.
_device_class = DeviceClass::SPATIAL_MOUSE;
}
else if (_name == "usb gamepad") {
_device_class = DeviceClass::GAMEPAD;
}
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, nullptr, 0);
if (elements) {
CFIndex count = CFArrayGetCount(elements);
for (CFIndex i = 0; i < count; ++i) {
IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
parse_element(element);
}
CFRelease(elements);
}
if (_hat_element != nullptr) {
_hat_left_button = (int)_buttons.size();
_buttons.push_back(ButtonState(GamepadButton::hat_left()));
_buttons.push_back(ButtonState(GamepadButton::hat_right()));
_buttons.push_back(ButtonState(GamepadButton::hat_down()));
_buttons.push_back(ButtonState(GamepadButton::hat_up()));
}
if (_pointer_x != nullptr && _pointer_y != nullptr) {
enable_feature(Feature::POINTER);
add_pointer(PointerType::UNKNOWN, 0);
}
_is_connected = true;
}
/**
*
*/
IOKitInputDevice::
~IOKitInputDevice() {
}
/**
*
*/
void IOKitInputDevice::
parse_element(IOHIDElementRef element) {
ButtonHandle handle = ButtonHandle::none();
Axis axis = Axis::NONE;
uint32_t page = IOHIDElementGetUsagePage(element);
uint32_t usage = IOHIDElementGetUsage(element);
switch (IOHIDElementGetType(element)) {
case kIOHIDElementTypeInput_Misc:
switch (page) {
case kHIDPage_GenericDesktop:
switch (usage) {
case kHIDUsage_GD_X:
if (_device_class == DeviceClass::GAMEPAD) {
axis = Axis::LEFT_X;
} else if (_device_class == DeviceClass::FLIGHT_STICK) {
axis = Axis::ROLL;
} else if (_device_class == DeviceClass::MOUSE) {
_pointer_x = element;
return;
} else {
axis = Axis::X;
}
break;
case kHIDUsage_GD_Y:
if (_device_class == DeviceClass::GAMEPAD) {
axis = Axis::LEFT_Y;
} else if (_device_class == DeviceClass::FLIGHT_STICK) {
axis = Axis::PITCH;
} else if (_device_class == DeviceClass::MOUSE) {
_pointer_y = element;
return;
} else {
axis = Axis::Y;
}
break;
case kHIDUsage_GD_Z:
if (_device_class == DeviceClass::GAMEPAD) {
axis = Axis::LEFT_TRIGGER;
} else if (_device_class == DeviceClass::FLIGHT_STICK) {
axis = Axis::THROTTLE;
} else {
axis = Axis::Z;
}
break;
case kHIDUsage_GD_Rx:
if (_device_class == DeviceClass::GAMEPAD) {
axis = Axis::RIGHT_X;
} else {
axis = Axis::PITCH;
}
break;
case kHIDUsage_GD_Ry:
if (_device_class == DeviceClass::GAMEPAD) {
axis = Axis::RIGHT_Y;
} else {
axis = Axis::ROLL;
}
break;
case kHIDUsage_GD_Rz:
if (_device_class == DeviceClass::GAMEPAD) {
axis = Axis::RIGHT_TRIGGER;
} else {
axis = Axis::YAW;
}
break;
case kHIDUsage_GD_Slider:
axis = Axis::RUDDER;
break;
case kHIDUsage_GD_Dial:
break;
case kHIDUsage_GD_Wheel:
_scroll_wheel = element;
return;
case kHIDUsage_GD_Hatswitch:
_hat_element = element;
return;
case kHIDUsage_GD_DPadUp:
handle = GamepadButton::dpad_up();
break;
case kHIDUsage_GD_DPadDown:
handle = GamepadButton::dpad_down();
break;
case kHIDUsage_GD_DPadRight:
handle = GamepadButton::dpad_right();
break;
case kHIDUsage_GD_DPadLeft:
handle = GamepadButton::dpad_left();
break;
case 0xffffffffu:
return;
default:
break;
}
break;
case kHIDPage_Simulation:
switch (usage) {
case kHIDUsage_Sim_Rudder:
axis = Axis::RUDDER;
break;
case kHIDUsage_Sim_Throttle:
axis = Axis::THROTTLE;
break;
case kHIDUsage_Sim_Accelerator:
axis = Axis::ACCELERATOR;
break;
case kHIDUsage_Sim_Brake:
axis = Axis::BRAKE;
break;
}
break;
}
if (axis != Axis::NONE) {
int min = IOHIDElementGetLogicalMin(element);
int max = IOHIDElementGetLogicalMax(element);
if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::THROTTLE) {
// T.Flight Hotas X throttle is reversed and can go backwards.
add_axis(axis, max, min, true);
} else if (axis == Axis::YAW || axis == Axis::RUDDER || axis == Axis::LEFT_Y || axis == Axis::RIGHT_Y ||
(_device_class == DeviceClass::SPATIAL_MOUSE && (axis == Axis::Y || axis == Axis::Z || axis == Axis::ROLL))) {
// We'd like to reverse the Y axis to match the XInput behavior.
// We also reverse yaw to obey the right-hand rule.
add_axis(axis, max, min);
} else {
add_axis(axis, min, max);
}
_analog_elements.push_back(element);
}
break;
case kIOHIDElementTypeInput_Button:
switch (page) {
case kHIDPage_GenericDesktop:
switch (usage) {
case kHIDUsage_GD_DPadUp:
handle = GamepadButton::dpad_up();
break;
case kHIDUsage_GD_DPadDown:
handle = GamepadButton::dpad_down();
break;
case kHIDUsage_GD_DPadRight:
handle = GamepadButton::dpad_right();
break;
case kHIDUsage_GD_DPadLeft:
handle = GamepadButton::dpad_left();
break;
default:
break;
}
break;
case kHIDPage_KeyboardOrKeypad:
switch (usage) {
case kHIDUsage_KeyboardA:
case kHIDUsage_KeyboardB:
case kHIDUsage_KeyboardC:
case kHIDUsage_KeyboardD:
case kHIDUsage_KeyboardE:
case kHIDUsage_KeyboardF:
case kHIDUsage_KeyboardG:
case kHIDUsage_KeyboardH:
case kHIDUsage_KeyboardI:
case kHIDUsage_KeyboardJ:
case kHIDUsage_KeyboardK:
case kHIDUsage_KeyboardL:
case kHIDUsage_KeyboardM:
case kHIDUsage_KeyboardN:
case kHIDUsage_KeyboardO:
case kHIDUsage_KeyboardP:
case kHIDUsage_KeyboardQ:
case kHIDUsage_KeyboardR:
case kHIDUsage_KeyboardS:
case kHIDUsage_KeyboardT:
case kHIDUsage_KeyboardU:
case kHIDUsage_KeyboardV:
case kHIDUsage_KeyboardW:
case kHIDUsage_KeyboardX:
case kHIDUsage_KeyboardY:
case kHIDUsage_KeyboardZ:
handle = KeyboardButton::ascii_key('a' + (usage - kHIDUsage_KeyboardA));
break;
case kHIDUsage_Keyboard1:
handle = KeyboardButton::ascii_key('1');
break;
case kHIDUsage_Keyboard2:
handle = KeyboardButton::ascii_key('2');
break;
case kHIDUsage_Keyboard3:
handle = KeyboardButton::ascii_key('3');
break;
case kHIDUsage_Keyboard4:
handle = KeyboardButton::ascii_key('4');
break;
case kHIDUsage_Keyboard5:
handle = KeyboardButton::ascii_key('5');
break;
case kHIDUsage_Keyboard6:
handle = KeyboardButton::ascii_key('6');
break;
case kHIDUsage_Keyboard7:
handle = KeyboardButton::ascii_key('7');
break;
case kHIDUsage_Keyboard8:
handle = KeyboardButton::ascii_key('8');
break;
case kHIDUsage_Keyboard9:
handle = KeyboardButton::ascii_key('9');
break;
case kHIDUsage_Keyboard0:
handle = KeyboardButton::ascii_key('0');
break;
case kHIDUsage_KeyboardReturnOrEnter:
handle = KeyboardButton::enter();
break;
case kHIDUsage_KeyboardEscape:
handle = KeyboardButton::escape();
break;
case kHIDUsage_KeyboardDeleteOrBackspace:
handle = KeyboardButton::backspace();
break;
case kHIDUsage_KeyboardTab:
handle = KeyboardButton::tab();
break;
case kHIDUsage_KeyboardSpacebar:
handle = KeyboardButton::ascii_key(' ');
break;
case kHIDUsage_KeyboardHyphen:
handle = KeyboardButton::ascii_key('-');
break;
case kHIDUsage_KeyboardEqualSign:
handle = KeyboardButton::ascii_key('=');
break;
case kHIDUsage_KeyboardOpenBracket:
handle = KeyboardButton::ascii_key('[');
break;
case kHIDUsage_KeyboardCloseBracket:
handle = KeyboardButton::ascii_key(']');
break;
case kHIDUsage_KeyboardBackslash:
handle = KeyboardButton::ascii_key('\\');
break;
case kHIDUsage_KeyboardNonUSPound:
handle = KeyboardButton::ascii_key('$');
break;
case kHIDUsage_KeyboardSemicolon:
handle = KeyboardButton::ascii_key(';');
break;
case kHIDUsage_KeyboardQuote:
handle = KeyboardButton::ascii_key('\'');
break;
case kHIDUsage_KeyboardGraveAccentAndTilde:
handle = KeyboardButton::ascii_key('`');
break;
case kHIDUsage_KeyboardComma:
handle = KeyboardButton::ascii_key(',');
break;
case kHIDUsage_KeyboardPeriod:
handle = KeyboardButton::ascii_key('.');
break;
case kHIDUsage_KeyboardSlash:
handle = KeyboardButton::ascii_key('/');
break;
case kHIDUsage_KeyboardCapsLock:
handle = KeyboardButton::caps_lock();
break;
case kHIDUsage_KeyboardF1:
handle = KeyboardButton::f1();
break;
case kHIDUsage_KeyboardF2:
handle = KeyboardButton::f2();
break;
case kHIDUsage_KeyboardF3:
handle = KeyboardButton::f3();
break;
case kHIDUsage_KeyboardF4:
handle = KeyboardButton::f4();
break;
case kHIDUsage_KeyboardF5:
handle = KeyboardButton::f5();
break;
case kHIDUsage_KeyboardF6:
handle = KeyboardButton::f6();
break;
case kHIDUsage_KeyboardF7:
handle = KeyboardButton::f7();
break;
case kHIDUsage_KeyboardF8:
handle = KeyboardButton::f8();
break;
case kHIDUsage_KeyboardF9:
handle = KeyboardButton::f9();
break;
case kHIDUsage_KeyboardF10:
handle = KeyboardButton::f10();
break;
case kHIDUsage_KeyboardF11:
handle = KeyboardButton::f11();
break;
case kHIDUsage_KeyboardF12:
handle = KeyboardButton::f12();
break;
case kHIDUsage_KeyboardPrintScreen:
handle = KeyboardButton::print_screen();
break;
case kHIDUsage_KeyboardScrollLock:
handle = KeyboardButton::scroll_lock();
break;
case kHIDUsage_KeyboardPause:
handle = KeyboardButton::pause();
break;
case kHIDUsage_KeyboardInsert:
handle = KeyboardButton::insert();
break;
case kHIDUsage_KeyboardHome:
handle = KeyboardButton::home();
break;
case kHIDUsage_KeyboardPageUp:
handle = KeyboardButton::page_up();
break;
case kHIDUsage_KeyboardDeleteForward:
handle = KeyboardButton::del();
break;
case kHIDUsage_KeyboardEnd:
handle = KeyboardButton::end();
break;
case kHIDUsage_KeyboardPageDown:
handle = KeyboardButton::page_down();
break;
case kHIDUsage_KeyboardRightArrow:
handle = KeyboardButton::right();
break;
case kHIDUsage_KeyboardLeftArrow:
handle = KeyboardButton::left();
break;
case kHIDUsage_KeyboardDownArrow:
handle = KeyboardButton::down();
break;
case kHIDUsage_KeyboardUpArrow:
handle = KeyboardButton::up();
break;
case kHIDUsage_KeypadNumLock:
handle = KeyboardButton::num_lock();
break;
case kHIDUsage_KeypadSlash:
handle = KeyboardButton::ascii_key('/');
break;
case kHIDUsage_KeypadAsterisk:
handle = KeyboardButton::ascii_key('*');
break;
case kHIDUsage_KeypadHyphen:
handle = KeyboardButton::ascii_key('-');
break;
case kHIDUsage_KeypadPlus:
handle = KeyboardButton::ascii_key('+');
break;
case kHIDUsage_KeypadEnter:
handle = KeyboardButton::enter();
break;
case kHIDUsage_Keypad1:
handle = KeyboardButton::ascii_key('1');
break;
case kHIDUsage_Keypad2:
handle = KeyboardButton::ascii_key('2');
break;
case kHIDUsage_Keypad3:
handle = KeyboardButton::ascii_key('3');
break;
case kHIDUsage_Keypad4:
handle = KeyboardButton::ascii_key('4');
break;
case kHIDUsage_Keypad5:
handle = KeyboardButton::ascii_key('5');
break;
case kHIDUsage_Keypad6:
handle = KeyboardButton::ascii_key('6');
break;
case kHIDUsage_Keypad7:
handle = KeyboardButton::ascii_key('7');
break;
case kHIDUsage_Keypad8:
handle = KeyboardButton::ascii_key('8');
break;
case kHIDUsage_Keypad9:
handle = KeyboardButton::ascii_key('9');
break;
case kHIDUsage_Keypad0:
handle = KeyboardButton::ascii_key('0');
break;
case kHIDUsage_KeypadPeriod:
handle = KeyboardButton::ascii_key('.');
break;
case kHIDUsage_KeyboardNonUSBackslash:
handle = KeyboardButton::ascii_key('\\');
break;
case kHIDUsage_KeypadEqualSign:
handle = KeyboardButton::ascii_key('=');
break;
case kHIDUsage_KeyboardF13:
handle = KeyboardButton::f13();
break;
case kHIDUsage_KeyboardF14:
handle = KeyboardButton::f14();
break;
case kHIDUsage_KeyboardF15:
handle = KeyboardButton::f15();
break;
case kHIDUsage_KeyboardF16:
handle = KeyboardButton::f16();
break;
case kHIDUsage_KeyboardExecute:
break;
case kHIDUsage_KeyboardHelp:
handle = KeyboardButton::help();
break;
case kHIDUsage_KeyboardMenu:
handle = KeyboardButton::menu();
break;
case kHIDUsage_KeypadComma:
handle = KeyboardButton::ascii_key(',');
break;
case kHIDUsage_KeypadEqualSignAS400:
handle = KeyboardButton::ascii_key('=');
break;
case kHIDUsage_KeyboardReturn:
handle = KeyboardButton::enter();
break;
case kHIDUsage_KeyboardLeftControl:
handle = KeyboardButton::lcontrol();
break;
case kHIDUsage_KeyboardLeftShift:
handle = KeyboardButton::lshift();
break;
case kHIDUsage_KeyboardLeftAlt:
handle = KeyboardButton::lalt();
break;
case kHIDUsage_KeyboardLeftGUI:
handle = KeyboardButton::lmeta();
break;
case kHIDUsage_KeyboardRightControl:
handle = KeyboardButton::rcontrol();
break;
case kHIDUsage_KeyboardRightShift:
handle = KeyboardButton::rshift();
break;
case kHIDUsage_KeyboardRightAlt:
handle = KeyboardButton::ralt();
break;
case kHIDUsage_KeyboardRightGUI:
handle = KeyboardButton::rmeta();
break;
default:
break;
}
break;
case kHIDPage_Button:
if (_device_class == DeviceClass::GAMEPAD) {
if (_vendor_id == 0x0810 && _product_id == 0xe501) {
// SNES-style USB gamepad
static const ButtonHandle gamepad_buttons[] = {
ButtonHandle::none(),
GamepadButton::face_x(),
GamepadButton::face_a(),
GamepadButton::face_b(),
GamepadButton::face_y(),
GamepadButton::lshoulder(),
GamepadButton::rshoulder(),
ButtonHandle::none(),
ButtonHandle::none(),
GamepadButton::back(),
GamepadButton::start(),
};
if (usage < sizeof(gamepad_buttons) / sizeof(ButtonHandle)) {
handle = gamepad_buttons[usage];
}
} else {
// These seem to be the button mappings exposed by the 360Controller
// driver. I don't know if other drivers do the same thing at all.
static const ButtonHandle gamepad_buttons[] = {
ButtonHandle::none(),
GamepadButton::face_a(),
GamepadButton::face_b(),
GamepadButton::face_x(),
GamepadButton::face_y(),
GamepadButton::lshoulder(),
GamepadButton::rshoulder(),
GamepadButton::lstick(),
GamepadButton::rstick(),
GamepadButton::start(),
GamepadButton::back(),
GamepadButton::guide(),
GamepadButton::dpad_up(),
GamepadButton::dpad_down(),
GamepadButton::dpad_left(),
GamepadButton::dpad_right(),
};
if (usage < sizeof(gamepad_buttons) / sizeof(ButtonHandle)) {
handle = gamepad_buttons[usage];
}
}
} else if (_device_class == DeviceClass::FLIGHT_STICK) {
if (usage > 0) {
handle = GamepadButton::joystick(usage - 1);
}
} else if (_device_class == DeviceClass::MOUSE) {
// In Panda, wheel and right button are flipped around...
int button = (usage == 2 || usage == 3) ? (4 - usage) : (usage - 1);
handle = MouseButton::button(button);
}
break;
}
_buttons.push_back(ButtonState(handle));
_button_elements.push_back(element);
break;
case kIOHIDElementTypeInput_Axis:
break;
case kIOHIDElementTypeInput_ScanCodes:
break;
case kIOHIDElementTypeOutput:
break;
case kIOHIDElementTypeFeature:
break;
case kIOHIDElementTypeCollection:
{
// This doesn't seem to be necessary and instead leads to duplication of
// axes and buttons.
/*
CFArrayRef children = IOHIDElementGetChildren(element);
CFIndex count = CFArrayGetCount(children);
for (CFIndex i = 0; i < count; ++i) {
IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(children, i);
parse_element(element, depth + 2);
}*/
}
break;
}
}
/**
* Polls the input device for new activity, to ensure it contains the latest
* events. This will only have any effect for some types of input devices;
* others may be updated automatically, and this method will be a no-op.
*/
void IOKitInputDevice::
do_poll() {
for (size_t i = 0; i < _button_elements.size(); ++i) {
IOHIDValueRef value_ref;
if (!_button_elements[i]) continue;
if (IOHIDDeviceGetValue(_device, _button_elements[i], &value_ref) == kIOReturnSuccess) {
int value = IOHIDValueGetIntegerValue(value_ref);
button_changed(i, value != 0);
}
}
for (size_t i = 0; i < _analog_elements.size(); ++i) {
IOHIDValueRef value_ref;
if (IOHIDDeviceGetValue(_device, _analog_elements[i], &value_ref) == kIOReturnSuccess) {
int value = IOHIDValueGetIntegerValue(value_ref);
axis_changed(i, value);
}
}
if (_hat_element != nullptr) {
IOHIDValueRef value_ref;
if (IOHIDDeviceGetValue(_device, _hat_element, &value_ref) == kIOReturnSuccess) {
int value = IOHIDValueGetIntegerValue(value_ref);
button_changed(_hat_left_button + 0, value >= 5 && value <= 7); // left
button_changed(_hat_left_button + 1, value >= 1 && value <= 3); // right
button_changed(_hat_left_button + 2, value >= 3 && value <= 5); // down
button_changed(_hat_left_button + 3, value == 7 || value == 0 || value == 1); // up
}
}
int x = 0, y = 0;
if (_pointer_x != nullptr) {
IOHIDValueRef value_ref;
if (IOHIDDeviceGetValue(_device, _pointer_x, &value_ref) == kIOReturnSuccess) {
uint64_t timestamp = IOHIDValueGetTimeStamp(value_ref);
if (timestamp != _pointer_x_timestamp) {
x = IOHIDValueGetIntegerValue(value_ref);
_pointer_x_timestamp = timestamp;
}
}
}
if (_pointer_y != nullptr) {
IOHIDValueRef value_ref;
if (IOHIDDeviceGetValue(_device, _pointer_y, &value_ref) == kIOReturnSuccess) {
uint64_t timestamp = IOHIDValueGetTimeStamp(value_ref);
if (timestamp != _pointer_y_timestamp) {
y = IOHIDValueGetIntegerValue(value_ref);
_pointer_y_timestamp = timestamp;
}
}
}
if (x != 0 || y != 0) {
pointer_moved(0, x, y, ClockObject::get_global_clock()->get_frame_time());
}
// Do we have a scroll wheel axis?
if (_scroll_wheel != nullptr) {
IOHIDValueRef value_ref;
if (IOHIDDeviceGetValue(_device, _scroll_wheel, &value_ref) == kIOReturnSuccess) {
uint64_t timestamp = IOHIDValueGetTimeStamp(value_ref);
if (timestamp != _scroll_wheel_timestamp) {
int value = IOHIDValueGetIntegerValue(value_ref);
if (value != 0) {
// Just fire off a rapid down/up event.
double time = ClockObject::get_global_clock()->get_frame_time();
ButtonHandle handle = (value > 0) ? MouseButton::wheel_up() : MouseButton::wheel_down();
_button_events->add_event(ButtonEvent(handle, ButtonEvent::T_down, time));
_button_events->add_event(ButtonEvent(handle, ButtonEvent::T_up, time));
}
_scroll_wheel_timestamp = timestamp;
}
}
}
}
#endif // __APPLE__