Files
ckb-next/src/daemon/input_mac_mouse_legacy.c
T

1442 lines
54 KiB
C

#include "includes.h"
#ifdef OS_MAC_LEGACY
// Most of this code shamelessly stolen from:
// http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-606.1.7/IOHIDSystem/IOHIPointing.cpp
// http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-606.1.7/IOHIDSystem/IOFixed64.h + IOFixed64.cpp
// http://www.opensource.apple.com/source/IOKitUser/IOKitUser-1050.1.21/hidsystem.subproj/IOEventStatusAPI.c
// Why they thought that mouse acceleration belongs in a low-level input driver is beyond me...
// Original license:
/*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2010 Apple Computer, Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
// The code has been modified for ckb. This file is distributed under the same license.
// So if anyone actually wants this ungodly mess, those are your terms.
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
struct timespec last_setup_poll;
static int has_setup = 0;
// The following functions are modified from IOHIDCopyCFTypeParameter, designed to get the mouse info properly
// (it's stored in several different locations; a naïve search will fail)
static kern_return_t IOServiceCopyCF(io_registry_entry_t service, CFStringRef key, CFTypeRef * parameter){
kern_return_t kr = KERN_SUCCESS;
CFDictionaryRef paramDict;
CFTypeRef tempParameter = NULL;
if( (paramDict = IORegistryEntryCreateCFProperty( service, CFSTR(kIOHIDParametersKey), kCFAllocatorDefault, kNilOptions)))
{
if ( (tempParameter = CFDictionaryGetValue( paramDict, key)) )
CFRetain(tempParameter);
CFRelease(paramDict);
}
if ( !tempParameter )
tempParameter = IORegistryEntryCreateCFProperty( service, key, kCFAllocatorDefault, kNilOptions);
if ( !tempParameter )
kr = kIOReturnBadArgument;
*parameter = tempParameter;
return( kr );
}
static kern_return_t IOServiceCopyCFRecursive(io_registry_entry_t service, CFStringRef key, CFTypeRef* parameter){
// Iterate child registries
kern_return_t res;
io_iterator_t child_iter;
if((res = IORegistryEntryCreateIterator(service, kIOServicePlane, kIORegistryIterateRecursively, &child_iter)) != KERN_SUCCESS)
return kIOReturnBadArgument;
io_registry_entry_t child_service;
while((child_service = IOIteratorNext(child_iter)) != 0){
io_string_t path;
IORegistryEntryGetPath(child_service, kIOServicePlane, path);
res = IOServiceCopyCF(child_service, key, parameter);
IOObjectRelease(child_service);
// If the child has it, return success
if(res == KERN_SUCCESS)
break;
}
IOObjectRelease(child_iter);
// Return found or not found, depending on above outcome
return res;
}
// Get a handle for an IOService
static io_service_t GetService(mach_port_t master, const char* name){
kern_return_t res;
io_iterator_t iter;
if((res = IOServiceGetMatchingServices(master, IOServiceMatching(name), &iter)) != KERN_SUCCESS)
return 0;
io_service_t service = IOIteratorNext(iter);
IOObjectRelease(iter);
return service;
}
// v Usable function
static kern_return_t IOPointingCopyCFTypeParameter(CFStringRef key, CFTypeRef * parameter){
// Open master port if not done yet
static mach_port_t master = 0;
kern_return_t res;
if(!master && (res = IOMasterPort(bootstrap_port, &master)) != KERN_SUCCESS){
master = 0;
return kIOReturnError;
}
// Open IOHIPointing and IOHIDSystem if not done yet
static io_service_t hidsystem = 0, hipointing = 0;
if(!hidsystem)
hidsystem = GetService(master, kIOHIDSystemClass);
if(!hipointing)
hipointing = GetService(master, kIOHIPointingClass);
// Find the parameter
CFTypeRef tempParameter = NULL;
// Try IOHIPointing first
if(IOServiceCopyCF(hipointing, key, &tempParameter) == KERN_SUCCESS)
*parameter = tempParameter;
// Failing that, try IOHIDSystem
else if(IOServiceCopyCF(hidsystem, key, &tempParameter) == KERN_SUCCESS)
*parameter = tempParameter;
// Try recursive searches
else if(IOServiceCopyCFRecursive(hipointing, key, &tempParameter) == KERN_SUCCESS)
*parameter = tempParameter;
else if(IOServiceCopyCFRecursive(hidsystem, key, &tempParameter) == KERN_SUCCESS)
*parameter = tempParameter;
else {
// Not found
*parameter = 0;
return kIOReturnBadArgument;
}
return KERN_SUCCESS;
}
// IOFixed64 emulation (this was originally a C++ class; it has been converted to C)
typedef struct {
SInt64 value;
} IOFixed64;
#define sc2f(sc) ((sc) * 65536LL)
#define f2sc(f) ((f).value / 65536LL)
static SInt32 as32(IOFixed64 f) { SInt64 res = f2sc(f); if(res > INT_MAX) return INT_MAX; if(res < INT_MIN) return INT_MIN; return (SInt32)res; }
static bool f_gt_sc(IOFixed64 lhs, SInt64 rhs) { return lhs.value > sc2f(rhs); }
static bool f_lt(IOFixed64 lhs, IOFixed64 rhs) { return lhs.value < rhs.value; }
static bool f_gt(IOFixed64 lhs, IOFixed64 rhs) { return lhs.value > rhs.value; }
static IOFixed64 f_add(IOFixed64 lhs, IOFixed64 rhs) { IOFixed64 r = { lhs.value + rhs.value }; return r; }
static IOFixed64 f_sub(IOFixed64 lhs, IOFixed64 rhs) { IOFixed64 r = { lhs.value - rhs.value }; return r; }
static IOFixed64 f_div(IOFixed64 lhs, IOFixed64 rhs) { IOFixed64 r = { lhs.value * 65536LL / rhs.value }; return r; }
static IOFixed64 f_mul(IOFixed64 lhs, IOFixed64 rhs) { IOFixed64 r = { (lhs.value * rhs.value) / 65536LL }; return r; }
static IOFixed64 f_mul_sc(IOFixed64 lhs, SInt64 rhs) { IOFixed64 r = { lhs.value * rhs }; return r; }
static IOFixed64 exponent(const IOFixed64 original, const UInt8 power)
{
IOFixed64 result = {0};
if (power) {
int i;
result = original;
for (i = 1; i < power; i++) {
result = f_mul(result, original);
}
}
return result;
}
static UInt32 llsqrt(UInt64 x)
{
UInt64 rem = 0;
UInt64 root = 0;
int i;
for (i = 0; i < 32; i++) {
root <<= 1;
rem = ((rem << 2) + (x >> 62));
x <<= 2;
root++;
if (root <= rem) {
rem -= root;
root++;
} else {
root--;
}
}
return(UInt32)(root >> 1);
}
UInt16 lsqrt(UInt32 x)
{
UInt32 rem = 0;
UInt32 root = 0;
int i;
for (i = 0; i < 16; i++) {
root <<= 1;
rem = ((rem << 2) + (x >> 30));
x <<= 2;
root++;
if (root <= rem) {
rem -= root;
root++;
} else {
root--;
}
}
return(UInt16)(root >> 1);
}
static IOFixed64 IOQuarticFunction( const IOFixed64 x, const IOFixed64 *gains )
{
// Computes hyper-cubic polynomial with 0-intercept: f(x) = m1*x + m2^2 * x^2 + m3^3 * x^3 + m4^4 * x^4
IOFixed64 function_at_x = f_add(f_mul(x, gains[0]), exponent(f_mul(x, gains[1]), 2));
// -- Because of IOFixed overhead, don't bother computing higher expansions unless their gain coefficients are non-zero:
if( gains[2].value != 0LL )
function_at_x = f_add(function_at_x, exponent(f_mul(x, gains[2]), 3));
if( gains[3].value != 0LL )
function_at_x = f_add(function_at_x, exponent(f_mul(x, gains[3]), 4));
return function_at_x;
}
static IOFixed64 IOQuarticDerivative( const IOFixed64 x, const IOFixed64 *gains )
{
// For hyper-cubic polynomial with 0-intercept: f(x) = m1*x + m2^2 * x^2 + m3^3 * x^3 + m4^4 * x^4
// This function evaluates the derivative: f'(x) = m1 + 2 * x * m2^2 + 3 * x^2 * m3^3 + 4 * x^3 * m4^4
IOFixed64 derivative_at_x = f_add(gains[0], f_mul_sc(f_mul(x, exponent(gains[1], 2)), 2LL));
// -- Because of IOFixed overhead, don't bother computing higher expansions unless their gain coefficients are non-zero:
if( gains[2].value != 0LL )
derivative_at_x = f_add(derivative_at_x, f_mul_sc(f_mul(exponent(x, 2), exponent(gains[2], 3)), 3LL));
if( gains[3].value != 0LL )
derivative_at_x = f_add(derivative_at_x, f_mul_sc(f_mul(exponent(x, 3), exponent(gains[3], 4)), 4LL));
return derivative_at_x;
}
static inline IOFixed IOFixedMultiply(IOFixed a, IOFixed b)
{
return (IOFixed)((((SInt64) a) * ((SInt64) b)) >> 16);
}
static inline IOFixed IOFixedDivide(IOFixed a, IOFixed b)
{
return (IOFixed)((((SInt64) a) << 16) / ((SInt64) b));
}
static IOFixed64 OSObjectToIOFixed64(CFNumberRef object){
IOFixed64 result = {0};
if(object && CFGetTypeID(object) == CFNumberGetTypeID())
CFNumberGetValue(object, kCFNumberIntType, &result.value);
return result;
}
// Constants
#define MAX_DEVICE_THRESHOLD 0x7fffffff
#define FRAME_RATE (67 << 16)
#define SCREEN_RESOLUTION (96 << 16)
#define kIOFixedOne 0x10000ULL
#define SCROLL_DEFAULT_RESOLUTION (9 * kIOFixedOne)
#define SCROLL_CONSUME_RESOLUTION (100 * kIOFixedOne)
#define SCROLL_CONSUME_COUNT_MULTIPLIER 3
#define SCROLL_EVENT_THRESHOLD_MS_LL 150ULL
#define SCROLL_EVENT_THRESHOLD_MS (SCROLL_EVENT_THRESHOLD_MS_LL * kIOFixedOne)
#define SCROLL_CLEAR_THRESHOLD_MS_LL 500ULL
#define SCROLL_MULTIPLIER_RANGE 0x00018000
#define SCROLL_MULTIPLIER_A 0x00000002 /*IOFixedDivide(SCROLL_MULTIPLIER_RANGE,SCROLL_EVENT_THRESHOLD_MS*2)*/
#define SCROLL_MULTIPLIER_B 0x000003bb /*IOFixedDivide(SCROLL_MULTIPLIER_RANGE*3,(SCROLL_EVENT_THRESHOLD_MS^2)*2)*/
#define SCROLL_MULTIPLIER_C 0x00018041
#define SCROLL_WHEEL_TO_PIXEL_SCALE 0x000a0000/* IOFixedDivide(SCREEN_RESOLUTION, SCROLL_DEFAULT_RESOLUTION) */
#define SCROLL_PIXEL_TO_WHEEL_SCALE 0x0000199a/* IOFixedDivide(SCREEN_RESOLUTION, SCROLL_DEFAULT_RESOLUTION) */
#define SCROLL_TIME_DELTA_COUNT 8
#define CONVERT_SCROLL_FIXED_TO_FRACTION(fixed, fraction) \
{ \
if( fixed >= 0) \
fraction = fixed & 0xffff; \
else \
fraction = fixed | 0xffff0000; \
}
#define CONVERT_SCROLL_FIXED_TO_INTEGER(fixedAxis, integer) \
{ \
SInt32 tempInt = 0; \
if((fixedAxis < 0) && (fixedAxis & 0xffff)) \
tempInt = (fixedAxis >> 16) + 1; \
else \
tempInt = (fixedAxis >> 16); \
integer = tempInt; \
}
#define CONVERT_SCROLL_FIXED_TO_COARSE(fixedAxis, coarse) \
{ \
SInt32 tempCoarse = 0; \
CONVERT_SCROLL_FIXED_TO_INTEGER(fixedAxis, tempCoarse) \
if (!tempCoarse && (fixedAxis & 0xffff)) \
tempCoarse = (fixedAxis < 0) ? -1 : 1; \
coarse = tempCoarse; \
}
enum {
kAccelTypeGlobal = -1,
kAccelTypeY = 0, //delta axis 1
kAccelTypeX = 1, //delta axis 2
kAccelTypeZ = 2 //delta axis 3
};
// Structures
typedef struct
{
IOFixed64 deviceMickysDivider;
IOFixed64 cursorSpeedMultiplier;
IOFixed64 accelIndex;
IOFixed64 gain[4];
IOFixed64 tangent[2];
} IOHIPointing__PAParameters;
typedef struct
{
int firstTangent;
IOFixed64 m0; // m1 == m0
IOFixed64 b0; // no b1
IOFixed64 y0;
IOFixed64 y1;
IOFixed64 m_root;
IOFixed64 b_root;
} IOHIPointing__PASecondaryParameters;
struct CursorDeviceSegment {
SInt32 devUnits;
SInt32 slope;
SInt32 intercept;
};
typedef struct CursorDeviceSegment CursorDeviceSegment;
struct ScaleDataState
{
UInt8 deltaIndex;
IOFixed deltaTime[SCROLL_TIME_DELTA_COUNT];
IOFixed deltaAxis[SCROLL_TIME_DELTA_COUNT];
IOFixed fraction;
};
typedef struct ScaleDataState ScaleDataState;
struct ScaleConsumeState
{
UInt32 consumeCount;
IOFixed consumeAccum;
};
typedef struct ScaleConsumeState ScaleConsumeState;
struct ScrollAxisAccelInfo
{
struct timespec lastEventTime;
void * scaleSegments;
IOItemCount scaleSegCount;
ScaleDataState state;
ScaleConsumeState consumeState;
IOHIPointing__PAParameters primaryParametrics;
IOHIPointing__PASecondaryParameters secondaryParametrics;
SInt32 lastValue;
UInt32 consumeClearThreshold;
UInt32 consumeCountThreshold;
bool isHighResScroll;
bool isParametric;
};
typedef struct ScrollAxisAccelInfo ScrollAxisAccelInfo;
struct ScrollAccelInfo
{
ScrollAxisAccelInfo axis[3];
IOFixed rateMultiplier;
UInt32 zoom:1;
};
typedef struct ScrollAccelInfo ScrollAccelInfo;
// Static variables
static IOHIPointing__PAParameters* _paraAccelParams = 0;
static IOHIPointing__PASecondaryParameters* _paraAccelSecondaryParams = 0;
static IOFixed _scrollFixedDeltaAxis1 = 0, _scrollFixedDeltaAxis2 = 0, _scrollFixedDeltaAxis3 = 0;
static SInt32 _scrollPointDeltaAxis1 = 0, _scrollPointDeltaAxis2 = 0, _scrollPointDeltaAxis3 = 0;
static void* _scaleSegments = 0;
static IOItemCount _scaleSegCount = 0;
static IOFixed _acceleration = -1, _fractX = 0, _fractY = 0;
static ScrollAccelInfo _scrollWheelInfo;
static ScrollAccelInfo _scrollPointerInfo;
// Misc parameters
static IOFixed resolution()
{
CFNumberRef number;
IOPointingCopyCFTypeParameter(CFSTR(kIOHIDPointerResolutionKey), (CFTypeRef*)&number);
IOFixed result = 100 << 16;
if (number && CFGetTypeID(number) == CFNumberGetTypeID())
CFNumberGetValue(number, kCFNumberIntType, &result);
if(number) CFRelease(number);
return result;
}
static IOFixed scrollReportRate()
{
IOFixed result = FRAME_RATE;
CFNumberRef number;
IOPointingCopyCFTypeParameter(CFSTR(kIOHIDScrollReportRateKey), (CFTypeRef*)&number);
if (number && CFGetTypeID(number) == CFNumberGetTypeID())
CFNumberGetValue(number, kCFNumberIntType, &result);
if(number) CFRelease(number);
if (result == 0)
result = FRAME_RATE;
return result;
}
static IOFixed scrollResolutionForType(SInt32 type)
{
IOFixed res = 0;
CFNumberRef number = NULL;
CFStringRef key = NULL;
switch ( type ) {
case kAccelTypeY:
key = CFSTR(kIOHIDScrollResolutionYKey);
break;
case kAccelTypeX:
key = CFSTR(kIOHIDScrollResolutionXKey);
break;
case kAccelTypeZ:
key = CFSTR(kIOHIDScrollResolutionZKey);
break;
default:
key = CFSTR(kIOHIDScrollResolutionKey);
break;
}
IOPointingCopyCFTypeParameter(key, (CFTypeRef*)&number);
if(number && CFGetTypeID(number) == CFNumberGetTypeID())
CFNumberGetValue(number, kCFNumberIntType, &res);
else {
if(number) CFRelease(number);
IOPointingCopyCFTypeParameter(CFSTR(kIOHIDScrollResolutionKey), (CFTypeRef*)&number);
if(number && CFGetTypeID(number) == CFNumberGetTypeID())
CFNumberGetValue(number, kCFNumberIntType, &res);
}
if(number) CFRelease(number);
return res;
}
// Parametric acceleration
static bool
PACurvesFillParamsFromDict(CFDictionaryRef parameters,
const IOFixed64 devScale,
const IOFixed64 crsrScale,
IOHIPointing__PAParameters *outParams)
{
require(parameters, exit_early);
require(CFGetTypeID(parameters) == CFDictionaryGetTypeID(), exit_early);
outParams->deviceMickysDivider = devScale;
outParams->cursorSpeedMultiplier = crsrScale;
outParams->accelIndex = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelIndexKey)));
outParams->gain[0] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelGainLinearKey)));
outParams->gain[1] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelGainParabolicKey)));
outParams->gain[2] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelGainCubicKey)));
outParams->gain[3] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelGainQuarticKey)));
outParams->tangent[0] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelTangentSpeedLinearKey)));
outParams->tangent[1] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelTangentSpeedParabolicRootKey)));
return ((outParams->gain[0].value != 0LL) ||
(outParams->gain[1].value != 0LL) ||
(outParams->gain[2].value != 0LL) ||
(outParams->gain[3].value != 0LL));
exit_early:
return false;
}
static bool
PACurvesSetupAccelParams (CFArrayRef parametricCurves,
IOFixed64 desired,
IOFixed64 devScale,
IOFixed64 crsrScale,
IOHIPointing__PAParameters *primaryParams,
IOHIPointing__PASecondaryParameters *secondaryParams)
{
bool success = false;
CFDictionaryRef dict = NULL;
IOHIPointing__PAParameters high_curve_params;
IOHIPointing__PAParameters low_curve_params;
require(parametricCurves, exit_early);
require(f_gt_sc(crsrScale, 0LL), exit_early);
require(f_gt_sc(devScale, 0LL), exit_early);
require(f_gt_sc(desired, 0LL), exit_early);
CFIndex itrCount = CFArrayGetCount(parametricCurves);
CFIndex itr = 0;
while (!success) {
itr = 0;
dict = (CFDictionaryRef)CFArrayGetValueAtIndex(parametricCurves, itr++);
require(PACurvesFillParamsFromDict(dict, devScale, crsrScale, &low_curve_params),
exit_early);
while (!success && (NULL != dict)) {
if (!PACurvesFillParamsFromDict(dict, devScale, crsrScale, &high_curve_params)) {
break;
}
if (desired.value <= high_curve_params.accelIndex.value) {
success = true;
}
else {
low_curve_params = high_curve_params;
}
if(itr == itrCount)
dict = NULL;
else
dict = (CFDictionaryRef)CFArrayGetValueAtIndex(parametricCurves, itr++);
}
require(success, exit_early);
};
if ( high_curve_params.accelIndex.value > low_curve_params.accelIndex.value ) {
IOFixed64 ratio = f_div(f_sub(desired, low_curve_params.accelIndex), f_sub(high_curve_params.accelIndex, low_curve_params.accelIndex));
int index;
primaryParams->deviceMickysDivider = high_curve_params.deviceMickysDivider;
primaryParams->cursorSpeedMultiplier = high_curve_params.cursorSpeedMultiplier;
primaryParams->accelIndex = desired;
for (index = 0; index < 4; index++) {
primaryParams->gain[index] = f_add(low_curve_params.gain[index], f_mul(f_sub(high_curve_params.gain[index], low_curve_params.gain[index]), ratio));
if (primaryParams->gain[index].value < 0LL)
primaryParams->gain[index].value = 0;
}
for (index = 0; index < 2; index++) {
primaryParams->tangent[index] = f_add(low_curve_params.tangent[index], f_mul(f_sub(high_curve_params.tangent[index], low_curve_params.tangent[index]), ratio));
if (primaryParams->tangent[index].value < 0LL)
primaryParams->tangent[index].value = 0;
}
}
else {
*primaryParams = high_curve_params;
}
success = ((primaryParams->gain[0].value != 0LL) ||
(primaryParams->gain[1].value != 0LL) ||
(primaryParams->gain[2].value != 0LL) ||
(primaryParams->gain[3].value != 0LL));
// calculate secondary values
bzero(secondaryParams, sizeof(*secondaryParams));
if ((primaryParams->tangent[1].value > 0LL) && (primaryParams->tangent[1].value < primaryParams->tangent[0].value))
secondaryParams->firstTangent = 1;
if (secondaryParams->firstTangent == 0) {
secondaryParams->y0 = IOQuarticFunction(primaryParams->tangent[0], primaryParams->gain);
secondaryParams->m0 = IOQuarticDerivative(primaryParams->tangent[0], primaryParams->gain);
secondaryParams->b0 = f_sub(secondaryParams->y0, f_mul(secondaryParams->m0, primaryParams->tangent[0]));
secondaryParams->y1 = f_add(f_mul(secondaryParams->m0, primaryParams->tangent[1]), secondaryParams->b0);
}
else {
secondaryParams->y1 = IOQuarticFunction( primaryParams->tangent[1], primaryParams->gain );
secondaryParams->m0 = IOQuarticDerivative( primaryParams->tangent[1], primaryParams->gain );
}
secondaryParams->m_root = f_mul_sc(f_mul(secondaryParams->m0, secondaryParams->y1), 2LL);
secondaryParams->b_root = f_sub(exponent(secondaryParams->y1, 2), f_mul(secondaryParams->m_root, primaryParams->tangent[1]));
exit_early:
return success;
}
static IOFixed64
PACurvesGetAccelerationMultiplier(const IOFixed64 device_speed_mickeys,
const IOHIPointing__PAParameters *params,
const IOHIPointing__PASecondaryParameters *secondaryParams)
{
IOFixed64 result = {0};
if ((device_speed_mickeys.value > result.value) && (params->deviceMickysDivider.value != result.value)) {
IOFixed64 standardized_speed = f_div(device_speed_mickeys, params->deviceMickysDivider);
IOFixed64 accelerated_speed;
if ((params->tangent[secondaryParams->firstTangent].value != 0LL) && (standardized_speed.value <= params->tangent[secondaryParams->firstTangent].value)) {
accelerated_speed = IOQuarticFunction(standardized_speed, params->gain);
}
else {
if ((secondaryParams->firstTangent == 0) && (params->tangent[1].value != 0LL) && (standardized_speed.value <= params->tangent[1].value)) {
accelerated_speed = f_add(f_mul(secondaryParams->m0, standardized_speed), secondaryParams->b0);
}
else {
accelerated_speed.value = sc2f(llsqrt(f2sc(f_add(f_mul(secondaryParams->m_root, standardized_speed), secondaryParams->b_root))));
}
}
IOFixed64 accelerated_pixels = f_mul(accelerated_speed, params->cursorSpeedMultiplier);
result = f_div(accelerated_pixels, device_speed_mickeys);
}
else {
result.value = 1;
}
return result;
}
// Classic acceleration
static CFDataRef copyAccelerationTable()
{
static const UInt8 accl[] = {
0x00, 0x00, 0x80, 0x00,
0x40, 0x32, 0x30, 0x30, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x09, 0x00, 0x00, 0x71, 0x3B, 0x00, 0x00,
0x60, 0x00, 0x00, 0x04, 0x4E, 0xC5, 0x00, 0x10,
0x80, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x5F,
0x00, 0x00, 0x00, 0x16, 0xEC, 0x4F, 0x00, 0x8B,
0x00, 0x00, 0x00, 0x1D, 0x3B, 0x14, 0x00, 0x94,
0x80, 0x00, 0x00, 0x22, 0x76, 0x27, 0x00, 0x96,
0x00, 0x00, 0x00, 0x24, 0x62, 0x76, 0x00, 0x96,
0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x96,
0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x96,
0x00, 0x00
};
CFDataRef data;
IOPointingCopyCFTypeParameter(CFSTR(kIOHIDPointerAccelerationTableKey), (CFTypeRef*)&data);
if(data && CFGetTypeID(data) != CFDataGetTypeID()){
CFRelease(data);
data = 0;
}
if (!data)
data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, accl, sizeof(accl), kCFAllocatorNull);
return( data );
}
static CFDataRef copyScrollAccelerationTableForType(SInt32 type)
{
CFDataRef data = NULL;
CFStringRef key = NULL;
switch ( type ) {
case kAccelTypeY:
key = CFSTR(kIOHIDScrollAccelerationTableYKey);
break;
case kAccelTypeX:
key = CFSTR(kIOHIDScrollAccelerationTableXKey);
break;
case kAccelTypeZ:
key = CFSTR(kIOHIDScrollAccelerationTableZKey);
break;
}
if ( key )
IOPointingCopyCFTypeParameter(key, (CFTypeRef*)&data);
if ( !data || CFGetTypeID(data) != CFDataGetTypeID()) {
if(data) CFRelease(data);
IOPointingCopyCFTypeParameter(CFSTR(kIOHIDScrollAccelerationTableKey), (CFTypeRef*)&data);
}
if ( !data || CFGetTypeID(data) != CFDataGetTypeID()) {
if(data) CFRelease(data);
data = copyAccelerationTable();
}
return( data );
}
/*
Routine: Interpolate
This routine interpolates to find a point on the line [x1,y1] [x2,y2] which
is intersected by the line [x3,y3] [x3,y"]. The resulting y' is calculated
by interpolating between y3 and y", towards the higher acceleration curve.
*/
static SInt32 Interpolate( SInt32 x1, SInt32 y1,
SInt32 x2, SInt32 y2,
SInt32 x3, SInt32 y3,
SInt32 scale, Boolean lower )
{
SInt32 slope;
SInt32 intercept;
SInt32 resultY;
slope = (x2 == x1) ? 0 : IOFixedDivide( y2 - y1, x2 - x1 );
intercept = y1 - IOFixedMultiply( slope, x1 );
resultY = intercept + IOFixedMultiply( slope, x3 );
if( lower)
resultY = y3 - IOFixedMultiply( scale, y3 - resultY );
else
resultY = resultY + IOFixedMultiply( scale, y3 - resultY );
return( resultY );
}
static bool SetupAcceleration (CFDataRef data, IOFixed desired, IOFixed devScale, IOFixed crsrScale, void ** scaleSegments, IOItemCount * scaleSegCount) {
const UInt16 * lowTable = 0;
const UInt16 * highTable;
SInt32 x1, y1, x2, y2, x3, y3;
SInt32 prevX1, prevY1;
SInt32 upperX, upperY;
SInt32 lowerX, lowerY;
SInt32 lowAccl = 0, lowPoints = 0;
SInt32 highAccl, highPoints;
SInt32 scale;
UInt32 count;
Boolean lower;
SInt32 scaledX1, scaledY1;
SInt32 scaledX2, scaledY2;
CursorDeviceSegment * segments;
CursorDeviceSegment * segment;
SInt32 segCount;
if( !data || !devScale || !crsrScale)
return false;
if( desired < (IOFixed) 0) {
// disabling mouse scaling
if(*scaleSegments && *scaleSegCount)
free( *scaleSegments);
*scaleSegments = NULL;
*scaleSegCount = 0;
return false;
}
highTable = (const UInt16 *)CFDataGetBytePtr(data);
scaledX1 = scaledY1 = 0;
scale = OSReadBigInt32((volatile void *)highTable, 0);
highTable += 4;
// normalize table's default (scale) to 0.5
if( desired > 0x8000) {
desired = IOFixedMultiply( desired - 0x8000,
0x10000 - scale );
desired <<= 1;
desired += scale;
} else {
desired = IOFixedMultiply( desired, scale );
desired <<= 1;
}
count = OSReadBigInt16((volatile void *)(highTable++), 0);
scale = (1 << 16);
// find curves bracketing the desired value
do {
highAccl = OSReadBigInt32((volatile void *)highTable, 0);
highTable += 2;
highPoints = OSReadBigInt16((volatile void *)(highTable++), 0);
if( desired <= highAccl)
break;
if( 0 == --count) {
// this much over the highest table
scale = (highAccl) ? IOFixedDivide( desired, highAccl ) : 0;
lowTable = 0;
break;
}
lowTable = highTable;
lowAccl = highAccl;
lowPoints = highPoints;
highTable += lowPoints * 4;
} while( true );
// scale between the two
if( lowTable) {
scale = (highAccl == lowAccl) ? 0 :
IOFixedDivide((desired - lowAccl), (highAccl - lowAccl));
}
// or take all the high one
else {
lowTable = highTable;
lowAccl = highAccl;
lowPoints = 0;
}
if( lowPoints > highPoints)
segCount = lowPoints;
else
segCount = highPoints;
segCount *= 2;
segments = calloc( sizeof(CursorDeviceSegment), segCount );
assert( segments );
segment = segments;
x1 = prevX1 = y1 = prevY1 = 0;
lowerX = OSReadBigInt32((volatile void *)lowTable, 0);
lowTable += 2;
lowerY = OSReadBigInt32((volatile void *)lowTable, 0);
lowTable += 2;
upperX = OSReadBigInt32((volatile void *)highTable, 0);
highTable += 2;
upperY = OSReadBigInt32((volatile void *)highTable, 0);
highTable += 2;
do {
// consume next point from first X
lower = (lowPoints && (!highPoints || (lowerX <= upperX)));
if( lower) {
/* highline */
x2 = upperX;
y2 = upperY;
x3 = lowerX;
y3 = lowerY;
if( lowPoints && (--lowPoints)) {
lowerX = OSReadBigInt32((volatile void *)lowTable, 0);
lowTable += 2;
lowerY = OSReadBigInt32((volatile void *)lowTable, 0);
lowTable += 2;
}
} else {
/* lowline */
x2 = lowerX;
y2 = lowerY;
x3 = upperX;
y3 = upperY;
if( highPoints && (--highPoints)) {
upperX = OSReadBigInt32((volatile void *)highTable, 0);
highTable += 2;
upperY = OSReadBigInt32((volatile void *)highTable, 0);
highTable += 2;
}
}
{
// convert to line segment
assert( segment < (segments + segCount) );
scaledX2 = IOFixedMultiply( devScale, /* newX */ x3 );
scaledY2 = IOFixedMultiply( crsrScale,
/* newY */ Interpolate( x1, y1, x2, y2, x3, y3,
scale, lower ) );
if( lowPoints || highPoints)
segment->devUnits = scaledX2;
else
segment->devUnits = MAX_DEVICE_THRESHOLD;
segment->slope = ((scaledX2 == scaledX1)) ? 0 :
IOFixedDivide((scaledY2 - scaledY1), (scaledX2 - scaledX1));
segment->intercept = scaledY2
- IOFixedMultiply( segment->slope, scaledX2 );
scaledX1 = scaledX2;
scaledY1 = scaledY2;
segment++;
}
// continue on from last point
if( lowPoints && highPoints) {
if( lowerX > upperX) {
prevX1 = x1;
prevY1 = y1;
} else {
/* swaplines */
prevX1 = x1;
prevY1 = y1;
x1 = x3;
y1 = y3;
}
} else {
x2 = x1;
y2 = y1;
x1 = prevX1;
y1 = prevY1;
prevX1 = x2;
prevY1 = y2;
}
} while( lowPoints || highPoints );
if( *scaleSegCount && *scaleSegments)
free( *scaleSegments);
*scaleSegCount = segCount;
*scaleSegments = (void *) segments;
return true;
}
// Putting it together
static void setupForAcceleration(IOFixed desired){
CFArrayRef parametricAccelerationCurves;
IOPointingCopyCFTypeParameter(CFSTR(kHIDTrackingAccelParametricCurvesKey), (CFTypeRef*)&parametricAccelerationCurves);
IOFixed devScale = IOFixedDivide( resolution(), FRAME_RATE );
IOFixed crsrScale = IOFixedDivide( SCREEN_RESOLUTION, FRAME_RATE );
bool useParametric = false;
if (!parametricAccelerationCurves || CFGetTypeID(parametricAccelerationCurves) != CFArrayGetTypeID()) {
if(parametricAccelerationCurves) CFRelease(parametricAccelerationCurves);
IOPointingCopyCFTypeParameter(CFSTR(kHIDAccelParametricCurvesKey), (CFTypeRef*)&parametricAccelerationCurves);
}
// Try to set up the parametric acceleration data
if (parametricAccelerationCurves && CFGetTypeID(parametricAccelerationCurves) == CFArrayGetTypeID()) {
if ( !_paraAccelParams )
{
_paraAccelParams = (IOHIPointing__PAParameters*)malloc(sizeof(IOHIPointing__PAParameters));
}
if ( !_paraAccelSecondaryParams )
{
_paraAccelSecondaryParams = (IOHIPointing__PASecondaryParameters*)malloc(sizeof(IOHIPointing__PASecondaryParameters));
}
if (_paraAccelParams && _paraAccelSecondaryParams) {
IOFixed64 desired64 = {desired};
IOFixed64 devScale64 = {devScale};
IOFixed64 crsrScale64 = {crsrScale};
useParametric = PACurvesSetupAccelParams(parametricAccelerationCurves,
desired64,
devScale64,
crsrScale64,
_paraAccelParams,
_paraAccelSecondaryParams);
}
}
if(parametricAccelerationCurves) CFRelease(parametricAccelerationCurves);
// If that fails, fall back to classic acceleration
if (!useParametric) {
CFDataRef table = copyAccelerationTable();
if (_paraAccelParams)
free(_paraAccelParams);
if (_paraAccelSecondaryParams)
free(_paraAccelSecondaryParams);
_paraAccelParams = NULL;
_paraAccelSecondaryParams = NULL;
if (SetupAcceleration (table, desired, devScale, crsrScale, &_scaleSegments, &_scaleSegCount))
{
_acceleration = desired;
_fractX = _fractY = 0;
}
if(table) CFRelease(table);
}
}
static void ScaleAxes (void * scaleSegments, int * axis1p, IOFixed *axis1Fractp, int * axis2p, IOFixed *axis2Fractp)
{
SInt32 dx, dy;
SInt32 mag;
IOFixed scale;
CursorDeviceSegment * segment;
if( !scaleSegments)
return;
dx = (*axis1p) << 16;
dy = (*axis2p) << 16;
// mag is (x^2 + y^2)^0.5 and converted to fixed point
mag = (lsqrt(*axis1p * *axis1p + *axis2p * *axis2p)) << 16;
if (mag == 0)
return;
// scale
for(
segment = (CursorDeviceSegment *) scaleSegments;
mag > segment->devUnits;
segment++) {}
scale = IOFixedDivide(
segment->intercept + IOFixedMultiply( mag, segment->slope ),
mag );
dx = IOFixedMultiply( dx, scale );
dy = IOFixedMultiply( dy, scale );
// add fract parts
dx += *axis1Fractp;
dy += *axis2Fractp;
*axis1p = dx / 65536;
*axis2p = dy / 65536;
// get fractional part with sign extend
if( dx >= 0)
*axis1Fractp = dx & 0xffff;
else
*axis1Fractp = dx | 0xffff0000;
if( dy >= 0)
*axis2Fractp = dy & 0xffff;
else
*axis2Fractp = dy | 0xffff0000;
}
static void scalePointer(int* dxp, int* dyp)
// Description: Perform pointer acceleration computations here.
// Given the resolution, dx, dy, and time, compute the velocity
// of the pointer over a Manhatten distance in inches/second.
// Using this velocity, do a lookup in the pointerScaling table
// to select a scaling factor. Scale dx and dy up as appropriate.
// Preconditions:
// * _deviceLock should be held on entry
{
if (_paraAccelParams && _paraAccelSecondaryParams) {
IOFixed64 deltaX = {sc2f(*dxp)};
IOFixed64 deltaY = {sc2f(*dyp)};
IOFixed64 fractX = {_fractX};
IOFixed64 fractY = {_fractY};
IOFixed64 mag = {sc2f(llsqrt(f2sc(f_add(f_mul(deltaX, deltaX), f_mul(deltaY, deltaY)))))};
IOFixed64 mult = PACurvesGetAccelerationMultiplier(mag, _paraAccelParams, _paraAccelSecondaryParams);
deltaX = f_mul(deltaX, mult);
deltaY = f_mul(deltaY, mult);
deltaX = f_add(deltaX, fractX);
deltaY = f_add(deltaY, fractY);
*dxp = as32(deltaX);
*dyp = as32(deltaY);
_fractX = deltaX.value;
_fractY = deltaY.value;
// sign extend fractional part
if( deltaX.value < 0LL )
_fractX |= 0xffff0000;
else
_fractX &= 0x0000ffff;
if( deltaY.value < 0LL)
_fractY |= 0xffff0000;
else
_fractY &= 0x0000ffff;
}
else {
ScaleAxes(_scaleSegments, dxp, &_fractX, dyp, &_fractY);
}
}
static void setupScrollForAcceleration( IOFixed desired ){
IOFixed devScale = 0;
IOFixed scrScale = 0;
IOFixed reportRate = scrollReportRate();
CFDataRef accelTable = NULL;
UInt32 type = 0;
_scrollWheelInfo.rateMultiplier = IOFixedDivide(reportRate, FRAME_RATE);
_scrollPointerInfo.rateMultiplier = IOFixedDivide(reportRate, FRAME_RATE);
if (desired < 0) {
}
else {
CFArrayRef parametricAccelerationCurves;
IOPointingCopyCFTypeParameter(CFSTR(kHIDScrollAccelParametricCurvesKey), (CFTypeRef*)&parametricAccelerationCurves);
for ( type=kAccelTypeY; type<=kAccelTypeZ; type++) {
IOFixed res = scrollResolutionForType(type);
// Zero scroll resolution says you don't want acceleration.
if ( res ) {
_scrollWheelInfo.axis[type].isHighResScroll = res > (SCROLL_DEFAULT_RESOLUTION * 2);
_scrollPointerInfo.axis[type].isHighResScroll = _scrollWheelInfo.axis[type].isHighResScroll;
_scrollWheelInfo.axis[type].consumeClearThreshold = (IOFixedDivide(res, SCROLL_CONSUME_RESOLUTION) >> 16) * 2;
_scrollPointerInfo.axis[type].consumeClearThreshold = _scrollWheelInfo.axis[type].consumeClearThreshold;
_scrollWheelInfo.axis[type].consumeCountThreshold = _scrollWheelInfo.axis[type].consumeClearThreshold * SCROLL_CONSUME_COUNT_MULTIPLIER;
_scrollPointerInfo.axis[type].consumeCountThreshold = _scrollPointerInfo.axis[type].consumeClearThreshold * SCROLL_CONSUME_COUNT_MULTIPLIER;
bzero(&(_scrollWheelInfo.axis[type].state), sizeof(ScaleDataState));
bzero(&(_scrollWheelInfo.axis[type].consumeState), sizeof(ScaleConsumeState));
bzero(&(_scrollPointerInfo.axis[type].state), sizeof(ScaleDataState));
bzero(&(_scrollPointerInfo.axis[type].consumeState), sizeof(ScaleConsumeState));
clock_gettime(CLOCK_MONOTONIC, &(_scrollWheelInfo.axis[type].lastEventTime));
_scrollPointerInfo.axis[type].lastEventTime = _scrollWheelInfo.axis[type].lastEventTime;
if (parametricAccelerationCurves && CFGetTypeID(parametricAccelerationCurves) == CFArrayGetTypeID() && reportRate) {
IOFixed64 desired64 = { desired };
IOFixed64 devScale64 = { res };
IOFixed64 scrScale64 = { SCREEN_RESOLUTION };
devScale64 = f_div(devScale64, *(IOFixed64[]){{ reportRate }});
scrScale64 = f_div(scrScale64, *(IOFixed64[]){{ FRAME_RATE }});
_scrollWheelInfo.axis[type].isParametric =
PACurvesSetupAccelParams(parametricAccelerationCurves,
desired64,
devScale64,
scrScale64,
&_scrollWheelInfo.axis[type].primaryParametrics,
&_scrollWheelInfo.axis[type].secondaryParametrics);
}
if (!_scrollWheelInfo.axis[type].isParametric) {
accelTable = copyScrollAccelerationTableForType(type);
// Setup pixel scroll wheel acceleration table
devScale = IOFixedDivide( res, reportRate );
scrScale = IOFixedDivide( SCREEN_RESOLUTION, FRAME_RATE );
SetupAcceleration (accelTable, desired, devScale, scrScale, &(_scrollWheelInfo.axis[type].scaleSegments), &(_scrollWheelInfo.axis[type].scaleSegCount));
// Grab the pointer resolution
res = resolution();
reportRate = FRAME_RATE;
// Setup pixel pointer drag/scroll acceleration table
devScale = IOFixedDivide( res, reportRate );
scrScale = IOFixedDivide( SCREEN_RESOLUTION, FRAME_RATE );
SetupAcceleration (accelTable, desired, devScale, scrScale, &(_scrollPointerInfo.axis[type].scaleSegments), &(_scrollPointerInfo.axis[type].scaleSegCount));
if (accelTable)
CFRelease(accelTable);
}
}
}
if(parametricAccelerationCurves) CFRelease(parametricAccelerationCurves);
}
}
static void AccelerateScrollAxis( IOFixed * axisp,
ScrollAxisAccelInfo * scaleInfo,
struct timespec* timeStamp,
IOFixed rateMultiplier,
bool clear)
{
IOFixed absAxis = 0;
int avgIndex = 0;
IOFixed avgCount = 0;
IOFixed avgAxis = 0;
IOFixed timeDeltaMS = 0;
IOFixed avgTimeDeltaMS = 0;
UInt64 currentTimeNSLL = 0;
UInt64 lastEventTimeNSLL = 0;
UInt64 timeDeltaMSLL = 0;
if ( ! (scaleInfo && ( scaleInfo->scaleSegments || scaleInfo->isParametric) ) )
return;
absAxis = abs(*axisp);
if( absAxis == 0 )
return;
currentTimeNSLL = timeStamp->tv_nsec + timeStamp->tv_sec * 1000000000;
lastEventTimeNSLL = scaleInfo->lastEventTime.tv_nsec + scaleInfo->lastEventTime.tv_sec * 1000000000;
scaleInfo->lastEventTime = *timeStamp;
timeDeltaMSLL = (currentTimeNSLL - lastEventTimeNSLL) / 1000000;
// RY: To compensate for non continual motion, we have added a second
// threshold. This whill allow a user with a standard scroll wheel
// to continue with acceleration when lifting the finger within a
// predetermined time. We should also clear out the last time deltas
// if the direction has changed.
if ((timeDeltaMSLL >= SCROLL_CLEAR_THRESHOLD_MS_LL) || clear) {
bzero(&(scaleInfo->state), sizeof(ScaleDataState));
timeDeltaMSLL = SCROLL_CLEAR_THRESHOLD_MS_LL;
}
timeDeltaMS = ((UInt32) timeDeltaMSLL) * kIOFixedOne;
scaleInfo->state.deltaTime[scaleInfo->state.deltaIndex] = timeDeltaMS;
scaleInfo->state.deltaAxis[scaleInfo->state.deltaIndex] = absAxis;
// RY: To eliminate jerkyness associated with the scroll acceleration,
// we scroll based on the average of the last n events. This has the
// effect of make acceleration smoother with accel and decel.
for (int index=0; index < SCROLL_TIME_DELTA_COUNT; index++)
{
avgIndex = (scaleInfo->state.deltaIndex + SCROLL_TIME_DELTA_COUNT - index) % SCROLL_TIME_DELTA_COUNT;
avgAxis += scaleInfo->state.deltaAxis[avgIndex];
avgCount ++;
if ((scaleInfo->state.deltaTime[avgIndex] <= 0) ||
(scaleInfo->state.deltaTime[avgIndex] >= SCROLL_EVENT_THRESHOLD_MS)) {
// the previous event was too long before this one. stop looking.
avgTimeDeltaMS += SCROLL_EVENT_THRESHOLD_MS;
break;
}
avgTimeDeltaMS += scaleInfo->state.deltaTime[avgIndex];
if (avgTimeDeltaMS >= (SCROLL_CLEAR_THRESHOLD_MS_LL * kIOFixedOne)) {
// the previous event was too long ago. stop looking.
break;
}
}
// Bump the next index
scaleInfo->state.deltaIndex = (scaleInfo->state.deltaIndex + 1) % SCROLL_TIME_DELTA_COUNT;
avgAxis = (avgCount) ? (avgAxis / avgCount) : 0;
avgTimeDeltaMS = (avgCount) ? (avgTimeDeltaMS / avgCount) : 0;
avgTimeDeltaMS = IOFixedMultiply(avgTimeDeltaMS, rateMultiplier);
if (avgTimeDeltaMS > SCROLL_EVENT_THRESHOLD_MS) {
avgTimeDeltaMS = SCROLL_EVENT_THRESHOLD_MS;
}
else if (avgTimeDeltaMS < kIOFixedOne) {
// anything less than 1 ms is not resonable
avgTimeDeltaMS = kIOFixedOne;
}
// RY: Since we want scroll acceleration to work with the
// time delta and the accel curves, we have come up with
// this approach:
//
// scrollMultiplier = (SCROLL_MULTIPLIER_A * (avgTimeDeltaMS^2)) +
// (SCROLL_MULTIPLIER_B * avgTimeDeltaMS) +
// SCROLL_MULTIPLIER_C
//
// scrollMultiplier *= avgDeviceDelta
//
// The boost curve follows a quadratic/parabolic curve which
// results in a smoother boost.
//
// The resulting multipler is applied to the average axis
// magnitude and then compared against the accleration curve.
//
// The value acquired from the graph will then be multiplied
// to the current axis delta.
IOFixed64 scrollMultiplier;
IOFixed64 timedDelta = { avgTimeDeltaMS };
IOFixed64 axisValue = { *axisp };
IOFixed64 minimumMultiplier = { kIOFixedOne >> 4 };
scrollMultiplier = f_mul(f_mul(*(IOFixed64[]){ { SCROLL_MULTIPLIER_A } }, timedDelta), timedDelta);
scrollMultiplier = f_sub(scrollMultiplier, f_mul(*(IOFixed64[]){ { SCROLL_MULTIPLIER_B } }, timedDelta));
scrollMultiplier = f_add(scrollMultiplier, *(IOFixed64[]){ { SCROLL_MULTIPLIER_C } });
scrollMultiplier = f_mul(scrollMultiplier, *(IOFixed64[]){ { rateMultiplier } });
scrollMultiplier = f_mul(scrollMultiplier, *(IOFixed64[]){ { avgAxis } });
if (f_lt(scrollMultiplier, minimumMultiplier)) {
scrollMultiplier = minimumMultiplier;
}
if (scaleInfo->isParametric) {
scrollMultiplier = PACurvesGetAccelerationMultiplier(scrollMultiplier, &scaleInfo->primaryParametrics, &scaleInfo->secondaryParametrics);
}
else {
CursorDeviceSegment *segment;
// scale
for(segment = (CursorDeviceSegment *) scaleInfo->scaleSegments;
f_gt(scrollMultiplier, *(IOFixed64[]){ { segment->devUnits } });
segment++)
{}
if (avgCount > 2) {
// Continuous scrolling in one direction indicates a desire to go faster.
scrollMultiplier = f_mul(scrollMultiplier, *(IOFixed64[]){ { sc2f((SInt64)lsqrt(avgCount * 16)) } });
scrollMultiplier = f_div(scrollMultiplier, *(IOFixed64[]){ { sc2f(4) } });
}
scrollMultiplier = f_add(*(IOFixed64[]){ { segment->intercept } }, f_div(f_mul(scrollMultiplier, *(IOFixed64[]){ { segment->slope } }), *(IOFixed64[]){ { absAxis } } ));
}
axisValue = f_mul(axisValue, scrollMultiplier);
*axisp = axisValue.value;
}
static void scaleWheel(int* deltaAxis1, SInt32* fixedDeltaAxis1, SInt32* pointDeltaAxis1, struct timespec ts){
int deltaAxis2 = 0, deltaAxis3 = 0;
bool negative = (*deltaAxis1 < 0);
_scrollFixedDeltaAxis1 = *deltaAxis1 * SCROLL_DEFAULT_RESOLUTION;
//_scrollFixedDeltaAxis1 = *deltaAxis1 << 16;
CONVERT_SCROLL_FIXED_TO_COARSE(IOFixedMultiply(_scrollFixedDeltaAxis1, SCROLL_WHEEL_TO_PIXEL_SCALE), _scrollPointDeltaAxis1);
bool directionChange[3] = {0,0,0};
bool typeChange = FALSE;
SInt32* pDeltaAxis[3] = {deltaAxis1, &deltaAxis2, &deltaAxis3};
SInt32* pScrollFixedDeltaAxis[3] = {&_scrollFixedDeltaAxis1, &_scrollFixedDeltaAxis2, &_scrollFixedDeltaAxis3};
IOFixed* pScrollPointDeltaAxis[3] = {&_scrollPointDeltaAxis1, &_scrollPointDeltaAxis2, &_scrollPointDeltaAxis3};
for (UInt32 type=kAccelTypeY; type<=kAccelTypeZ; type++ ) {
directionChange[type] = ((_scrollWheelInfo.axis[type].lastValue == 0) ||
((_scrollWheelInfo.axis[type].lastValue < 0) && (*(pDeltaAxis[type]) > 0)) ||
((_scrollWheelInfo.axis[type].lastValue > 0) && (*(pDeltaAxis[type]) < 0)));
_scrollWheelInfo.axis[type].lastValue = *(pDeltaAxis[type]);
if ( _scrollWheelInfo.axis[type].scaleSegments || _scrollWheelInfo.axis[type].isParametric ) {
*(pScrollPointDeltaAxis[type]) = _scrollWheelInfo.axis[type].lastValue << 16;
AccelerateScrollAxis(pScrollPointDeltaAxis[type],
&(_scrollWheelInfo.axis[type]),
&ts,
_scrollWheelInfo.rateMultiplier,
directionChange[type] || typeChange);
CONVERT_SCROLL_FIXED_TO_COARSE(pScrollPointDeltaAxis[type][0], pScrollPointDeltaAxis[type][0]);
// RY: Convert pixel value to points
*(pScrollFixedDeltaAxis[type]) = *(pScrollPointDeltaAxis[type]) << 16;
if ( directionChange[type] )
bzero(&(_scrollWheelInfo.axis[type].consumeState), sizeof(ScaleConsumeState));
// RY: throttle the tranlation of scroll based on the resolution threshold.
// This allows us to not generated traditional scroll wheel (line) events
// for high res devices at really low (fine granularity) speeds. This
// prevents a succession of single scroll events that can make scrolling
// slowly actually seem faster.
if ( _scrollWheelInfo.axis[type].consumeCountThreshold )
{
_scrollWheelInfo.axis[type].consumeState.consumeAccum += *(pScrollFixedDeltaAxis[type]) + ((*(pScrollFixedDeltaAxis[type])) ? _scrollWheelInfo.axis[type].state.fraction : 0);
_scrollWheelInfo.axis[type].consumeState.consumeCount += abs(_scrollWheelInfo.axis[type].lastValue);
if (*(pScrollFixedDeltaAxis[type]) &&
((abs(_scrollWheelInfo.axis[type].lastValue) >= (SInt32)_scrollWheelInfo.axis[type].consumeClearThreshold) ||
(_scrollWheelInfo.axis[type].consumeState.consumeCount >= _scrollWheelInfo.axis[type].consumeCountThreshold)))
{
*(pScrollFixedDeltaAxis[type]) = _scrollWheelInfo.axis[type].consumeState.consumeAccum;
_scrollWheelInfo.axis[type].consumeState.consumeAccum = 0;
_scrollWheelInfo.axis[type].consumeState.consumeCount = 0;
}
else
{
*(pScrollFixedDeltaAxis[type]) = 0;
}
}
*(pScrollFixedDeltaAxis[type]) = IOFixedMultiply(*(pScrollFixedDeltaAxis[type]), SCROLL_PIXEL_TO_WHEEL_SCALE);
// RY: Generate fixed point and course scroll deltas.
CONVERT_SCROLL_FIXED_TO_COARSE(*(pScrollFixedDeltaAxis[type]), *(pDeltaAxis[type]));
}
}
*fixedDeltaAxis1 = _scrollFixedDeltaAxis1;
*pointDeltaAxis1 = _scrollPointDeltaAxis1;
// Prevent direction reversing (I'm not sure why this happens...)
if(negative != (*deltaAxis1 < 0))
*deltaAxis1 = -*deltaAxis1;
if(negative != (*fixedDeltaAxis1 < 0))
*fixedDeltaAxis1 = -*fixedDeltaAxis1;
if(negative != (*pointDeltaAxis1 < 0))
*pointDeltaAxis1 = -*pointDeltaAxis1;
}
// Setup utilities
static uint get_desired(CFStringRef type_key, CFStringRef fallback_key){
int res = 0;
CFTypeRef number = 0;
CFTypeRef accelKey = 0;
if(IOPointingCopyCFTypeParameter(type_key, &accelKey) == kIOReturnSuccess){
if(CFGetTypeID(accelKey) == CFStringGetTypeID())
IOPointingCopyCFTypeParameter(accelKey, &number);
if(accelKey) CFRelease(accelKey);
}
if(!number)
IOPointingCopyCFTypeParameter(fallback_key, (CFTypeRef*)&number);
if(!number)
return 0;
if(CFGetTypeID(number) == CFNumberGetTypeID())
CFNumberGetValue(number, kCFNumberIntType, &res);
else if(CFGetTypeID(number) == CFDataGetTypeID())
CFDataGetBytes(number, CFRangeMake(0, sizeof(int)), (UInt8*)&res);
CFRelease(number);
return res;
}
static void do_setup(io_connect_t event, struct timespec now){
// If it's been a while since the last check (1s) or we haven't set up yet, get the desired acceleration values
struct timespec last = last_setup_poll;
timespec_add(&last, 1000000000);
if(!has_setup || timespec_gt(now, last)){
static uint desired_mouse = UINT_MAX, desired_wheel = UINT_MAX;
uint desired = get_desired(CFSTR(kIOHIDPointerAccelerationTypeKey), CFSTR(kIOHIDPointerAccelerationKey));
// Set up parameters again if the value has changed
if(desired != desired_mouse || !has_setup)
setupForAcceleration(desired_mouse = desired);
desired = get_desired(CFSTR(kIOHIDScrollAccelerationTypeKey), CFSTR(kIOHIDScrollAccelerationKey));
if(desired != desired_wheel || !has_setup)
setupScrollForAcceleration(desired_wheel = desired);
has_setup = 1;
last_setup_poll = now;
}
}
// External functions (called from input_mac.c)
void mouse_accel(io_connect_t event, int* x, int* y){
pthread_mutex_lock(&mutex);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
do_setup(event, now);
scalePointer(x, y);
pthread_mutex_unlock(&mutex);
}
void wheel_accel(io_connect_t event, int* deltaAxis1, SInt32* fixedDeltaAxis1, SInt32* pointDeltaAxis1){
pthread_mutex_lock(&mutex);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
do_setup(event, now);
scaleWheel(deltaAxis1, fixedDeltaAxis1, pointDeltaAxis1, now);
pthread_mutex_unlock(&mutex);
}
#endif // OS_MAC_LEGACY