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    if (result == 0)
1624        result = FRAME_RATE;
1625
1626    return result;
1627}
1628
1629
1630OSData * IOHIPointing::copyAccelerationTable()
1631{
1632    static const UInt8 accl[] = {
1633	0x00, 0x00, 0x80, 0x00,
1634        0x40, 0x32, 0x30, 0x30, 0x00, 0x02, 0x00, 0x00,
1635        0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
1636        0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
1637        0x00, 0x09, 0x00, 0x00, 0x71, 0x3B, 0x00, 0x00,
1638        0x60, 0x00, 0x00, 0x04, 0x4E, 0xC5, 0x00, 0x10,
1639        0x80, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x5F,
1640        0x00, 0x00, 0x00, 0x16, 0xEC, 0x4F, 0x00, 0x8B,
1641        0x00, 0x00, 0x00, 0x1D, 0x3B, 0x14, 0x00, 0x94,
1642        0x80, 0x00, 0x00, 0x22, 0x76, 0x27, 0x00, 0x96,
1643        0x00, 0x00, 0x00, 0x24, 0x62, 0x76, 0x00, 0x96,
1644        0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x96,
1645        0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x96,
1646        0x00, 0x00
1647    };
1648
1649    OSData * data = (OSData*)copyProperty( kIOHIDPointerAccelerationTableKey );
1650    if (!OSDynamicCast( OSData, data ))
1651        OSSafeReleaseNULL(data);
1652    if (!data)
1653        data = OSData::withBytesNoCopy( (void *) accl, sizeof( accl ) );
1654
1655    return( data );
1656}
1657
1658// RY: Added for scroll acceleration
1659// If no scroll accel table is present, this will
1660// default to the pointer acceleration table
1661OSData * IOHIPointing::copyScrollAccelerationTable()
1662{
1663    return( copyScrollAccelerationTableForType() );
1664}
1665
1666OSData * IOHIPointing::copyScrollAccelerationTableForType(SInt32 type)
1667{
1668    OSData *    data    = NULL;
1669    const char *key     = NULL;
1670
1671    switch ( type ) {
1672        case kAccelTypeY:
1673            key = kIOHIDScrollAccelerationTableYKey;
1674            break;
1675        case kAccelTypeX:
1676            key = kIOHIDScrollAccelerationTableXKey;
1677            break;
1678        case kAccelTypeZ:
1679            key = kIOHIDScrollAccelerationTableZKey;
1680            break;
1681    }
1682
1683    if ( key )
1684        data = (OSData*)copyProperty( key );
1685
1686    if ( !OSDynamicCast( OSData, data ) ) {
1687        OSSafeRelease(data);
1688		data = (OSData*)copyProperty( kIOHIDScrollAccelerationTableKey );
1689		if (data && !OSDynamicCast( OSData, data )) {
1690            data->release();
1691            data = NULL;
1692        }
1693	}
1694
1695	if ( !data )
1696		data = copyAccelerationTable();
1697
1698    return( data );
1699}
1700
1701/*bool GetOSDataValue (OSData * data, UInt32 * value)
1702{
1703	bool 	validValue = false;
1704
1705	switch (data->getLength())
1706	{
1707		case sizeof(UInt8):
1708			*value = *(UInt8 *) data->getBytesNoCopy();
1709			validValue = true;
1710			break;
1711
1712		case sizeof(UInt16):
1713			*value = *(UInt16 *) data->getBytesNoCopy();
1714			validValue = true;
1715			break;
1716
1717		case sizeof(UInt32):
1718			*value = *(UInt32 *) data->getBytesNoCopy();
1719			validValue = true;
1720			break;
1721	}
1722
1723	return validValue;
1724}*/
1725
1726// RY: This function contains the original portions of
1727// setupForAcceleration.  This was separated out to
1728// accomidate the acceleration of scroll axes
1729bool SetupAcceleration (OSData * data, IOFixed desired, IOFixed devScale, IOFixed crsrScale, void ** scaleSegments, IOItemCount * scaleSegCount) {
1730    const UInt16 *	lowTable = 0;
1731    const UInt16 *	highTable;
1732
1733    SInt32	x1, y1, x2, y2, x3, y3;
1734    SInt32	prevX1, prevY1;
1735    SInt32	upperX, upperY;
1736    SInt32	lowerX, lowerY;
1737    SInt32	lowAccl = 0, lowPoints = 0;
1738    SInt32	highAccl, highPoints;
1739    SInt32	scale;
1740    UInt32	count;
1741    Boolean	lower;
1742
1743    SInt32	scaledX1, scaledY1;
1744    SInt32	scaledX2, scaledY2;
1745
1746    CursorDeviceSegment *	segments;
1747    CursorDeviceSegment *	segment;
1748    SInt32			segCount;
1749
1750    if( !data || !devScale || !crsrScale)
1751        return false;
1752
1753    if( desired < (IOFixed) 0) {
1754        // disabling mouse scaling
1755        if(*scaleSegments && *scaleSegCount)
1756            IODelete( *scaleSegments,
1757                        CursorDeviceSegment, *scaleSegCount );
1758        *scaleSegments = NULL;
1759        *scaleSegCount = 0;
1760        return false;
1761    }
1762
1763    highTable = (const UInt16 *) data->getBytesNoCopy();
1764
1765    scaledX1 = scaledY1 = 0;
1766
1767    scale = OSReadBigInt32((volatile void *)highTable, 0);
1768    highTable += 4;
1769
1770    // normalize table's default (scale) to 0.5
1771    if( desired > 0x8000) {
1772        desired = IOFixedMultiply( desired - 0x8000,
1773                                   0x10000 - scale );
1774        desired <<= 1;
1775        desired += scale;
1776    } else {
1777        desired = IOFixedMultiply( desired, scale );
1778        desired <<= 1;
1779    }
1780
1781    count = OSReadBigInt16((volatile void *)(highTable++), 0);
1782    scale = (1 << 16);
1783
1784    // find curves bracketing the desired value
1785    do {
1786        highAccl = OSReadBigInt32((volatile void *)highTable, 0);
1787        highTable += 2;
1788        highPoints = OSReadBigInt16((volatile void *)(highTable++), 0);
1789
1790        if( desired <= highAccl)
1791            break;
1792
1793        if( 0 == --count) {
1794            // this much over the highest table
1795            scale = (highAccl) ? IOFixedDivide( desired, highAccl ) : 0;
1796            lowTable	= 0;
1797            break;
1798        }
1799
1800        lowTable	= highTable;
1801        lowAccl		= highAccl;
1802        lowPoints	= highPoints;
1803        highTable	+= lowPoints * 4;
1804
1805    } while( true );
1806
1807    // scale between the two
1808    if( lowTable) {
1809        scale = (highAccl == lowAccl) ? 0 :
1810                IOFixedDivide((desired - lowAccl), (highAccl - lowAccl));
1811
1812    }
1813
1814    // or take all the high one
1815    else {
1816        lowTable	= highTable;
1817        lowAccl		= highAccl;
1818        lowPoints	= 0;
1819    }
1820
1821    if( lowPoints > highPoints)
1822        segCount = lowPoints;
1823    else
1824        segCount = highPoints;
1825    segCount *= 2;
1826/*    IOLog("lowPoints %ld, highPoints %ld, segCount %ld\n",
1827            lowPoints, highPoints, segCount); */
1828    segments = IONew( CursorDeviceSegment, segCount );
1829    assert( segments );
1830    segment = segments;
1831
1832    x1 = prevX1 = y1 = prevY1 = 0;
1833
1834    lowerX = OSReadBigInt32((volatile void *)lowTable, 0);
1835    lowTable += 2;
1836    lowerY = OSReadBigInt32((volatile void *)lowTable, 0);
1837    lowTable += 2;
1838    upperX = OSReadBigInt32((volatile void *)highTable, 0);
1839    highTable += 2;
1840    upperY = OSReadBigInt32((volatile void *)highTable, 0);
1841    highTable += 2;
1842
1843    do {
1844        // consume next point from first X
1845        lower = (lowPoints && (!highPoints || (lowerX <= upperX)));
1846
1847        if( lower) {
1848            /* highline */
1849            x2 = upperX;
1850            y2 = upperY;
1851            x3 = lowerX;
1852            y3 = lowerY;
1853            if( lowPoints && (--lowPoints)) {
1854                lowerX = OSReadBigInt32((volatile void *)lowTable, 0);
1855                lowTable += 2;
1856                lowerY = OSReadBigInt32((volatile void *)lowTable, 0);
1857                lowTable += 2;
1858            }
1859        } else  {
1860            /* lowline */
1861            x2 = lowerX;
1862            y2 = lowerY;
1863            x3 = upperX;
1864            y3 = upperY;
1865            if( highPoints && (--highPoints)) {
1866                upperX = OSReadBigInt32((volatile void *)highTable, 0);
1867                highTable += 2;
1868                upperY = OSReadBigInt32((volatile void *)highTable, 0);
1869                highTable += 2;
1870            }
1871        }
1872        {
1873        // convert to line segment
1874        assert( segment < (segments + segCount) );
1875
1876        scaledX2 = IOFixedMultiply( devScale, /* newX */ x3 );
1877        scaledY2 = IOFixedMultiply( crsrScale,
1878                      /* newY */    Interpolate( x1, y1, x2, y2, x3, y3,
1879                                            scale, lower ) );
1880        if( lowPoints || highPoints)
1881            segment->devUnits = scaledX2;
1882        else
1883            segment->devUnits = MAX_DEVICE_THRESHOLD;
1884
1885        segment->slope = ((scaledX2 == scaledX1)) ? 0 :
1886                IOFixedDivide((scaledY2 - scaledY1), (scaledX2 - scaledX1));
1887
1888        segment->intercept = scaledY2
1889                            - IOFixedMultiply( segment->slope, scaledX2 );
1890/*        IOLog("devUnits = %08lx, slope = %08lx, intercept = %08lx\n",
1891                segment->devUnits, segment->slope, segment->intercept); */
1892
1893        scaledX1 = scaledX2;
1894        scaledY1 = scaledY2;
1895        segment++;
1896        }
1897
1898        // continue on from last point
1899        if( lowPoints && highPoints) {
1900            if( lowerX > upperX) {
1901                prevX1 = x1;
1902                prevY1 = y1;
1903            } else {
1904                /* swaplines */
1905                prevX1 = x1;
1906                prevY1 = y1;
1907                x1 = x3;
1908                y1 = y3;
1909            }
1910        } else {
1911            x2 = x1;
1912            y2 = y1;
1913            x1 = prevX1;
1914            y1 = prevY1;
1915            prevX1 = x2;
1916            prevY1 = y2;
1917        }
1918
1919    } while( lowPoints || highPoints );
1920
1921    if( *scaleSegCount && *scaleSegments)
1922        IODelete( *scaleSegments,
1923                    CursorDeviceSegment, *scaleSegCount );
1924    *scaleSegCount = segCount;
1925    *scaleSegments = (void *) segments;
1926
1927    return true;
1928}
1929
1930IOFixed64 OSObjectToIOFixed64(OSObject *in)
1931{
1932    OSNumber *num = OSDynamicCast(OSNumber, in);
1933    IOFixed64 result;
1934    if (num) {
1935        result.fromFixed(num->unsigned32BitValue());
1936    }
1937    return result;
1938}
1939
1940bool
1941PACurvesFillParamsFromDict(OSDictionary *parameters,
1942                           const IOFixed64 devScale,
1943                           const IOFixed64 crsrScale,
1944                           IOHIPointing__PAParameters &outParams)
1945{
1946    require(parameters, exit_early);
1947
1948    outParams.deviceMickysDivider = devScale;
1949    outParams.cursorSpeedMultiplier = crsrScale;
1950
1951    outParams.accelIndex = OSObjectToIOFixed64(parameters->getObject(kHIDAccelIndexKey));
1952
1953    outParams.gain[0] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelGainLinearKey));
1954    outParams.gain[1] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelGainParabolicKey));
1955    outParams.gain[2] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelGainCubicKey));
1956    outParams.gain[3] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelGainQuarticKey));
1957
1958    outParams.tangent[0] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelTangentSpeedLinearKey));
1959    outParams.tangent[1] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelTangentSpeedParabolicRootKey));
1960//    outParams.tangent[2] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelTangentSpeedCubicRootKey));
1961//    outParams.tangent[3] = OSObjectToIOFixed64(parameters->getObject(kHIDAccelTangentSpeedQuarticRootKey));
1962
1963    return ((outParams.gain[0] != 0LL) ||
1964            (outParams.gain[1] != 0LL) ||
1965            (outParams.gain[2] != 0LL) ||
1966            (outParams.gain[3] != 0LL));
1967
1968exit_early:
1969    return false;
1970}
1971
1972bool
1973PACurvesSetupAccelParams (OSArray *parametricCurves,
1974                          IOFixed64 desired,
1975                          IOFixed64 devScale,
1976                          IOFixed64 crsrScale,
1977                          IOHIPointing__PAParameters &primaryParams,
1978                          IOHIPointing__PASecondaryParameters &secondaryParams)
1979{
1980    bool                    success = false;
1981    OSCollectionIterator    *itr = NULL;
1982    OSDictionary            *dict = NULL;
1983
1984    IOHIPointing__PAParameters high_curve_params;
1985    IOHIPointing__PAParameters low_curve_params;
1986
1987//  IOLog("%s %d: Called with %08x, %08x, %08x\n", __PRETTY_FUNCTION__, __LINE__, desired.asFixed(), devScale.asFixed(), crsrScale.asFixed());
1988
1989    require(parametricCurves, exit_early);
1990    require(crsrScale > 0LL, exit_early);
1991    require(devScale > 0LL, exit_early);
1992    require(desired > 0LL, exit_early);
1993
1994    itr = OSCollectionIterator::withCollection(parametricCurves);
1995    require(itr, exit_early);
1996
1997    while (!success) {
1998        itr->reset();
1999        dict = OSDynamicCast(OSDictionary, itr->getNextObject());
2000        require(PACurvesFillParamsFromDict(dict, devScale, crsrScale, low_curve_params),
2001                exit_early);
2002
2003        while (!success && (NULL != dict)) {
2004            if (!PACurvesFillParamsFromDict(dict, devScale, crsrScale, high_curve_params)) {
2005                break;
2006            }
2007            if (desired <= high_curve_params.accelIndex) {
2008                success = true;
2009            }
2010            else {
2011                low_curve_params = high_curve_params;
2012            }
2013            dict = OSDynamicCast(OSDictionary, itr->getNextObject());
2014        }
2015
2016        require(success || !itr->isValid(), exit_early);
2017    };
2018
2019    if ( high_curve_params.accelIndex > low_curve_params.accelIndex ) {
2020        IOFixed64   ratio = (desired - low_curve_params.accelIndex) / (high_curve_params.accelIndex - low_curve_params.accelIndex);
2021        int         index;
2022
2023//      IOLog("%s %d: Using %08x, %08x, %08x\n", __PRETTY_FUNCTION__, __LINE__, high_curve_params.accelIndex.asFixed(), low_curve_params.accelIndex.asFixed(), ratio.asFixed());
2024
2025        primaryParams.deviceMickysDivider   = high_curve_params.deviceMickysDivider;
2026        primaryParams.cursorSpeedMultiplier = high_curve_params.cursorSpeedMultiplier;
2027        primaryParams.accelIndex            = desired;
2028
2029        for (index = 0; index < 4; index++) {
2030            primaryParams.gain[index] = low_curve_params.gain[index] + (high_curve_params.gain[index] - low_curve_params.gain[index]) * ratio;
2031            if (primaryParams.gain[index] < 0LL)
2032                primaryParams.gain[index].fromFixed(0);
2033        }
2034        for (index = 0; index < 2; index++) {
2035            primaryParams.tangent[index] = low_curve_params.tangent[index] + (high_curve_params.tangent[index] - low_curve_params.tangent[index]) * ratio;
2036            if (primaryParams.tangent[index] < 0LL)
2037                primaryParams.tangent[index].fromFixed(0);
2038        }
2039    }
2040    else {
2041        primaryParams = high_curve_params;
2042    }
2043
2044    success = ((primaryParams.gain[0] != 0LL) ||
2045               (primaryParams.gain[1] != 0LL) ||
2046               (primaryParams.gain[2] != 0LL) ||
2047               (primaryParams.gain[3] != 0LL));
2048
2049    // calculate secondary values
2050    bzero(&secondaryParams, sizeof(secondaryParams));
2051    if ((primaryParams.tangent[1] > 0LL) && (primaryParams.tangent[1] < primaryParams.tangent[0]))
2052        secondaryParams.firstTangent = 1;
2053
2054    if (secondaryParams.firstTangent == 0) {
2055        secondaryParams.y0 = IOQuarticFunction(primaryParams.tangent[0], primaryParams.gain);
2056        secondaryParams.m0 = IOQuarticDerivative(primaryParams.tangent[0], primaryParams.gain);
2057        secondaryParams.b0 = secondaryParams.y0 - secondaryParams.m0 * primaryParams.tangent[0];
2058        secondaryParams.y1 = secondaryParams.m0 * primaryParams.tangent[1] + secondaryParams.b0;
2059    }
2060    else {
2061		secondaryParams.y1 = IOQuarticFunction( primaryParams.tangent[1], primaryParams.gain );
2062		secondaryParams.m0 = IOQuarticDerivative( primaryParams.tangent[1], primaryParams.gain );
2063    }
2064
2065    secondaryParams.m_root = secondaryParams.m0 * secondaryParams.y1 * 2LL;
2066    secondaryParams.b_root = exponent(secondaryParams.y1, 2) - secondaryParams.m_root * primaryParams.tangent[1];
2067
2068exit_early:
2069    if (itr) {
2070        itr->release();
2071    }
2072
2073    return success;
2074}
2075
2076OSDictionary*
2077PACurvesDebugDictionary(IOHIPointing__PAParameters &primaryParams,
2078                        IOHIPointing__PASecondaryParameters &secondaryParams)
2079{
2080    OSDictionary    *result = OSDictionary::withCapacity(20);
2081
2082    require(result, exit_early);
2083
2084#define ADD_NUMBER_FOR(X) \
2085    do { \
2086        OSNumber *value = OSNumber::withNumber(X.as64(), 64); \
2087        if (value) { \
2088            result->setObject(#X, value); \
2089            value->release(); \
2090        } \
2091    } \
2092    while (0)
2093
2094    ADD_NUMBER_FOR(primaryParams.deviceMickysDivider);
2095    ADD_NUMBER_FOR(primaryParams.cursorSpeedMultiplier);
2096    ADD_NUMBER_FOR(primaryParams.accelIndex);
2097    ADD_NUMBER_FOR(primaryParams.gain[0]);
2098    ADD_NUMBER_FOR(primaryParams.gain[1]);
2099    ADD_NUMBER_FOR(primaryParams.gain[2]);
2100    ADD_NUMBER_FOR(primaryParams.gain[3]);
2101    ADD_NUMBER_FOR(primaryParams.tangent[0]);
2102    ADD_NUMBER_FOR(primaryParams.tangent[1]);
2103
2104    ADD_NUMBER_FOR(secondaryParams.m0);
2105    ADD_NUMBER_FOR(secondaryParams.b0);
2106    ADD_NUMBER_FOR(secondaryParams.y0);
2107    ADD_NUMBER_FOR(secondaryParams.y1);
2108    ADD_NUMBER_FOR(secondaryParams.m_root);
2109    ADD_NUMBER_FOR(secondaryParams.b_root);
2110
2111#undef ADD_NUMBER_FOR
2112
2113exit_early:
2114    return result;
2115}
2116
2117IOFixed64
2118PACurvesGetAccelerationMultiplier(const IOFixed64 device_speed_mickeys,
2119                                  const IOHIPointing__PAParameters &params,
2120                                  const IOHIPointing__PASecondaryParameters &secondaryParams)
2121{
2122    IOFixed64 result; // defaults to zero
2123
2124    if ((device_speed_mickeys > result) && (params.deviceMickysDivider != result)) {
2125		IOFixed64 standardized_speed = device_speed_mickeys / params.deviceMickysDivider;
2126		IOFixed64 accelerated_speed;
2127        if ((params.tangent[secondaryParams.firstTangent] != 0LL) && (standardized_speed <= params.tangent[secondaryParams.firstTangent])) {
2128            accelerated_speed = IOQuarticFunction(standardized_speed, params.gain);
2129        }
2130        else {
2131            if ((secondaryParams.firstTangent == 0) && (params.tangent[1] != 0LL) && (standardized_speed <= params.tangent[1])) {
2132                accelerated_speed = secondaryParams.m0 * standardized_speed + secondaryParams.b0;
2133            }
2134            else {
2135                accelerated_speed.fromIntFloor(llsqrt(((secondaryParams.m_root * standardized_speed) + secondaryParams.b_root).as64()));
2136            }
2137        }
2138		IOFixed64 accelerated_pixels = accelerated_speed * params.cursorSpeedMultiplier;
2139		result = accelerated_pixels / device_speed_mickeys;
2140    }
2141    else {
2142        result.fromFixed(1);
2143    }
2144
2145    return result;
2146}
2147
2148// RY: This function contains the original portions of
2149// scalePointer.  This was separated out to accomidate
2150// the acceleration of other axes
2151void ScaleAxes (void * scaleSegments, int * axis1p, IOFixed *axis1Fractp, int * axis2p, IOFixed *axis2Fractp)
2152{
2153    SInt32			dx, dy;
2154    SInt32			mag;
2155    IOFixed			scale;
2156    CursorDeviceSegment	*	segment;
2157
2158    if( !scaleSegments)
2159        return;
2160
2161    dx = (*axis1p) << 16;
2162    dy = (*axis2p) << 16;
2163
2164    // mag is (x^2 + y^2)^0.5 and converted to fixed point
2165    mag = (lsqrt(*axis1p * *axis1p + *axis2p * *axis2p)) << 16;
2166    if (mag == 0)
2167        return;
2168
2169    // scale
2170    for(
2171        segment = (CursorDeviceSegment *) scaleSegments;
2172        mag > segment->devUnits;
2173        segment++)	{}
2174
2175    scale = IOFixedDivide(
2176            segment->intercept + IOFixedMultiply( mag, segment->slope ),
2177            mag );
2178
2179    dx = IOFixedMultiply( dx, scale );
2180    dy = IOFixedMultiply( dy, scale );
2181
2182    // add fract parts
2183    dx += *axis1Fractp;
2184    dy += *axis2Fractp;
2185
2186    *axis1p = dx / 65536;
2187    *axis2p = dy / 65536;
2188
2189    // get fractional part with sign extend
2190    if( dx >= 0)
2191	*axis1Fractp = dx & 0xffff;
2192    else
2193	*axis1Fractp = dx | 0xffff0000;
2194    if( dy >= 0)
2195	*axis2Fractp = dy & 0xffff;
2196    else
2197	*axis2Fractp = dy | 0xffff0000;
2198}
2199
2200void IOHIPointing::_relativePointerEvent( IOHIPointing * self,
2201				    int        buttons,
2202                       /* deltaX */ int        dx,
2203                       /* deltaY */ int        dy,
2204                       /* atTime */ AbsoluteTime ts)
2205{
2206    RelativePointerEventCallback rpeCallback;
2207    rpeCallback = (RelativePointerEventCallback)self->_relativePointerEventAction;
2208
2209    if (rpeCallback)
2210        (*rpeCallback)(self->_relativePointerEventTarget,
2211                                    buttons,
2212                                    dx,
2213                                    dy,
2214                                    ts,
2215                                    self,
2216                                    0);
2217}
2218
2219  /* Tablet event reporting */
2220void IOHIPointing::_absolutePointerEvent(IOHIPointing * self,
2221				    int        buttons,
2222                 /* at */           IOGPoint * newLoc,
2223                 /* withBounds */   IOGBounds *bounds,
2224                 /* inProximity */  bool       proximity,
2225                 /* withPressure */ int        pressure,
2226                 /* withAngle */    int        stylusAngle,
2227                 /* atTime */       AbsoluteTime ts)
2228{
2229    AbsolutePointerEventCallback apeCallback;
2230    apeCallback = (AbsolutePointerEventCallback)self->_absolutePointerEventAction;
2231
2232    if (apeCallback)
2233        (*apeCallback)(self->_absolutePointerEventTarget,
2234                                buttons,
2235                                newLoc,
2236                                bounds,
2237                                proximity,
2238                                pressure,
2239                                stylusAngle,
2240                                ts,
2241                                self,
2242                                0);
2243
2244}
2245
2246  /* Mouse scroll wheel event reporting */
2247void IOHIPointing::_scrollWheelEvent(IOHIPointing *self,
2248                                short deltaAxis1,
2249                                short deltaAxis2,
2250                                short deltaAxis3,
2251                                AbsoluteTime ts)
2252{
2253    ScrollWheelEventCallback sweCallback;
2254    sweCallback = (ScrollWheelEventCallback)self->_scrollWheelEventAction;
2255
2256    if (sweCallback)
2257        (*sweCallback)(self->_scrollWheelEventTarget,
2258                                (short) deltaAxis1,
2259                                (short) deltaAxis2,
2260                                (short) deltaAxis3,
2261                                self->_scrollFixedDeltaAxis1,
2262                                self->_scrollFixedDeltaAxis2,
2263                                self->_scrollFixedDeltaAxis3,
2264                                self->_scrollPointDeltaAxis1,
2265                                self->_scrollPointDeltaAxis2,
2266                                self->_scrollPointDeltaAxis3,
2267                                self->_scrollType,
2268                                ts,
2269                                self,
2270                                0);
2271}
2272