IOHIDPointerEventTranslation.c [plain text]
#include <AssertMacros.h>
#include <dispatch/dispatch.h>
#include <CoreFoundation/CFRuntime.h>
#include <IOKit/hid/IOHIDServiceClient.h>
#include <IOKit/hid/IOHIDService.h>
#include <IOKit/hid/IOHIDUsageTables.h>
#include "AppleHIDUsageTables.h"
#include "IOHIDFamilyPrivate.h"
#include <IOKit/usb/USB.h>
#include "IOHIDEventData.h"
#include "IOHIDPointerEventTranslation.h"
#include "IOHIDevicePrivateKeys.h"
#include "IOHIDDebug.h"
#include <mach/mach_time.h>
#define kPhaseMask (kIOHIDEventPhaseBegan | kIOHIDEventPhaseChanged | kIOHIDEventPhaseEnded | kIOHIDEventPhaseCancelled | kIOHIDEventPhaseMayBegin)
#define ABS_TO_NS(t,b) ((t * (b).numer)/ (b).denom)
#define NS_TO_ABS(t,b) ((t * (b).denom)/ (b).numer)
#define FIXED_TO_DOUBLE(x) ((x)/65536.0)
#define EV_DCLICKTIME 500000000
#define EV_DCLICKSPACE 3
#define kAbsoluteToPixelTranslation 1024
#define SIGN(x) ((x > 0) - (x < 0))
#define TRANSLATE_SCROLL_MOMENTUM(scrollEvent) \
(((IOHIDEventGetPhase (scrollEvent) & kPhaseMask) << 8) | \
((IOHIDEventGetPhase (scrollEvent) >> 2) & kScrollTypeMomentumContinue) | \
((IOHIDEventGetPhase (scrollEvent) >> 1) & \
(kScrollTypeMomentumStart | kScrollTypeMomentumEnd)))
#define kIOHIDScrollCountMaxTimeDeltaBetween 600
#define kIOHIDScrollCountMaxTimeDeltaToSustain 250
#define kIOHIDScrollCountMinDeltaToStart (30)
#define kIOHIDScrollCountMinDeltaToSustain (20)
#define kIOHIDScrollCountIgnoreMomentumScrolls true
#define kIOHIDScrollCountMouseCanReset true
#define kIOHIDScrollCountMax 2000
#define kIOHIDScrollCountAccelerationFactor FIXED_TO_DOUBLE(163840)
#define NULLEVENTNUM 0
#define INITEVENTNUM 13
#define kZoomModifierMask (NX_COMMANDMASK| NX_ALTERNATEMASK | NX_CONTROLMASK | NX_SHIFTMASK)
#define kLegacyMouseEventsMask (NX_LMOUSEDOWNMASK|NX_RMOUSEDOWNMASK|NX_LMOUSEUPMASK|NX_RMOUSEUPMASK|NX_OMOUSEDOWNMASK|NX_OMOUSEUPMASK)
typedef struct {
uint32_t buttons;
boolean_t isMultiTouchService;
boolean_t isContinuousScroll;
uint32_t buttonCount;
boolean_t clickRemappingDisable;
CFTypeRef service;
IOHIDFloat lastAbsoluteX;
IOHIDFloat lastAbsoluteY;
} SERVICE_RECORD;
typedef struct {
uint32_t globalButtons;
uint32_t flags;
uint64_t timestamp;
uint64_t serviceid;
SERVICE_RECORD *serviceRecord;
CFMutableArrayRef eventCollection;
IOHIDEventRef event;
} EVENT_TRANSLATOR_CONTEXT;
static uint64_t __IOHIDEventTranslatorGetServiceIDForObject (CFTypeRef service);
CFTypeRef __IOHIDEventTranslatorCopyServiceProperty (CFTypeRef service, CFStringRef key);
void __IOHIDPointerEventTranslatorCreateMouseUpDownEvent (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef pointerEvent);
void __IOHIDPointerEventTranslatorCreateSysdefineEvent (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context);
static void __IOHIDPointerEventTranslatorFree( CFTypeRef object );
static CFStringRef __IOHIDPointerEventTranslatorCopyDebugDescription(CFTypeRef cf);
IOHIDPointerEventTranslatorRef __IOHIDPointerEventTranslatorCreatePrivate(CFAllocatorRef allocator, CFAllocatorContext * context __unused);
IOHIDEventRef __IOHIDPointerEventTranslatorCreateMouseMoveEvent (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef pointerEvent);
void __IOHIDPointerEventTranslatorInitNxEvent (EVENT_TRANSLATOR_CONTEXT *context, NXEventExt *nxEvent, uint8_t type);
uint32_t __IOHIDPointerEventTranslatorGetGlobalButtonState (IOHIDPointerEventTranslatorRef translator);
void __IOHIDPointerEventTranslatorProcessButtonState (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef pointerEvent);
void __IOHIDPointerEventTranslatorProcessClickState (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef pointerEvent, IOHIDEventRef buttonEvent);
void __IOHIDPointerEventTranslatorProcessScrollCount (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef scrollEvent);
IOHIDEventRef __IOHIDPointerEventTranslatorCreateScrollEvent (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef scrollEvent);
boolean_t __IOHIDEventTranslatorGetBooleanProperty (CFTypeRef service, CFStringRef key, boolean_t defaultValue);
uint64_t __IOHIDEventTranslatorGetIntegerProperty (CFTypeRef service, CFStringRef key, uint64_t defaultValue);
SERVICE_RECORD * __IOHIDEventTranslatorGetServiceRecordForServiceID (IOHIDPointerEventTranslatorRef translator, uint64_t serviceID);
static uint32_t __IOHIDPointerEventTranslatorGetUniqueEventNumber (IOHIDPointerEventTranslatorRef translator);
uint32_t __IOHIDPointerEventTranslatorRemapButtons (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, uint32_t buttons);
void __IOHIDPointerEventTranslatorProcessLegacyEvent (IOHIDPointerEventTranslatorRef translator,EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef legacyMouseEvent);
NXEventExt * __IOHIDPointerEventTranslatorGetNxMouseEvents (IOHIDEventRef event);
static const CFRuntimeClass __IOHIDPointerEventTranslatorClass = {
0, "IOHIDPointerEventTranslator", NULL, NULL, __IOHIDPointerEventTranslatorFree, NULL, NULL, NULL, __IOHIDPointerEventTranslatorCopyDebugDescription,
NULL,
NULL
};
typedef struct __IOHIDPointerEventTranslator {
CFRuntimeBase cfBase; uint32_t globalButtons;
CFMutableDictionaryRef serviceRecord;
mach_timebase_info_data_t timebaseInfo;
uint32_t flags;
uint8_t clickCount;
uint64_t lastClickTime;
uint64_t clickCountTimeTreshold;
uint32_t clickCountPixelTreshold;
IOHIDFloat clickCountDeltaX;
IOHIDFloat clickCountDeltaY;
uint16_t scrollCount;
uint32_t scrollDicrection;
uint64_t lastScrollTime;
uint64_t lastScrollSustainTime;
IOHIDFloat scrollCountDeltaX;
IOHIDFloat scrollCountDeltaY;
boolean_t scrollIncrementThisPhase;
uint64_t scrollCountMaxTimeDeltaBetween;
uint32_t scrollCountMaxTimeDeltaToSustain;
uint32_t scrollCountMinDeltaToStartPow2;
uint32_t scrollCountMinDeltaToSustainPow2;
boolean_t scrollCountIgnoreMomentumScrolls;
boolean_t scrollCountMouseCanReset;
uint32_t scrollCountMax;
double scrollCountAccelerationFactor;
uint32_t zoomModifierMask;
boolean_t zoomLastScrollWasZoom;
uint32_t eventNumber;
uint32_t lastLeftEventNum;
uint32_t lastRightEventNum;
uint32_t buttonMode;
double deltaAxisX;
double deltaAxisY;
double deltaAxisZ;
double fixedAxisX;
double fixedAxisY;
double fixedAxisZ;
} __IOHIDPointerEventTranslator;
static dispatch_once_t __pointerTranslatorTypeInit = 0;
static CFTypeID __pointerTranslatorTypeID = _kCFRuntimeNotATypeID;
CFTypeID IOHIDPointerEventTranslatorGetTypeID(void)
{
if ( _kCFRuntimeNotATypeID == __pointerTranslatorTypeID ) {
dispatch_once(&__pointerTranslatorTypeInit, ^{
__pointerTranslatorTypeID = _CFRuntimeRegisterClass(&__IOHIDPointerEventTranslatorClass);
});
}
return __pointerTranslatorTypeID;
}
IOHIDPointerEventTranslatorRef IOHIDPointerEventTranslatorCreate (CFAllocatorRef allocator, uint32_t options)
{
(void)options;
IOHIDPointerEventTranslatorRef translator = __IOHIDPointerEventTranslatorCreatePrivate(allocator, NULL);
if (!translator) {
return translator;
}
translator->serviceRecord = CFDictionaryCreateMutable (CFGetAllocator(translator), 0, NULL, &kCFTypeDictionaryValueCallBacks);
require(translator->serviceRecord, error_exit);
mach_timebase_info(&(translator->timebaseInfo));
translator->clickCountTimeTreshold = EV_DCLICKTIME;
translator->clickCountPixelTreshold = EV_DCLICKSPACE;
translator->scrollCountMaxTimeDeltaBetween = NS_TO_ABS (kIOHIDScrollCountMaxTimeDeltaBetween*kMillisecondScale,translator->timebaseInfo);
translator->scrollCountMaxTimeDeltaToSustain = NS_TO_ABS (kIOHIDScrollCountMaxTimeDeltaToSustain*kMillisecondScale,translator->timebaseInfo);
translator->scrollCountMinDeltaToStartPow2 = kIOHIDScrollCountMinDeltaToStart * kIOHIDScrollCountMinDeltaToStart;
translator->scrollCountMinDeltaToSustainPow2 = kIOHIDScrollCountMinDeltaToSustain * kIOHIDScrollCountMinDeltaToSustain;
translator->scrollCountIgnoreMomentumScrolls = kIOHIDScrollCountIgnoreMomentumScrolls;
translator->scrollCountMouseCanReset = kIOHIDScrollCountMouseCanReset;
translator->scrollCountMax = kIOHIDScrollCountMax;
translator->scrollCountAccelerationFactor = kIOHIDScrollCountAccelerationFactor;
translator->eventNumber = INITEVENTNUM;
translator->buttonMode = NX_RightButton;
return translator;
error_exit:
CFRelease (translator);
translator = NULL;
return translator;
}
void IOHIDPointerEventTranslatorRegisterService (IOHIDPointerEventTranslatorRef translator, CFTypeRef service)
{
if (service == NULL || translator == NULL) {
return;
}
uint64_t serviceID = __IOHIDEventTranslatorGetServiceIDForObject (service);
HIDLogDebug ("register service %llu", serviceID);
if (CFDictionaryContainsKey (translator->serviceRecord, (const void*)serviceID)) {
return;
}
SERVICE_RECORD record = {
.buttons = 0,
.isMultiTouchService = false,
.isContinuousScroll = false,
.buttonCount = 0,
.clickRemappingDisable = false,
.service = service
};
record.isMultiTouchService = __IOHIDEventTranslatorGetBooleanProperty(service, CFSTR("MTEventSource"), false);
if (__IOHIDEventTranslatorGetIntegerProperty (service, CFSTR(kIOHIDScrollResolutionXKey), 0) > (18 << 16) ||
__IOHIDEventTranslatorGetIntegerProperty (service, CFSTR(kIOHIDScrollResolutionYKey), 0) > (18 << 16) ||
__IOHIDEventTranslatorGetIntegerProperty (service, CFSTR(kIOHIDScrollResolutionZKey), 0) > (18 << 16) ||
__IOHIDEventTranslatorGetIntegerProperty (service, CFSTR(kIOHIDScrollResolutionKey), 0) > (18 << 16) ) {
record.isContinuousScroll = true;
}
record.buttonCount = (uint32_t)__IOHIDEventTranslatorGetIntegerProperty (service, CFSTR(kIOHIDPointerButtonCountKey), 1);
record.clickRemappingDisable = __IOHIDEventTranslatorGetBooleanProperty (service, CFSTR(kIOHIDDisallowRemappingOfPrimaryClickKey), false);
CFMutableDataRef serviceRecord = CFDataCreateMutable(CFGetAllocator(translator), sizeof (record));
if (serviceRecord) {
CFDataSetLength(serviceRecord, sizeof (record));
UInt8 * serviceRecordPtr = (UInt8*)CFDataGetBytePtr(serviceRecord);
if (serviceRecordPtr) {
memcpy(serviceRecordPtr, &record, sizeof(record));
CFDictionarySetValue(translator->serviceRecord, (const void *) serviceID, serviceRecord);
}
CFRelease(serviceRecord);
}
}
void IOHIDPointerEventTranslatorUnRegisterService (IOHIDPointerEventTranslatorRef translator, CFTypeRef service)
{
if (service == NULL || translator == NULL) {
return;
}
uint64_t serviceID = __IOHIDEventTranslatorGetServiceIDForObject (service);
CFDictionaryRemoveValue (translator->serviceRecord, (const void *)serviceID);
}
void IOHIDPointerEventTranslatorSetProperty (IOHIDPointerEventTranslatorRef translator, CFStringRef key, CFTypeRef property) {
HIDLogDebug("%@ : %@", key, property);
if (CFEqual(key, CFSTR(kIOHIDClickTimeKey)) && property && CFGetTypeID(property) == CFNumberGetTypeID()) {
CFNumberGetValue(property, kCFNumberSInt64Type, &translator->clickCountTimeTreshold);
} else if (CFEqual(key, CFSTR(kIOHIDScrollZoomModifierMaskKey)) && property && CFGetTypeID(property) == CFNumberGetTypeID()) {
CFNumberGetValue(property, kCFNumberSInt32Type, &translator->zoomModifierMask);
translator->zoomModifierMask &= kZoomModifierMask;
} else if (CFEqual(key, CFSTR(kIOHIDPointerButtonMode)) && property && CFGetTypeID(property) == CFNumberGetTypeID()) {
CFNumberGetValue(property, kCFNumberSInt32Type, &translator->buttonMode);
}
}
CFArrayRef IOHIDPointerEventTranslatorCreateEventCollection (IOHIDPointerEventTranslatorRef translator, IOHIDEventRef event, CFTypeRef sender, uint32_t flags, uint32_t options __unused)
{
if (!event) {
return NULL;
}
CFMutableArrayRef eventCollection = CFArrayCreateMutable(kCFAllocatorDefault, 2, &kCFTypeArrayCallBacks);
if (eventCollection == NULL) {
return NULL;
}
IOHIDEventRef translatedEvent;
EVENT_TRANSLATOR_CONTEXT context;
context.serviceid = IOHIDEventGetSenderID (event);
context.timestamp = IOHIDEventGetTimeStamp (event);
context.flags = flags;
context.globalButtons = translator->globalButtons;
context.eventCollection = eventCollection;
context.event = event;
context.serviceRecord = __IOHIDEventTranslatorGetServiceRecordForServiceID (translator, context.serviceid);
if (context.serviceRecord == NULL) {
IOHIDPointerEventTranslatorRegisterService(translator, sender);
context.serviceRecord = __IOHIDEventTranslatorGetServiceRecordForServiceID (translator, context.serviceid);
if (context.serviceRecord == NULL) {
HIDLogError("unable to get/create service record for pointer event translation");
return NULL;
}
}
IOHIDEventRef legacyMouseEvent = __IOHIDPointerEventTranslatorGetNxMouseEvents (event) ? event : NULL;
if (legacyMouseEvent) {
__IOHIDPointerEventTranslatorProcessLegacyEvent (translator, &context, legacyMouseEvent);
}
IOHIDEventRef pointerEvent = IOHIDEventGetEvent (event, kIOHIDEventTypePointer);
if (pointerEvent != NULL && (IOHIDEventGetEventFlags(pointerEvent) & kIOHIDAccelerated) == 0) {
CFIndex i, count;
CFArrayRef children = IOHIDEventGetChildren(pointerEvent);
for (i=0, count = (children) ? CFArrayGetCount(children) : 0; i < count; i++) {
IOHIDEventRef child = IOHIDEventGetEvent((IOHIDEventRef)CFArrayGetValueAtIndex(children, i), kIOHIDEventTypePointer);
if (child && IOHIDEventGetEventFlags(child) & kIOHIDAccelerated) {
pointerEvent = child;
break;
}
}
}
IOHIDEventRef scrollEvent = IOHIDEventGetEvent (event, kIOHIDEventTypeScroll);
if (scrollEvent != NULL && (IOHIDEventGetEventFlags(scrollEvent) & kIOHIDAccelerated) == 0) {
CFIndex i, count;
CFArrayRef children = IOHIDEventGetChildren(scrollEvent);
for (i=0, count = (children) ? CFArrayGetCount(children) : 0; i < count; i++) {
IOHIDEventRef child = IOHIDEventGetEvent((IOHIDEventRef)CFArrayGetValueAtIndex(children, i), kIOHIDEventTypeScroll);
if (child && IOHIDEventGetEventFlags(child) & kIOHIDAccelerated) {
scrollEvent = child;
break;
}
}
}
IOHIDEventRef buttonEvent = IOHIDEventGetEvent (event, kIOHIDEventTypeButton);
if (buttonEvent || pointerEvent) {
__IOHIDPointerEventTranslatorProcessClickState (translator, &context, pointerEvent, buttonEvent);
}
if (scrollEvent) {
__IOHIDPointerEventTranslatorProcessScrollCount (translator, &context, scrollEvent);
translatedEvent = __IOHIDPointerEventTranslatorCreateScrollEvent (translator, &context, scrollEvent);
if (translatedEvent) {
CFArrayAppendValue(eventCollection, translatedEvent);
CFRelease(translatedEvent);
}
}
if (pointerEvent) {
__IOHIDPointerEventTranslatorProcessButtonState (translator, &context, pointerEvent);
if (context.globalButtons != translator->globalButtons) {
__IOHIDPointerEventTranslatorCreateSysdefineEvent (translator, &context);
__IOHIDPointerEventTranslatorCreateMouseUpDownEvent (translator, &context, pointerEvent);
}
translatedEvent = __IOHIDPointerEventTranslatorCreateMouseMoveEvent (translator, &context, pointerEvent);
if (translatedEvent) {
CFArrayAppendValue(eventCollection, translatedEvent);
CFRelease(translatedEvent);
}
if (IOHIDEventIsAbsolute(pointerEvent)) {
context.serviceRecord->lastAbsoluteX = IOHIDEventGetFloatValue (pointerEvent, kIOHIDEventFieldPointerX);
context.serviceRecord->lastAbsoluteY = IOHIDEventGetFloatValue (pointerEvent, kIOHIDEventFieldPointerY);
}
}
translator->flags = context.flags;
return eventCollection;
}
#define __IOHIDPointerEventTranslatorCreateHidEventForNXEvent(nxEvent, hidEvent, usage) \
IOHIDEventCreateVendorDefinedEvent ( \
CFGetAllocator(hidEvent), \
nxEvent.payload.time, \
kHIDPage_AppleVendor, \
usage, \
0, \
(uint8_t*)&nxEvent, \
sizeof (nxEvent), \
0 \
)
void __IOHIDPointerEventTranslatorInitNxEvent (EVENT_TRANSLATOR_CONTEXT *context, NXEventExt *nxEvent, uint8_t type) {
memset(nxEvent, 0, sizeof(*nxEvent));
nxEvent->payload.service_id = context->serviceid;
nxEvent->payload.time = context->timestamp;
nxEvent->payload.type = type;
nxEvent->payload.flags = context->flags;
nxEvent->extension.flags = NX_EVENT_EXTENSION_LOCATION_INVALID;
}
IOHIDEventRef __IOHIDPointerEventTranslatorCreateMouseMoveEvent (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef pointerEvent) {
NXEventExt nxEvent;
uint8_t type;
uint32_t button = __IOHIDPointerEventTranslatorRemapButtons (translator, context, translator->globalButtons);
if (button & 0x1) {
type = NX_LMOUSEDRAGGED;
} else if (button & 0x2) {
type = NX_RMOUSEDRAGGED;
} else {
type = NX_MOUSEMOVED;
}
__IOHIDPointerEventTranslatorInitNxEvent (context, &nxEvent, type);
float x = (float)IOHIDEventGetFloatValue (pointerEvent, kIOHIDEventFieldPointerX);
float y = (float)IOHIDEventGetFloatValue (pointerEvent, kIOHIDEventFieldPointerY);
if ((x == 0.0 && y == 0.0) ||
(IOHIDEventIsAbsolute(pointerEvent) && x == context->serviceRecord->lastAbsoluteX && y == context->serviceRecord->lastAbsoluteY)) {
return NULL;
}
if (IOHIDEventIsAbsolute(pointerEvent)) {
*((float*)&nxEvent.payload.location.x) = x;
*((float*)&nxEvent.payload.location.y) = y;
nxEvent.extension.flags = NX_EVENT_EXTENSION_LOCATION_TYPE_FLOAT | NX_EVENT_EXTENSION_LOCATION_DEVICE_SCALED;
} else {
*((float*)&nxEvent.payload.data.mouseMove.dx) = x;
*((float*)&nxEvent.payload.data.mouseMove.dy) = y;
nxEvent.extension.flags |= NX_EVENT_EXTENSION_MOUSE_DELTA_TYPE_FLOAT;
}
if (context->serviceRecord->isMultiTouchService) {
nxEvent.payload.data.mouseMove.subType = NX_SUBTYPE_MOUSE_TOUCH;
}
return __IOHIDPointerEventTranslatorCreateHidEventForNXEvent(nxEvent, pointerEvent, kHIDUsage_AppleVendor_NXEvent_Translated);
}
void __IOHIDPointerEventTranslatorProcessButtonState (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context __unused, IOHIDEventRef pointerEvent) {
uint32_t buttons = (uint32_t)IOHIDEventGetIntegerValue (pointerEvent, kIOHIDEventFieldPointerButtonMask);
if (context->serviceRecord->buttons != buttons) {
context->serviceRecord->buttons = buttons;
translator->globalButtons = __IOHIDPointerEventTranslatorGetGlobalButtonState (translator);
}
}
uint32_t __IOHIDPointerEventTranslatorRemapButtons (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, uint32_t buttons) {
uint32_t result = buttons;
if (context->serviceRecord->clickRemappingDisable) {
return result;
}
if (translator->buttonMode == NX_OneButton) {
result = ((buttons & 0x3) ? 0x1 : 0) | (buttons & (~0x3));
} else if (context->serviceRecord->buttonCount > 1 && translator->buttonMode == NX_LeftButton) {
result = ((buttons & 0x1) << 1) | ((buttons & 0x2) >> 1) | (buttons & (~0x3));
}
return result;
}
void __IOHIDPointerEventTranslatorProcessClickState (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef pointerEvent, IOHIDEventRef buttonEvent) {
if (context->flags != translator->flags) {
translator->clickCount = 0;
}
if (pointerEvent) {
IOHIDFloat dx = IOHIDEventGetFloatValue (pointerEvent, kIOHIDEventFieldPointerX);
IOHIDFloat dy = IOHIDEventGetFloatValue (pointerEvent, kIOHIDEventFieldPointerY);
if (IOHIDEventIsAbsolute(pointerEvent)) {
dx = (dx - context->serviceRecord->lastAbsoluteX) * kAbsoluteToPixelTranslation;
dy = (dy - context->serviceRecord->lastAbsoluteY) * kAbsoluteToPixelTranslation;
}
if (translator->clickCount >= 1) {
translator->clickCountDeltaX += dx;
translator->clickCountDeltaY += dy;
if ((fabs(translator->clickCountDeltaX) > (IOHIDFloat)translator->clickCountPixelTreshold / 2) ||
(fabs(translator->clickCountDeltaY) > (IOHIDFloat)translator->clickCountPixelTreshold / 2)) {
translator->clickCount = 0;
}
} else {
translator->clickCountDeltaX = 0;
translator->clickCountDeltaY = 0;
}
if (translator->scrollCount >= 1) {
translator->scrollCountDeltaX += dx;
translator->scrollCountDeltaY += dy;
if ((fabs(translator->scrollCountDeltaX) > (IOHIDFloat)translator->clickCountPixelTreshold / 2) ||
(fabs(translator->scrollCountDeltaY) > (IOHIDFloat)translator->clickCountPixelTreshold / 2)) {
translator->scrollCount = 0;
}
} else {
translator->scrollCountDeltaX = 0;
translator->scrollCountDeltaY = 0;
}
}
if (buttonEvent && IOHIDEventGetIntegerValue (buttonEvent, kIOHIDEventFieldButtonState)) {
uint64_t eventTime = ABS_TO_NS(IOHIDEventGetTimeStamp(buttonEvent), translator->timebaseInfo) ;
if ((uint64_t)llabs((int64_t)eventTime - (int64_t)translator->lastClickTime) < translator->clickCountTimeTreshold) {
++translator->clickCount;
} else {
translator->clickCount = 1;
}
translator->lastClickTime = eventTime;
}
}
void __IOHIDPointerEventTranslatorCreateSysdefineEvent (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context) {
uint32_t buttons;
NXEventExt nxEvent;
IOHIDEventRef translated;
buttons = translator->globalButtons ^ context->globalButtons;
__IOHIDPointerEventTranslatorInitNxEvent (context, &nxEvent, NX_SYSDEFINED);
nxEvent.payload.data.compound.subType = NX_SUBTYPE_AUX_MOUSE_BUTTONS;
nxEvent.payload.data.compound.misc.L[0] = __IOHIDPointerEventTranslatorRemapButtons (translator, context, buttons);
nxEvent.payload.data.compound.misc.L[1] = __IOHIDPointerEventTranslatorRemapButtons (translator, context, translator->globalButtons);
translated = __IOHIDPointerEventTranslatorCreateHidEventForNXEvent(nxEvent, context->event, kHIDUsage_AppleVendor_NXEvent_Translated);
if (translated) {
CFArrayAppendValue(context->eventCollection, translated);
CFRelease(translated);
}
}
void __IOHIDPointerEventTranslatorCreateMouseUpDownEvent (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef pointerEvent) {
uint32_t changedButtons;
uint32_t buttons;
NXEventExt nxEvent;
IOHIDEventRef translated;
changedButtons = __IOHIDPointerEventTranslatorRemapButtons (translator, context, translator->globalButtons ^ context->globalButtons);
if (changedButtons == 0) {
return;
}
__IOHIDPointerEventTranslatorInitNxEvent (context, &nxEvent, 0);
buttons = __IOHIDPointerEventTranslatorRemapButtons (translator, context, translator->globalButtons);
nxEvent.payload.data.mouse.click = translator->clickCount;
if (context->serviceRecord->isMultiTouchService) {
nxEvent.payload.data.mouse.subType = NX_SUBTYPE_MOUSE_TOUCH;
}
if (IOHIDEventIsAbsolute(pointerEvent)) {
*((float*)&nxEvent.payload.location.x) = (float)IOHIDEventGetFloatValue (pointerEvent, kIOHIDEventFieldPointerX);
*((float*)&nxEvent.payload.location.y) = (float)IOHIDEventGetFloatValue (pointerEvent, kIOHIDEventFieldPointerY);
nxEvent.extension.flags = NX_EVENT_EXTENSION_LOCATION_TYPE_FLOAT | NX_EVENT_EXTENSION_LOCATION_DEVICE_SCALED;
}
if (changedButtons & 1) {
if (buttons & 1) {
nxEvent.payload.type = NX_LMOUSEDOWN;
nxEvent.payload.data.mouse.pressure = 255;
translator->lastLeftEventNum = __IOHIDPointerEventTranslatorGetUniqueEventNumber (translator);
nxEvent.payload.data.mouse.eventNum = translator->lastLeftEventNum;
} else {
nxEvent.payload.type = NX_LMOUSEUP;
nxEvent.payload.data.mouse.eventNum = translator->lastLeftEventNum;
translator->lastLeftEventNum = NULLEVENTNUM;
}
translated = __IOHIDPointerEventTranslatorCreateHidEventForNXEvent(nxEvent, context->event, kHIDUsage_AppleVendor_NXEvent_Translated);
if (translated) {
CFArrayAppendValue(context->eventCollection, translated);
CFRelease(translated);
}
}
if (changedButtons & 2) {
if (buttons & 2) {
nxEvent.payload.type = NX_RMOUSEDOWN;
nxEvent.payload.data.mouse.pressure = 255;
translator->lastRightEventNum = __IOHIDPointerEventTranslatorGetUniqueEventNumber (translator);
nxEvent.payload.data.mouse.eventNum = translator->lastRightEventNum;
} else {
nxEvent.payload.type = NX_RMOUSEUP;
nxEvent.payload.data.mouse.eventNum = translator->lastRightEventNum;
translator->lastRightEventNum = NULLEVENTNUM;
}
translated = __IOHIDPointerEventTranslatorCreateHidEventForNXEvent(nxEvent, context->event, kHIDUsage_AppleVendor_NXEvent_Translated);
if (translated) {
CFArrayAppendValue(context->eventCollection, translated);
CFRelease(translated);
}
}
}
IOHIDEventRef __IOHIDPointerEventTranslatorCreateScrollEvent (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef scrollEvent) {
NXEventExt nxEvent;
SInt32 eventType = NX_SCROLLWHEELMOVED;
if (translator->zoomModifierMask) {
bool isZoom = ((context->flags & kZoomModifierMask) == translator->zoomModifierMask);
bool isMomentum = (0 != (TRANSLATE_SCROLL_MOMENTUM(scrollEvent) & kScrollTypeMomentumAny));
if ((isMomentum && translator->zoomLastScrollWasZoom) || (isZoom && !isMomentum)) {
translator->zoomLastScrollWasZoom = true;
eventType = NX_ZOOM;
} else {
translator->zoomLastScrollWasZoom = false;
}
} else {
translator->zoomLastScrollWasZoom = false;
}
__IOHIDPointerEventTranslatorInitNxEvent (context, &nxEvent, eventType);
double dx = IOHIDEventGetFloatValue (scrollEvent, kIOHIDEventFieldScrollX);
double dy = IOHIDEventGetFloatValue (scrollEvent, kIOHIDEventFieldScrollY);
double dz = IOHIDEventGetFloatValue (scrollEvent, kIOHIDEventFieldScrollZ);
translator->deltaAxisX += dx;
translator->deltaAxisY += dy;
translator->deltaAxisZ += dz;
translator->fixedAxisX += dx;
translator->fixedAxisY += dy;
translator->fixedAxisZ += dz;
nxEvent.payload.data.scrollWheel.deltaAxis1 = (SInt16)translator->deltaAxisY;
if (nxEvent.payload.data.scrollWheel.deltaAxis1) {
nxEvent.payload.data.scrollWheel.fixedDeltaAxis1 = (SInt32)(translator->fixedAxisY * 65536);
translator->fixedAxisY = 0;
translator->deltaAxisY -= nxEvent.payload.data.scrollWheel.deltaAxis1;
}
nxEvent.payload.data.scrollWheel.deltaAxis2 = (SInt16)translator->deltaAxisX;
if (nxEvent.payload.data.scrollWheel.deltaAxis2) {
nxEvent.payload.data.scrollWheel.fixedDeltaAxis2 = (SInt32)(translator->fixedAxisX * 65536);
translator->fixedAxisX = 0;
translator->deltaAxisX -= nxEvent.payload.data.scrollWheel.deltaAxis2;
}
nxEvent.payload.data.scrollWheel.deltaAxis3 = (SInt16)translator->deltaAxisZ;
if (nxEvent.payload.data.scrollWheel.deltaAxis3) {
nxEvent.payload.data.scrollWheel.fixedDeltaAxis3 = (SInt32)(translator->fixedAxisZ * 65536);
translator->fixedAxisZ = 0;
translator->deltaAxisZ -= nxEvent.payload.data.scrollWheel.deltaAxis3;
}
nxEvent.payload.data.scrollWheel.pointDeltaAxis1 = dy < 0 ? floor(dy*10) : ceil(dy*10);
nxEvent.payload.data.scrollWheel.pointDeltaAxis2 = dx < 0 ? floor(dx*10) : ceil(dx*10);
nxEvent.payload.data.scrollWheel.pointDeltaAxis3 = dz < 0 ? floor(dz*10) : ceil(dz*10);
nxEvent.payload.data.scrollWheel.reserved1 = TRANSLATE_SCROLL_MOMENTUM(scrollEvent);
if (context->serviceRecord->isMultiTouchService) {
nxEvent.payload.data.scrollWheel.reserved1 |= kScrollTypeTouch;
}
if (context->serviceRecord->isContinuousScroll) {
nxEvent.payload.data.scrollWheel.reserved1 |= kScrollTypeContinuous;
}
nxEvent.payload.data.scrollWheel.reserved8[1] = translator->scrollCount;
nxEvent.payload.data.scrollWheel.reserved8[2] = (SInt32)context->serviceid;
return __IOHIDPointerEventTranslatorCreateHidEventForNXEvent(nxEvent, scrollEvent, kHIDUsage_AppleVendor_NXEvent_Translated);
}
void __IOHIDPointerEventTranslatorProcessScrollCount (IOHIDPointerEventTranslatorRef translator, EVENT_TRANSLATOR_CONTEXT *context __unused, IOHIDEventRef scrollEvent) {
uint32_t phaseAndMomentum = TRANSLATE_SCROLL_MOMENTUM(scrollEvent);
if (phaseAndMomentum == 0) {
return;
}
boolean_t checkSustain = false;
uint32_t scrollDirection;
double axis [3] = {
10 * IOHIDEventGetFloatValue (scrollEvent, kIOHIDEventFieldScrollX),
10 * IOHIDEventGetFloatValue (scrollEvent, kIOHIDEventFieldScrollY),
10 * IOHIDEventGetFloatValue (scrollEvent, kIOHIDEventFieldScrollZ)};
double axisPow2 [3] = {axis[0] * axis[0],axis[1] * axis[1], axis[2] * axis[2]};
double scrollMagnitudeSquared = axisPow2[0] + axisPow2[1] + axisPow2[2];
int m = 0;
(void)((axisPow2[m] < axisPow2[1]) && (m = 1));
(void)((axisPow2[m] < axisPow2[2]) && (m = 2));
scrollDirection = SIGN (axis[m]) * m;
if (scrollDirection && translator->scrollDicrection != scrollDirection) {
translator->scrollCount = 0;
translator->scrollDicrection = scrollDirection;
}
uint64_t ts = IOHIDEventGetTimeStamp(scrollEvent);
switch (phaseAndMomentum) {
case kScrollTypeOptionPhaseBegan: {
if (translator->scrollCount > 0) {
if ((translator->lastScrollTime + translator->scrollCountMaxTimeDeltaBetween) > ts) {
if (!translator->scrollIncrementThisPhase) {
translator->scrollCount++;
translator->scrollIncrementThisPhase = true;
}
translator->lastScrollSustainTime = ts;
}
else {
translator->scrollCount = 0;
}
}
break;
}
case kScrollTypeOptionPhaseChanged: {
if (translator->scrollCount == 0) {
if (scrollMagnitudeSquared >= translator->scrollCountMinDeltaToStartPow2) {
translator->scrollCount = 1;
translator->lastScrollSustainTime = ts;
}
}
else {
if (translator->scrollCount > 2) {
translator->scrollCount += sqrt(scrollMagnitudeSquared) / translator->scrollCountAccelerationFactor;
if (translator->scrollCount > translator->scrollCountMax) {
translator->scrollCount = translator->scrollCountMax;
}
}
checkSustain = true;
}
break;
}
case kScrollTypeOptionPhaseEnded: {
if (translator->scrollCount > 0) {
translator->lastScrollTime = ts;
translator->scrollIncrementThisPhase = false;
}
break;
}
case kScrollTypeOptionPhaseCanceled: {
translator->scrollIncrementThisPhase = false;
translator->scrollCount = 0;
break;
}
case kScrollTypeOptionPhaseMayBegin: {
if (translator->scrollCount > 0) {
if (((translator->lastScrollTime + translator->scrollCountMaxTimeDeltaBetween) > ts) &&
!translator->scrollIncrementThisPhase) {
translator->scrollCount++;
translator->scrollIncrementThisPhase = true;
translator->lastScrollSustainTime = ts;
}
else {
translator->scrollCount = 0;
translator->scrollIncrementThisPhase = false;
}
}
break;
}
case kScrollTypeMomentumStart: {
break;
}
case kScrollTypeMomentumContinue: {
checkSustain = true;
break;
}
case kScrollTypeMomentumEnd: {
if (translator->scrollCount > 0) {
translator->lastScrollTime = ts;
translator->scrollIncrementThisPhase = false;
}
break;
}
default:
HIDLogDebug ("*SCROLLCOUNT: Unknown phase 0x%x", phaseAndMomentum);
}
if (checkSustain) {
if (scrollMagnitudeSquared > translator->scrollCountMinDeltaToSustainPow2) {
translator->lastScrollSustainTime = ts;
}
else if (translator->lastScrollSustainTime + translator->scrollCountMaxTimeDeltaToSustain < ts) {
translator->scrollCount = 0;
}
}
}
IOHIDPointerEventTranslatorRef __IOHIDPointerEventTranslatorCreatePrivate(CFAllocatorRef allocator, CFAllocatorContext * context __unused)
{
IOHIDPointerEventTranslatorRef translator = NULL;
void * offset = NULL;
uint32_t size;
size = sizeof(__IOHIDPointerEventTranslator) - sizeof(CFRuntimeBase);
translator = (IOHIDPointerEventTranslatorRef)_CFRuntimeCreateInstance(allocator, IOHIDPointerEventTranslatorGetTypeID(), size, NULL);
if (!translator)
return NULL;
offset = translator;
bzero(offset + sizeof(CFRuntimeBase), size);
return translator;
}
static void __IOHIDPointerEventTranslatorFree( CFTypeRef object )
{
IOHIDPointerEventTranslatorRef translator = (IOHIDPointerEventTranslatorRef) object;
if (!translator) {
return;
}
CFRelease(translator->serviceRecord);
}
static CFStringRef __IOHIDPointerEventTranslatorCopyDebugDescription(CFTypeRef cf)
{
return CFStringCreateWithFormat(CFGetAllocator(cf), NULL, CFSTR("IOHIDPointerEventTranslatorRef"));
}
static void __ButtonsApplierFunction(const void *key __unused, const void *value, void *context)
{
uint32_t *globalButtons = (uint32_t*)context;
SERVICE_RECORD *serviceRecord = (SERVICE_RECORD*)CFDataGetBytePtr ((CFDataRef)value);
*globalButtons |= serviceRecord->buttons;
}
uint32_t __IOHIDPointerEventTranslatorGetGlobalButtonState (IOHIDPointerEventTranslatorRef translator) {
uint32_t globalButtons = 0;
CFDictionaryApplyFunction (translator->serviceRecord, __ButtonsApplierFunction, &globalButtons);
return globalButtons;
}
uint32_t __IOHIDPointerEventTranslatorGetUniqueEventNumber (IOHIDPointerEventTranslatorRef translator) {
while (++translator->eventNumber == 0);
return translator->eventNumber;
}
static uint64_t __IOHIDEventTranslatorGetServiceIDForObject (CFTypeRef service) {
uint64_t result = 0;
CFNumberRef registryId = NULL;
if (CFGetTypeID(service) == IOHIDServiceGetTypeID ()) {
registryId = IOHIDServiceGetRegistryID((IOHIDServiceRef) service);
} else if (CFGetTypeID(service) == IOHIDServiceClientGetTypeID ()) {
registryId = IOHIDServiceClientGetRegistryID((IOHIDServiceClientRef) service);
} else {
HIDLogDebug ("Unknown service object type");
}
if (registryId) {
CFNumberGetValue(registryId, kCFNumberSInt64Type, &result);
}
return result;
}
CFTypeRef __IOHIDEventTranslatorCopyServiceProperty (CFTypeRef service, CFStringRef key) {
CFTypeRef result = NULL;
if (CFGetTypeID(service) == IOHIDServiceGetTypeID ()) {
result = IOHIDServiceCopyProperty((IOHIDServiceRef) service, key);
} else if (CFGetTypeID(service) == IOHIDServiceClientGetTypeID ()) {
result = IOHIDServiceClientCopyProperty((IOHIDServiceClientRef) service, key);
} else {
HIDLogDebug ("Unknown service object type");
}
return result;
}
boolean_t __IOHIDEventTranslatorGetBooleanProperty (CFTypeRef service, CFStringRef key, boolean_t defaultValue) {
boolean_t result = defaultValue;
CFTypeRef value = __IOHIDEventTranslatorCopyServiceProperty (service, key);
if (value) {
if (CFGetTypeID(value) == CFBooleanGetTypeID()) {
result = CFBooleanGetValue(value);
}
CFRelease(value);
}
return result;
}
uint64_t __IOHIDEventTranslatorGetIntegerProperty (CFTypeRef service, CFStringRef key, uint64_t defaultValue) {
uint64_t result = defaultValue;
CFTypeRef value = __IOHIDEventTranslatorCopyServiceProperty (service, key);
if (value) {
if (CFGetTypeID(value) == CFNumberGetTypeID()) {
CFNumberGetValue(value, kCFNumberSInt64Type, &result);
}
CFRelease(value);
}
return result;
}
SERVICE_RECORD * __IOHIDEventTranslatorGetServiceRecordForServiceID (IOHIDPointerEventTranslatorRef translator, uint64_t serviceID) {
CFDataRef record = CFDictionaryGetValue(translator->serviceRecord, (const void*) serviceID);
if (record == NULL) {
return NULL;
}
return (SERVICE_RECORD*)CFDataGetBytePtr (record);
}
NXEventExt * __IOHIDPointerEventTranslatorGetNxMouseEvents (IOHIDEventRef event) {
if (IOHIDEventGetType(event) != kIOHIDEventTypeVendorDefined ||
IOHIDEventGetIntegerValue(event, kIOHIDEventFieldVendorDefinedUsage) != kHIDUsage_AppleVendor_NXEvent) {
return NULL;
}
NXEventExt *nxEvent = NULL;
CFIndex eventLength = 0;
IOHIDEventGetVendorDefinedData (event, (uint8_t**)&nxEvent, &eventLength);
if (nxEvent) {
if ((1 << nxEvent->payload.type) & kLegacyMouseEventsMask) {
return nxEvent;
}
}
return NULL;
}
void __IOHIDPointerEventTranslatorProcessLegacyEvent (IOHIDPointerEventTranslatorRef translator,EVENT_TRANSLATOR_CONTEXT *context, IOHIDEventRef legacyMouseEvent) {
if (legacyMouseEvent) {
NXEventExt * nxEvent = __IOHIDPointerEventTranslatorGetNxMouseEvents (legacyMouseEvent);
uint32_t eventMask = (1 << nxEvent->payload.type);
if (nxEvent) {
if (eventMask & (NX_LMOUSEDOWNMASK | NX_RMOUSEDOWNMASK)) {
uint64_t eventTime = ABS_TO_NS(IOHIDEventGetTimeStamp(legacyMouseEvent), translator->timebaseInfo) ;
if ((uint64_t)llabs((int64_t)eventTime - (int64_t)translator->lastClickTime) < translator->clickCountTimeTreshold) {
++translator->clickCount;
} else {
translator->clickCount = 1;
}
translator->lastClickTime = eventTime;
nxEvent->payload.data.mouse.click = translator->clickCount;
}
if (eventMask & (NX_LMOUSEUPMASK | NX_RMOUSEUPMASK)) {
nxEvent->payload.data.mouse.click = translator->clickCount;
}
switch (nxEvent->payload.type) {
case NX_RMOUSEDOWN:
translator->lastRightEventNum = __IOHIDPointerEventTranslatorGetUniqueEventNumber (translator);
nxEvent->payload.data.mouse.eventNum = translator->lastRightEventNum;
break;
case NX_RMOUSEUP:
nxEvent->payload.data.mouse.eventNum = translator->lastRightEventNum;
translator->lastRightEventNum = NULLEVENTNUM;
break;
case NX_LMOUSEDOWN:
translator->lastLeftEventNum = __IOHIDPointerEventTranslatorGetUniqueEventNumber (translator);
nxEvent->payload.data.mouse.eventNum = translator->lastLeftEventNum;
break;
case NX_LMOUSEUP:
nxEvent->payload.data.mouse.eventNum = translator->lastLeftEventNum;
translator->lastLeftEventNum = NULLEVENTNUM;
break;
}
if (eventMask & (NX_LMOUSEDRAGGEDMASK | NX_RMOUSEDRAGGEDMASK | NX_MOUSEMOVEDMASK)) {
if (translator->clickCount >= 1) {
translator->clickCountDeltaX += nxEvent->payload.data.mouseMove.dx;
translator->clickCountDeltaY += nxEvent->payload.data.mouseMove.dy;
if ((fabs(translator->clickCountDeltaX) > (float)translator->clickCountPixelTreshold / 2) ||
(fabs(translator->clickCountDeltaY) > (float)translator->clickCountPixelTreshold / 2)) {
translator->clickCount = 0;
}
} else {
translator->clickCountDeltaX = 0;
translator->clickCountDeltaY = 0;
}
}
}
}
}