#include <IOKit/IOLib.h>
#include <IOKit/assert.h>
#include <IOKit/hidsystem/IOHIPointing.h>
#include <IOKit/hidsystem/IOHIDParameter.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#ifndef abs
#define abs(_a) ((_a >= 0) ? _a : -_a)
#endif
#define super IOHIDevice
IOFixed glob_accel = 0x8000;
bool HIPointinghasRoot( OSObject * us, void *, IOService * yourDevice );
OSDefineMetaClassAndStructors(IOHIPointing, IOHIDevice);
bool IOHIPointing::init(OSDictionary * properties)
{
if (!super::init(properties)) return false;
_fractX = 0;
_fractY = 0;
_acceleration = -1;
_convertAbsoluteToRelative = false;
_contactToMove = false;
_hadContact = false;
_pressureThresholdToClick = 128;
_previousLocation.x = 0;
_previousLocation.y = 0;
_rootDomain = 0;
_deviceLock = IOLockAlloc();
if (!_deviceLock) return false;
IOLockInit(_deviceLock);
return true;
}
bool IOHIPointing::start(IOService * provider)
{
if (!super::start(provider)) return false;
registerService();
addNotification( gIOPublishNotification,serviceMatching("IOPMrootDomain"),
(IOServiceNotificationHandler)HIPointinghasRoot, this, 0 );
return true;
}
bool HIPointinghasRoot( OSObject * us, void *, IOService * yourDevice )
{
if (( yourDevice != NULL ) && ((IOHIPointing *)us)->_rootDomain == 0)
{
((IOHIPointing *)us)->_rootDomain = yourDevice;
((IOPMrootDomain *)yourDevice)->registerInterestedDriver((IOService *) us);
}
return true;
}
IOReturn IOHIPointing::powerStateWillChangeTo ( IOPMPowerFlags theFlags, unsigned long unused1,
IOService* unused2)
{
if ( ! (theFlags & IOPMPowerOn) )
{
glob_accel = _acceleration; }
return IOPMAckImplied;
}
IOReturn IOHIPointing::powerStateDidChangeTo ( IOPMPowerFlags theFlags, unsigned long unused1,
IOService* unused2)
{
if (theFlags & IOPMPowerOn)
{
if (glob_accel > 0x10000) glob_accel = 0x10000;
setupForAcceleration(glob_accel);
updateProperties();
}
return IOPMAckImplied;
}
void IOHIPointing::free()
{
glob_accel = _acceleration;
if (_deviceLock)
{
IOLock * lock;
IOTakeLock(_deviceLock);
lock = _deviceLock;
_deviceLock = NULL;
IOUnlock(lock);
IOLockFree(lock);
}
if (_rootDomain)
{
_rootDomain->deRegisterInterestedDriver((IOService *) this);
_rootDomain = 0;
}
super::free();
}
bool IOHIPointing::open(IOService * client,
IOOptionBits options,
RelativePointerEventAction rpeAction,
AbsolutePointerEventAction apeAction,
ScrollWheelEventAction sweAction)
{
if ( (-1 == _acceleration) && (!resetPointer())) return false;
if (super::open(client, options))
{
_relativePointerEventTarget = client;
_relativePointerEventAction = rpeAction;
_absolutePointerEventTarget = client;
_absolutePointerEventAction = apeAction;
_scrollWheelEventTarget = client;
_scrollWheelEventAction = sweAction;
return true;
}
return false;
}
void IOHIPointing::close(IOService * client, IOOptionBits)
{
glob_accel = _acceleration;
_relativePointerEventAction = NULL;
_relativePointerEventTarget = 0;
_absolutePointerEventAction = NULL;
_absolutePointerEventTarget = 0;
if (_rootDomain)
{
_rootDomain->deRegisterInterestedDriver((IOService *) this);
_rootDomain = 0;
}
super::close(client);
}
IOHIDKind IOHIPointing::hidKind()
{
return kHIRelativePointingDevice;
}
struct CursorDeviceSegment {
SInt32 devUnits;
SInt32 slope;
SInt32 intercept;
};
typedef struct CursorDeviceSegment CursorDeviceSegment;
void IOHIPointing::scalePointer(int * dxp, int * dyp)
{
SInt32 dx, dy;
SInt32 absDx, absDy;
SInt32 mag;
IOFixed scale;
CursorDeviceSegment * segment;
if( !_scaleSegments)
return;
dx = (*dxp) << 16;
dy = (*dyp) << 16;
absDx = (dx < 0) ? -dx : dx;
absDy = (dy < 0) ? -dy : dy;
if( absDx > absDy)
mag = (absDx + (absDy / 2));
else
mag = (absDy + (absDx / 2));
if( !mag)
return;
for(
segment = (CursorDeviceSegment *) _scaleSegments;
mag > segment->devUnits;
segment++) {}
scale = IOFixedDivide(
segment->intercept + IOFixedMultiply( mag, segment->slope ),
mag );
dx = IOFixedMultiply( dx, scale );
dy = IOFixedMultiply( dy, scale );
dx += _fractX;
dy += _fractY;
*dxp = dx / 65536;
*dyp = dy / 65536;
if( dx >= 0)
_fractX = dx & 0xffff;
else
_fractX = dx | 0xffff0000;
if( dy >= 0)
_fractY = dy & 0xffff;
else
_fractY = dy | 0xffff0000;
}
static SInt32 Interpolate( SInt32 x1, SInt32 y1,
SInt32 x2, SInt32 y2,
SInt32 x3, SInt32 y3,
SInt32 scale, Boolean lower )
{
SInt32 slope;
SInt32 intercept;
SInt32 resultY;
slope = IOFixedDivide( y2 - y1, x2 - x1 );
intercept = y1 - IOFixedMultiply( slope, x1 );
resultY = intercept + IOFixedMultiply( slope, x3 );
if( lower)
resultY = y3 - IOFixedMultiply( scale, y3 - resultY );
else
resultY = resultY + IOFixedMultiply( scale, y3 - resultY );
return( resultY );
}
static SInt32 Fetch32( const UInt16 * p )
{
SInt32 result;
result = (*(p++)) << 16;
result |= (*(p++));
return( result );
}
void IOHIPointing::setupForAcceleration( IOFixed desired )
{
OSData * data;
const UInt16 * lowTable = 0;
const UInt16 * highTable;
SInt32 x1, y1, x2, y2, x3, y3;
SInt32 prevX1, prevY1;
SInt32 upperX, upperY;
SInt32 lowerX, lowerY;
SInt32 lowAccl = 0, lowPoints = 0;
SInt32 highAccl, highPoints;
SInt32 scale;
UInt32 count;
Boolean lower;
SInt32 pointerResolution = resolution();
SInt32 frameRate = (67 << 16);
SInt32 screenResolution = (72 << 16);
SInt32 devScale, crsrScale;
SInt32 scaledX1, scaledY1;
SInt32 scaledX2, scaledY2;
CursorDeviceSegment * segments;
CursorDeviceSegment * segment;
SInt32 segCount;
assert(pointerResolution);
data = copyAccelerationTable();
if( !data)
return;
if( desired < (IOFixed) 0) {
if(_scaleSegments && _scaleSegCount)
IODelete( _scaleSegments,
CursorDeviceSegment, _scaleSegCount );
_scaleSegments = NULL;
_scaleSegCount = 0;
data->release();
return;
}
highTable = (const UInt16 *) data->getBytesNoCopy();
devScale = IOFixedDivide( pointerResolution, frameRate );
crsrScale = IOFixedDivide( screenResolution, frameRate );
scaledX1 = scaledY1 = 0;
scale = Fetch32( highTable );
highTable += 4;
_acceleration = desired;
if( desired > 0x8000) {
desired = IOFixedMultiply( desired - 0x8000,
0x10000 - scale );
desired <<= 1;
desired += scale;
} else {
desired = IOFixedMultiply( desired, scale );
desired <<= 1;
}
if( desired > (1 << 16))
desired = (1 << 16);
count = *(highTable++);
do {
highAccl = Fetch32( highTable );
highTable += 2;
highPoints = *(highTable++);
if( desired <= highAccl)
break;
lowTable = highTable;
lowAccl = highAccl;
lowPoints = highPoints;
highTable += lowPoints * 4;
} while( true );
if( lowTable)
scale = IOFixedDivide( desired - lowAccl,
highAccl - lowAccl );
else {
scale = (1 << 16);
lowTable = highTable;
lowAccl = highAccl;
lowPoints = 0;
}
if( lowPoints > highPoints)
segCount = lowPoints;
else
segCount = highPoints;
segCount *= 2;
segments = IONew( CursorDeviceSegment, segCount );
assert( segments );
segment = segments;
x1 = prevX1 = y1 = prevY1 = 0;
lowerX = Fetch32( lowTable );
lowTable += 2;
lowerY = Fetch32( lowTable );
lowTable += 2;
upperX = Fetch32( highTable );
highTable += 2;
upperY = Fetch32( highTable );
highTable += 2;
do {
lower = (lowPoints && (!highPoints || (lowerX <= upperX)));
if( lower) {
x2 = upperX;
y2 = upperY;
x3 = lowerX;
y3 = lowerY;
if( lowPoints && (--lowPoints)) {
lowerX = Fetch32( lowTable );
lowTable += 2;
lowerY = Fetch32( lowTable );
lowTable += 2;
}
} else {
x2 = lowerX;
y2 = lowerY;
x3 = upperX;
y3 = upperY;
if( highPoints && (--highPoints)) {
upperX = Fetch32( highTable );
highTable += 2;
upperY = Fetch32( highTable );
highTable += 2;
}
}
{
assert( segment < (segments + segCount) );
scaledX2 = IOFixedMultiply( devScale, x3 );
scaledY2 = IOFixedMultiply( crsrScale,
Interpolate( x1, y1, x2, y2, x3, y3,
scale, lower ) );
if( lowPoints || highPoints)
segment->devUnits = scaledX2;
else
segment->devUnits = 0x7fffffff;
segment->slope = IOFixedDivide( scaledY2 - scaledY1,
scaledX2 - scaledX1 );
segment->intercept = scaledY2
- IOFixedMultiply( segment->slope, scaledX2 );
scaledX1 = scaledX2;
scaledY1 = scaledY2;
segment++;
}
if( lowPoints && highPoints) {
if( lowerX > upperX) {
prevX1 = x1;
prevY1 = y1;
} else {
prevX1 = x1;
prevY1 = y1;
x1 = x3;
y1 = y3;
}
} else {
x2 = x1;
y2 = y1;
x1 = prevX1;
y1 = prevY1;
prevX1 = x2;
prevY1 = y2;
}
} while( lowPoints || highPoints );
if( _scaleSegCount && _scaleSegments)
IODelete( _scaleSegments,
CursorDeviceSegment, _scaleSegCount );
_scaleSegCount = segCount;
_scaleSegments = (void *) segments;
_fractX = _fractY = 0;
data->release();
}
bool IOHIPointing::resetPointer()
{
IOTakeLock( _deviceLock);
_buttonMode = NX_RightButton;
if( glob_accel > 0 )
{
setupForAcceleration(glob_accel);
}
else
{
setupForAcceleration(0x8000);
}
updateProperties();
IOUnlock( _deviceLock);
return true;
}
void IOHIPointing::dispatchAbsolutePointerEvent(Point * newLoc,
Bounds * bounds,
UInt32 buttonState,
bool proximity,
int pressure,
int pressureMin,
int pressureMax,
int stylusAngle,
AbsoluteTime ts)
{
int buttons = 0;
int dx, dy;
IOTakeLock(_deviceLock);
if (buttonState & 1) {
buttons |= EV_LB;
}
if (buttonCount() > 1) {
if (buttonState & -2) { buttons |= EV_RB;
}
}
if ((_pressureThresholdToClick < 255) && ((pressure - pressureMin) > ((pressureMax - pressureMin) * _pressureThresholdToClick / 256))) {
buttons |= EV_LB;
}
if (_buttonMode == NX_OneButton) {
if ((buttons & (EV_LB|EV_RB)) != 0) {
buttons = EV_LB;
}
}
if (_convertAbsoluteToRelative) {
dx = newLoc->x - _previousLocation.x;
dy = newLoc->y - _previousLocation.y;
if ((_contactToMove && !_hadContact && (pressure > pressureMin)) || (abs(dx) > ((bounds->maxx - bounds->minx) / 20)) || (abs(dy) > ((bounds->maxy - bounds->miny) / 20))) {
dx = 0;
dy = 0;
} else {
scalePointer(&dx, &dy);
}
_previousLocation.x = newLoc->x;
_previousLocation.y = newLoc->y;
}
IOUnlock(_deviceLock);
_hadContact = (pressure > pressureMin);
if (!_contactToMove || (pressure > pressureMin)) {
pressure -= pressureMin;
if (pressure > 255) {
pressure = 255;
}
if (_convertAbsoluteToRelative) {
if (_relativePointerEventAction && _relativePointerEventTarget) {
(*_relativePointerEventAction)(_relativePointerEventTarget,
buttons,
dx,
dy,
ts);
}
} else {
if (_absolutePointerEventAction && _absolutePointerEventTarget) {
(*_absolutePointerEventAction)(_absolutePointerEventTarget,
buttons,
newLoc,
bounds,
proximity,
pressure,
stylusAngle,
ts);
}
}
}
return;
}
void IOHIPointing::dispatchRelativePointerEvent(int dx,
int dy,
UInt32 buttonState,
AbsoluteTime ts)
{
int buttons;
IOTakeLock( _deviceLock);
buttons = 0;
if( buttonState & 1)
buttons |= EV_LB;
if( buttonCount() > 1) {
if( buttonState & 2) buttons |= EV_RB;
if(buttonState & 4)
buttons |= 2;
buttons |= buttonState & ~0x7;
}
scalePointer(&dx, &dy);
if ( _buttonMode == NX_OneButton )
{
if ( (buttons & (EV_LB|EV_RB)) != 0 ) {
buttons |= EV_LB;
buttons &= ~EV_RB;
}
}
else if ( (buttonCount() > 1) && (_buttonMode == NX_LeftButton) )
{
int temp = 0;
if ( buttons & EV_LB )
temp = EV_RB;
if ( buttons & EV_RB )
temp |= EV_LB;
buttons = (buttons & ~(EV_LB|EV_RB)) | temp;
}
IOUnlock( _deviceLock);
if (_relativePointerEventAction)
{
(*_relativePointerEventAction)(_relativePointerEventTarget,
buttons,
dx,
dy,
ts);
}
}
void IOHIPointing::dispatchScrollWheelEvent(short deltaAxis1,
short deltaAxis2,
short deltaAxis3,
AbsoluteTime ts)
{
if (_scrollWheelEventAction) {
(*_scrollWheelEventAction)(_scrollWheelEventTarget,
deltaAxis1,
deltaAxis2,
deltaAxis3,
ts);
}
}
bool IOHIPointing::updateProperties( void )
{
bool ok;
UInt32 res = resolution();
ok = setProperty( kIOHIDPointerResolutionKey, &res, sizeof( res))
& setProperty( kIOHIDPointerAccelerationKey, &_acceleration,
sizeof( _acceleration))
& setProperty( kIOHIDPointerConvertAbsoluteKey, &_convertAbsoluteToRelative,
sizeof( _convertAbsoluteToRelative))
& setProperty( kIOHIDPointerContactToMoveKey, &_contactToMove,
sizeof( _contactToMove));
return( ok & super::updateProperties() );
}
IOReturn IOHIPointing::setParamProperties( OSDictionary * dict )
{
OSData * data;
IOReturn err = kIOReturnSuccess;
bool updated = false;
UInt8 * bytes;
IOTakeLock( _deviceLock);
if( (data = OSDynamicCast( OSData,
dict->getObject(kIOHIDPointerAccelerationKey)))) {
setupForAcceleration( *((IOFixed *)data->getBytesNoCopy()) );
updated = true;
}
IOUnlock( _deviceLock);
if( dict->getObject(kIOHIDResetPointerKey))
resetPointer();
if ((data = OSDynamicCast(OSData,
dict->getObject(kIOHIDPointerConvertAbsoluteKey)))) {
bytes = (UInt8 *) data->getBytesNoCopy();
_convertAbsoluteToRelative = (bytes[0] != 0) ? true : false;
updated = true;
}
if ((data = OSDynamicCast(OSData,
dict->getObject(kIOHIDPointerContactToMoveKey)))) {
bytes = (UInt8 *) data->getBytesNoCopy();
_contactToMove = (bytes[0] != 0) ? true : false;
updated = true;
}
if( updated )
updateProperties();
return( err );
}
IOItemCount IOHIPointing::buttonCount()
{
return (1);
}
IOFixed IOHIPointing::resolution()
{
return (100 << 16);
}
OSData * IOHIPointing::copyAccelerationTable()
{
static const UInt16 accl[] = {
0x0000, 0x8000,
0x4032, 0x3030, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000,
0x0001, 0x0000, 0x0001, 0x0000, 0x0009, 0x0000, 0x713B, 0x0000,
0x6000, 0x0004, 0x4EC5, 0x0010, 0x8000, 0x000C, 0x0000, 0x005F,
0x0000, 0x0016, 0xEC4F, 0x008B, 0x0000, 0x001D, 0x3B14, 0x0094,
0x8000, 0x0022, 0x7627, 0x0096, 0x0000, 0x0024, 0x6276, 0x0096,
0x0000, 0x0026, 0x0000, 0x0096, 0x0000, 0x0028, 0x0000, 0x0096,
0x0000
};
OSData * data = OSDynamicCast( OSData,
getProperty( "HIDPointerAccelerationTable" ));
if( data)
data->retain();
else
data = OSData::withBytesNoCopy( accl, sizeof( accl ) );
return( data );
}