ckb-next  v0.2.8 at branch master
ckb-next driver for corsair devices
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
input_mac_mouse.c
Go to the documentation of this file.
1 #include "includes.h"
2 
3 #ifdef OS_MAC
4 
5 // Most of this code shamelessly stolen from:
6 // http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-606.1.7/IOHIDSystem/IOHIPointing.cpp
7 // http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-606.1.7/IOHIDSystem/IOFixed64.h + IOFixed64.cpp
8 // http://www.opensource.apple.com/source/IOKitUser/IOKitUser-1050.1.21/hidsystem.subproj/IOEventStatusAPI.c
9 // Why they thought that mouse acceleration belongs in a low-level input driver is beyond me...
10 
11 // Original license:
12 /*
13  * @APPLE_LICENSE_HEADER_START@
14  *
15  * Copyright (c) 1999-2010 Apple Computer, Inc. All Rights Reserved.
16  *
17  * This file contains Original Code and/or Modifications of Original Code
18  * as defined in and that are subject to the Apple Public Source License
19  * Version 2.0 (the 'License'). You may not use this file except in
20  * compliance with the License. Please obtain a copy of the License at
21  * http://www.opensource.apple.com/apsl/ and read it before using this
22  * file.
23  *
24  * The Original Code and all software distributed under the License are
25  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
26  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
27  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
28  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
29  * Please see the License for the specific language governing rights and
30  * limitations under the License.
31  *
32  * @APPLE_LICENSE_HEADER_END@
33  */
34 
35 // The code has been modified for ckb. This file is distributed under the same license.
36 // So if anyone actually wants this ungodly mess, those are your terms.
37 
38 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
39 struct timespec last_setup_poll;
40 static int has_setup = 0;
41 
42 // The following functions are modified from IOHIDCopyCFTypeParameter, designed to get the mouse info properly
43 // (it's stored in several different locations; a naïve search will fail)
44 
45 static kern_return_t IOServiceCopyCF(io_registry_entry_t service, CFStringRef key, CFTypeRef * parameter){
46  kern_return_t kr = KERN_SUCCESS;
47  CFDictionaryRef paramDict;
48  CFTypeRef tempParameter = NULL;
49 
50  if( (paramDict = IORegistryEntryCreateCFProperty( service, CFSTR(kIOHIDParametersKey), kCFAllocatorDefault, kNilOptions)))
51  {
52  if ( (tempParameter = CFDictionaryGetValue( paramDict, key)) )
53  CFRetain(tempParameter);
54 
55  CFRelease(paramDict);
56  }
57 
58  if ( !tempParameter )
59  tempParameter = IORegistryEntryCreateCFProperty( service, key, kCFAllocatorDefault, kNilOptions);
60 
61  if ( !tempParameter )
62  kr = kIOReturnBadArgument;
63 
64  *parameter = tempParameter;
65 
66  return( kr );
67 }
68 
69 static kern_return_t IOServiceCopyCFRecursive(io_registry_entry_t service, CFStringRef key, CFTypeRef* parameter){
70  // Iterate child registries
71  kern_return_t res;
72  io_iterator_t child_iter;
73  if((res = IORegistryEntryCreateIterator(service, kIOServicePlane, kIORegistryIterateRecursively, &child_iter)) != KERN_SUCCESS)
74  return kIOReturnBadArgument;
75 
76  io_registry_entry_t child_service;
77  while((child_service = IOIteratorNext(child_iter)) != 0){
78  io_string_t path;
79  IORegistryEntryGetPath(child_service, kIOServicePlane, path);
80  res = IOServiceCopyCF(child_service, key, parameter);
81  IOObjectRelease(child_service);
82  // If the child has it, return success
83  if(res == KERN_SUCCESS)
84  break;
85  }
86  IOObjectRelease(child_iter);
87  // Return found or not found, depending on above outcome
88  return res;
89 }
90 
91 // Get a handle for an IOService
92 static io_service_t GetService(mach_port_t master, const char* name){
93  kern_return_t res;
94  io_iterator_t iter;
95  if((res = IOServiceGetMatchingServices(master, IOServiceMatching(name), &iter)) != KERN_SUCCESS)
96  return 0;
97  io_service_t service = IOIteratorNext(iter);
98  IOObjectRelease(iter);
99  return service;
100 }
101 
102 // v Usable function
103 static kern_return_t IOPointingCopyCFTypeParameter(CFStringRef key, CFTypeRef * parameter){
104  // Open master port if not done yet
105  static mach_port_t master = 0;
106  kern_return_t res;
107  if(!master && (res = IOMasterPort(bootstrap_port, &master)) != KERN_SUCCESS){
108  master = 0;
109  return kIOReturnError;
110  }
111  // Open IOHIPointing and IOHIDSystem if not done yet
112  static io_service_t hidsystem = 0, hipointing = 0;
113  if(!hidsystem)
114  hidsystem = GetService(master, kIOHIDSystemClass);
115  if(!hipointing)
116  hipointing = GetService(master, kIOHIPointingClass);
117 
118  // Find the parameter
119  CFTypeRef tempParameter = NULL;
120  // Try IOHIPointing first
121  if(IOServiceCopyCF(hipointing, key, &tempParameter) == KERN_SUCCESS)
122  *parameter = tempParameter;
123  // Failing that, try IOHIDSystem
124  else if(IOServiceCopyCF(hidsystem, key, &tempParameter) == KERN_SUCCESS)
125  *parameter = tempParameter;
126  // Try recursive searches
127  else if(IOServiceCopyCFRecursive(hipointing, key, &tempParameter) == KERN_SUCCESS)
128  *parameter = tempParameter;
129  else if(IOServiceCopyCFRecursive(hidsystem, key, &tempParameter) == KERN_SUCCESS)
130  *parameter = tempParameter;
131  else {
132  // Not found
133  *parameter = 0;
134  return kIOReturnBadArgument;
135  }
136  return KERN_SUCCESS;
137 }
138 
139 // IOFixed64 emulation (this was originally a C++ class; it has been converted to C)
140 
141 typedef struct {
142  SInt64 value;
143 } IOFixed64;
144 
145 #define sc2f(sc) ((sc) * 65536LL)
146 #define f2sc(f) ((f).value / 65536LL)
147 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; }
148 static bool f_gt_sc(IOFixed64 lhs, SInt64 rhs) { return lhs.value > sc2f(rhs); }
149 static bool f_lt(IOFixed64 lhs, IOFixed64 rhs) { return lhs.value < rhs.value; }
150 static bool f_gt(IOFixed64 lhs, IOFixed64 rhs) { return lhs.value > rhs.value; }
151 static IOFixed64 f_add(IOFixed64 lhs, IOFixed64 rhs) { IOFixed64 r = { lhs.value + rhs.value }; return r; }
152 static IOFixed64 f_sub(IOFixed64 lhs, IOFixed64 rhs) { IOFixed64 r = { lhs.value - rhs.value }; return r; }
153 static IOFixed64 f_div(IOFixed64 lhs, IOFixed64 rhs) { IOFixed64 r = { lhs.value * 65536LL / rhs.value }; return r; }
154 static IOFixed64 f_mul(IOFixed64 lhs, IOFixed64 rhs) { IOFixed64 r = { (lhs.value * rhs.value) / 65536LL }; return r; }
155 static IOFixed64 f_mul_sc(IOFixed64 lhs, SInt64 rhs) { IOFixed64 r = { lhs.value * rhs }; return r; }
156 
157 static IOFixed64 exponent(const IOFixed64 original, const UInt8 power)
158 {
159  IOFixed64 result = {0};
160  if (power) {
161  int i;
162  result = original;
163  for (i = 1; i < power; i++) {
164  result = f_mul(result, original);
165  }
166  }
167  return result;
168 }
169 
170 static UInt32 llsqrt(UInt64 x)
171 {
172  UInt64 rem = 0;
173  UInt64 root = 0;
174  int i;
175 
176  for (i = 0; i < 32; i++) {
177  root <<= 1;
178  rem = ((rem << 2) + (x >> 62));
179  x <<= 2;
180 
181  root++;
182 
183  if (root <= rem) {
184  rem -= root;
185  root++;
186  } else {
187  root--;
188  }
189  }
190 
191  return(UInt32)(root >> 1);
192 }
193 
194 UInt16 lsqrt(UInt32 x)
195 {
196  UInt32 rem = 0;
197  UInt32 root = 0;
198  int i;
199 
200  for (i = 0; i < 16; i++) {
201  root <<= 1;
202  rem = ((rem << 2) + (x >> 30));
203  x <<= 2;
204 
205  root++;
206 
207  if (root <= rem) {
208  rem -= root;
209  root++;
210  } else {
211  root--;
212  }
213  }
214 
215  return(UInt16)(root >> 1);
216 }
217 
218 static IOFixed64 IOQuarticFunction( const IOFixed64 x, const IOFixed64 *gains )
219 {
220  // Computes hyper-cubic polynomial with 0-intercept: f(x) = m1*x + m2^2 * x^2 + m3^3 * x^3 + m4^4 * x^4
221  IOFixed64 function_at_x = f_add(f_mul(x, gains[0]), exponent(f_mul(x, gains[1]), 2));
222 
223  // -- Because of IOFixed overhead, don't bother computing higher expansions unless their gain coefficients are non-zero:
224  if( gains[2].value != 0LL )
225  function_at_x = f_add(function_at_x, exponent(f_mul(x, gains[2]), 3));
226 
227  if( gains[3].value != 0LL )
228  function_at_x = f_add(function_at_x, exponent(f_mul(x, gains[3]), 4));
229 
230  return function_at_x;
231 }
232 
233 static IOFixed64 IOQuarticDerivative( const IOFixed64 x, const IOFixed64 *gains )
234 {
235  // For hyper-cubic polynomial with 0-intercept: f(x) = m1*x + m2^2 * x^2 + m3^3 * x^3 + m4^4 * x^4
236  // This function evaluates the derivative: f'(x) = m1 + 2 * x * m2^2 + 3 * x^2 * m3^3 + 4 * x^3 * m4^4
237  IOFixed64 derivative_at_x = f_add(gains[0], f_mul_sc(f_mul(x, exponent(gains[1], 2)), 2LL));
238 
239  // -- Because of IOFixed overhead, don't bother computing higher expansions unless their gain coefficients are non-zero:
240  if( gains[2].value != 0LL )
241  derivative_at_x = f_add(derivative_at_x, f_mul_sc(f_mul(exponent(x, 2), exponent(gains[2], 3)), 3LL));
242 
243  if( gains[3].value != 0LL )
244  derivative_at_x = f_add(derivative_at_x, f_mul_sc(f_mul(exponent(x, 3), exponent(gains[3], 4)), 4LL));
245 
246  return derivative_at_x;
247 }
248 
249 static inline IOFixed IOFixedMultiply(IOFixed a, IOFixed b)
250 {
251  return (IOFixed)((((SInt64) a) * ((SInt64) b)) >> 16);
252 }
253 
254 static inline IOFixed IOFixedDivide(IOFixed a, IOFixed b)
255 {
256  return (IOFixed)((((SInt64) a) << 16) / ((SInt64) b));
257 }
258 
259 static IOFixed64 OSObjectToIOFixed64(CFNumberRef object){
260  IOFixed64 result = {0};
261  if(object && CFGetTypeID(object) == CFNumberGetTypeID())
262  CFNumberGetValue(object, kCFNumberIntType, &result.value);
263  return result;
264 }
265 
266 // Constants
267 
268 #define MAX_DEVICE_THRESHOLD 0x7fffffff
269 
270 #define FRAME_RATE (67 << 16)
271 #define SCREEN_RESOLUTION (96 << 16)
272 
273 #define kIOFixedOne 0x10000ULL
274 #define SCROLL_DEFAULT_RESOLUTION (9 * kIOFixedOne)
275 #define SCROLL_CONSUME_RESOLUTION (100 * kIOFixedOne)
276 #define SCROLL_CONSUME_COUNT_MULTIPLIER 3
277 #define SCROLL_EVENT_THRESHOLD_MS_LL 150ULL
278 #define SCROLL_EVENT_THRESHOLD_MS (SCROLL_EVENT_THRESHOLD_MS_LL * kIOFixedOne)
279 #define SCROLL_CLEAR_THRESHOLD_MS_LL 500ULL
280 
281 #define SCROLL_MULTIPLIER_RANGE 0x00018000
282 #define SCROLL_MULTIPLIER_A 0x00000002 /*IOFixedDivide(SCROLL_MULTIPLIER_RANGE,SCROLL_EVENT_THRESHOLD_MS*2)*/
283 #define SCROLL_MULTIPLIER_B 0x000003bb /*IOFixedDivide(SCROLL_MULTIPLIER_RANGE*3,(SCROLL_EVENT_THRESHOLD_MS^2)*2)*/
284 #define SCROLL_MULTIPLIER_C 0x00018041
285 
286 
287 #define SCROLL_WHEEL_TO_PIXEL_SCALE 0x000a0000/* IOFixedDivide(SCREEN_RESOLUTION, SCROLL_DEFAULT_RESOLUTION) */
288 #define SCROLL_PIXEL_TO_WHEEL_SCALE 0x0000199a/* IOFixedDivide(SCREEN_RESOLUTION, SCROLL_DEFAULT_RESOLUTION) */
289 #define SCROLL_TIME_DELTA_COUNT 8
290 
291 #define CONVERT_SCROLL_FIXED_TO_FRACTION(fixed, fraction) \
292 { \
293  if( fixed >= 0) \
294  fraction = fixed & 0xffff; \
295  else \
296  fraction = fixed | 0xffff0000; \
297  }
298 
299 #define CONVERT_SCROLL_FIXED_TO_INTEGER(fixedAxis, integer) \
300 { \
301  SInt32 tempInt = 0; \
302  if((fixedAxis < 0) && (fixedAxis & 0xffff)) \
303  tempInt = (fixedAxis >> 16) + 1; \
304  else \
305  tempInt = (fixedAxis >> 16); \
306  integer = tempInt; \
307  }
308 
309 #define CONVERT_SCROLL_FIXED_TO_COARSE(fixedAxis, coarse) \
310 { \
311  SInt32 tempCoarse = 0; \
312  CONVERT_SCROLL_FIXED_TO_INTEGER(fixedAxis, tempCoarse) \
313  if (!tempCoarse && (fixedAxis & 0xffff)) \
314  tempCoarse = (fixedAxis < 0) ? -1 : 1; \
315  coarse = tempCoarse; \
316  }
317 
318 enum {
319  kAccelTypeGlobal = -1,
320  kAccelTypeY = 0, //delta axis 1
321  kAccelTypeX = 1, //delta axis 2
322  kAccelTypeZ = 2 //delta axis 3
323 };
324 
325 // Structures
326 
327 typedef struct
328 {
329  IOFixed64 deviceMickysDivider;
330  IOFixed64 cursorSpeedMultiplier;
331  IOFixed64 accelIndex;
332  IOFixed64 gain[4];
333  IOFixed64 tangent[2];
334 } IOHIPointing__PAParameters;
335 
336 typedef struct
337 {
338  int firstTangent;
339  IOFixed64 m0; // m1 == m0
340  IOFixed64 b0; // no b1
341  IOFixed64 y0;
342  IOFixed64 y1;
343  IOFixed64 m_root;
344  IOFixed64 b_root;
345 } IOHIPointing__PASecondaryParameters;
346 
347 struct CursorDeviceSegment {
348  SInt32 devUnits;
349  SInt32 slope;
350  SInt32 intercept;
351 };
352 typedef struct CursorDeviceSegment CursorDeviceSegment;
353 
354 struct ScaleDataState
355 {
356  UInt8 deltaIndex;
357  IOFixed deltaTime[SCROLL_TIME_DELTA_COUNT];
358  IOFixed deltaAxis[SCROLL_TIME_DELTA_COUNT];
359  IOFixed fraction;
360 };
361 typedef struct ScaleDataState ScaleDataState;
362 struct ScaleConsumeState
363 {
364  UInt32 consumeCount;
365  IOFixed consumeAccum;
366 };
367 typedef struct ScaleConsumeState ScaleConsumeState;
368 struct ScrollAxisAccelInfo
369 {
370  struct timespec lastEventTime;
371  void * scaleSegments;
372  IOItemCount scaleSegCount;
373  ScaleDataState state;
374  ScaleConsumeState consumeState;
375  IOHIPointing__PAParameters primaryParametrics;
376  IOHIPointing__PASecondaryParameters secondaryParametrics;
377  SInt32 lastValue;
378  UInt32 consumeClearThreshold;
379  UInt32 consumeCountThreshold;
380  bool isHighResScroll;
381  bool isParametric;
382 };
383 typedef struct ScrollAxisAccelInfo ScrollAxisAccelInfo;
384 struct ScrollAccelInfo
385 {
386  ScrollAxisAccelInfo axis[3];
387 
388  IOFixed rateMultiplier;
389  UInt32 zoom:1;
390 };
391 typedef struct ScrollAccelInfo ScrollAccelInfo;
392 
393 // Static variables
394 
395 static IOHIPointing__PAParameters* _paraAccelParams = 0;
396 static IOHIPointing__PASecondaryParameters* _paraAccelSecondaryParams = 0;
397 
398 static IOFixed _scrollFixedDeltaAxis1 = 0, _scrollFixedDeltaAxis2 = 0, _scrollFixedDeltaAxis3 = 0;
399 static SInt32 _scrollPointDeltaAxis1 = 0, _scrollPointDeltaAxis2 = 0, _scrollPointDeltaAxis3 = 0;
400 
401 static void* _scaleSegments = 0;
402 static IOItemCount _scaleSegCount = 0;
403 static IOFixed _acceleration = -1, _fractX = 0, _fractY = 0;
404 
405 static ScrollAccelInfo _scrollWheelInfo;
406 static ScrollAccelInfo _scrollPointerInfo;
407 
408 // Misc parameters
409 
410 static IOFixed resolution()
411 {
412  CFNumberRef number;
413  IOPointingCopyCFTypeParameter(CFSTR(kIOHIDPointerResolutionKey), (CFTypeRef*)&number);
414  IOFixed result = 100 << 16;
415 
416  if (number && CFGetTypeID(number) == CFNumberGetTypeID())
417  CFNumberGetValue(number, kCFNumberIntType, &result);
418  if(number) CFRelease(number);
419 
420  return result;
421 }
422 
423 static IOFixed scrollReportRate()
424 {
425  IOFixed result = FRAME_RATE;
426  CFNumberRef number;
427  IOPointingCopyCFTypeParameter(CFSTR(kIOHIDScrollReportRateKey), (CFTypeRef*)&number);
428 
429  if (number && CFGetTypeID(number) == CFNumberGetTypeID())
430  CFNumberGetValue(number, kCFNumberIntType, &result);
431  if(number) CFRelease(number);
432 
433  if (result == 0)
434  result = FRAME_RATE;
435 
436  return result;
437 }
438 
439 static IOFixed scrollResolutionForType(SInt32 type)
440 {
441  IOFixed res = 0;
442  CFNumberRef number = NULL;
443  CFStringRef key = NULL;
444 
445  switch ( type ) {
446  case kAccelTypeY:
447  key = CFSTR(kIOHIDScrollResolutionYKey);
448  break;
449  case kAccelTypeX:
450  key = CFSTR(kIOHIDScrollResolutionXKey);
451  break;
452  case kAccelTypeZ:
453  key = CFSTR(kIOHIDScrollResolutionZKey);
454  break;
455  default:
456  key = CFSTR(kIOHIDScrollResolutionKey);
457  break;
458 
459  }
460  IOPointingCopyCFTypeParameter(key, (CFTypeRef*)&number);
461  if(number && CFGetTypeID(number) == CFNumberGetTypeID())
462  CFNumberGetValue(number, kCFNumberIntType, &res);
463  else {
464  if(number) CFRelease(number);
465  IOPointingCopyCFTypeParameter(CFSTR(kIOHIDScrollResolutionKey), (CFTypeRef*)&number);
466  if(number && CFGetTypeID(number) == CFNumberGetTypeID())
467  CFNumberGetValue(number, kCFNumberIntType, &res);
468  }
469  if(number) CFRelease(number);
470 
471  return res;
472 }
473 
474 // Parametric acceleration
475 
476 static bool
477 PACurvesFillParamsFromDict(CFDictionaryRef parameters,
478  const IOFixed64 devScale,
479  const IOFixed64 crsrScale,
480  IOHIPointing__PAParameters *outParams)
481 {
482  require(parameters, exit_early);
483  require(CFGetTypeID(parameters) == CFDictionaryGetTypeID(), exit_early);
484 
485  outParams->deviceMickysDivider = devScale;
486  outParams->cursorSpeedMultiplier = crsrScale;
487 
488  outParams->accelIndex = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelIndexKey)));
489 
490  outParams->gain[0] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelGainLinearKey)));
491  outParams->gain[1] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelGainParabolicKey)));
492  outParams->gain[2] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelGainCubicKey)));
493  outParams->gain[3] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelGainQuarticKey)));
494 
495  outParams->tangent[0] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelTangentSpeedLinearKey)));
496  outParams->tangent[1] = OSObjectToIOFixed64(CFDictionaryGetValue(parameters, CFSTR(kHIDAccelTangentSpeedParabolicRootKey)));
497 
498  return ((outParams->gain[0].value != 0LL) ||
499  (outParams->gain[1].value != 0LL) ||
500  (outParams->gain[2].value != 0LL) ||
501  (outParams->gain[3].value != 0LL));
502 
503 exit_early:
504  return false;
505 }
506 
507 static bool
508 PACurvesSetupAccelParams (CFArrayRef parametricCurves,
509  IOFixed64 desired,
510  IOFixed64 devScale,
511  IOFixed64 crsrScale,
512  IOHIPointing__PAParameters *primaryParams,
513  IOHIPointing__PASecondaryParameters *secondaryParams)
514 {
515  bool success = false;
516  CFDictionaryRef dict = NULL;
517 
518  IOHIPointing__PAParameters high_curve_params;
519  IOHIPointing__PAParameters low_curve_params;
520 
521  require(parametricCurves, exit_early);
522  require(f_gt_sc(crsrScale, 0LL), exit_early);
523  require(f_gt_sc(devScale, 0LL), exit_early);
524  require(f_gt_sc(desired, 0LL), exit_early);
525 
526  CFIndex itrCount = CFArrayGetCount(parametricCurves);
527  CFIndex itr = 0;
528 
529  while (!success) {
530  itr = 0;
531  dict = (CFDictionaryRef)CFArrayGetValueAtIndex(parametricCurves, itr++);
532  require(PACurvesFillParamsFromDict(dict, devScale, crsrScale, &low_curve_params),
533  exit_early);
534 
535  while (!success && (NULL != dict)) {
536  if (!PACurvesFillParamsFromDict(dict, devScale, crsrScale, &high_curve_params)) {
537  break;
538  }
539  if (desired.value <= high_curve_params.accelIndex.value) {
540  success = true;
541  }
542  else {
543  low_curve_params = high_curve_params;
544  }
545  if(itr == itrCount)
546  dict = NULL;
547  else
548  dict = (CFDictionaryRef)CFArrayGetValueAtIndex(parametricCurves, itr++);
549  }
550 
551  require(success, exit_early);
552  };
553 
554  if ( high_curve_params.accelIndex.value > low_curve_params.accelIndex.value ) {
555  IOFixed64 ratio = f_div(f_sub(desired, low_curve_params.accelIndex), f_sub(high_curve_params.accelIndex, low_curve_params.accelIndex));
556  int index;
557 
558  primaryParams->deviceMickysDivider = high_curve_params.deviceMickysDivider;
559  primaryParams->cursorSpeedMultiplier = high_curve_params.cursorSpeedMultiplier;
560  primaryParams->accelIndex = desired;
561 
562  for (index = 0; index < 4; index++) {
563  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));
564  if (primaryParams->gain[index].value < 0LL)
565  primaryParams->gain[index].value = 0;
566  }
567  for (index = 0; index < 2; index++) {
568  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));
569  if (primaryParams->tangent[index].value < 0LL)
570  primaryParams->tangent[index].value = 0;
571  }
572  }
573  else {
574  *primaryParams = high_curve_params;
575  }
576 
577  success = ((primaryParams->gain[0].value != 0LL) ||
578  (primaryParams->gain[1].value != 0LL) ||
579  (primaryParams->gain[2].value != 0LL) ||
580  (primaryParams->gain[3].value != 0LL));
581 
582  // calculate secondary values
583  bzero(secondaryParams, sizeof(*secondaryParams));
584  if ((primaryParams->tangent[1].value > 0LL) && (primaryParams->tangent[1].value < primaryParams->tangent[0].value))
585  secondaryParams->firstTangent = 1;
586 
587  if (secondaryParams->firstTangent == 0) {
588  secondaryParams->y0 = IOQuarticFunction(primaryParams->tangent[0], primaryParams->gain);
589  secondaryParams->m0 = IOQuarticDerivative(primaryParams->tangent[0], primaryParams->gain);
590  secondaryParams->b0 = f_sub(secondaryParams->y0, f_mul(secondaryParams->m0, primaryParams->tangent[0]));
591  secondaryParams->y1 = f_add(f_mul(secondaryParams->m0, primaryParams->tangent[1]), secondaryParams->b0);
592  }
593  else {
594  secondaryParams->y1 = IOQuarticFunction( primaryParams->tangent[1], primaryParams->gain );
595  secondaryParams->m0 = IOQuarticDerivative( primaryParams->tangent[1], primaryParams->gain );
596  }
597 
598  secondaryParams->m_root = f_mul_sc(f_mul(secondaryParams->m0, secondaryParams->y1), 2LL);
599  secondaryParams->b_root = f_sub(exponent(secondaryParams->y1, 2), f_mul(secondaryParams->m_root, primaryParams->tangent[1]));
600 
601 exit_early:
602 
603  return success;
604 }
605 
606 static IOFixed64
607 PACurvesGetAccelerationMultiplier(const IOFixed64 device_speed_mickeys,
608  const IOHIPointing__PAParameters *params,
609  const IOHIPointing__PASecondaryParameters *secondaryParams)
610 {
611  IOFixed64 result = {0};
612 
613  if ((device_speed_mickeys.value > result.value) && (params->deviceMickysDivider.value != result.value)) {
614  IOFixed64 standardized_speed = f_div(device_speed_mickeys, params->deviceMickysDivider);
615  IOFixed64 accelerated_speed;
616  if ((params->tangent[secondaryParams->firstTangent].value != 0LL) && (standardized_speed.value <= params->tangent[secondaryParams->firstTangent].value)) {
617  accelerated_speed = IOQuarticFunction(standardized_speed, params->gain);
618  }
619  else {
620  if ((secondaryParams->firstTangent == 0) && (params->tangent[1].value != 0LL) && (standardized_speed.value <= params->tangent[1].value)) {
621  accelerated_speed = f_add(f_mul(secondaryParams->m0, standardized_speed), secondaryParams->b0);
622  }
623  else {
624  accelerated_speed.value = sc2f(llsqrt(f2sc(f_add(f_mul(secondaryParams->m_root, standardized_speed), secondaryParams->b_root))));
625  }
626  }
627  IOFixed64 accelerated_pixels = f_mul(accelerated_speed, params->cursorSpeedMultiplier);
628  result = f_div(accelerated_pixels, device_speed_mickeys);
629  }
630  else {
631  result.value = 1;
632  }
633 
634  return result;
635 }
636 
637 // Classic acceleration
638 
639 static CFDataRef copyAccelerationTable()
640 {
641  static const UInt8 accl[] = {
642  0x00, 0x00, 0x80, 0x00,
643  0x40, 0x32, 0x30, 0x30, 0x00, 0x02, 0x00, 0x00,
644  0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
645  0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
646  0x00, 0x09, 0x00, 0x00, 0x71, 0x3B, 0x00, 0x00,
647  0x60, 0x00, 0x00, 0x04, 0x4E, 0xC5, 0x00, 0x10,
648  0x80, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x5F,
649  0x00, 0x00, 0x00, 0x16, 0xEC, 0x4F, 0x00, 0x8B,
650  0x00, 0x00, 0x00, 0x1D, 0x3B, 0x14, 0x00, 0x94,
651  0x80, 0x00, 0x00, 0x22, 0x76, 0x27, 0x00, 0x96,
652  0x00, 0x00, 0x00, 0x24, 0x62, 0x76, 0x00, 0x96,
653  0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x96,
654  0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x96,
655  0x00, 0x00
656  };
657 
658  CFDataRef data;
659  IOPointingCopyCFTypeParameter(CFSTR(kIOHIDPointerAccelerationTableKey), (CFTypeRef*)&data);
660  if(data && CFGetTypeID(data) != CFDataGetTypeID()){
661  CFRelease(data);
662  data = 0;
663  }
664  if (!data)
665  data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, accl, sizeof(accl), kCFAllocatorNull);
666 
667  return( data );
668 }
669 
670 static CFDataRef copyScrollAccelerationTableForType(SInt32 type)
671 {
672  CFDataRef data = NULL;
673  CFStringRef key = NULL;
674 
675  switch ( type ) {
676  case kAccelTypeY:
677  key = CFSTR(kIOHIDScrollAccelerationTableYKey);
678  break;
679  case kAccelTypeX:
680  key = CFSTR(kIOHIDScrollAccelerationTableXKey);
681  break;
682  case kAccelTypeZ:
683  key = CFSTR(kIOHIDScrollAccelerationTableZKey);
684  break;
685  }
686 
687  if ( key )
688  IOPointingCopyCFTypeParameter(key, (CFTypeRef*)&data);
689 
690  if ( !data || CFGetTypeID(data) != CFDataGetTypeID()) {
691  if(data) CFRelease(data);
692  IOPointingCopyCFTypeParameter(CFSTR(kIOHIDScrollAccelerationTableKey), (CFTypeRef*)&data);
693  }
694 
695  if ( !data || CFGetTypeID(data) != CFDataGetTypeID()) {
696  if(data) CFRelease(data);
697  data = copyAccelerationTable();
698  }
699 
700  return( data );
701 }
702 
703 /*
704  Routine: Interpolate
705  This routine interpolates to find a point on the line [x1,y1] [x2,y2] which
706  is intersected by the line [x3,y3] [x3,y"]. The resulting y' is calculated
707  by interpolating between y3 and y", towards the higher acceleration curve.
708 */
709 static SInt32 Interpolate( SInt32 x1, SInt32 y1,
710  SInt32 x2, SInt32 y2,
711  SInt32 x3, SInt32 y3,
712  SInt32 scale, Boolean lower )
713 {
714 
715  SInt32 slope;
716  SInt32 intercept;
717  SInt32 resultY;
718 
719  slope = (x2 == x1) ? 0 : IOFixedDivide( y2 - y1, x2 - x1 );
720  intercept = y1 - IOFixedMultiply( slope, x1 );
721  resultY = intercept + IOFixedMultiply( slope, x3 );
722  if( lower)
723  resultY = y3 - IOFixedMultiply( scale, y3 - resultY );
724  else
725  resultY = resultY + IOFixedMultiply( scale, y3 - resultY );
726 
727  return( resultY );
728 }
729 
730 static bool SetupAcceleration (CFDataRef data, IOFixed desired, IOFixed devScale, IOFixed crsrScale, void ** scaleSegments, IOItemCount * scaleSegCount) {
731  const UInt16 * lowTable = 0;
732  const UInt16 * highTable;
733 
734  SInt32 x1, y1, x2, y2, x3, y3;
735  SInt32 prevX1, prevY1;
736  SInt32 upperX, upperY;
737  SInt32 lowerX, lowerY;
738  SInt32 lowAccl = 0, lowPoints = 0;
739  SInt32 highAccl, highPoints;
740  SInt32 scale;
741  UInt32 count;
742  Boolean lower;
743 
744  SInt32 scaledX1, scaledY1;
745  SInt32 scaledX2, scaledY2;
746 
747  CursorDeviceSegment * segments;
748  CursorDeviceSegment * segment;
749  SInt32 segCount;
750 
751  if( !data || !devScale || !crsrScale)
752  return false;
753 
754  if( desired < (IOFixed) 0) {
755  // disabling mouse scaling
756  if(*scaleSegments && *scaleSegCount)
757  free( *scaleSegments);
758  *scaleSegments = NULL;
759  *scaleSegCount = 0;
760  return false;
761  }
762 
763  highTable = (const UInt16 *)CFDataGetBytePtr(data);
764 
765  scaledX1 = scaledY1 = 0;
766 
767  scale = OSReadBigInt32((volatile void *)highTable, 0);
768  highTable += 4;
769 
770  // normalize table's default (scale) to 0.5
771  if( desired > 0x8000) {
772  desired = IOFixedMultiply( desired - 0x8000,
773  0x10000 - scale );
774  desired <<= 1;
775  desired += scale;
776  } else {
777  desired = IOFixedMultiply( desired, scale );
778  desired <<= 1;
779  }
780 
781  count = OSReadBigInt16((volatile void *)(highTable++), 0);
782  scale = (1 << 16);
783 
784  // find curves bracketing the desired value
785  do {
786  highAccl = OSReadBigInt32((volatile void *)highTable, 0);
787  highTable += 2;
788  highPoints = OSReadBigInt16((volatile void *)(highTable++), 0);
789 
790  if( desired <= highAccl)
791  break;
792 
793  if( 0 == --count) {
794  // this much over the highest table
795  scale = (highAccl) ? IOFixedDivide( desired, highAccl ) : 0;
796  lowTable = 0;
797  break;
798  }
799 
800  lowTable = highTable;
801  lowAccl = highAccl;
802  lowPoints = highPoints;
803  highTable += lowPoints * 4;
804 
805  } while( true );
806 
807  // scale between the two
808  if( lowTable) {
809  scale = (highAccl == lowAccl) ? 0 :
810  IOFixedDivide((desired - lowAccl), (highAccl - lowAccl));
811 
812  }
813 
814  // or take all the high one
815  else {
816  lowTable = highTable;
817  lowAccl = highAccl;
818  lowPoints = 0;
819  }
820 
821  if( lowPoints > highPoints)
822  segCount = lowPoints;
823  else
824  segCount = highPoints;
825  segCount *= 2;
826  segments = calloc( sizeof(CursorDeviceSegment), segCount );
827  assert( segments );
828  segment = segments;
829 
830  x1 = prevX1 = y1 = prevY1 = 0;
831 
832  lowerX = OSReadBigInt32((volatile void *)lowTable, 0);
833  lowTable += 2;
834  lowerY = OSReadBigInt32((volatile void *)lowTable, 0);
835  lowTable += 2;
836  upperX = OSReadBigInt32((volatile void *)highTable, 0);
837  highTable += 2;
838  upperY = OSReadBigInt32((volatile void *)highTable, 0);
839  highTable += 2;
840 
841  do {
842  // consume next point from first X
843  lower = (lowPoints && (!highPoints || (lowerX <= upperX)));
844 
845  if( lower) {
846  /* highline */
847  x2 = upperX;
848  y2 = upperY;
849  x3 = lowerX;
850  y3 = lowerY;
851  if( lowPoints && (--lowPoints)) {
852  lowerX = OSReadBigInt32((volatile void *)lowTable, 0);
853  lowTable += 2;
854  lowerY = OSReadBigInt32((volatile void *)lowTable, 0);
855  lowTable += 2;
856  }
857  } else {
858  /* lowline */
859  x2 = lowerX;
860  y2 = lowerY;
861  x3 = upperX;
862  y3 = upperY;
863  if( highPoints && (--highPoints)) {
864  upperX = OSReadBigInt32((volatile void *)highTable, 0);
865  highTable += 2;
866  upperY = OSReadBigInt32((volatile void *)highTable, 0);
867  highTable += 2;
868  }
869  }
870  {
871  // convert to line segment
872  assert( segment < (segments + segCount) );
873 
874  scaledX2 = IOFixedMultiply( devScale, /* newX */ x3 );
875  scaledY2 = IOFixedMultiply( crsrScale,
876  /* newY */ Interpolate( x1, y1, x2, y2, x3, y3,
877  scale, lower ) );
878  if( lowPoints || highPoints)
879  segment->devUnits = scaledX2;
880  else
881  segment->devUnits = MAX_DEVICE_THRESHOLD;
882 
883  segment->slope = ((scaledX2 == scaledX1)) ? 0 :
884  IOFixedDivide((scaledY2 - scaledY1), (scaledX2 - scaledX1));
885 
886  segment->intercept = scaledY2
887  - IOFixedMultiply( segment->slope, scaledX2 );
888 
889  scaledX1 = scaledX2;
890  scaledY1 = scaledY2;
891  segment++;
892  }
893 
894  // continue on from last point
895  if( lowPoints && highPoints) {
896  if( lowerX > upperX) {
897  prevX1 = x1;
898  prevY1 = y1;
899  } else {
900  /* swaplines */
901  prevX1 = x1;
902  prevY1 = y1;
903  x1 = x3;
904  y1 = y3;
905  }
906  } else {
907  x2 = x1;
908  y2 = y1;
909  x1 = prevX1;
910  y1 = prevY1;
911  prevX1 = x2;
912  prevY1 = y2;
913  }
914 
915  } while( lowPoints || highPoints );
916 
917  if( *scaleSegCount && *scaleSegments)
918  free( *scaleSegments);
919  *scaleSegCount = segCount;
920  *scaleSegments = (void *) segments;
921 
922  return true;
923 }
924 
925 // Putting it together
926 
927 static void setupForAcceleration(IOFixed desired){
928  CFArrayRef parametricAccelerationCurves;
929  IOPointingCopyCFTypeParameter(CFSTR(kHIDTrackingAccelParametricCurvesKey), (CFTypeRef*)&parametricAccelerationCurves);
930  IOFixed devScale = IOFixedDivide( resolution(), FRAME_RATE );
931  IOFixed crsrScale = IOFixedDivide( SCREEN_RESOLUTION, FRAME_RATE );
932  bool useParametric = false;
933 
934  if (!parametricAccelerationCurves || CFGetTypeID(parametricAccelerationCurves) != CFArrayGetTypeID()) {
935  if(parametricAccelerationCurves) CFRelease(parametricAccelerationCurves);
936  IOPointingCopyCFTypeParameter(CFSTR(kHIDAccelParametricCurvesKey), (CFTypeRef*)&parametricAccelerationCurves);
937  }
938  // Try to set up the parametric acceleration data
939  if (parametricAccelerationCurves && CFGetTypeID(parametricAccelerationCurves) == CFArrayGetTypeID()) {
940  if ( !_paraAccelParams )
941  {
942  _paraAccelParams = (IOHIPointing__PAParameters*)malloc(sizeof(IOHIPointing__PAParameters));
943  }
944  if ( !_paraAccelSecondaryParams )
945  {
946  _paraAccelSecondaryParams = (IOHIPointing__PASecondaryParameters*)malloc(sizeof(IOHIPointing__PASecondaryParameters));
947  }
948 
949  if (_paraAccelParams && _paraAccelSecondaryParams) {
950  IOFixed64 desired64 = {desired};
951  IOFixed64 devScale64 = {devScale};
952  IOFixed64 crsrScale64 = {crsrScale};
953 
954  useParametric = PACurvesSetupAccelParams(parametricAccelerationCurves,
955  desired64,
956  devScale64,
957  crsrScale64,
958  _paraAccelParams,
959  _paraAccelSecondaryParams);
960  }
961  }
962  if(parametricAccelerationCurves) CFRelease(parametricAccelerationCurves);
963 
964  // If that fails, fall back to classic acceleration
965  if (!useParametric) {
966  CFDataRef table = copyAccelerationTable();
967 
968  if (_paraAccelParams)
969  free(_paraAccelParams);
970  if (_paraAccelSecondaryParams)
971  free(_paraAccelSecondaryParams);
972  _paraAccelParams = NULL;
973  _paraAccelSecondaryParams = NULL;
974 
975  if (SetupAcceleration (table, desired, devScale, crsrScale, &_scaleSegments, &_scaleSegCount))
976  {
977  _acceleration = desired;
978  _fractX = _fractY = 0;
979  }
980  if(table) CFRelease(table);
981  }
982 }
983 
984 static void ScaleAxes (void * scaleSegments, int * axis1p, IOFixed *axis1Fractp, int * axis2p, IOFixed *axis2Fractp)
985 {
986  SInt32 dx, dy;
987  SInt32 mag;
988  IOFixed scale;
989  CursorDeviceSegment * segment;
990 
991  if( !scaleSegments)
992  return;
993 
994  dx = (*axis1p) << 16;
995  dy = (*axis2p) << 16;
996 
997  // mag is (x^2 + y^2)^0.5 and converted to fixed point
998  mag = (lsqrt(*axis1p * *axis1p + *axis2p * *axis2p)) << 16;
999  if (mag == 0)
1000  return;
1001 
1002  // scale
1003  for(
1004  segment = (CursorDeviceSegment *) scaleSegments;
1005  mag > segment->devUnits;
1006  segment++) {}
1007 
1008  scale = IOFixedDivide(
1009  segment->intercept + IOFixedMultiply( mag, segment->slope ),
1010  mag );
1011 
1012  dx = IOFixedMultiply( dx, scale );
1013  dy = IOFixedMultiply( dy, scale );
1014 
1015  // add fract parts
1016  dx += *axis1Fractp;
1017  dy += *axis2Fractp;
1018 
1019  *axis1p = dx / 65536;
1020  *axis2p = dy / 65536;
1021 
1022  // get fractional part with sign extend
1023  if( dx >= 0)
1024  *axis1Fractp = dx & 0xffff;
1025  else
1026  *axis1Fractp = dx | 0xffff0000;
1027  if( dy >= 0)
1028  *axis2Fractp = dy & 0xffff;
1029  else
1030  *axis2Fractp = dy | 0xffff0000;
1031 }
1032 
1033 static void scalePointer(int* dxp, int* dyp)
1034 // Description: Perform pointer acceleration computations here.
1035 // Given the resolution, dx, dy, and time, compute the velocity
1036 // of the pointer over a Manhatten distance in inches/second.
1037 // Using this velocity, do a lookup in the pointerScaling table
1038 // to select a scaling factor. Scale dx and dy up as appropriate.
1039 // Preconditions:
1040 // * _deviceLock should be held on entry
1041 {
1042  if (_paraAccelParams && _paraAccelSecondaryParams) {
1043  IOFixed64 deltaX = {sc2f(*dxp)};
1044  IOFixed64 deltaY = {sc2f(*dyp)};
1045  IOFixed64 fractX = {_fractX};
1046  IOFixed64 fractY = {_fractY};
1047  IOFixed64 mag = {sc2f(llsqrt(f2sc(f_add(f_mul(deltaX, deltaX), f_mul(deltaY, deltaY)))))};
1048 
1049  IOFixed64 mult = PACurvesGetAccelerationMultiplier(mag, _paraAccelParams, _paraAccelSecondaryParams);
1050  deltaX = f_mul(deltaX, mult);
1051  deltaY = f_mul(deltaY, mult);
1052  deltaX = f_add(deltaX, fractX);
1053  deltaY = f_add(deltaY, fractY);
1054 
1055  *dxp = as32(deltaX);
1056  *dyp = as32(deltaY);
1057 
1058  _fractX = deltaX.value;
1059  _fractY = deltaY.value;
1060 
1061  // sign extend fractional part
1062  if( deltaX.value < 0LL )
1063  _fractX |= 0xffff0000;
1064  else
1065  _fractX &= 0x0000ffff;
1066 
1067  if( deltaY.value < 0LL)
1068  _fractY |= 0xffff0000;
1069  else
1070  _fractY &= 0x0000ffff;
1071  }
1072  else {
1073  ScaleAxes(_scaleSegments, dxp, &_fractX, dyp, &_fractY);
1074  }
1075 }
1076 
1077 static void setupScrollForAcceleration( IOFixed desired ){
1078  IOFixed devScale = 0;
1079  IOFixed scrScale = 0;
1080  IOFixed reportRate = scrollReportRate();
1081  CFDataRef accelTable = NULL;
1082  UInt32 type = 0;
1083 
1084  _scrollWheelInfo.rateMultiplier = IOFixedDivide(reportRate, FRAME_RATE);
1085  _scrollPointerInfo.rateMultiplier = IOFixedDivide(reportRate, FRAME_RATE);
1086 
1087  if (desired < 0) {
1088  }
1089  else {
1090 
1091  CFArrayRef parametricAccelerationCurves;
1092  IOPointingCopyCFTypeParameter(CFSTR(kHIDScrollAccelParametricCurvesKey), (CFTypeRef*)&parametricAccelerationCurves);
1093 
1094  for ( type=kAccelTypeY; type<=kAccelTypeZ; type++) {
1095  IOFixed res = scrollResolutionForType(type);
1096  // Zero scroll resolution says you don't want acceleration.
1097  if ( res ) {
1098  _scrollWheelInfo.axis[type].isHighResScroll = res > (SCROLL_DEFAULT_RESOLUTION * 2);
1099  _scrollPointerInfo.axis[type].isHighResScroll = _scrollWheelInfo.axis[type].isHighResScroll;
1100 
1101  _scrollWheelInfo.axis[type].consumeClearThreshold = (IOFixedDivide(res, SCROLL_CONSUME_RESOLUTION) >> 16) * 2;
1102  _scrollPointerInfo.axis[type].consumeClearThreshold = _scrollWheelInfo.axis[type].consumeClearThreshold;
1103 
1104  _scrollWheelInfo.axis[type].consumeCountThreshold = _scrollWheelInfo.axis[type].consumeClearThreshold * SCROLL_CONSUME_COUNT_MULTIPLIER;
1105  _scrollPointerInfo.axis[type].consumeCountThreshold = _scrollPointerInfo.axis[type].consumeClearThreshold * SCROLL_CONSUME_COUNT_MULTIPLIER;
1106 
1107  bzero(&(_scrollWheelInfo.axis[type].state), sizeof(ScaleDataState));
1108  bzero(&(_scrollWheelInfo.axis[type].consumeState), sizeof(ScaleConsumeState));
1109  bzero(&(_scrollPointerInfo.axis[type].state), sizeof(ScaleDataState));
1110  bzero(&(_scrollPointerInfo.axis[type].consumeState), sizeof(ScaleConsumeState));
1111 
1112  clock_gettime(CLOCK_MONOTONIC, &(_scrollWheelInfo.axis[type].lastEventTime));
1113  _scrollPointerInfo.axis[type].lastEventTime = _scrollWheelInfo.axis[type].lastEventTime;
1114 
1115  if (parametricAccelerationCurves && CFGetTypeID(parametricAccelerationCurves) == CFArrayGetTypeID() && reportRate) {
1116  IOFixed64 desired64 = { desired };
1117  IOFixed64 devScale64 = { res };
1118  IOFixed64 scrScale64 = { SCREEN_RESOLUTION };
1119 
1120  devScale64 = f_div(devScale64, *(IOFixed64[]){{ reportRate }});
1121 
1122  scrScale64 = f_div(scrScale64, *(IOFixed64[]){{ FRAME_RATE }});
1123 
1124  _scrollWheelInfo.axis[type].isParametric =
1125  PACurvesSetupAccelParams(parametricAccelerationCurves,
1126  desired64,
1127  devScale64,
1128  scrScale64,
1129  &_scrollWheelInfo.axis[type].primaryParametrics,
1130  &_scrollWheelInfo.axis[type].secondaryParametrics);
1131  }
1132 
1133  if (!_scrollWheelInfo.axis[type].isParametric) {
1134  accelTable = copyScrollAccelerationTableForType(type);
1135 
1136  // Setup pixel scroll wheel acceleration table
1137  devScale = IOFixedDivide( res, reportRate );
1138  scrScale = IOFixedDivide( SCREEN_RESOLUTION, FRAME_RATE );
1139 
1140  SetupAcceleration (accelTable, desired, devScale, scrScale, &(_scrollWheelInfo.axis[type].scaleSegments), &(_scrollWheelInfo.axis[type].scaleSegCount));
1141 
1142  // Grab the pointer resolution
1143  res = resolution();
1144  reportRate = FRAME_RATE;
1145 
1146  // Setup pixel pointer drag/scroll acceleration table
1147  devScale = IOFixedDivide( res, reportRate );
1148  scrScale = IOFixedDivide( SCREEN_RESOLUTION, FRAME_RATE );
1149 
1150  SetupAcceleration (accelTable, desired, devScale, scrScale, &(_scrollPointerInfo.axis[type].scaleSegments), &(_scrollPointerInfo.axis[type].scaleSegCount));
1151 
1152  if (accelTable)
1153  CFRelease(accelTable);
1154  }
1155  }
1156  }
1157  if(parametricAccelerationCurves) CFRelease(parametricAccelerationCurves);
1158  }
1159 }
1160 
1161 static void AccelerateScrollAxis( IOFixed * axisp,
1162  ScrollAxisAccelInfo * scaleInfo,
1163  struct timespec* timeStamp,
1164  IOFixed rateMultiplier,
1165  bool clear)
1166 {
1167  IOFixed absAxis = 0;
1168  int avgIndex = 0;
1169  IOFixed avgCount = 0;
1170  IOFixed avgAxis = 0;
1171  IOFixed timeDeltaMS = 0;
1172  IOFixed avgTimeDeltaMS = 0;
1173  UInt64 currentTimeNSLL = 0;
1174  UInt64 lastEventTimeNSLL = 0;
1175  UInt64 timeDeltaMSLL = 0;
1176 
1177  if ( ! (scaleInfo && ( scaleInfo->scaleSegments || scaleInfo->isParametric) ) )
1178  return;
1179 
1180  absAxis = abs(*axisp);
1181 
1182  if( absAxis == 0 )
1183  return;
1184 
1185  currentTimeNSLL = timeStamp->tv_nsec + timeStamp->tv_sec * 1000000000;
1186  lastEventTimeNSLL = scaleInfo->lastEventTime.tv_nsec + scaleInfo->lastEventTime.tv_sec * 1000000000;
1187 
1188  scaleInfo->lastEventTime = *timeStamp;
1189 
1190  timeDeltaMSLL = (currentTimeNSLL - lastEventTimeNSLL) / 1000000;
1191 
1192  // RY: To compensate for non continual motion, we have added a second
1193  // threshold. This whill allow a user with a standard scroll wheel
1194  // to continue with acceleration when lifting the finger within a
1195  // predetermined time. We should also clear out the last time deltas
1196  // if the direction has changed.
1197  if ((timeDeltaMSLL >= SCROLL_CLEAR_THRESHOLD_MS_LL) || clear) {
1198  bzero(&(scaleInfo->state), sizeof(ScaleDataState));
1199  timeDeltaMSLL = SCROLL_CLEAR_THRESHOLD_MS_LL;
1200  }
1201 
1202  timeDeltaMS = ((UInt32) timeDeltaMSLL) * kIOFixedOne;
1203 
1204  scaleInfo->state.deltaTime[scaleInfo->state.deltaIndex] = timeDeltaMS;
1205  scaleInfo->state.deltaAxis[scaleInfo->state.deltaIndex] = absAxis;
1206 
1207  // RY: To eliminate jerkyness associated with the scroll acceleration,
1208  // we scroll based on the average of the last n events. This has the
1209  // effect of make acceleration smoother with accel and decel.
1210  for (int index=0; index < SCROLL_TIME_DELTA_COUNT; index++)
1211  {
1212  avgIndex = (scaleInfo->state.deltaIndex + SCROLL_TIME_DELTA_COUNT - index) % SCROLL_TIME_DELTA_COUNT;
1213  avgAxis += scaleInfo->state.deltaAxis[avgIndex];
1214  avgCount ++;
1215 
1216  if ((scaleInfo->state.deltaTime[avgIndex] <= 0) ||
1217  (scaleInfo->state.deltaTime[avgIndex] >= SCROLL_EVENT_THRESHOLD_MS)) {
1218  // the previous event was too long before this one. stop looking.
1219  avgTimeDeltaMS += SCROLL_EVENT_THRESHOLD_MS;
1220  break;
1221  }
1222 
1223  avgTimeDeltaMS += scaleInfo->state.deltaTime[avgIndex];
1224 
1225  if (avgTimeDeltaMS >= (SCROLL_CLEAR_THRESHOLD_MS_LL * kIOFixedOne)) {
1226  // the previous event was too long ago. stop looking.
1227  break;
1228  }
1229  }
1230 
1231  // Bump the next index
1232  scaleInfo->state.deltaIndex = (scaleInfo->state.deltaIndex + 1) % SCROLL_TIME_DELTA_COUNT;
1233 
1234  avgAxis = (avgCount) ? (avgAxis / avgCount) : 0;
1235  avgTimeDeltaMS = (avgCount) ? (avgTimeDeltaMS / avgCount) : 0;
1236  avgTimeDeltaMS = IOFixedMultiply(avgTimeDeltaMS, rateMultiplier);
1237  if (avgTimeDeltaMS > SCROLL_EVENT_THRESHOLD_MS) {
1238  avgTimeDeltaMS = SCROLL_EVENT_THRESHOLD_MS;
1239  }
1240  else if (avgTimeDeltaMS < kIOFixedOne) {
1241  // anything less than 1 ms is not resonable
1242  avgTimeDeltaMS = kIOFixedOne;
1243  }
1244 
1245  // RY: Since we want scroll acceleration to work with the
1246  // time delta and the accel curves, we have come up with
1247  // this approach:
1248  //
1249  // scrollMultiplier = (SCROLL_MULTIPLIER_A * (avgTimeDeltaMS^2)) +
1250  // (SCROLL_MULTIPLIER_B * avgTimeDeltaMS) +
1251  // SCROLL_MULTIPLIER_C
1252  //
1253  // scrollMultiplier *= avgDeviceDelta
1254  //
1255  // The boost curve follows a quadratic/parabolic curve which
1256  // results in a smoother boost.
1257  //
1258  // The resulting multipler is applied to the average axis
1259  // magnitude and then compared against the accleration curve.
1260  //
1261  // The value acquired from the graph will then be multiplied
1262  // to the current axis delta.
1263  IOFixed64 scrollMultiplier;
1264  IOFixed64 timedDelta = { avgTimeDeltaMS };
1265  IOFixed64 axisValue = { *axisp };
1266  IOFixed64 minimumMultiplier = { kIOFixedOne >> 4 };
1267 
1268  scrollMultiplier = f_mul(f_mul(*(IOFixed64[]){ { SCROLL_MULTIPLIER_A } }, timedDelta), timedDelta);
1269  scrollMultiplier = f_sub(scrollMultiplier, f_mul(*(IOFixed64[]){ { SCROLL_MULTIPLIER_B } }, timedDelta));
1270  scrollMultiplier = f_add(scrollMultiplier, *(IOFixed64[]){ { SCROLL_MULTIPLIER_C } });
1271  scrollMultiplier = f_mul(scrollMultiplier, *(IOFixed64[]){ { rateMultiplier } });
1272  scrollMultiplier = f_mul(scrollMultiplier, *(IOFixed64[]){ { avgAxis } });
1273  if (f_lt(scrollMultiplier, minimumMultiplier)) {
1274  scrollMultiplier = minimumMultiplier;
1275  }
1276 
1277  if (scaleInfo->isParametric) {
1278  scrollMultiplier = PACurvesGetAccelerationMultiplier(scrollMultiplier, &scaleInfo->primaryParametrics, &scaleInfo->secondaryParametrics);
1279  }
1280  else {
1281  CursorDeviceSegment *segment;
1282 
1283  // scale
1284  for(segment = (CursorDeviceSegment *) scaleInfo->scaleSegments;
1285  f_gt(scrollMultiplier, *(IOFixed64[]){ { segment->devUnits } });
1286  segment++)
1287  {}
1288 
1289  if (avgCount > 2) {
1290  // Continuous scrolling in one direction indicates a desire to go faster.
1291  scrollMultiplier = f_mul(scrollMultiplier, *(IOFixed64[]){ { sc2f((SInt64)lsqrt(avgCount * 16)) } });
1292  scrollMultiplier = f_div(scrollMultiplier, *(IOFixed64[]){ { sc2f(4) } });
1293  }
1294 
1295  scrollMultiplier = f_add(*(IOFixed64[]){ { segment->intercept } }, f_div(f_mul(scrollMultiplier, *(IOFixed64[]){ { segment->slope } }), *(IOFixed64[]){ { absAxis } } ));
1296  }
1297  axisValue = f_mul(axisValue, scrollMultiplier);
1298  *axisp = axisValue.value;
1299 }
1300 
1301 static void scaleWheel(int* deltaAxis1, SInt32* fixedDeltaAxis1, SInt32* pointDeltaAxis1, struct timespec ts){
1302  int deltaAxis2 = 0, deltaAxis3 = 0;
1303 
1304  bool negative = (*deltaAxis1 < 0);
1305  _scrollFixedDeltaAxis1 = *deltaAxis1 * SCROLL_DEFAULT_RESOLUTION;
1306  //_scrollFixedDeltaAxis1 = *deltaAxis1 << 16;
1307  CONVERT_SCROLL_FIXED_TO_COARSE(IOFixedMultiply(_scrollFixedDeltaAxis1, SCROLL_WHEEL_TO_PIXEL_SCALE), _scrollPointDeltaAxis1);
1308 
1309  bool directionChange[3] = {0,0,0};
1310  bool typeChange = FALSE;
1311  SInt32* pDeltaAxis[3] = {deltaAxis1, &deltaAxis2, &deltaAxis3};
1312  SInt32* pScrollFixedDeltaAxis[3] = {&_scrollFixedDeltaAxis1, &_scrollFixedDeltaAxis2, &_scrollFixedDeltaAxis3};
1313  IOFixed* pScrollPointDeltaAxis[3] = {&_scrollPointDeltaAxis1, &_scrollPointDeltaAxis2, &_scrollPointDeltaAxis3};
1314 
1315  for (UInt32 type=kAccelTypeY; type<=kAccelTypeZ; type++ ) {
1316  directionChange[type] = ((_scrollWheelInfo.axis[type].lastValue == 0) ||
1317  ((_scrollWheelInfo.axis[type].lastValue < 0) && (*(pDeltaAxis[type]) > 0)) ||
1318  ((_scrollWheelInfo.axis[type].lastValue > 0) && (*(pDeltaAxis[type]) < 0)));
1319  _scrollWheelInfo.axis[type].lastValue = *(pDeltaAxis[type]);
1320 
1321  if ( _scrollWheelInfo.axis[type].scaleSegments || _scrollWheelInfo.axis[type].isParametric ) {
1322 
1323  *(pScrollPointDeltaAxis[type]) = _scrollWheelInfo.axis[type].lastValue << 16;
1324 
1325  AccelerateScrollAxis(pScrollPointDeltaAxis[type],
1326  &(_scrollWheelInfo.axis[type]),
1327  &ts,
1328  _scrollWheelInfo.rateMultiplier,
1329  directionChange[type] || typeChange);
1330 
1331  CONVERT_SCROLL_FIXED_TO_COARSE(pScrollPointDeltaAxis[type][0], pScrollPointDeltaAxis[type][0]);
1332 
1333  // RY: Convert pixel value to points
1334  *(pScrollFixedDeltaAxis[type]) = *(pScrollPointDeltaAxis[type]) << 16;
1335 
1336  if ( directionChange[type] )
1337  bzero(&(_scrollWheelInfo.axis[type].consumeState), sizeof(ScaleConsumeState));
1338 
1339  // RY: throttle the tranlation of scroll based on the resolution threshold.
1340  // This allows us to not generated traditional scroll wheel (line) events
1341  // for high res devices at really low (fine granularity) speeds. This
1342  // prevents a succession of single scroll events that can make scrolling
1343  // slowly actually seem faster.
1344  if ( _scrollWheelInfo.axis[type].consumeCountThreshold )
1345  {
1346  _scrollWheelInfo.axis[type].consumeState.consumeAccum += *(pScrollFixedDeltaAxis[type]) + ((*(pScrollFixedDeltaAxis[type])) ? _scrollWheelInfo.axis[type].state.fraction : 0);
1347  _scrollWheelInfo.axis[type].consumeState.consumeCount += abs(_scrollWheelInfo.axis[type].lastValue);
1348 
1349  if (*(pScrollFixedDeltaAxis[type]) &&
1350  ((abs(_scrollWheelInfo.axis[type].lastValue) >= (SInt32)_scrollWheelInfo.axis[type].consumeClearThreshold) ||
1351  (_scrollWheelInfo.axis[type].consumeState.consumeCount >= _scrollWheelInfo.axis[type].consumeCountThreshold)))
1352  {
1353  *(pScrollFixedDeltaAxis[type]) = _scrollWheelInfo.axis[type].consumeState.consumeAccum;
1354  _scrollWheelInfo.axis[type].consumeState.consumeAccum = 0;
1355  _scrollWheelInfo.axis[type].consumeState.consumeCount = 0;
1356  }
1357  else
1358  {
1359  *(pScrollFixedDeltaAxis[type]) = 0;
1360  }
1361  }
1362 
1363  *(pScrollFixedDeltaAxis[type]) = IOFixedMultiply(*(pScrollFixedDeltaAxis[type]), SCROLL_PIXEL_TO_WHEEL_SCALE);
1364 
1365  // RY: Generate fixed point and course scroll deltas.
1366  CONVERT_SCROLL_FIXED_TO_COARSE(*(pScrollFixedDeltaAxis[type]), *(pDeltaAxis[type]));
1367  }
1368  }
1369  *fixedDeltaAxis1 = _scrollFixedDeltaAxis1;
1370  *pointDeltaAxis1 = _scrollPointDeltaAxis1;
1371  // Prevent direction reversing (I'm not sure why this happens...)
1372  if(negative != (*deltaAxis1 < 0))
1373  *deltaAxis1 = -*deltaAxis1;
1374  if(negative != (*fixedDeltaAxis1 < 0))
1375  *fixedDeltaAxis1 = -*fixedDeltaAxis1;
1376  if(negative != (*pointDeltaAxis1 < 0))
1377  *pointDeltaAxis1 = -*pointDeltaAxis1;
1378 }
1379 
1380 // Setup utilities
1381 
1382 static uint get_desired(CFStringRef type_key, CFStringRef fallback_key){
1383  int res = 0;
1384  CFTypeRef number = 0;
1385  CFTypeRef accelKey = 0;
1386  if(IOPointingCopyCFTypeParameter(type_key, &accelKey) == kIOReturnSuccess){
1387  if(CFGetTypeID(accelKey) == CFStringGetTypeID())
1388  IOPointingCopyCFTypeParameter(accelKey, &number);
1389  if(accelKey) CFRelease(accelKey);
1390  }
1391  if(!number)
1392  IOPointingCopyCFTypeParameter(fallback_key, (CFTypeRef*)&number);
1393  if(!number)
1394  return 0;
1395  if(CFGetTypeID(number) == CFNumberGetTypeID())
1396  CFNumberGetValue(number, kCFNumberIntType, &res);
1397  else if(CFGetTypeID(number) == CFDataGetTypeID())
1398  CFDataGetBytes(number, CFRangeMake(0, sizeof(int)), (UInt8*)&res);
1399  CFRelease(number);
1400  return res;
1401 }
1402 
1403 static void do_setup(io_connect_t event, struct timespec now){
1404  // If it's been a while since the last check (1s) or we haven't set up yet, get the desired acceleration values
1405  struct timespec last = last_setup_poll;
1406  timespec_add(&last, 1000000000);
1407  if(!has_setup || timespec_gt(now, last)){
1408  static uint desired_mouse = UINT_MAX, desired_wheel = UINT_MAX;
1409  uint desired = get_desired(CFSTR(kIOHIDPointerAccelerationTypeKey), CFSTR(kIOHIDPointerAccelerationKey));
1410  // Set up parameters again if the value has changed
1411  if(desired != desired_mouse || !has_setup)
1412  setupForAcceleration(desired_mouse = desired);
1413  desired = get_desired(CFSTR(kIOHIDScrollAccelerationTypeKey), CFSTR(kIOHIDScrollAccelerationKey));
1414  if(desired != desired_wheel || !has_setup)
1415  setupScrollForAcceleration(desired_wheel = desired);
1416  has_setup = 1;
1417  last_setup_poll = now;
1418  }
1419 }
1420 
1421 // External functions (called from input_mac.c)
1422 
1423 void mouse_accel(io_connect_t event, int* x, int* y){
1424  pthread_mutex_lock(&mutex);
1425  struct timespec now;
1426  clock_gettime(CLOCK_MONOTONIC, &now);
1427  do_setup(event, now);
1428  scalePointer(x, y);
1429  pthread_mutex_unlock(&mutex);
1430 }
1431 
1432 void wheel_accel(io_connect_t event, int* deltaAxis1, SInt32* fixedDeltaAxis1, SInt32* pointDeltaAxis1){
1433  pthread_mutex_lock(&mutex);
1434  struct timespec now;
1435  clock_gettime(CLOCK_MONOTONIC, &now);
1436  do_setup(event, now);
1437  scaleWheel(deltaAxis1, fixedDeltaAxis1, pointDeltaAxis1, now);
1438  pthread_mutex_unlock(&mutex);
1439 }
1440 
1441 #endif // OS_MAC
int power
Definition: main.c:59
#define FALSE
Definition: ckb-anim.h:49
#define timespec_gt(left, right)
Definition: includes.h:59
float y
Definition: main.c:66
float x
Definition: main.c:66
struct parameter contains the values for a fresh started macro_play thread. parameter_t is the typede...
Definition: input.h:54
Definition: keymap.h:49
void timespec_add(struct timespec *timespec, long nanoseconds)
Definition: main.c:19