Source to iokit/Families/IOHIDSystem/IOHIPointing.cpp


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * 17 July 1998 sdouglas
 */

#include <IOKit/IOLib.h>
#include <IOKit/assert.h>
#include <IOKit/hidsystem/IOHIPointing.h>
#include <IOKit/hidsystem/IOHIDParameter.h>

#ifndef abs
#define abs(_a)	((_a >= 0) ? _a : -_a)
#endif

#define super IOHIDevice
OSDefineMetaClassAndStructors(IOHIPointing, IOHIDevice);

bool IOHIPointing::init(OSDictionary * properties)
{
  if (!super::init(properties))  return false;

  /*
   * Initialize minimal state.
   */

  _fractX	= 0;
  _fractY     	= 0;
  _acceleration	= -1;
  _convertAbsoluteToRelative = false;
  _contactToMove = false;
  _hadContact = false;
  _pressureThresholdToClick = 128;
  _previousLocation.x = 0;
  _previousLocation.y = 0;

  _deviceLock = IOLockAlloc();

  if (!_deviceLock)  return false;

  IOLockInit(_deviceLock);

  return true;
}

bool IOHIPointing::start(IOService * provider)
{
  if (!super::start(provider))  return false;

  /*
   * IOHIPointing serves both as a service and a nub (we lead a double
   * life).  Register ourselves as a nub to kick off matching.
   */

  registerService();

  return true;
}

void IOHIPointing::free()
// Description:	Go Away. Be careful when freeing the lock.
{
    if (_deviceLock)
    {
	IOLock * lock;

	IOTakeLock(_deviceLock);

	lock = _deviceLock;
	_deviceLock = NULL;

	IOUnlock(lock);
	IOLockFree(lock);
    }
    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))
  {
    // Note: client object is already retained by superclass' open()
    _relativePointerEventTarget = client;
    _relativePointerEventAction = rpeAction;
    _absolutePointerEventTarget = client;
    _absolutePointerEventAction = apeAction;
    _scrollWheelEventTarget = client;
    _scrollWheelEventAction = sweAction;
    return true;
  }

  return false;
}

void IOHIPointing::close(IOService * client, IOOptionBits)
{
  _relativePointerEventAction = NULL;
  _relativePointerEventTarget = 0;
  _absolutePointerEventAction = NULL;
  _absolutePointerEventTarget = 0;

  super::close(client);
} 

IOHIDKind IOHIPointing::hidKind()
{
  return kHIRelativePointingDevice;
}

void IOHIPointing::scalePointer(int * dxp, int * dyp)
// Description:	Perform pointer acceleration computations here.
//		Given the resolution, dx, dy, and time, compute the velocity
//		of the pointer over a Manhatten distance in inches/second.
//		Using this velocity, do a lookup in the pointerScaling table
//		to select a scaling factor. Scale dx and dy up as appropriate.
// Preconditions:
// *	_deviceLock should be held on entry
{
    int dx, dy;
    int absDx, absDy;
    unsigned delta;

    dx = *dxp;
    dy = *dyp;
    absDx = (dx < 0) ? -dx : dx;
    absDy = (dy < 0) ? -dy : dy;

#if 1
    if( absDx > absDy)
	delta = (absDx + (absDy / 2));
    else
	delta = (absDy + (absDx / 2));
#else
    delta = absDx + absDy;
#endif

    // scale
    if( delta > (MAXMAG - 1))
	delta = MAXMAG - 1;
    dx = IOFixedMultiply( dx << 16, _scaleValues[delta]);
    dy = IOFixedMultiply( dy << 16, _scaleValues[delta]);

    // if no direction changes add fract parts
    if( (dx ^ _fractX) >= 0)
	dx += _fractX;
    if( (dy ^ _fractY) >= 0)
	dy += _fractY;

    *dxp = dx / 65536;
    *dyp = dy / 65536;

    // get fractional part with sign extend
    if( dx >= 0)
	_fractX = dx & 0xffff;
    else
	_fractX = dx | 0xffff0000;
    if( dy >= 0)
	_fractY = dy & 0xffff;
    else
	_fractY = dy | 0xffff0000;
}


void IOHIPointing::setupForAcceleration(IOFixed accl)
{
    int				slope = 0, i, j = 0;
    int				x;
    int 			devSpeed, crsrSpeed;
    int 			lastCrsrSpeed = 0, nextCrsrSpeed;
    int 			lastDeviceSpeed = 0, nextDeviceSpeed;
    IOHIAccelerationPoint *	table;
    IOItemCount			numEntries;
    IOFixed                     res = resolution();

    assert(res);

    accelerationTable(&table, &numEntries);

    if( accl > (1 << 16))
	accl = (1 << 16);

    // scale for device speed
    // no vbl sync, so 90 autopolls /s
    devSpeed = IOFixedDivide( 90 << 16, res );
    // scale for cursor speed
    // screen is assumed 72 dpi, like MacOS
    crsrSpeed = IOFixedDivide( 72 << 16, res );

    nextCrsrSpeed = 0;
    nextDeviceSpeed = 0;

    // Precalculate fixed point scales. Not as accurate as MacOS,
    //	but no IOFixedDivide() in handler
    for ( i = 1; i < MAXMAG; i++ )
    {
        x = IOFixedMultiply( i << 16, devSpeed );
        if( x > table[j].deviceSpeed) {
            lastCrsrSpeed = nextCrsrSpeed;
            lastDeviceSpeed = nextDeviceSpeed;
            j++;
            nextDeviceSpeed = table[j].deviceSpeed;
            // Interpolate by accl between y=x and y=acclTable(x)
	    //  to get nextCrsrSpeed
            {
                int factorCursor;
                int factorDevice;
                if( table[j].cursorSpeed < nextDeviceSpeed) {
                    factorDevice = accl;
                    factorCursor = (0x10000 - accl);
                } else {
                    factorCursor = accl;
                    factorDevice = (0x10000 - accl);
                }
                nextCrsrSpeed = IOFixedMultiply( factorCursor, table[j].cursorSpeed)
			      + IOFixedMultiply( factorDevice, nextDeviceSpeed);
            }
            slope = IOFixedDivide( nextCrsrSpeed - lastCrsrSpeed,
			nextDeviceSpeed - lastDeviceSpeed);
        }
        _scaleValues[i] = IOFixedDivide( IOFixedMultiply( crsrSpeed,
                    IOFixedMultiply( slope, x - lastDeviceSpeed ) + lastCrsrSpeed), x);
    }
    _scaleValues[0] = _scaleValues[1];
    _fractX = _fractY = 0;
    _acceleration = accl;
}

bool IOHIPointing::resetPointer()
{
    IOTakeLock( _deviceLock);

    _buttonMode = NX_OneButton;

    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) {	// any other buttons
            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)	// any others down
	    buttons |= EV_RB;
    }

    // Perform pointer acceleration computations
    scalePointer(&dx, &dy);

    // Perform button tying and mapping.  This
    // stuff applies to relative posn devices (mice) only.
    if ( _buttonMode == NX_OneButton )
    {
	if ( (buttons & (EV_LB|EV_RB)) != 0 )
	    buttons = EV_LB;
    }
    else if ( (buttonCount() > 1) && (_buttonMode == NX_LeftButton) )	
    // Menus on left button. Swap!
    {
	int temp = 0;
	if ( buttons & EV_LB )
	    temp = EV_RB;
	if ( buttons & EV_RB )
	    temp |= EV_LB;
	buttons = temp;
    }
    IOUnlock( _deviceLock);

    if (_relativePointerEventAction)          /* upstream call */
    {
      (*_relativePointerEventAction)(_relativePointerEventTarget,
                       /* buttons */ buttons,
                       /* deltaX */  dx,
                       /* deltaY */  dy,
                       /* atTime */  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 );
}

// subclasses override

IOItemCount IOHIPointing::buttonCount()
{
    return (1);
}

IOFixed IOHIPointing::resolution()
{
    return (100 << 16);
}

void IOHIPointing::accelerationTable(IOHIAccelerationPoint ** table,
	                             IOItemCount * numEntries)
{
    static IOHIAccelerationPoint defaultTable[] = {
	{ 0x000000, 0x000000 },
	{ 0x7ffffff, 0x0960000 }
    };

    *table = defaultTable;
    *numEntries = sizeof( defaultTable) / sizeof( defaultTable[0]);
}