1/*
2 * @APPLE_LICENSE_HEADER_START@
3 *
4 * Copyright (c) 1999-2009 Apple Computer, Inc.  All Rights Reserved.
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * 17 July 	1998 	sdouglas	Initial creation
25 * 01 April 	2002 	ryepez		added support for scroll acceleration
26 */
27
28#if 0
29#define DEBUG_ASSERT_COMPONENT_NAME_STRING  "IOHIPointing"
30#define DEBUG_ASSERT_PRODUCTION_CODE        0
31#endif
32
33#include <AssertMacros.h>
34#include <IOKit/IOLib.h>
35#include <IOKit/assert.h>
36#include <IOKit/hidsystem/IOHIPointing.h>
37#include <IOKit/pwr_mgt/RootDomain.h>
38#include <libkern/OSByteOrder.h>
39#include "IOHIDParameter.h"
40#include "IOHIDSystem.h"
41#include "IOHIDPointingDevice.h"
42#include "IOHIDevicePrivateKeys.h"
43#include "IOHIDParameter.h"
44#include "IOFixed64.h"
45#include "ev_private.h"
46
47#ifndef abs
48#define abs(_a)	((_a >= 0) ? _a : -_a)
49#endif
50
51#ifndef IOFixedSquared
52#define IOFixedSquared(a) IOFixedMultiply(a, a)
53#endif
54
55#define FRAME_RATE                  (67 << 16)
56#define SCREEN_RESOLUTION           (96 << 16)
57
58#define MAX_DEVICE_THRESHOLD        0x7fffffff
59
60#define kIOFixedOne                     0x10000ULL
61#define SCROLL_DEFAULT_RESOLUTION       (9 * kIOFixedOne)
62#define SCROLL_CONSUME_RESOLUTION       (100 * kIOFixedOne)
63#define SCROLL_CONSUME_COUNT_MULTIPLIER 3
64#define SCROLL_EVENT_THRESHOLD_MS_LL    150ULL
65#define SCROLL_EVENT_THRESHOLD_MS       (SCROLL_EVENT_THRESHOLD_MS_LL * kIOFixedOne)
66#define SCROLL_CLEAR_THRESHOLD_MS_LL    500ULL
67
68#define SCROLL_MULTIPLIER_RANGE         0x00018000
69#define SCROLL_MULTIPLIER_A             0x00000002 /*IOFixedDivide(SCROLL_MULTIPLIER_RANGE,SCROLL_EVENT_THRESHOLD_MS*2)*/
70#define SCROLL_MULTIPLIER_B             0x000003bb /*IOFixedDivide(SCROLL_MULTIPLIER_RANGE*3,(SCROLL_EVENT_THRESHOLD_MS^2)*2)*/
71#define SCROLL_MULTIPLIER_C             0x00018041
72
73
74#define SCROLL_WHEEL_TO_PIXEL_SCALE     0x000a0000/* IOFixedDivide(SCREEN_RESOLUTION, SCROLL_DEFAULT_RESOLUTION) */
75#define SCROLL_PIXEL_TO_WHEEL_SCALE     0x0000199a/* IOFixedDivide(SCREEN_RESOLUTION, SCROLL_DEFAULT_RESOLUTION) */
76
77#define CONVERT_SCROLL_FIXED_TO_FRACTION(fixed, fraction)   \
78{                                                           \
79        if( fixed >= 0)                                     \
80            fraction = fixed & 0xffff;                      \
81        else                                                \
82            fraction = fixed | 0xffff0000;                  \
83}
84
85#define CONVERT_SCROLL_FIXED_TO_INTEGER(fixedAxis, integer) \
86{                                                           \
87        SInt32 tempInt = 0;                                 \
88        if((fixedAxis < 0) && (fixedAxis & 0xffff))         \
89            tempInt = (fixedAxis >> 16) + 1;                \
90        else                                                \
91            tempInt = (fixedAxis >> 16);                    \
92        integer = tempInt;                                  \
93}
94
95#define CONVERT_SCROLL_FIXED_TO_COARSE(fixedAxis, coarse)   \
96{                                                           \
97        SInt32 tempCoarse = 0;                              \
98        CONVERT_SCROLL_FIXED_TO_INTEGER(fixedAxis, tempCoarse)  \
99        if (!tempCoarse && (fixedAxis & 0xffff))            \
100            tempCoarse = (fixedAxis < 0) ? -1 : 1;          \
101        coarse = tempCoarse;                                \
102}
103
104
105#define _scrollButtonMask                   _reserved->scrollButtonMask
106#define _scrollType                         _reserved->scrollType
107#define _scrollZoomMask                     _reserved->scrollZoomMask
108#define _scrollOff                          _reserved->scrollOff
109#define _lastScrollWasZoom                  _reserved->lastScrollWasZoom
110
111#define _scrollWheelInfo                    _reserved->scrollWheelInfo
112#define _scrollPointerInfo                  _reserved->scrollPointerInfo
113#define _paraAccelParams                    _reserved->paraAccelParams
114#define _paraAccelSecondaryParams           _reserved->paraAccelSecondaryParams
115
116#define _scrollFixedDeltaAxis1              _reserved->scrollFixedDeltaAxis1
117#define _scrollFixedDeltaAxis2              _reserved->scrollFixedDeltaAxis2
118#define _scrollFixedDeltaAxis3              _reserved->scrollFixedDeltaAxis3
119#define _scrollPointDeltaAxis1              _reserved->scrollPointDeltaAxis1
120#define _scrollPointDeltaAxis2              _reserved->scrollPointDeltaAxis2
121#define _scrollPointDeltaAxis3              _reserved->scrollPointDeltaAxis3
122
123#define _hidPointingNub                     _reserved->hidPointingNub
124#define _isSeized                           _reserved->isSeized
125#define _openClient                         _reserved->openClient
126#define _accelerateMode                     _reserved->accelerateMode
127
128#define DEVICE_LOCK     IOLockLock( _deviceLock )
129#define DEVICE_UNLOCK   IOLockUnlock( _deviceLock )
130
131enum {
132    kAccelTypeGlobal = -1,
133    kAccelTypeY = 0, //delta axis 1
134    kAccelTypeX = 1, //delta axis 2
135    kAccelTypeZ = 2  //delta axis 3
136};
137
138struct CursorDeviceSegment {
139    SInt32	devUnits;
140    SInt32	slope;
141    SInt32	intercept;
142};
143typedef struct CursorDeviceSegment CursorDeviceSegment;
144
145#define SCROLL_TIME_DELTA_COUNT		8
146struct ScaleDataState
147{
148    UInt8           deltaIndex;
149    IOFixed         deltaTime[SCROLL_TIME_DELTA_COUNT];
150    IOFixed         deltaAxis[SCROLL_TIME_DELTA_COUNT];
151    IOFixed         fraction;
152};
153typedef ScaleDataState ScaleDataState;
154
155struct ScaleConsumeState
156{
157    UInt32      consumeCount;
158    IOFixed     consumeAccum;
159};
160typedef ScaleConsumeState ScaleConsumeState;
161
162struct IOHIPointing__PAParameters
163{
164    IOFixed64   deviceMickysDivider;
165    IOFixed64   cursorSpeedMultiplier;
166    IOFixed64   accelIndex;
167    IOFixed64   gain[4];
168    IOFixed64   tangent[2];
169};
170
171struct IOHIPointing__PASecondaryParameters
172{
173    int         firstTangent;
174    IOFixed64   m0; // m1 == m0
175    IOFixed64   b0; // no b1
176    IOFixed64   y0;
177    IOFixed64   y1;
178    IOFixed64   m_root;
179    IOFixed64   b_root;
180};
181
182struct ScrollAxisAccelInfo
183{
184    AbsoluteTime        lastEventTime;
185    void *              scaleSegments;
186    IOItemCount         scaleSegCount;
187    ScaleDataState      state;
188    ScaleConsumeState   consumeState;
189    IOHIPointing__PAParameters          primaryParametrics;
190    IOHIPointing__PASecondaryParameters secondaryParametrics;
191    SInt32              lastValue;
192    UInt32              consumeClearThreshold;
193    UInt32              consumeCountThreshold;
194    bool                isHighResScroll;
195    bool                isParametric;
196};
197typedef ScrollAxisAccelInfo ScrollAxisAccelInfo;
198
199struct ScrollAccelInfo
200{
201    ScrollAxisAccelInfo axis[3];
202
203    IOFixed             rateMultiplier;
204    UInt32              zoom:1;
205};
206typedef ScrollAccelInfo ScrollAccelInfo;
207
208static bool SetupAcceleration (OSData * data, IOFixed desired, IOFixed devScale, IOFixed crsrScale, void ** scaleSegments, IOItemCount * scaleSegCount);
209static void ScaleAxes (void * scaleSegments, int * axis1p, IOFixed *axis1Fractp, int * axis2p, IOFixed *axis2Fractp);
210static IOFixed64 OSObjectToIOFixed64(OSObject *in);
211static bool PACurvesFillParamsFromDict(OSDictionary *parameters, const IOFixed64 devScale, const IOFixed64 crsrScale, IOHIPointing__PAParameters &outParams);
212static bool PACurvesSetupAccelParams (OSArray *parametricCurves, IOFixed64 desired, IOFixed64 devScale, IOFixed64 crsrScale, IOHIPointing__PAParameters &primaryParams, IOHIPointing__PASecondaryParameters &secondaryParams);
213static IOFixed64 PACurvesGetAccelerationMultiplier(const IOFixed64 device_speed_mickeys, const IOHIPointing__PAParameters &params, const IOHIPointing__PASecondaryParameters &secondaryParams);
214static OSDictionary* PACurvesDebugDictionary(IOHIPointing__PAParameters &primaryParams, IOHIPointing__PASecondaryParameters &secondaryParams);
215
216
217struct IOHIPointing::ExpansionData
218{
219    UInt32      scrollType;
220
221    ScrollAccelInfo * scrollWheelInfo;
222    ScrollAccelInfo * scrollPointerInfo;
223    IOHIPointing__PAParameters *paraAccelParams;
224    IOHIPointing__PASecondaryParameters *paraAccelSecondaryParams;
225
226    IOFixed		scrollFixedDeltaAxis1;
227    IOFixed		scrollFixedDeltaAxis2;
228    IOFixed		scrollFixedDeltaAxis3;
229    SInt32		scrollPointDeltaAxis1;
230    SInt32		scrollPointDeltaAxis2;
231    SInt32		scrollPointDeltaAxis3;
232    UInt32      scrollButtonMask;
233
234    // Added to post events to the HID Manager
235    IOHIDPointingDevice	* hidPointingNub;
236    IOService   * openClient;
237
238    UInt32      accelerateMode;
239    UInt32      scrollZoomMask;
240    bool		isSeized;
241    bool        lastScrollWasZoom;
242    bool        scrollOff;
243    bool        scrollResolutionWarningComplete;
244};
245
246#define super IOHIDevice
247OSDefineMetaClassAndStructors(IOHIPointing, IOHIDevice);
248
249bool IOHIPointing::init(OSDictionary * properties)
250{
251    if (!super::init(properties))  return false;
252
253    /*
254    * Initialize minimal state.
255    */
256
257    _reserved = IONew(ExpansionData, 1);
258
259    if (!_reserved) return false;
260
261    bzero(_reserved, sizeof(ExpansionData));
262
263    // Initialize pointer accel items
264    _scaleSegments 		= 0;
265    _scaleSegCount 		= 0;
266    _fractX			= 0;
267    _fractY     		= 0;
268
269    _acceleration	= -1;
270    _accelerateMode = ( kAccelScroll | kAccelMouse );
271    _convertAbsoluteToRelative = false;
272    _contactToMove = false;
273    _hadContact = false;
274    _pressureThresholdToClick = 128;
275    _previousLocation.x = 0;
276    _previousLocation.y = 0;
277
278    _hidPointingNub = 0;
279
280    _isSeized = false;
281
282    // default to right mouse button generating unique events
283    _buttonMode = NX_RightButton;
284
285    _scrollWheelInfo = (ScrollAccelInfo *) IOMalloc(sizeof(ScrollAccelInfo));
286    if (!_scrollWheelInfo) return false;
287
288    bzero(_scrollWheelInfo, sizeof(ScrollAccelInfo));
289
290    _scrollPointerInfo = (ScrollAccelInfo *) IOMalloc(sizeof(ScrollAccelInfo));
291    if (!_scrollPointerInfo) return false;
292
293    bzero(_scrollPointerInfo, sizeof(ScrollAccelInfo));
294
295    _deviceLock = IOLockAlloc();
296    if (!_deviceLock)  return false;
297
298    return true;
299}
300
301bool IOHIPointing::start(IOService * provider)
302{
303  if (!super::start(provider))  return false;
304
305  // default acceleration settings
306  if (!getProperty(kIOHIDPointerAccelerationTypeKey))
307    setProperty(kIOHIDPointerAccelerationTypeKey, kIOHIDMouseAccelerationType);
308
309  if (!getProperty(kIOHIDScrollAccelerationTypeKey))
310    setProperty(kIOHIDScrollAccelerationTypeKey, kIOHIDMouseScrollAccelerationKey);
311
312	if (!getProperty(kIOHIDDisallowRemappingOfPrimaryClickKey))
313		if (provider->getProperty(kIOHIDDisallowRemappingOfPrimaryClickKey))
314			setProperty(kIOHIDDisallowRemappingOfPrimaryClickKey, provider->getProperty(kIOHIDDisallowRemappingOfPrimaryClickKey));
315
316  /*
317   * RY: Publish a property containing the button Count.  This will
318   * will be used to determine whether or not the button
319   * behaviors can be modified.
320   */
321  if (buttonCount() > 1)
322  {
323     setProperty(kIOHIDPointerButtonCountKey, buttonCount(), 32);
324  }
325
326    OSNumber * number = (OSNumber*)copyProperty(kIOHIDScrollMouseButtonKey);
327
328    if (OSDynamicCast(OSNumber, number))
329	{
330		UInt32 value = number->unsigned32BitValue();
331
332        if (!value)
333            _scrollButtonMask = 0;
334        else
335            _scrollButtonMask = (1 << (value-1));
336    }
337    OSSafeReleaseNULL(number);
338
339  // create a IOHIDPointingDevice to post events to the HID Manager
340  _hidPointingNub = IOHIDPointingDevice::newPointingDeviceAndStart(this, buttonCount(), resolution() >> 16);
341
342  /*
343   * IOHIPointing serves both as a service and a nub (we lead a double
344   * life).  Register ourselves as a nub to kick off matching.
345   */
346
347	registerService(kIOServiceAsynchronous);
348
349	return true;
350}
351
352void IOHIPointing::free()
353// Description:	Go Away. Be careful when freeing the lock.
354{
355
356    if (_deviceLock)
357    {
358	IOLock * lock;
359
360	IOLockLock(_deviceLock);
361
362	lock = _deviceLock;
363	_deviceLock = NULL;
364
365	IOLockUnlock(lock);
366	IOLockFree(lock);
367    }
368
369    if(_scaleSegments && _scaleSegCount)
370        IODelete( _scaleSegments, CursorDeviceSegment, _scaleSegCount );
371
372    if ( _scrollWheelInfo )
373    {
374        UInt32 type;
375        for (type=kAccelTypeY; type<=kAccelTypeZ; type++) {
376            if(_scrollWheelInfo->axis[type].scaleSegments && _scrollWheelInfo->axis[type].scaleSegCount)
377                IODelete( _scrollWheelInfo->axis[type].scaleSegments, CursorDeviceSegment, _scrollWheelInfo->axis[type].scaleSegCount );
378        }
379
380        IOFree(_scrollWheelInfo, sizeof(ScrollAccelInfo));
381        _scrollWheelInfo = 0;
382    }
383
384    if ( _paraAccelParams )
385    {
386        IOFree(_paraAccelParams, sizeof(IOHIPointing__PAParameters));
387        _paraAccelParams = 0;
388    }
389
390    if ( _paraAccelSecondaryParams )
391    {
392        IOFree(_paraAccelSecondaryParams, sizeof(IOHIPointing__PASecondaryParameters));
393        _paraAccelSecondaryParams = 0;
394    }
395
396    if ( _scrollPointerInfo )
397    {
398        UInt32 type;
399        for (type=kAccelTypeY; type<=kAccelTypeZ; type++) {
400            if(_scrollPointerInfo->axis[type].scaleSegments && _scrollPointerInfo->axis[type].scaleSegCount)
401                IODelete( _scrollPointerInfo->axis[type].scaleSegments, CursorDeviceSegment, _scrollPointerInfo->axis[type].scaleSegCount );
402        }
403
404        IOFree(_scrollPointerInfo, sizeof(ScrollAccelInfo));
405        _scrollPointerInfo = 0;
406    }
407
408    if ( _hidPointingNub )
409    {
410        _hidPointingNub->release();
411        _hidPointingNub = 0;
412    }
413
414    if (_reserved) {
415        IODelete(_reserved, ExpansionData, 1);
416    }
417
418    super::free();
419}
420
421bool IOHIPointing::open(IOService *                client,
422			IOOptionBits	           options,
423                        RelativePointerEventAction rpeAction,
424                        AbsolutePointerEventAction apeAction,
425                        ScrollWheelEventAction     sweAction)
426{
427    if (client == this) {
428        return super::open(_openClient, options);
429    }
430
431    return open(client,
432                options,
433                0,
434                (RelativePointerEventCallback)rpeAction,
435                (AbsolutePointerEventCallback)apeAction,
436                (ScrollWheelEventCallback)sweAction);
437}
438
439bool IOHIPointing::open(IOService *			client,
440                      IOOptionBits			options,
441                      void *				refcon __unused,
442                      RelativePointerEventCallback	rpeCallback,
443                      AbsolutePointerEventCallback	apeCallback,
444                      ScrollWheelEventCallback		sweCallback)
445{
446    if (client == this) return true;
447
448    _openClient = client;
449
450    bool returnValue = open(this, options,
451                            (RelativePointerEventAction)_relativePointerEvent,
452                            (AbsolutePointerEventAction)_absolutePointerEvent,
453                            (ScrollWheelEventAction)_scrollWheelEvent);
454
455    if (!returnValue)
456        return false;
457
458    // Note: client object is already retained by superclass' open()
459    _relativePointerEventTarget = client;
460    _relativePointerEventAction = (RelativePointerEventAction)rpeCallback;
461    _absolutePointerEventTarget = client;
462    _absolutePointerEventAction = (AbsolutePointerEventAction)apeCallback;
463    _scrollWheelEventTarget = client;
464    _scrollWheelEventAction = (ScrollWheelEventAction)sweCallback;
465
466    return true;
467}
468
469void IOHIPointing::close(IOService * client, IOOptionBits)
470{
471  _relativePointerEventAction = NULL;
472  _relativePointerEventTarget = 0;
473  _absolutePointerEventAction = NULL;
474  _absolutePointerEventTarget = 0;
475  super::close(client);
476}
477
478IOReturn IOHIPointing::message( UInt32 type, IOService * provider,
479                                void * argument)
480{
481    IOReturn ret = kIOReturnSuccess;
482
483    switch(type)
484    {
485        case kIOHIDSystemDeviceSeizeRequestMessage:
486            if (OSDynamicCast(IOHIDDevice, provider))
487            {
488                _isSeized = (bool)argument;
489            }
490            break;
491
492        default:
493            ret = super::message(type, provider, argument);
494            break;
495    }
496
497    return ret;
498}
499
500IOReturn IOHIPointing::powerStateWillChangeTo( IOPMPowerFlags powerFlags,
501                        unsigned long newState, IOService * device )
502{
503  return( super::powerStateWillChangeTo( powerFlags, newState, device ));
504}
505
506IOReturn IOHIPointing::powerStateDidChangeTo( IOPMPowerFlags powerFlags,
507                        unsigned long newState, IOService * device )
508{
509  return( super::powerStateDidChangeTo( powerFlags, newState, device ));
510}
511
512IOHIDKind IOHIPointing::hidKind()
513{
514  return kHIRelativePointingDevice;
515}
516
517static void AccelerateScrollAxis(   IOFixed *               axisp,
518                                    ScrollAxisAccelInfo *   scaleInfo,
519                                    AbsoluteTime            timeStamp,
520                                    IOFixed                 rateMultiplier,
521                                    bool                    clear = false)
522{
523    IOFixed absAxis             = 0;
524    int     avgIndex            = 0;
525    IOFixed	avgCount            = 0;
526    IOFixed avgAxis             = 0;
527    IOFixed	timeDeltaMS         = 0;
528    IOFixed	avgTimeDeltaMS      = 0;
529    UInt64	currentTimeNSLL     = 0;
530    UInt64	lastEventTimeNSLL   = 0;
531    UInt64  timeDeltaMSLL       = 0;
532
533    if ( ! (scaleInfo && ( scaleInfo->scaleSegments || scaleInfo->isParametric) ) )
534        return;
535
536    absAxis = abs(*axisp);
537
538    if( absAxis == 0 )
539        return;
540
541    absolutetime_to_nanoseconds(timeStamp, &currentTimeNSLL);
542    absolutetime_to_nanoseconds(scaleInfo->lastEventTime, &lastEventTimeNSLL);
543
544    scaleInfo->lastEventTime = timeStamp;
545
546    timeDeltaMSLL = (currentTimeNSLL - lastEventTimeNSLL) / 1000000;
547
548    // RY: To compensate for non continual motion, we have added a second
549    // threshold.  This whill allow a user with a standard scroll wheel
550    // to continue with acceleration when lifting the finger within a
551    // predetermined time.  We should also clear out the last time deltas
552    // if the direction has changed.
553    if ((timeDeltaMSLL >= SCROLL_CLEAR_THRESHOLD_MS_LL) || clear) {
554        bzero(&(scaleInfo->state), sizeof(ScaleDataState));
555        timeDeltaMSLL = SCROLL_CLEAR_THRESHOLD_MS_LL;
556    }
557
558    timeDeltaMS = ((UInt32) timeDeltaMSLL) * kIOFixedOne;
559
560    scaleInfo->state.deltaTime[scaleInfo->state.deltaIndex] = timeDeltaMS;
561    scaleInfo->state.deltaAxis[scaleInfo->state.deltaIndex] = absAxis;
562
563    // RY: To eliminate jerkyness associated with the scroll acceleration,
564    // we scroll based on the average of the last n events.  This has the
565    // effect of make acceleration smoother with accel and decel.
566    for (int index=0; index < SCROLL_TIME_DELTA_COUNT; index++)
567    {
568        avgIndex = (scaleInfo->state.deltaIndex + SCROLL_TIME_DELTA_COUNT - index) % SCROLL_TIME_DELTA_COUNT;
569        avgAxis         += scaleInfo->state.deltaAxis[avgIndex];
570        avgCount ++;
571
572        if ((scaleInfo->state.deltaTime[avgIndex] <= 0) ||
573            (scaleInfo->state.deltaTime[avgIndex] >= SCROLL_EVENT_THRESHOLD_MS)) {
574            // the previous event was too long before this one. stop looking.
575            avgTimeDeltaMS += SCROLL_EVENT_THRESHOLD_MS;
576            break;
577        }
578
579        avgTimeDeltaMS  += scaleInfo->state.deltaTime[avgIndex];
580
581        if (avgTimeDeltaMS >= (SCROLL_CLEAR_THRESHOLD_MS_LL * kIOFixedOne)) {
582            // the previous event was too long ago. stop looking.
583            break;
584        }
585    }
586
587    // Bump the next index
588    scaleInfo->state.deltaIndex = (scaleInfo->state.deltaIndex + 1) % SCROLL_TIME_DELTA_COUNT;
589
590    avgAxis         = (avgCount) ? (avgAxis / avgCount) : 0;
591    avgTimeDeltaMS  = (avgCount) ? (avgTimeDeltaMS / avgCount) : 0;
592    avgTimeDeltaMS  = IOFixedMultiply(avgTimeDeltaMS, rateMultiplier);
593    if (avgTimeDeltaMS > SCROLL_EVENT_THRESHOLD_MS) {
594        avgTimeDeltaMS = SCROLL_EVENT_THRESHOLD_MS;
595    }
596    else if (avgTimeDeltaMS < kIOFixedOne) {
597        // anything less than 1 ms is not resonable
598        avgTimeDeltaMS = kIOFixedOne;
599    }
600
601    // RY: Since we want scroll acceleration to work with the
602    // time delta and the accel curves, we have come up with
603    // this approach:
604    //
605    // scrollMultiplier = (SCROLL_MULTIPLIER_A * (avgTimeDeltaMS^2)) +
606    //                      (SCROLL_MULTIPLIER_B * avgTimeDeltaMS) +
607    //                          SCROLL_MULTIPLIER_C
608    //
609    // scrollMultiplier *= avgDeviceDelta
610    //
611    // The boost curve follows a quadratic/parabolic curve which
612    // results in a smoother boost.
613    //
614    // The resulting multipler is applied to the average axis
615    // magnitude and then compared against the accleration curve.
616    //
617    // The value acquired from the graph will then be multiplied
618    // to the current axis delta.
619    IOFixed64           scrollMultiplier;
620    IOFixed64           timedDelta = IOFixed64::withFixed(avgTimeDeltaMS);
621    IOFixed64           axisValue = IOFixed64::withFixed(*axisp);
622    IOFixed64           minimumMultiplier = IOFixed64::withFixed(kIOFixedOne >> 4);
623
624    scrollMultiplier    = IOFixed64::withFixed(SCROLL_MULTIPLIER_A) * timedDelta * timedDelta;
625    scrollMultiplier    -= IOFixed64::withFixed(SCROLL_MULTIPLIER_B) * timedDelta;
626    scrollMultiplier    += IOFixed64::withFixed(SCROLL_MULTIPLIER_C);
627    scrollMultiplier    *= IOFixed64::withFixed(rateMultiplier);
628    scrollMultiplier    *= IOFixed64::withFixed(avgAxis);
629    if (scrollMultiplier < minimumMultiplier) {
630        scrollMultiplier = minimumMultiplier;
631    }
632
633    if (scaleInfo->isParametric) {
634        scrollMultiplier = PACurvesGetAccelerationMultiplier(scrollMultiplier, scaleInfo->primaryParametrics, scaleInfo->secondaryParametrics);
635    }
636    else {
637        CursorDeviceSegment	*segment;
638
639        // scale
640        for(segment = (CursorDeviceSegment *) scaleInfo->scaleSegments;
641            scrollMultiplier > IOFixed64::withFixed(segment->devUnits);
642            segment++)
643        {}
644
645        if (avgCount > 2) {
646            // Continuous scrolling in one direction indicates a desire to go faster.
647            scrollMultiplier *= (SInt64)lsqrt(avgCount * 16);
648            scrollMultiplier /= 4;
649        }
650
651        scrollMultiplier = (IOFixed64::withFixed(segment->intercept) + scrollMultiplier * IOFixed64::withFixed(segment->slope)) / IOFixed64::withFixed(absAxis);
652    }
653    axisValue *= scrollMultiplier;
654    *axisp = axisValue.asFixed();
655}
656
657void IOHIPointing::setPointingMode(UInt32 accelerateMode)
658{
659    _accelerateMode = accelerateMode;
660
661    _convertAbsoluteToRelative = ((accelerateMode & kAbsoluteConvertMouse) != 0);
662}
663
664UInt32 IOHIPointing::getPointingMode()
665{
666    return _accelerateMode;
667}
668
669void IOHIPointing::setScrollType(UInt32 scrollType)
670{
671    _scrollType = scrollType;
672}
673
674UInt32 IOHIPointing::getScrollType()
675{
676    return _scrollType;
677}
678
679void IOHIPointing::scalePointer(int * dxp, int * dyp)
680// Description:	Perform pointer acceleration computations here.
681//		Given the resolution, dx, dy, and time, compute the velocity
682//		of the pointer over a Manhatten distance in inches/second.
683//		Using this velocity, do a lookup in the pointerScaling table
684//		to select a scaling factor. Scale dx and dy up as appropriate.
685// Preconditions:
686// *	_deviceLock should be held on entry
687{
688    if (_paraAccelParams && _paraAccelSecondaryParams) {
689        IOFixed64 deltaX;
690        IOFixed64 deltaY;
691        IOFixed64 fractX;
692        IOFixed64 fractY;
693        IOFixed64 mag;
694        deltaX.fromIntFloor(*dxp);
695        deltaY.fromIntFloor(*dyp);
696        fractX.fromFixed(_fractX);
697        fractY.fromFixed(_fractY);
698        mag.fromIntFloor(llsqrt((deltaX * deltaX + deltaY * deltaY).as64()));
699
700        IOFixed64 mult = PACurvesGetAccelerationMultiplier(mag, *_paraAccelParams, *_paraAccelSecondaryParams);
701        deltaX *= mult;
702        deltaY *= mult;
703        deltaX += fractX;
704        deltaY += fractY;
705
706        *dxp = deltaX.as32();
707        *dyp = deltaY.as32();
708
709        _fractX = deltaX.asFixed();
710        _fractY = deltaY.asFixed();
711
712        // sign extend fractional part
713        if( deltaX < 0LL )
714            _fractX |= 0xffff0000;
715        else
716            _fractX &= 0x0000ffff;
717
718        if( deltaY < 0LL)
719            _fractY |= 0xffff0000;
720        else
721            _fractY &= 0x0000ffff;
722    }
723    else {
724        ScaleAxes(_scaleSegments, dxp, &_fractX, dyp, &_fractY);
725    }
726}
727
728/*
729 Routine:    Interpolate
730 This routine interpolates to find a point on the line [x1,y1] [x2,y2] which
731 is intersected by the line [x3,y3] [x3,y"].  The resulting y' is calculated
732 by interpolating between y3 and y", towards the higher acceleration curve.
733*/
734
735static SInt32 Interpolate(  SInt32 x1, SInt32 y1,
736                            SInt32 x2, SInt32 y2,
737                            SInt32 x3, SInt32 y3,
738                            SInt32 scale, Boolean lower )
739{
740
741    SInt32 slope;
742    SInt32 intercept;
743    SInt32 resultY;
744
745    slope = (x2 == x1) ? 0 : IOFixedDivide( y2 - y1, x2 - x1 );
746    intercept = y1 - IOFixedMultiply( slope, x1 );
747    resultY = intercept + IOFixedMultiply( slope, x3 );
748    if( lower)
749        resultY = y3 - IOFixedMultiply( scale, y3 - resultY );
750    else
751        resultY = resultY + IOFixedMultiply( scale, y3 - resultY );
752
753    return( resultY );
754}
755
756
757void IOHIPointing::setupForAcceleration( IOFixed desired )
758{
759    OSArray         *parametricAccelerationCurves = (OSArray*)copyProperty(kHIDTrackingAccelParametricCurvesKey, gIOServicePlane);
760    IOFixed         devScale    = IOFixedDivide( resolution(), FRAME_RATE );
761    IOFixed         crsrScale   = IOFixedDivide( SCREEN_RESOLUTION, FRAME_RATE );
762    bool            useParametric = false;
763
764//  IOLog("%s %d: got %08x and %p\n", __PRETTY_FUNCTION__, __LINE__, desired, parametricAccelerationCurves);
765    if (!OSDynamicCast( OSArray, parametricAccelerationCurves)) {
766        OSSafeReleaseNULL(parametricAccelerationCurves);
767        parametricAccelerationCurves = (OSArray*)copyProperty(kHIDAccelParametricCurvesKey, gIOServicePlane);
768    }
769    // Try to set up the parametric acceleration data
770    if (OSDynamicCast( OSArray, parametricAccelerationCurves )) {
771        if ( !_paraAccelParams )
772        {
773            _paraAccelParams = (IOHIPointing__PAParameters*)IOMalloc(sizeof(IOHIPointing__PAParameters));
774        }
775        if ( !_paraAccelSecondaryParams )
776        {
777            _paraAccelSecondaryParams = (IOHIPointing__PASecondaryParameters*)IOMalloc(sizeof(IOHIPointing__PASecondaryParameters));
778        }
779
780//      IOLog("%s %d: have %p and %p\n", __PRETTY_FUNCTION__, __LINE__, _paraAccelParams, _paraAccelSecondaryParams);
781
782        if (_paraAccelParams && _paraAccelSecondaryParams) {
783            IOFixed64 desired64;
784            IOFixed64 devScale64;
785            IOFixed64 crsrScale64;
786
787        //  IOLog("%s: Calling PACurvesSetupAccelParams with %08x, %08x, %08x\n", __PRETTY_FUNCTION__, desired, devScale, crsrScale);
788
789            useParametric = PACurvesSetupAccelParams(parametricAccelerationCurves,
790                                                      desired64.fromFixed(desired),
791                                                      devScale64.fromFixed(devScale),
792                                                      crsrScale64.fromFixed(crsrScale),
793                                                      *_paraAccelParams,
794                                                      *_paraAccelSecondaryParams);
795            if (useParametric && getProperty(kHIDAccelParametricCurvesDebugKey, gIOServicePlane)) {
796                OSDictionary *debugInfo = PACurvesDebugDictionary(*_paraAccelParams, *_paraAccelSecondaryParams);
797                if (debugInfo) {
798                    setProperty(kHIDAccelParametricCurvesDebugKey, debugInfo);
799                    debugInfo->release();
800                }
801            }
802        }
803    }
804    OSSafeReleaseNULL(parametricAccelerationCurves);
805
806//  IOLog("%s %d: %s parametric\n", __PRETTY_FUNCTION__, __LINE__, useParametric ? "using" : "NOT using");
807
808    // If that fails, fall back to classic acceleration
809    if (!useParametric) {
810        OSData *  table         = copyAccelerationTable();
811
812        if (_paraAccelParams)
813            IOFree(_paraAccelParams, sizeof(IOHIPointing__PAParameters));
814        if (_paraAccelSecondaryParams)
815            IOFree(_paraAccelSecondaryParams, sizeof(IOHIPointing__PASecondaryParameters));
816        _paraAccelParams = NULL;
817        _paraAccelSecondaryParams = NULL;
818
819        if (SetupAcceleration (table, desired, devScale, crsrScale, &_scaleSegments, &_scaleSegCount))
820        {
821            _acceleration = desired;
822            _fractX = _fractY = 0;
823        }
824        if (table) table->release();
825    }
826}
827
828void IOHIPointing::setupScrollForAcceleration( IOFixed desired )
829{
830    IOFixed     devScale    = 0;
831    IOFixed     scrScale    = 0;
832    IOFixed     reportRate  = scrollReportRate();
833    OSData *    accelTable  = NULL;
834    UInt32      type        = 0;
835
836    _scrollWheelInfo->rateMultiplier    = IOFixedDivide(reportRate, FRAME_RATE);
837    _scrollPointerInfo->rateMultiplier  = IOFixedDivide(reportRate, FRAME_RATE);
838
839    if (desired < 0) {
840        setPointingMode(getPointingMode() | kAccelNoScrollAcceleration);
841        setProperty(kHIDScrollAccelParametricCurvesDebugKey, OSSymbol::withCString("desired < 0"));
842    }
843    else {
844        setPointingMode(getPointingMode() & ~kAccelNoScrollAcceleration);
845
846        OSArray *parametricAccelerationCurves = (OSArray*)copyProperty(kHIDScrollAccelParametricCurvesKey, gIOServicePlane);
847//#define SWITCH_ALL_SCROLL_ACCELERATION_TO_PARAMETRICS
848#ifdef SWITCH_ALL_SCROLL_ACCELERATION_TO_PARAMETRICS // {
849#warning SWITCH_ALL_SCROLL_ACCELERATION_TO_PARAMETRICS is defined
850        if (!OSDynamicCast( OSArray, parametricAccelerationCurves)) {
851            OSSafeReleaseNULL(parametricAccelerationCurves);
852            parametricAccelerationCurves = (OSArray*)copyProperty(kHIDAccelParametricCurvesKey, gIOServicePlane);
853        }
854#endif // } SWITCH_ALL_SCROLL_ACCELERATION_TO_PARAMETRICS
855
856        OSArray *currentDebugArray = (OSArray *)copyProperty(kHIDScrollAccelParametricCurvesDebugKey);
857        OSArray *newDebugArray = NULL;
858        if (OSDynamicCast(OSArray, currentDebugArray)) {
859            newDebugArray = OSArray::withArray(currentDebugArray);
860        }
861        else {
862            OSSymbol    const * base[] = {
863                OSSymbol::withCString("initted"),
864                OSSymbol::withCString("initted"),
865                OSSymbol::withCString("initted") };
866            newDebugArray = OSArray::withObjects((OSObject const **)base, 3);
867        }
868        OSSafeReleaseNULL(currentDebugArray);
869
870        for ( type=kAccelTypeY; type<=kAccelTypeZ; type++) {
871            IOFixed     res = scrollResolutionForType(type);
872            // Zero scroll resolution says you don't want acceleration.
873            if ( res ) {
874                _scrollWheelInfo->axis[type].isHighResScroll    = res > (SCROLL_DEFAULT_RESOLUTION * 2);
875                _scrollPointerInfo->axis[type].isHighResScroll  = _scrollWheelInfo->axis[type].isHighResScroll;
876
877                _scrollWheelInfo->axis[type].consumeClearThreshold = (IOFixedDivide(res, SCROLL_CONSUME_RESOLUTION) >> 16) * 2;
878                _scrollPointerInfo->axis[type].consumeClearThreshold = _scrollWheelInfo->axis[type].consumeClearThreshold;
879
880                _scrollWheelInfo->axis[type].consumeCountThreshold = _scrollWheelInfo->axis[type].consumeClearThreshold * SCROLL_CONSUME_COUNT_MULTIPLIER;
881                _scrollPointerInfo->axis[type].consumeCountThreshold = _scrollPointerInfo->axis[type].consumeClearThreshold * SCROLL_CONSUME_COUNT_MULTIPLIER;
882
883                bzero(&(_scrollWheelInfo->axis[type].state), sizeof(ScaleDataState));
884                bzero(&(_scrollWheelInfo->axis[type].consumeState), sizeof(ScaleConsumeState));
885                bzero(&(_scrollPointerInfo->axis[type].state), sizeof(ScaleDataState));
886                bzero(&(_scrollPointerInfo->axis[type].consumeState), sizeof(ScaleConsumeState));
887
888                clock_get_uptime(&(_scrollWheelInfo->axis[type].lastEventTime));
889                _scrollPointerInfo->axis[type].lastEventTime = _scrollWheelInfo->axis[type].lastEventTime;
890
891                if (OSDynamicCast( OSArray, parametricAccelerationCurves ) && reportRate) {
892                    IOFixed64 desired64;
893                    IOFixed64 devScale64;
894                    IOFixed64 scrScale64;
895                    IOFixed64 temp;
896
897                    desired64.fromFixed(desired);
898
899                    devScale64.fromFixed(res);
900                    devScale64 /= temp.fromFixed(reportRate);
901
902                    scrScale64.fromFixed(SCREEN_RESOLUTION);
903                    scrScale64 /= temp.fromFixed(FRAME_RATE);
904
905                    _scrollWheelInfo->axis[type].isParametric =
906                    PACurvesSetupAccelParams(parametricAccelerationCurves,
907                                                            desired64,
908                                                            devScale64,
909                                                            scrScale64,
910                                                            _scrollWheelInfo->axis[type].primaryParametrics,
911                                                            _scrollWheelInfo->axis[type].secondaryParametrics);
912                    if (_scrollWheelInfo->axis[type].isParametric) {
913                        OSDictionary *debugInfo =
914                        PACurvesDebugDictionary(_scrollWheelInfo->axis[type].primaryParametrics,
915                                                                          _scrollWheelInfo->axis[type].secondaryParametrics);
916                        if (debugInfo) {
917                            newDebugArray->replaceObject(type, debugInfo);
918                            debugInfo->release();
919                        }
920                        else {
921                            IOLog("IOHIPointing 0x%llx unable to generate debug info for scroll axis %d\n", getRegistryEntryID(), type);
922                            newDebugArray->replaceObject(type, OSSymbol::withCString("no debug info"));
923                        }
924                    }
925                    else {
926                        IOLog("IOHIPointing 0x%llx unable to generate parametric data for axis %d\n", getRegistryEntryID(), type);
927                        newDebugArray->replaceObject(type, OSSymbol::withCString("not parametric"));
928                    }
929                }
930
931                if (!_scrollWheelInfo->axis[type].isParametric) {
932                    accelTable = copyScrollAccelerationTableForType(type);
933
934                    // Setup pixel scroll wheel acceleration table
935                    devScale = IOFixedDivide( res, reportRate );
936                    scrScale = IOFixedDivide( SCREEN_RESOLUTION, FRAME_RATE );
937
938                    SetupAcceleration (accelTable, desired, devScale, scrScale, &(_scrollWheelInfo->axis[type].scaleSegments), &(_scrollWheelInfo->axis[type].scaleSegCount));
939
940                    // Grab the pointer resolution
941                    res = this->resolution();
942                    reportRate = FRAME_RATE;
943
944                    // Setup pixel pointer drag/scroll acceleration table
945                    devScale = IOFixedDivide( res, reportRate );
946                    scrScale = IOFixedDivide( SCREEN_RESOLUTION, FRAME_RATE );
947
948                    SetupAcceleration (accelTable, desired, devScale, scrScale, &(_scrollPointerInfo->axis[type].scaleSegments), &(_scrollPointerInfo->axis[type].scaleSegCount));
949
950                    char buff[256] = "";
951                    snprintf(buff, sizeof(buff), "Non Parametric: desired = 0x%08x; devScale = 0x%08x; scrScale = 0x%08x", desired, devScale, scrScale);
952                    OSString *debugInfo = OSString::withCString(buff);
953                    if (debugInfo) {
954                        newDebugArray->replaceObject(type, debugInfo);
955                        debugInfo->release();
956                    }
957                    else {
958                        IOLog("IOHIPointing 0x%llx unable to generate traditional debug info for scroll axis %d\n", getRegistryEntryID(), type);
959                        newDebugArray->replaceObject(type, OSSymbol::withCString("traditional but no debug info"));
960                    }
961
962                    if (accelTable)
963                        accelTable->release();
964                }
965            }
966            else {
967                newDebugArray->replaceObject(type, OSSymbol::withCString("no scroll resolution for type"));
968        }
969        }
970        setProperty(kHIDScrollAccelParametricCurvesDebugKey, newDebugArray);
971        OSSafeReleaseNULL(newDebugArray);
972        OSSafeReleaseNULL(parametricAccelerationCurves);
973    }
974}
975
976
977bool IOHIPointing::resetPointer()
978{
979    DEVICE_LOCK;
980
981    _buttonMode = NX_RightButton;
982    setupForAcceleration(EV_DEFAULTPOINTERACCELLEVEL);
983    updateProperties();
984
985    DEVICE_UNLOCK;
986    return true;
987}
988
989bool IOHIPointing::resetScroll()
990{
991    DEVICE_LOCK;
992
993    setupScrollForAcceleration(EV_DEFAULTSCROLLACCELLEVEL);
994
995    DEVICE_UNLOCK;
996    return true;
997}
998
999static void ScalePressure(int *pressure, int pressureMin, int pressureMax)
1000{
1001    // scaled pressure value; MAX=(2^16)-1, MIN=0
1002    *pressure = ((pressureMin != pressureMax)) ?
1003                (((unsigned)(*pressure - pressureMin) * 65535LL) /
1004                (unsigned)( pressureMax - pressureMin)) : 0;
1005}
1006
1007
1008void IOHIPointing::dispatchAbsolutePointerEvent(IOGPoint *  newLoc,
1009                                                IOGBounds *	bounds,
1010                                                UInt32		buttonState,
1011                                                bool		proximity,
1012                                                int		pressure,
1013                                                int		pressureMin,
1014                                                int		pressureMax,
1015                                                int		stylusAngle,
1016                                                AbsoluteTime	ts)
1017{
1018    int buttons = 0;
1019    int dx, dy;
1020
1021    DEVICE_LOCK;
1022
1023    if( buttonState & 1)
1024        buttons |= EV_LB;
1025
1026    //if( buttonCount() > 1) {
1027	if( buttonState & 2)	// any others down
1028            buttons |= EV_RB;
1029	// Other magic bit reshuffling stuff.  It seems there was space
1030	// left over at some point for a "middle" mouse button between EV_LB and EV_RB
1031	if(buttonState & 4)
1032            buttons |= 2;
1033	// Add in the rest of the buttons in a linear fasion...
1034	buttons |= buttonState & ~0x7;
1035   // }
1036
1037    /* 	There should not be a threshold applied to pressure for generating a button event.
1038        As soon as the pen hits the tablet, a mouse down should occur
1039
1040    if ((_pressureThresholdToClick < 255) && ((pressure - pressureMin) > ((pressureMax - pressureMin) * _pressureThresholdToClick / 256))) {
1041        buttons |= EV_LB;
1042    }
1043    */
1044    if ( pressure > pressureMin )
1045    {
1046        buttons |= EV_LB;
1047    }
1048
1049    if (_buttonMode == NX_OneButton) {
1050        if ((buttons & (EV_LB|EV_RB)) != 0) {
1051            buttons = EV_LB;
1052        }
1053    }
1054
1055    if (_convertAbsoluteToRelative) {
1056        dx = newLoc->x - _previousLocation.x;
1057        dy = newLoc->y - _previousLocation.y;
1058
1059        if ((_contactToMove && !_hadContact && (pressure > pressureMin)) || (abs(dx) > ((bounds->maxx - bounds->minx) / 20)) || (abs(dy) > ((bounds->maxy - bounds->miny) / 20))) {
1060            dx = 0;
1061            dy = 0;
1062        } else {
1063            scalePointer(&dx, &dy);
1064        }
1065
1066        _previousLocation.x = newLoc->x;
1067        _previousLocation.y = newLoc->y;
1068    }
1069
1070    DEVICE_UNLOCK;
1071
1072    _hadContact = (pressure > pressureMin);
1073
1074    if (!_contactToMove || (pressure > pressureMin)) {
1075
1076        ScalePressure(&pressure, pressureMin, pressureMax);
1077
1078        if (_convertAbsoluteToRelative) {
1079            _relativePointerEvent(  this,
1080                                    buttons,
1081                                    dx,
1082                                    dy,
1083                                    ts);
1084        } else {
1085            _absolutePointerEvent(  this,
1086                                    buttons,
1087                                    newLoc,
1088                                    bounds,
1089                                    proximity,
1090                                    pressure,
1091                                    stylusAngle,
1092                                    ts);
1093        }
1094    }
1095
1096    return;
1097}
1098
1099void IOHIPointing::dispatchRelativePointerEvent(int        dx,
1100                                                int        dy,
1101                                                UInt32     buttonState,
1102                                                AbsoluteTime ts)
1103{
1104    int buttons;
1105
1106    DEVICE_LOCK;
1107
1108    // post the raw event to the IOHIDPointingDevice
1109    if (_hidPointingNub)
1110        _hidPointingNub->postMouseEvent(buttonState, dx, dy, 0);
1111
1112    if (_isSeized)
1113    {
1114        DEVICE_UNLOCK;
1115        return;
1116    }
1117
1118    buttons = 0;
1119
1120    if( buttonState & 1)
1121        buttons |= EV_LB;
1122
1123    //if( buttonCount() > 1) {
1124	if( buttonState & 2)	// any others down
1125            buttons |= EV_RB;
1126	// Other magic bit reshuffling stuff.  It seems there was space
1127	// left over at some point for a "middle" mouse button between EV_LB and EV_RB
1128	if(buttonState & 4)
1129            buttons |= 2;
1130	// Add in the rest of the buttons in a linear fasion...
1131	buttons |= buttonState & ~0x7;
1132    //}
1133
1134    if ( _scrollButtonMask & buttonState )
1135    {
1136
1137        DEVICE_UNLOCK;
1138
1139        dispatchScrollWheelEventWithAccelInfo(-dy, -dx, 0, _scrollPointerInfo, ts);
1140
1141        return;
1142    }
1143
1144    // Perform pointer acceleration computations
1145    if ( _accelerateMode & kAccelMouse ) {
1146        int oldDx = dx;
1147        int oldDy = dy;
1148
1149        scalePointer(&dx, &dy);
1150
1151        if (((oldDx < 0) && (dx > 0)) || ((oldDx > 0) && (dx < 0))) {
1152            IOLog("IOHIPointing::dispatchRelativePointerEvent: Unwanted Direction Change X: oldDx=%d dx=%d\n", oldDy, dy);
1153        }
1154
1155
1156        if (((oldDy < 0) && (dy > 0)) || ((oldDy > 0) && (dy < 0))) {
1157            IOLog("IOHIPointing::dispatchRelativePointerEvent: Unwanted Direction Change Y: oldDy=%d dy=%d\n", oldDy, dy);
1158        }
1159    }
1160
1161    // Perform button tying and mapping.  This
1162    // stuff applies to relative posn devices (mice) only.
1163    if ( _buttonMode == NX_OneButton )
1164    {
1165	// Remap both Left and Right (but no others?) to Left.
1166	if ( (buttons & (EV_LB|EV_RB)) != 0 ) {
1167            buttons |= EV_LB;
1168            buttons &= ~EV_RB;
1169	}
1170    }
1171    else if ( (buttonCount() > 1) && (_buttonMode == NX_LeftButton) )
1172    // Menus on left button. Swap!
1173    {
1174	int temp = 0;
1175	if ( buttons & EV_LB )
1176	    temp = EV_RB;
1177	if ( buttons & EV_RB )
1178	    temp |= EV_LB;
1179	// Swap Left and Right, preserve everything else
1180	buttons = (buttons & ~(EV_LB|EV_RB)) | temp;
1181    }
1182    DEVICE_UNLOCK;
1183
1184    _relativePointerEvent(this,
1185            /* buttons */ buttons,
1186            /* deltaX */  dx,
1187            /* deltaY */  dy,
1188            /* atTime */  ts);
1189}
1190
1191void IOHIPointing::dispatchScrollWheelEvent(short deltaAxis1,
1192                                            short deltaAxis2,
1193                                            short deltaAxis3,
1194                                            AbsoluteTime ts)
1195{
1196    dispatchScrollWheelEventWithAccelInfo(deltaAxis1, deltaAxis2, deltaAxis3, _scrollWheelInfo, ts);
1197}
1198
1199void IOHIPointing::dispatchScrollWheelEventWithAccelInfo(
1200                                SInt32              deltaAxis1,
1201                                SInt32              deltaAxis2,
1202                                SInt32              deltaAxis3,
1203                                ScrollAccelInfo *   info,
1204                                AbsoluteTime        ts)
1205{
1206    Boolean         isHighResScroll = FALSE;
1207    IOHIDSystem *   hidSystem       = IOHIDSystem::instance();
1208    UInt32          eventFlags      = (hidSystem ? hidSystem->eventFlags() : 0);
1209
1210    DEVICE_LOCK;
1211
1212    // Change the report descriptor for the IOHIDPointingDevice
1213    // to include a scroll whell
1214    if (_hidPointingNub && !_hidPointingNub->isScrollPresent())
1215    {
1216        IOHIDPointingDevice * nub = _hidPointingNub;
1217
1218        _hidPointingNub = 0;
1219
1220        DEVICE_UNLOCK;
1221
1222        nub->terminate(kIOServiceAsynchronous); // rdar://8810574
1223        nub->release();
1224
1225        nub = IOHIDPointingDevice::newPointingDeviceAndStart(this, buttonCount(), resolution() >> 16, true);
1226
1227        DEVICE_LOCK;
1228
1229        _hidPointingNub = nub;
1230    }
1231
1232    // Post the raw event to IOHIDPointingDevice
1233    if (_hidPointingNub)
1234        _hidPointingNub->postMouseEvent(0, 0, 0, deltaAxis1);
1235
1236    if (_isSeized) {
1237        DEVICE_UNLOCK;
1238        return;
1239    }
1240
1241    if (_scrollZoomMask) {
1242        bool isModifiedToZoom = ((SPECIALKEYS_MODIFIER_MASK & eventFlags) == _scrollZoomMask);
1243        bool isMomentum = (0 != (_scrollType & kScrollTypeMomentumAny));
1244        if ((isMomentum && _lastScrollWasZoom) || (isModifiedToZoom && !isMomentum)) {
1245            _lastScrollWasZoom = true;
1246            _scrollType |= kScrollTypeZoom;
1247        }
1248        else {
1249            _lastScrollWasZoom = false;
1250        }
1251    }
1252    else {
1253        _lastScrollWasZoom = false;
1254    }
1255
1256    if (!(_scrollType & kScrollTypeZoom) && _scrollOff ) {
1257        DEVICE_UNLOCK;
1258        return;
1259    }
1260
1261    _scrollFixedDeltaAxis1 = deltaAxis1 << 16;
1262    _scrollFixedDeltaAxis2 = deltaAxis2 << 16;
1263    _scrollFixedDeltaAxis3 = deltaAxis3 << 16;
1264
1265    CONVERT_SCROLL_FIXED_TO_COARSE(IOFixedMultiply(_scrollFixedDeltaAxis1, SCROLL_WHEEL_TO_PIXEL_SCALE), _scrollPointDeltaAxis1);
1266    CONVERT_SCROLL_FIXED_TO_COARSE(IOFixedMultiply(_scrollFixedDeltaAxis2, SCROLL_WHEEL_TO_PIXEL_SCALE), _scrollPointDeltaAxis2);
1267    CONVERT_SCROLL_FIXED_TO_COARSE(IOFixedMultiply(_scrollFixedDeltaAxis3, SCROLL_WHEEL_TO_PIXEL_SCALE), _scrollPointDeltaAxis3);
1268
1269    isHighResScroll =   info->axis[kAccelTypeX].isHighResScroll ||
1270                        info->axis[kAccelTypeY].isHighResScroll ||
1271                        info->axis[kAccelTypeZ].isHighResScroll;
1272
1273    // Perform pointer acceleration computations
1274    if (( _accelerateMode & kAccelScroll ) && !( _accelerateMode & kAccelNoScrollAcceleration ))
1275    {
1276        bool            directionChange[3]          = {0,0,0};
1277        bool            typeChange                  = FALSE;
1278        SInt32*         pDeltaAxis[3]               = {&deltaAxis1, &deltaAxis2, &deltaAxis3};
1279        SInt32*         pScrollFixedDeltaAxis[3]    = {&_scrollFixedDeltaAxis1, &_scrollFixedDeltaAxis2, &_scrollFixedDeltaAxis3};
1280        IOFixed*        pScrollPointDeltaAxis[3]    = {&_scrollPointDeltaAxis1, &_scrollPointDeltaAxis2, &_scrollPointDeltaAxis3};
1281
1282        if ( info->zoom != (_scrollType == kScrollTypeZoom))
1283        {
1284            info->zoom = (_scrollType == kScrollTypeZoom);
1285            typeChange = TRUE;
1286        }
1287
1288        for (UInt32 type=kAccelTypeY; type<=kAccelTypeZ; type++ ) {
1289            directionChange[type]       = ((info->axis[type].lastValue == 0) ||
1290                                           ((info->axis[type].lastValue < 0) && (*(pDeltaAxis[type]) > 0)) ||
1291                                           ((info->axis[type].lastValue > 0) && (*(pDeltaAxis[type]) < 0)));
1292            info->axis[type].lastValue  = *(pDeltaAxis[type]);
1293
1294            if ( info->axis[type].scaleSegments || info->axis[type].isParametric )
1295            {
1296                *(pScrollPointDeltaAxis[type])  = info->axis[type].lastValue << 16;
1297
1298                AccelerateScrollAxis(pScrollPointDeltaAxis[type],
1299                                     &(info->axis[type]),
1300                                     ts,
1301                                     info->rateMultiplier,
1302                                     directionChange[type] || typeChange);
1303
1304                CONVERT_SCROLL_FIXED_TO_COARSE(pScrollPointDeltaAxis[type][0], pScrollPointDeltaAxis[type][0]);
1305
1306                // RY: Convert pixel value to points
1307                *(pScrollFixedDeltaAxis[type]) = *(pScrollPointDeltaAxis[type]) << 16;
1308
1309                if ( directionChange[type] )
1310                    bzero(&(info->axis[type].consumeState), sizeof(ScaleConsumeState));
1311
1312                // RY: throttle the tranlation of scroll based on the resolution threshold.
1313                // This allows us to not generated traditional scroll wheel (line) events
1314                // for high res devices at really low (fine granularity) speeds.  This
1315                // prevents a succession of single scroll events that can make scrolling
1316                // slowly actually seem faster.
1317                if ( info->axis[type].consumeCountThreshold )
1318                {
1319                    info->axis[type].consumeState.consumeAccum += *(pScrollFixedDeltaAxis[type]) + ((*(pScrollFixedDeltaAxis[type])) ? info->axis[type].state.fraction : 0);
1320                    info->axis[type].consumeState.consumeCount += abs(info->axis[type].lastValue);
1321
1322                    if (*(pScrollFixedDeltaAxis[type]) &&
1323                       ((abs(info->axis[type].lastValue) >= (SInt32)info->axis[type].consumeClearThreshold) ||
1324                        (info->axis[type].consumeState.consumeCount >= info->axis[type].consumeCountThreshold)))
1325                    {
1326                        *(pScrollFixedDeltaAxis[type]) = info->axis[type].consumeState.consumeAccum;
1327                        info->axis[type].consumeState.consumeAccum = 0;
1328                        info->axis[type].consumeState.consumeCount = 0;
1329                    }
1330                    else
1331                    {
1332                        *(pScrollFixedDeltaAxis[type]) = 0;
1333                    }
1334                }
1335
1336                *(pScrollFixedDeltaAxis[type]) = IOFixedMultiply(*(pScrollFixedDeltaAxis[type]), SCROLL_PIXEL_TO_WHEEL_SCALE);
1337
1338                // RY: Generate fixed point and course scroll deltas.
1339                CONVERT_SCROLL_FIXED_TO_COARSE(*(pScrollFixedDeltaAxis[type]), *(pDeltaAxis[type]));
1340            }
1341        }
1342    }
1343
1344    DEVICE_UNLOCK;
1345
1346    _scrollType |= (isHighResScroll) ? kScrollTypeContinuous : 0;
1347
1348    _scrollWheelEvent(  this,
1349                        deltaAxis1,
1350                        deltaAxis2,
1351                        deltaAxis3,
1352                        ts);
1353    _scrollType = 0;
1354}
1355
1356bool IOHIPointing::updateProperties( void )
1357{
1358    bool	ok;
1359    UInt32	res = resolution();
1360
1361    ok = setProperty( kIOHIDPointerResolutionKey, res, 32)
1362    &    setProperty( kIOHIDPointerConvertAbsoluteKey, &_convertAbsoluteToRelative,
1363                        sizeof( _convertAbsoluteToRelative))
1364    &    setProperty( kIOHIDPointerContactToMoveKey, &_contactToMove,
1365                        sizeof( _contactToMove));
1366
1367    return( ok & super::updateProperties() );
1368}
1369
1370IOReturn IOHIPointing::setParamProperties( OSDictionary * dict )
1371{
1372    OSData			*data;
1373    OSNumber		*number;
1374    OSString		*pointerAccelKey;
1375    OSString		*scrollAccelKey;
1376    IOReturn		err = kIOReturnSuccess;
1377    bool		updated = false;
1378    UInt32		value;
1379
1380    // The resetPointer and resetScroll methods attempt to grab the DEVICE_LOCK
1381    // We should make these calls outside of DEVICE_LOCK as it is not recursive
1382    if( dict->getObject(kIOHIDResetPointerKey))
1383        resetPointer();
1384
1385    if( dict->getObject(kIOHIDScrollResetKey))
1386        resetScroll();
1387
1388    pointerAccelKey = (OSString*)copyProperty(kIOHIDPointerAccelerationTypeKey);
1389    scrollAccelKey = (OSString*)copyProperty(kIOHIDScrollAccelerationTypeKey);
1390
1391    DEVICE_LOCK;
1392
1393    if( (number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDScrollZoomModifierMaskKey))))
1394    {
1395        _scrollZoomMask = number->unsigned32BitValue() & SPECIALKEYS_MODIFIER_MASK;
1396    }
1397
1398    number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDDeviceScrollWithTrackpadKey));
1399    if((number) && OSDynamicCast( OSString, scrollAccelKey ) && scrollAccelKey->isEqualTo(kIOHIDTrackpadScrollAccelerationKey))
1400    {
1401        _scrollOff = number->unsigned32BitValue() == 0;
1402    }
1403
1404    number = OSDynamicCast(OSNumber, dict->getObject(kIOHIDDeviceScrollDisableKey));
1405    if (number) {
1406        _scrollOff = number->unsigned32BitValue() != 0;
1407    }
1408
1409    if ( OSDynamicCast( OSString, pointerAccelKey ) &&
1410        ((number = OSDynamicCast( OSNumber, dict->getObject(pointerAccelKey))) ||
1411         (data = OSDynamicCast( OSData, dict->getObject(pointerAccelKey)))))
1412    {
1413        value = (number) ? number->unsigned32BitValue() :
1414                            *((UInt32 *) (data->getBytesNoCopy()));
1415        setupForAcceleration( value );
1416        updated = true;
1417    }
1418    else if ( (number = OSDynamicCast( OSNumber,
1419		dict->getObject(kIOHIDPointerAccelerationKey))) ||
1420             (data = OSDynamicCast( OSData,
1421		dict->getObject(kIOHIDPointerAccelerationKey)))) {
1422
1423        value = (number) ? number->unsigned32BitValue() :
1424                            *((UInt32 *) (data->getBytesNoCopy()));
1425
1426        setupForAcceleration( value );
1427        updated = true;
1428        if( pointerAccelKey) {
1429            // If this is an OSData object, create an OSNumber to store in the registry
1430            if (!number)
1431            {
1432                number = OSNumber::withNumber(value, 32);
1433                dict->setObject( pointerAccelKey, number );
1434                number->release();
1435            }
1436            else
1437                dict->setObject( pointerAccelKey, number );
1438        }
1439
1440    }
1441    OSSafeReleaseNULL(pointerAccelKey);
1442
1443    // Scroll accel setup
1444    // use same mechanism as pointer accel setup
1445    if( OSDynamicCast( OSString, scrollAccelKey ) &&
1446        ((number = OSDynamicCast( OSNumber, dict->getObject(scrollAccelKey))) ||
1447         (data = OSDynamicCast( OSData, dict->getObject(scrollAccelKey))))) {
1448        value = (number) ? number->unsigned32BitValue() :
1449                            *((UInt32 *) (data->getBytesNoCopy()));
1450        setupScrollForAcceleration( value );
1451        updated = true;
1452    }
1453    else if((number = OSDynamicCast( OSNumber, dict->getObject(kIOHIDScrollAccelerationKey))) ||
1454            (data = OSDynamicCast( OSData, dict->getObject(kIOHIDScrollAccelerationKey)))) {
1455
1456        value = (number) ? number->unsigned32BitValue() :
1457                            *((UInt32 *) (data->getBytesNoCopy()));
1458
1459        setupScrollForAcceleration( value );
1460        updated = true;
1461        if( OSDynamicCast( OSString, scrollAccelKey) ) {
1462            // If this is an OSData object, create an OSNumber to store in the registry
1463            if (!number)
1464            {
1465                number = OSNumber::withNumber(value, 32);
1466                dict->setObject( scrollAccelKey, number );
1467                number->release();
1468            }
1469            else
1470                dict->setObject( scrollAccelKey, number );
1471        }
1472    }
1473    OSSafeReleaseNULL(scrollAccelKey);
1474
1475    DEVICE_UNLOCK;
1476
1477    if ((number = OSDynamicCast(OSNumber,
1478                              dict->getObject(kIOHIDPointerConvertAbsoluteKey))) ||
1479        (data = OSDynamicCast(OSData,
1480                              dict->getObject(kIOHIDPointerConvertAbsoluteKey))))
1481    {
1482        value = (number) ? number->unsigned32BitValue() : *((UInt32 *) (data->getBytesNoCopy()));
1483        _convertAbsoluteToRelative = (value != 0) ? true : false;
1484        updated = true;
1485    }
1486
1487    if ((number = OSDynamicCast(OSNumber,
1488                              dict->getObject(kIOHIDPointerContactToMoveKey))) ||
1489        (data = OSDynamicCast(OSData,
1490                              dict->getObject(kIOHIDPointerContactToMoveKey))))
1491    {
1492        value = (number) ? number->unsigned32BitValue() : *((UInt32 *) (data->getBytesNoCopy()));
1493        _contactToMove = (value != 0) ? true : false;
1494        updated = true;
1495    }
1496
1497    if ((number = OSDynamicCast(OSNumber, dict->getObject(kIOHIDPointerButtonMode))) ||
1498        (data = OSDynamicCast(OSData, dict->getObject(kIOHIDPointerButtonMode))))
1499    {
1500        value = (number) ? number->unsigned32BitValue() :
1501                                            *((UInt32 *) (data->getBytesNoCopy()));
1502
1503        if (getProperty(kIOHIDPointerButtonCountKey))
1504        {
1505            switch (value) {
1506                case kIOHIDButtonMode_BothLeftClicks:
1507                    _buttonMode = NX_OneButton;
1508                    break;
1509
1510                case kIOHIDButtonMode_EnableRightClick:
1511                    _buttonMode = NX_RightButton;
1512                    break;
1513
1514                case kIOHIDButtonMode_ReverseLeftRightClicks:
1515                    // vtn3: rdar://problem/5816671
1516                    _buttonMode = (getProperty(kIOHIDDisallowRemappingOfPrimaryClickKey) == kOSBooleanTrue) ? _buttonMode : NX_LeftButton;
1517                    break;
1518
1519                default:
1520                    // vtn3: rdar://problem/5816671
1521                    _buttonMode = (getProperty(kIOHIDDisallowRemappingOfPrimaryClickKey) == kOSBooleanTrue) ? _buttonMode : value;
1522            }
1523            updated = true;
1524        }
1525    }
1526
1527    if ((number = OSDynamicCast(OSNumber, dict->getObject(kIOHIDScrollMouseButtonKey))) ||
1528        (data = OSDynamicCast(OSData, dict->getObject(kIOHIDScrollMouseButtonKey))))
1529	{
1530		value = (number) ? number->unsigned32BitValue() :
1531                                            *((UInt32 *) (data->getBytesNoCopy())) ;
1532
1533        if (!value)
1534            _scrollButtonMask = 0;
1535        else
1536            _scrollButtonMask = (1 << (value-1));
1537    }
1538
1539    if( updated )
1540        updateProperties();
1541
1542    return( err == kIOReturnSuccess ) ? super::setParamProperties(dict) : err;
1543}
1544
1545// subclasses override
1546
1547IOItemCount IOHIPointing::buttonCount()
1548{
1549    return (1);
1550}
1551
1552IOFixed IOHIPointing::resolution()
1553{
1554    OSNumber * number = (OSNumber*)copyProperty(kIOHIDPointerResolutionKey);
1555    IOFixed result = 100 << 16;
1556
1557    if ( OSDynamicCast(OSNumber, number) )
1558        result = number->unsigned32BitValue();
1559    OSSafeReleaseNULL(number);
1560
1561    return result;
1562}
1563
1564// RY: Added this method to obtain the resolution
1565// of the scroll wheel.  The default value is 0,
1566// which should prevent any accel from being applied.
1567IOFixed	IOHIPointing::scrollResolutionForType(SInt32 type)
1568{
1569    IOFixed     res         = 0;
1570    OSNumber * 	number      = NULL;
1571    const char *key         = NULL;
1572
1573    switch ( type ) {
1574        case kAccelTypeY:
1575            key = kIOHIDScrollResolutionYKey;
1576            break;
1577        case kAccelTypeX:
1578            key = kIOHIDScrollResolutionXKey;
1579            break;
1580        case kAccelTypeZ:
1581            key = kIOHIDScrollResolutionZKey;
1582            break;
1583        default:
1584            key = kIOHIDScrollResolutionKey;
1585            break;
1586
1587    }
1588
1589    number = (OSNumber*)copyProperty(key);
1590    if( !OSDynamicCast( OSNumber, number ) ) {
1591        OSSafeRelease(number);
1592		number = (OSNumber*)copyProperty(kIOHIDScrollResolutionKey);
1593	}
1594
1595    if (!number) {
1596        if (_reserved && !_reserved->scrollResolutionWarningComplete) {
1597            kprintf("IOHIPointing::0x%llx has no %s. This /implies/ no scroll acceleration.\n",
1598                    getRegistryEntryID(),
1599                    kIOHIDScrollResolutionKey);
1600            _reserved->scrollResolutionWarningComplete = true;
1601        }
1602    }
1603
1604	if( OSDynamicCast( OSNumber, number ) )
1605		res = number->unsigned32BitValue();
1606    OSSafeRelease(number);
1607
1608    return( res );
1609}
1610
1611// RY: Added this method to obtain the report rate
1612// of the scroll wheel.  The default value is 67 << 16.
1613IOFixed	IOHIPointing::scrollReportRate()
1614{
1615    IOFixed     result = FRAME_RATE;
1616    OSNumber    *number = (OSNumber*)copyProperty( kIOHIDScrollReportRateKey );
1617
1618    if (OSDynamicCast( OSNumber, number ))
1619        if (number->unsigned32BitValue())
1620            result = number->unsigned32BitValue();
1621    OSSafeRelease(number);
1622
1623    return result;
1624}
1625
1626
1627OSData * IOHIPointing::copyAccelerationTable()
1628{
1629    static const UInt8 accl[] = {
1630	0x00, 0x00, 0x80, 0x00,
1631        0x40, 0x32, 0x30, 0x30, 0x00, 0x02, 0x00, 0x00,
1632        0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
1633        0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
1634        0x00, 0x09, 0x00, 0x00, 0x71, 0x3B, 0x00, 0x00,
1635        0x60, 0x00, 0x00, 0x04, 0x4E, 0xC5, 0x00, 0x10,
1636        0x80, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x5F,
1637        0x00, 0x00, 0x00, 0x16, 0xEC, 0x4F, 0x00, 0x8B,
1638        0x00, 0x00, 0x00, 0x1D, 0x3B, 0x14, 0x00, 0x94,
1639        0x80, 0x00, 0x00, 0x22, 0x76, 0x27, 0x00, 0x96,
1640        0x00, 0x00, 0x00, 0x24, 0x62, 0x76, 0x00, 0x96,
1641        0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x96,
1642        0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x96,
1643        0x00, 0x00
1644    };
1645
1646    OSData * data = (OSData*)copyProperty( kIOHIDPointerAccelerationTableKey );
1647    if (!OSDynamicCast( OSData, data ))
1648        OSSafeReleaseNULL(data);
1649    if (!data)
1650        data = OSData::withBytesNoCopy( (void *) accl, sizeof( accl ) );
1651
1652    return( data );
1653}
1654
1655// RY: Added for scroll acceleration
1656// If no scroll accel table is present, this will
1657// default to the pointer acceleration table
1658OSData * IOHIPointing::copyScrollAccelerationTable()
1659{
1660    return( copyScrollAccelerationTableForType() );
1661}
1662
1663OSData * IOHIPointing::copyScrollAccelerationTableForType(SInt32 type)
1664{
1665    OSData *    data    = NULL;
1666    const char *key     = NULL;
1667
1668    switch ( type ) {
1669        case kAccelTypeY:
1670            key = kIOHIDScrollAccelerationTableYKey;
1671            break;
1672        case kAccelTypeX:
1673            key = kIOHIDScrollAccelerationTableXKey;
1674            break;
1675        case kAccelTypeZ:
1676            key = kIOHIDScrollAccelerationTableZKey;
1677            break;
1678    }
1679
1680    if ( key )
1681        data = (OSData*)copyProperty( key );
1682
1683    if ( !OSDynamicCast( OSData, data ) ) {
1684        OSSafeRelease(data);
1685		data = (OSData*)copyProperty( kIOHIDScrollAccelerationTableKey );
1686		if (data && !OSDynamicCast( OSData, data )) {
1687            data->release();
1688            data = NULL;
1689        }
1690	}
1691
1692	if ( !data )
1693		data = copyAccelerationTable();
1694
1695    return( data );
1696}
1697
1698/*bool GetOSDataValue (OSData * data, UInt32 * value)
1699{
1700	bool 	validValue = false;
1701
1702	switch (data->getLength())
1703	{
1704		case sizeof(UInt8):
1705			*value = *(UInt8 *) data->getBytesNoCopy();
1706			validValue = true;
1707			break;
1708
1709		case sizeof(UInt16):
1710			*value = *(UInt16 *) data->getBytesNoCopy();
1711			validValue = true;
1712			break;
1713
1714		case sizeof(UInt32):
1715			*value = *(UInt32 *) data->getBytesNoCopy();
1716			validValue = true;
1717			break;
1718	}
1719
1720	return validValue;
1721}*/
1722
1723// RY: This function contains the original portions of
1724// setupForAcceleration.  This was separated out to
1725// accomidate the acceleration of scroll axes
1726bool SetupAcceleration (OSData * data, IOFixed desired, IOFixed devScale, IOFixed crsrScale, void ** scaleSegments, IOItemCount * scaleSegCount) {
1727    const UInt16 *	lowTable = 0;
1728    const UInt16 *	highTable;
1729
1730    SInt32	x1, y1, x2, y2, x3, y3;
1731    SInt32	prevX1, prevY1;
1732    SInt32	upperX, upperY;
1733    SInt32	lowerX, lowerY;
1734    SInt32	lowAccl = 0, lowPoints = 0;
1735    SInt32	highAccl, highPoints;
1736    SInt32	scale;
1737    UInt32	count;
1738    Boolean	lower;
1739
1740    SInt32	scaledX1, scaledY1;
1741    SInt32	scaledX2, scaledY2;
1742
1743    CursorDeviceSegment *	segments;
1744    CursorDeviceSegment *	segment;
1745    SInt32			segCount;
1746
1747    if( !data || !devScale || !crsrScale)
1748        return false;
1749
1750    if( desired < (IOFixed) 0) {
1751        // disabling mouse scaling
1752        if(*scaleSegments && *scaleSegCount)
1753            IODelete( *scaleSegments,
1754                        CursorDeviceSegment, *scaleSegCount );
1755        *scaleSegments = NULL;
1756        *scaleSegCount = 0;
1757        return false;
1758    }
1759
1760    highTable = (const UInt16 *) data->getBytesNoCopy();
1761
1762    scaledX1 = scaledY1 = 0;
1763
1764    scale = OSReadBigInt32((volatile void *)highTable, 0);
1765    highTable += 4;
1766
1767    // normalize table's default (scale) to 0.5
1768    if( desired > 0x8000) {
1769        desired = IOFixedMultiply( desired - 0x8000,
1770                                   0x10000 - scale );
1771        desired <<= 1;
1772        desired += scale;
1773    } else {
1774        desired = IOFixedMultiply( desired, scale );
1775        desired <<= 1;
1776    }
1777
1778    count = OSReadBigInt16((volatile void *)(highTable++), 0);
1779    scale = (1 << 16);
1780
1781    // find curves bracketing the desired value
1782    do {
1783        highAccl = OSReadBigInt32((volatile void *)highTable, 0);
1784        highTable += 2;
1785        highPoints = OSReadBigInt16((volatile void *)(highTable++), 0);
1786
1787        if( desired <= highAccl)
1788            break;
1789
1790        if( 0 == --count) {
1791            // this much over the highest table
1792            scale = (highAccl) ? IOFixedDivide( desired, highAccl ) : 0;
1793            lowTable	= 0;
1794            break;
1795        }
1796
1797        lowTable	= highTable;
1798        lowAccl		= highAccl;
1799        lowPoints	= highPoints;
1800        highTable	+= lowPoints * 4;
1801
1802    } while( true );
1803
1804    // scale between the two
1805    if( lowTable) {
1806        scale = (highAccl == lowAccl) ? 0 :
1807                IOFixedDivide((desired - lowAccl), (highAccl - lowAccl));
1808
1809    }
1810
1811    // or take all the high one
1812    else {
1813        lowTable	= highTable;
1814        lowAccl		= highAccl;
1815        lowPoints	= 0;
1816    }
1817
1818    if( lowPoints > highPoints)
1819        segCount = lowPoints;
1820    else
1821        segCount = highPoints;
1822    segCount *= 2;
1823/*    IOLog("lowPoints %ld, highPoints %ld, segCount %ld\n",
1824            lowPoints, highPoints, segCount); */
1825    segments = IONew( CursorDeviceSegment, segCount );
1826    assert( segments );
1827    segment = segments;
1828
1829    x1 = prevX1 = y1 = prevY1 = 0;
1830
1831    lowerX = OSReadBigInt32((volatile void *)lowTable, 0);
1832    lowTable += 2;
1833    lowerY = OSReadBigInt32((volatile void *)lowTable, 0);
1834    lowTable += 2;
1835    upperX = OSReadBigInt32((volatile void *)highTable, 0);
1836    highTable += 2;
1837    upperY = OSReadBigInt32((volatile void *)highTable, 0);
1838    highTable += 2;
1839
1840    do {
1841        // consume next point from first X
1842        lower = (lowPoints && (!highPoints || (lowerX <= upperX)));
1843
1844        if( lower) {
1845            /* highline */
1846            x2 = upperX;
1847            y2 = upperY;
1848            x3 = lowerX;
1849            y3 = lowerY;
1850            if( lowPoints && (--lowPoints)) {
1851                lowerX = OSReadBigInt32((volatile void *)lowTable, 0);
1852                lowTable += 2;
1853                lowerY = OSReadBigInt32((volatile void *)lowTable, 0);
1854                lowTable += 2;
1855            }
1856        } else  {
1857            /* lowline */
1858            x2 = lowerX;
1859            y2 = lowerY;
1860            x3 = upperX;
1861            y3 = upperY;
1862            if( highPoints && (--highPoints)) {
1863                upperX = OSReadBigInt32((volatile void *)highTable, 0);
1864                highTable += 2;
1865                upperY = OSReadBigInt32((volatile void *)highTable, 0);
1866                highTable += 2;
1867            }
1868        }
1869        {
1870        // convert to line segment
1871        assert( segment < (segments + segCount) );
1872
1873        scaledX2 = IOFixedMultiply( devScale, /* newX */ x3 );
1874        scaledY2 = IOFixedMultiply( crsrScale,
1875                      /* newY */    Interpolate( x1, y1, x2, y2, x3, y3,
1876                                            scale, lower ) );
1877        if( lowPoints || highPoints)
1878            segment->devUnits = scaledX2;
1879        else
1880            segment->devUnits = MAX_DEVICE_THRESHOLD;
1881
1882        segment->slope = ((scaledX2 == scaledX1)) ? 0 :
1883                IOFixedDivide((scaledY2 - scaledY1), (scaledX2 - scaledX1));
1884
1885        segment->intercept = scaledY2
1886                            - IOFixedMultiply( segment->slope, scaledX2 );
1887/*        IOLog("devUnits = %08lx, slope = %08lx, intercept = %08lx\n",
1888                segment->devUnits, segment->slope, segment->intercept); */
1889
1890        scaledX1 = scaledX2;
1891        scaledY1 = scaledY2;
1892        segment++;
1893        }
1894
1895        // continue on from last point
1896        if( lowPoints && highPoints) {
1897            if( lowerX > upperX) {
1898                prevX1 = x1;
1899                prevY1 = y1;
1900            } else {
1901                /* swaplines */
1902                prevX1 = x1;
1903                prevY1 = y1;
1904                x1 = x3;
1905                y1 = y3;
1906            }
1907        } else {
1908            x2 = x1;
1909            y2 = y1;
1910            x1 = prevX1;
1911            y1 = prevY1;
1912            prevX1 = x2;
1913            prevY1 = y2;
1914        }
1915
1916    } while( lowPoints || highPoints );
1917
1918    if( *scaleSegCount && *scaleSegments)
1919        IODelete( *scaleSegments,
1920                    CursorDeviceSegment, *scaleSegCount );
1921    *scaleSegCount = segCount;
1922    *scaleSegments = (void *) segments;
1923
1924    return true;
1925}
1926
1927IOFixed64 OSObjectToIOFixed64(OSObject *in)
1928{
1929    OSNumber *num = OSDynamicCast(OSNumber, in);
1930    IOFixed64 result;
1931    if (num) {
1932        result.fromFixed(num->unsigned32BitValue());
1933    }
1934    return result;
1935}
1936
1937bool
1938PACurvesFillParamsFromDict(OSDictionary *parameters,
1939                           const IOFixed64 devScale,
1940                           const IOFixed64 crsrScale,
1941                           IOHIPointing__PAParameters &outParams)
1942{
1943    require(parameters, exit_early);
1944
1945    outParams.deviceMickysDivider = devScale;
1946    outParams.cursorSpeedMultiplier = crsrScale;
1947
1948    outParams.accelIndex = OSObjectToIOFixed64(parameters->getObject(kHIDAccelIndexKey));
1949
1950    outParams.gain[0] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelGainLinearKey));
1951    outParams.gain[1] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelGainParabolicKey));
1952    outParams.gain[2] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelGainCubicKey));
1953    outParams.gain[3] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelGainQuarticKey));
1954
1955    outParams.tangent[0] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelTangentSpeedLinearKey));
1956    outParams.tangent[1] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelTangentSpeedParabolicRootKey));
1957//    outParams.tangent[2] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelTangentSpeedCubicRootKey));
1958//    outParams.tangent[3] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelTangentSpeedQuarticRootKey));
1959
1960    return ((outParams.gain[0] != 0LL) ||
1961            (outParams.gain[1] != 0LL) ||
1962            (outParams.gain[2] != 0LL) ||
1963            (outParams.gain[3] != 0LL));
1964
1965exit_early:
1966    return false;
1967}
1968
1969bool
1970PACurvesSetupAccelParams (OSArray *parametricCurves,
1971                          IOFixed64 desired,
1972                          IOFixed64 devScale,
1973                          IOFixed64 crsrScale,
1974                          IOHIPointing__PAParameters &primaryParams,
1975                          IOHIPointing__PASecondaryParameters &secondaryParams)
1976{
1977    bool                    success = false;
1978    OSCollectionIterator    *itr = NULL;
1979    OSDictionary            *dict = NULL;
1980
1981    IOHIPointing__PAParameters high_curve_params;
1982    IOHIPointing__PAParameters low_curve_params;
1983
1984//  IOLog("%s %d: Called with %08x, %08x, %08x\n", __PRETTY_FUNCTION__, __LINE__, desired.asFixed(), devScale.asFixed(), crsrScale.asFixed());
1985
1986    require(parametricCurves, exit_early);
1987    require(crsrScale > 0LL, exit_early);
1988    require(devScale > 0LL, exit_early);
1989    require(desired > 0LL, exit_early);
1990
1991    itr = OSCollectionIterator::withCollection(parametricCurves);
1992    require(itr, exit_early);
1993
1994    while (!success) {
1995        itr->reset();
1996        dict = OSDynamicCast(OSDictionary, itr->getNextObject());
1997        require(PACurvesFillParamsFromDict(dict, devScale, crsrScale, low_curve_params),
1998                exit_early);
1999
2000        while (!success && (NULL != dict)) {
2001            if (!PACurvesFillParamsFromDict(dict, devScale, crsrScale, high_curve_params)) {
2002                break;
2003            }
2004            if (desired <= high_curve_params.accelIndex) {
2005                success = true;
2006            }
2007            else {
2008                low_curve_params = high_curve_params;
2009            }
2010            dict = OSDynamicCast(OSDictionary, itr->getNextObject());
2011        }
2012
2013        require(success || !itr->isValid(), exit_early);
2014    };
2015
2016    if ( high_curve_params.accelIndex > low_curve_params.accelIndex ) {
2017        IOFixed64   ratio = (desired - low_curve_params.accelIndex) / (high_curve_params.accelIndex - low_curve_params.accelIndex);
2018        int         index;
2019
2020//      IOLog("%s %d: Using %08x, %08x, %08x\n", __PRETTY_FUNCTION__, __LINE__, high_curve_params.accelIndex.asFixed(), low_curve_params.accelIndex.asFixed(), ratio.asFixed());
2021
2022        primaryParams.deviceMickysDivider   = high_curve_params.deviceMickysDivider;
2023        primaryParams.cursorSpeedMultiplier = high_curve_params.cursorSpeedMultiplier;
2024        primaryParams.accelIndex            = desired;
2025
2026        for (index = 0; index < 4; index++) {
2027            primaryParams.gain[index] = low_curve_params.gain[index] + (high_curve_params.gain[index] - low_curve_params.gain[index]) * ratio;
2028            if (primaryParams.gain[index] < 0LL)
2029                primaryParams.gain[index].fromFixed(0);
2030        }
2031        for (index = 0; index < 2; index++) {
2032            primaryParams.tangent[index] = low_curve_params.tangent[index] + (high_curve_params.tangent[index] - low_curve_params.tangent[index]) * ratio;
2033            if (primaryParams.tangent[index] < 0LL)
2034                primaryParams.tangent[index].fromFixed(0);
2035        }
2036    }
2037    else {
2038        primaryParams = high_curve_params;
2039    }
2040
2041    success = ((primaryParams.gain[0] != 0LL) ||
2042               (primaryParams.gain[1] != 0LL) ||
2043               (primaryParams.gain[2] != 0LL) ||
2044               (primaryParams.gain[3] != 0LL));
2045
2046    // calculate secondary values
2047    bzero(&secondaryParams, sizeof(secondaryParams));
2048    if ((primaryParams.tangent[1] > 0LL) && (primaryParams.tangent[1] < primaryParams.tangent[0]))
2049        secondaryParams.firstTangent = 1;
2050
2051    if (secondaryParams.firstTangent == 0) {
2052        secondaryParams.y0 = IOQuarticFunction(primaryParams.tangent[0], primaryParams.gain);
2053        secondaryParams.m0 = IOQuarticDerivative(primaryParams.tangent[0], primaryParams.gain);
2054        secondaryParams.b0 = secondaryParams.y0 - secondaryParams.m0 * primaryParams.tangent[0];
2055        secondaryParams.y1 = secondaryParams.m0 * primaryParams.tangent[1] + secondaryParams.b0;
2056    }
2057    else {
2058		secondaryParams.y1 = IOQuarticFunction( primaryParams.tangent[1], primaryParams.gain );
2059		secondaryParams.m0 = IOQuarticDerivative( primaryParams.tangent[1], primaryParams.gain );
2060    }
2061
2062    secondaryParams.m_root = secondaryParams.m0 * secondaryParams.y1 * 2LL;
2063    secondaryParams.b_root = exponent(secondaryParams.y1, 2) - secondaryParams.m_root * primaryParams.tangent[1];
2064
2065exit_early:
2066    if (itr) {
2067        itr->release();
2068    }
2069
2070    return success;
2071}
2072
2073OSDictionary*
2074PACurvesDebugDictionary(IOHIPointing__PAParameters &primaryParams,
2075                        IOHIPointing__PASecondaryParameters &secondaryParams)
2076{
2077    OSDictionary    *result = OSDictionary::withCapacity(20);
2078
2079    require(result, exit_early);
2080
2081#define ADD_NUMBER_FOR(X) \
2082    do { \
2083        OSNumber *value = OSNumber::withNumber(X.as64(), 64); \
2084        if (value) { \
2085            result->setObject(#X, value); \
2086            value->release(); \
2087        } \
2088    } \
2089    while (0)
2090
2091    ADD_NUMBER_FOR(primaryParams.deviceMickysDivider);
2092    ADD_NUMBER_FOR(primaryParams.cursorSpeedMultiplier);
2093    ADD_NUMBER_FOR(primaryParams.accelIndex);
2094    ADD_NUMBER_FOR(primaryParams.gain[0]);
2095    ADD_NUMBER_FOR(primaryParams.gain[1]);
2096    ADD_NUMBER_FOR(primaryParams.gain[2]);
2097    ADD_NUMBER_FOR(primaryParams.gain[3]);
2098    ADD_NUMBER_FOR(primaryParams.tangent[0]);
2099    ADD_NUMBER_FOR(primaryParams.tangent[1]);
2100
2101    ADD_NUMBER_FOR(secondaryParams.m0);
2102    ADD_NUMBER_FOR(secondaryParams.b0);
2103    ADD_NUMBER_FOR(secondaryParams.y0);
2104    ADD_NUMBER_FOR(secondaryParams.y1);
2105    ADD_NUMBER_FOR(secondaryParams.m_root);
2106    ADD_NUMBER_FOR(secondaryParams.b_root);
2107
2108#undef ADD_NUMBER_FOR
2109
2110exit_early:
2111    return result;
2112}
2113
2114IOFixed64
2115PACurvesGetAccelerationMultiplier(const IOFixed64 device_speed_mickeys,
2116                                  const IOHIPointing__PAParameters &params,
2117                                  const IOHIPointing__PASecondaryParameters &secondaryParams)
2118{
2119    IOFixed64 result; // defaults to zero
2120
2121    if ((device_speed_mickeys > result) && (params.deviceMickysDivider != result)) {
2122		IOFixed64 standardized_speed = device_speed_mickeys / params.deviceMickysDivider;
2123		IOFixed64 accelerated_speed;
2124        if ((params.tangent[secondaryParams.firstTangent] != 0LL) && (standardized_speed <= params.tangent[secondaryParams.firstTangent])) {
2125            accelerated_speed = IOQuarticFunction(standardized_speed, params.gain);
2126        }
2127        else {
2128            if ((secondaryParams.firstTangent == 0) && (params.tangent[1] != 0LL) && (standardized_speed <= params.tangent[1])) {
2129                accelerated_speed = secondaryParams.m0 * standardized_speed + secondaryParams.b0;
2130            }
2131            else {
2132                accelerated_speed.fromIntFloor(llsqrt(((secondaryParams.m_root * standardized_speed) + secondaryParams.b_root).as64()));
2133            }
2134        }
2135		IOFixed64 accelerated_pixels = accelerated_speed * params.cursorSpeedMultiplier;
2136		result = accelerated_pixels / device_speed_mickeys;
2137    }
2138    else {
2139        result.fromFixed(1);
2140    }
2141
2142    return result;
2143}
2144
2145// RY: This function contains the original portions of
2146// scalePointer.  This was separated out to accomidate
2147// the acceleration of other axes
2148void ScaleAxes (void * scaleSegments, int * axis1p, IOFixed *axis1Fractp, int * axis2p, IOFixed *axis2Fractp)
2149{
2150    SInt32			dx, dy;
2151    SInt32			mag;
2152    IOFixed			scale;
2153    CursorDeviceSegment	*	segment;
2154
2155    if( !scaleSegments)
2156        return;
2157
2158    dx = (*axis1p) << 16;
2159    dy = (*axis2p) << 16;
2160
2161    // mag is (x^2 + y^2)^0.5 and converted to fixed point
2162    mag = (lsqrt(*axis1p * *axis1p + *axis2p * *axis2p)) << 16;
2163    if (mag == 0)
2164        return;
2165
2166    // scale
2167    for(
2168        segment = (CursorDeviceSegment *) scaleSegments;
2169        mag > segment->devUnits;
2170        segment++)	{}
2171
2172    scale = IOFixedDivide(
2173            segment->intercept + IOFixedMultiply( mag, segment->slope ),
2174            mag );
2175
2176    dx = IOFixedMultiply( dx, scale );
2177    dy = IOFixedMultiply( dy, scale );
2178
2179    // add fract parts
2180    dx += *axis1Fractp;
2181    dy += *axis2Fractp;
2182
2183    *axis1p = dx / 65536;
2184    *axis2p = dy / 65536;
2185
2186    // get fractional part with sign extend
2187    if( dx >= 0)
2188	*axis1Fractp = dx & 0xffff;
2189    else
2190	*axis1Fractp = dx | 0xffff0000;
2191    if( dy >= 0)
2192	*axis2Fractp = dy & 0xffff;
2193    else
2194	*axis2Fractp = dy | 0xffff0000;
2195}
2196
2197void IOHIPointing::_relativePointerEvent( IOHIPointing * self,
2198				    int        buttons,
2199                       /* deltaX */ int        dx,
2200                       /* deltaY */ int        dy,
2201                       /* atTime */ AbsoluteTime ts)
2202{
2203    RelativePointerEventCallback rpeCallback;
2204    rpeCallback = (RelativePointerEventCallback)self->_relativePointerEventAction;
2205
2206    if (rpeCallback)
2207        (*rpeCallback)(self->_relativePointerEventTarget,
2208                                    buttons,
2209                                    dx,
2210                                    dy,
2211                                    ts,
2212                                    self,
2213                                    0);
2214}
2215
2216  /* Tablet event reporting */
2217void IOHIPointing::_absolutePointerEvent(IOHIPointing * self,
2218				    int        buttons,
2219                 /* at */           IOGPoint * newLoc,
2220                 /* withBounds */   IOGBounds *bounds,
2221                 /* inProximity */  bool       proximity,
2222                 /* withPressure */ int        pressure,
2223                 /* withAngle */    int        stylusAngle,
2224                 /* atTime */       AbsoluteTime ts)
2225{
2226    AbsolutePointerEventCallback apeCallback;
2227    apeCallback = (AbsolutePointerEventCallback)self->_absolutePointerEventAction;
2228
2229    if (apeCallback)
2230        (*apeCallback)(self->_absolutePointerEventTarget,
2231                                buttons,
2232                                newLoc,
2233                                bounds,
2234                                proximity,
2235                                pressure,
2236                                stylusAngle,
2237                                ts,
2238                                self,
2239                                0);
2240
2241}
2242
2243  /* Mouse scroll wheel event reporting */
2244void IOHIPointing::_scrollWheelEvent(IOHIPointing *self,
2245                                short deltaAxis1,
2246                                short deltaAxis2,
2247                                short deltaAxis3,
2248                                AbsoluteTime ts)
2249{
2250    ScrollWheelEventCallback sweCallback;
2251    sweCallback = (ScrollWheelEventCallback)self->_scrollWheelEventAction;
2252
2253    if (sweCallback)
2254        (*sweCallback)(self->_scrollWheelEventTarget,
2255                                (short) deltaAxis1,
2256                                (short) deltaAxis2,
2257                                (short) deltaAxis3,
2258                                self->_scrollFixedDeltaAxis1,
2259                                self->_scrollFixedDeltaAxis2,
2260                                self->_scrollFixedDeltaAxis3,
2261                                self->_scrollPointDeltaAxis1,
2262                                self->_scrollPointDeltaAxis2,
2263                                self->_scrollPointDeltaAxis3,
2264                                self->_scrollType,
2265                                ts,
2266                                self,
2267                                0);
2268}
2269