1185 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1185 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
/*
 | 
						|
 *
 | 
						|
 * Copyright © 2006-2009 Simon Thum             simon dot thum at gmx dot de
 | 
						|
 *
 | 
						|
 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
						|
 * copy of this software and associated documentation files (the "Software"),
 | 
						|
 * to deal in the Software without restriction, including without limitation
 | 
						|
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
						|
 * and/or sell copies of the Software, and to permit persons to whom the
 | 
						|
 * Software is furnished to do so, subject to the following conditions:
 | 
						|
 *
 | 
						|
 * The above copyright notice and this permission notice (including the next
 | 
						|
 * paragraph) shall be included in all copies or substantial portions of the
 | 
						|
 * Software.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
						|
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
						|
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
						|
 * DEALINGS IN THE SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
#ifdef HAVE_DIX_CONFIG_H
 | 
						|
#include <dix-config.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <math.h>
 | 
						|
#include <ptrveloc.h>
 | 
						|
#include <exevents.h>
 | 
						|
#include <X11/Xatom.h>
 | 
						|
#include <os.h>
 | 
						|
 | 
						|
#include <xserver-properties.h>
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 * Predictable pointer acceleration
 | 
						|
 *
 | 
						|
 * 2006-2009 by Simon Thum (simon [dot] thum [at] gmx de)
 | 
						|
 *
 | 
						|
 * Serves 3 complementary functions:
 | 
						|
 * 1) provide a sophisticated ballistic velocity estimate to improve
 | 
						|
 *    the relation between velocity (of the device) and acceleration
 | 
						|
 * 2) make arbitrary acceleration profiles possible
 | 
						|
 * 3) decelerate by two means (constant and adaptive) if enabled
 | 
						|
 *
 | 
						|
 * Important concepts are the
 | 
						|
 *
 | 
						|
 * - Scheme
 | 
						|
 *      which selects the basic algorithm
 | 
						|
 *      (see devices.c/InitPointerAccelerationScheme)
 | 
						|
 * - Profile
 | 
						|
 *      which returns an acceleration
 | 
						|
 *      for a given velocity
 | 
						|
 *
 | 
						|
 *  The profile can be selected by the user at runtime.
 | 
						|
 *  The classic profile is intended to cleanly perform old-style
 | 
						|
 *  function selection (threshold =/!= 0)
 | 
						|
 *
 | 
						|
 ****************************************************************************/
 | 
						|
 | 
						|
/* fwds */
 | 
						|
static double
 | 
						|
SimpleSmoothProfile(DeviceIntPtr dev, DeviceVelocityPtr vel, double velocity,
 | 
						|
                    double threshold, double acc);
 | 
						|
static PointerAccelerationProfileFunc
 | 
						|
GetAccelerationProfile(DeviceVelocityPtr vel, int profile_num);
 | 
						|
static BOOL
 | 
						|
InitializePredictableAccelerationProperties(DeviceIntPtr,
 | 
						|
                                            DeviceVelocityPtr,
 | 
						|
                                            PredictableAccelSchemePtr);
 | 
						|
static BOOL
 | 
						|
DeletePredictableAccelerationProperties(DeviceIntPtr,
 | 
						|
                                        PredictableAccelSchemePtr);
 | 
						|
 | 
						|
/*#define PTRACCEL_DEBUGGING*/
 | 
						|
 | 
						|
#ifdef PTRACCEL_DEBUGGING
 | 
						|
#define DebugAccelF ErrorF
 | 
						|
#else
 | 
						|
#define DebugAccelF(...)        /* */
 | 
						|
#endif
 | 
						|
 | 
						|
/********************************
 | 
						|
 *  Init/Uninit
 | 
						|
 *******************************/
 | 
						|
 | 
						|
/* some int which is not a profile number */
 | 
						|
#define PROFILE_UNINITIALIZE (-100)
 | 
						|
 | 
						|
/**
 | 
						|
 * Init DeviceVelocity struct so it should match the average case
 | 
						|
 */
 | 
						|
void
 | 
						|
InitVelocityData(DeviceVelocityPtr vel)
 | 
						|
{
 | 
						|
    memset(vel, 0, sizeof(DeviceVelocityRec));
 | 
						|
 | 
						|
    vel->corr_mul = 10.0;       /* dots per 10 milisecond should be usable */
 | 
						|
    vel->const_acceleration = 1.0;      /* no acceleration/deceleration  */
 | 
						|
    vel->reset_time = 300;
 | 
						|
    vel->use_softening = 1;
 | 
						|
    vel->min_acceleration = 1.0;        /* don't decelerate */
 | 
						|
    vel->max_rel_diff = 0.2;
 | 
						|
    vel->max_diff = 1.0;
 | 
						|
    vel->initial_range = 2;
 | 
						|
    vel->average_accel = TRUE;
 | 
						|
    SetAccelerationProfile(vel, AccelProfileClassic);
 | 
						|
    InitTrackers(vel, 16);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Clean up DeviceVelocityRec
 | 
						|
 */
 | 
						|
void
 | 
						|
FreeVelocityData(DeviceVelocityPtr vel)
 | 
						|
{
 | 
						|
    free(vel->tracker);
 | 
						|
    SetAccelerationProfile(vel, PROFILE_UNINITIALIZE);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Init predictable scheme
 | 
						|
 */
 | 
						|
Bool
 | 
						|
InitPredictableAccelerationScheme(DeviceIntPtr dev,
 | 
						|
                                  ValuatorAccelerationPtr protoScheme)
 | 
						|
{
 | 
						|
    DeviceVelocityPtr vel;
 | 
						|
    ValuatorAccelerationRec scheme;
 | 
						|
    PredictableAccelSchemePtr schemeData;
 | 
						|
 | 
						|
    scheme = *protoScheme;
 | 
						|
    vel = calloc(1, sizeof(DeviceVelocityRec));
 | 
						|
    schemeData = calloc(1, sizeof(PredictableAccelSchemeRec));
 | 
						|
    if (!vel || !schemeData)
 | 
						|
        return FALSE;
 | 
						|
    InitVelocityData(vel);
 | 
						|
    schemeData->vel = vel;
 | 
						|
    scheme.accelData = schemeData;
 | 
						|
    if (!InitializePredictableAccelerationProperties(dev, vel, schemeData))
 | 
						|
        return FALSE;
 | 
						|
    /* all fine, assign scheme to device */
 | 
						|
    dev->valuator->accelScheme = scheme;
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 *  Uninit scheme
 | 
						|
 */
 | 
						|
void
 | 
						|
AccelerationDefaultCleanup(DeviceIntPtr dev)
 | 
						|
{
 | 
						|
    DeviceVelocityPtr vel = GetDevicePredictableAccelData(dev);
 | 
						|
 | 
						|
    if (vel) {
 | 
						|
        /* the proper guarantee would be that we're not inside of
 | 
						|
         * AccelSchemeProc(), but that seems impossible. Schemes don't get
 | 
						|
         * switched often anyway.
 | 
						|
         */
 | 
						|
        OsBlockSignals();
 | 
						|
        dev->valuator->accelScheme.AccelSchemeProc = NULL;
 | 
						|
        FreeVelocityData(vel);
 | 
						|
        free(vel);
 | 
						|
        DeletePredictableAccelerationProperties(dev,
 | 
						|
                                                (PredictableAccelSchemePtr)
 | 
						|
                                                dev->valuator->accelScheme.
 | 
						|
                                                accelData);
 | 
						|
        free(dev->valuator->accelScheme.accelData);
 | 
						|
        dev->valuator->accelScheme.accelData = NULL;
 | 
						|
        OsReleaseSignals();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*************************
 | 
						|
 * Input property support
 | 
						|
 ************************/
 | 
						|
 | 
						|
/**
 | 
						|
 * choose profile
 | 
						|
 */
 | 
						|
static int
 | 
						|
AccelSetProfileProperty(DeviceIntPtr dev, Atom atom,
 | 
						|
                        XIPropertyValuePtr val, BOOL checkOnly)
 | 
						|
{
 | 
						|
    DeviceVelocityPtr vel;
 | 
						|
    int profile, *ptr = &profile;
 | 
						|
    int rc;
 | 
						|
    int nelem = 1;
 | 
						|
 | 
						|
    if (atom != XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER))
 | 
						|
        return Success;
 | 
						|
 | 
						|
    vel = GetDevicePredictableAccelData(dev);
 | 
						|
    if (!vel)
 | 
						|
        return BadValue;
 | 
						|
    rc = XIPropToInt(val, &nelem, &ptr);
 | 
						|
 | 
						|
    if (checkOnly) {
 | 
						|
        if (rc)
 | 
						|
            return rc;
 | 
						|
 | 
						|
        if (GetAccelerationProfile(vel, profile) == NULL)
 | 
						|
            return BadValue;
 | 
						|
    }
 | 
						|
    else
 | 
						|
        SetAccelerationProfile(vel, profile);
 | 
						|
 | 
						|
    return Success;
 | 
						|
}
 | 
						|
 | 
						|
static long
 | 
						|
AccelInitProfileProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
 | 
						|
{
 | 
						|
    int profile = vel->statistics.profile_number;
 | 
						|
    Atom prop_profile_number = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
 | 
						|
 | 
						|
    XIChangeDeviceProperty(dev, prop_profile_number, XA_INTEGER, 32,
 | 
						|
                           PropModeReplace, 1, &profile, FALSE);
 | 
						|
    XISetDevicePropertyDeletable(dev, prop_profile_number, FALSE);
 | 
						|
    return XIRegisterPropertyHandler(dev, AccelSetProfileProperty, NULL, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * constant deceleration
 | 
						|
 */
 | 
						|
static int
 | 
						|
AccelSetDecelProperty(DeviceIntPtr dev, Atom atom,
 | 
						|
                      XIPropertyValuePtr val, BOOL checkOnly)
 | 
						|
{
 | 
						|
    DeviceVelocityPtr vel;
 | 
						|
    float v, *ptr = &v;
 | 
						|
    int rc;
 | 
						|
    int nelem = 1;
 | 
						|
 | 
						|
    if (atom != XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION))
 | 
						|
        return Success;
 | 
						|
 | 
						|
    vel = GetDevicePredictableAccelData(dev);
 | 
						|
    if (!vel)
 | 
						|
        return BadValue;
 | 
						|
    rc = XIPropToFloat(val, &nelem, &ptr);
 | 
						|
 | 
						|
    if (checkOnly) {
 | 
						|
        if (rc)
 | 
						|
            return rc;
 | 
						|
        return (v >= 1.0f) ? Success : BadValue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (v >= 1.0f)
 | 
						|
        vel->const_acceleration = 1 / v;
 | 
						|
 | 
						|
    return Success;
 | 
						|
}
 | 
						|
 | 
						|
static long
 | 
						|
AccelInitDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
 | 
						|
{
 | 
						|
    float fval = 1.0 / vel->const_acceleration;
 | 
						|
    Atom prop_const_decel =
 | 
						|
        XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
 | 
						|
    XIChangeDeviceProperty(dev, prop_const_decel,
 | 
						|
                           XIGetKnownProperty(XATOM_FLOAT), 32, PropModeReplace,
 | 
						|
                           1, &fval, FALSE);
 | 
						|
    XISetDevicePropertyDeletable(dev, prop_const_decel, FALSE);
 | 
						|
    return XIRegisterPropertyHandler(dev, AccelSetDecelProperty, NULL, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * adaptive deceleration
 | 
						|
 */
 | 
						|
static int
 | 
						|
AccelSetAdaptDecelProperty(DeviceIntPtr dev, Atom atom,
 | 
						|
                           XIPropertyValuePtr val, BOOL checkOnly)
 | 
						|
{
 | 
						|
    DeviceVelocityPtr veloc;
 | 
						|
    float v, *ptr = &v;
 | 
						|
    int rc;
 | 
						|
    int nelem = 1;
 | 
						|
 | 
						|
    if (atom != XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION))
 | 
						|
        return Success;
 | 
						|
 | 
						|
    veloc = GetDevicePredictableAccelData(dev);
 | 
						|
    if (!veloc)
 | 
						|
        return BadValue;
 | 
						|
    rc = XIPropToFloat(val, &nelem, &ptr);
 | 
						|
 | 
						|
    if (checkOnly) {
 | 
						|
        if (rc)
 | 
						|
            return rc;
 | 
						|
        return (v >= 1.0f) ? Success : BadValue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (v >= 1.0f)
 | 
						|
        veloc->min_acceleration = 1 / v;
 | 
						|
 | 
						|
    return Success;
 | 
						|
}
 | 
						|
 | 
						|
static long
 | 
						|
AccelInitAdaptDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
 | 
						|
{
 | 
						|
    float fval = 1.0 / vel->min_acceleration;
 | 
						|
    Atom prop_adapt_decel =
 | 
						|
        XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
 | 
						|
 | 
						|
    XIChangeDeviceProperty(dev, prop_adapt_decel,
 | 
						|
                           XIGetKnownProperty(XATOM_FLOAT), 32, PropModeReplace,
 | 
						|
                           1, &fval, FALSE);
 | 
						|
    XISetDevicePropertyDeletable(dev, prop_adapt_decel, FALSE);
 | 
						|
    return XIRegisterPropertyHandler(dev, AccelSetAdaptDecelProperty, NULL,
 | 
						|
                                     NULL);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * velocity scaling
 | 
						|
 */
 | 
						|
static int
 | 
						|
AccelSetScaleProperty(DeviceIntPtr dev, Atom atom,
 | 
						|
                      XIPropertyValuePtr val, BOOL checkOnly)
 | 
						|
{
 | 
						|
    DeviceVelocityPtr vel;
 | 
						|
    float v, *ptr = &v;
 | 
						|
    int rc;
 | 
						|
    int nelem = 1;
 | 
						|
 | 
						|
    if (atom != XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING))
 | 
						|
        return Success;
 | 
						|
 | 
						|
    vel = GetDevicePredictableAccelData(dev);
 | 
						|
    if (!vel)
 | 
						|
        return BadValue;
 | 
						|
    rc = XIPropToFloat(val, &nelem, &ptr);
 | 
						|
 | 
						|
    if (checkOnly) {
 | 
						|
        if (rc)
 | 
						|
            return rc;
 | 
						|
 | 
						|
        return (v > 0) ? Success : BadValue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (v > 0)
 | 
						|
        vel->corr_mul = v;
 | 
						|
 | 
						|
    return Success;
 | 
						|
}
 | 
						|
 | 
						|
static long
 | 
						|
AccelInitScaleProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
 | 
						|
{
 | 
						|
    float fval = vel->corr_mul;
 | 
						|
    Atom prop_velo_scale = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
 | 
						|
 | 
						|
    XIChangeDeviceProperty(dev, prop_velo_scale,
 | 
						|
                           XIGetKnownProperty(XATOM_FLOAT), 32, PropModeReplace,
 | 
						|
                           1, &fval, FALSE);
 | 
						|
    XISetDevicePropertyDeletable(dev, prop_velo_scale, FALSE);
 | 
						|
    return XIRegisterPropertyHandler(dev, AccelSetScaleProperty, NULL, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static BOOL
 | 
						|
InitializePredictableAccelerationProperties(DeviceIntPtr dev,
 | 
						|
                                            DeviceVelocityPtr vel,
 | 
						|
                                            PredictableAccelSchemePtr
 | 
						|
                                            schemeData)
 | 
						|
{
 | 
						|
    int num_handlers = 4;
 | 
						|
 | 
						|
    if (!vel)
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    schemeData->prop_handlers = calloc(num_handlers, sizeof(long));
 | 
						|
    if (!schemeData->prop_handlers)
 | 
						|
        return FALSE;
 | 
						|
    schemeData->num_prop_handlers = num_handlers;
 | 
						|
    schemeData->prop_handlers[0] = AccelInitProfileProperty(dev, vel);
 | 
						|
    schemeData->prop_handlers[1] = AccelInitDecelProperty(dev, vel);
 | 
						|
    schemeData->prop_handlers[2] = AccelInitAdaptDecelProperty(dev, vel);
 | 
						|
    schemeData->prop_handlers[3] = AccelInitScaleProperty(dev, vel);
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
BOOL
 | 
						|
DeletePredictableAccelerationProperties(DeviceIntPtr dev,
 | 
						|
                                        PredictableAccelSchemePtr scheme)
 | 
						|
{
 | 
						|
    DeviceVelocityPtr vel;
 | 
						|
    Atom prop;
 | 
						|
    int i;
 | 
						|
 | 
						|
    prop = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
 | 
						|
    XIDeleteDeviceProperty(dev, prop, FALSE);
 | 
						|
    prop = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
 | 
						|
    XIDeleteDeviceProperty(dev, prop, FALSE);
 | 
						|
    prop = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
 | 
						|
    XIDeleteDeviceProperty(dev, prop, FALSE);
 | 
						|
    prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
 | 
						|
    XIDeleteDeviceProperty(dev, prop, FALSE);
 | 
						|
 | 
						|
    vel = GetDevicePredictableAccelData(dev);
 | 
						|
    if (vel) {
 | 
						|
        for (i = 0; i < scheme->num_prop_handlers; i++)
 | 
						|
            if (scheme->prop_handlers[i])
 | 
						|
                XIUnregisterPropertyHandler(dev, scheme->prop_handlers[i]);
 | 
						|
    }
 | 
						|
 | 
						|
    free(scheme->prop_handlers);
 | 
						|
    scheme->prop_handlers = NULL;
 | 
						|
    scheme->num_prop_handlers = 0;
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/*********************
 | 
						|
 * Tracking logic
 | 
						|
 ********************/
 | 
						|
 | 
						|
void
 | 
						|
InitTrackers(DeviceVelocityPtr vel, int ntracker)
 | 
						|
{
 | 
						|
    if (ntracker < 1) {
 | 
						|
        ErrorF("(dix ptracc) invalid number of trackers\n");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    free(vel->tracker);
 | 
						|
    vel->tracker = (MotionTrackerPtr) calloc(ntracker, sizeof(MotionTracker));
 | 
						|
    vel->num_tracker = ntracker;
 | 
						|
}
 | 
						|
 | 
						|
enum directions {
 | 
						|
    N = (1 << 0),
 | 
						|
    NE = (1 << 1),
 | 
						|
    E = (1 << 2),
 | 
						|
    SE = (1 << 3),
 | 
						|
    S = (1 << 4),
 | 
						|
    SW = (1 << 5),
 | 
						|
    W = (1 << 6),
 | 
						|
    NW = (1 << 7),
 | 
						|
    UNDEFINED = 0xFF
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * return a bit field of possible directions.
 | 
						|
 * There's no reason against widening to more precise directions (<45 degrees),
 | 
						|
 * should it not perform well. All this is needed for is sort out non-linear
 | 
						|
 * motion, so precision isn't paramount. However, one should not flag direction
 | 
						|
 * too narrow, since it would then cut the linear segment to zero size way too
 | 
						|
 * often.
 | 
						|
 *
 | 
						|
 * @return A bitmask for N, NE, S, SE, etc. indicating the directions for
 | 
						|
 * this movement.
 | 
						|
 */
 | 
						|
static int
 | 
						|
DoGetDirection(int dx, int dy)
 | 
						|
{
 | 
						|
    int dir = 0;
 | 
						|
 | 
						|
    /* on insignificant mickeys, flag 135 degrees */
 | 
						|
    if (abs(dx) < 2 && abs(dy) < 2) {
 | 
						|
        /* first check diagonal cases */
 | 
						|
        if (dx > 0 && dy > 0)
 | 
						|
            dir = E | SE | S;
 | 
						|
        else if (dx > 0 && dy < 0)
 | 
						|
            dir = N | NE | E;
 | 
						|
        else if (dx < 0 && dy < 0)
 | 
						|
            dir = W | NW | N;
 | 
						|
        else if (dx < 0 && dy > 0)
 | 
						|
            dir = W | SW | S;
 | 
						|
        /* check axis-aligned directions */
 | 
						|
        else if (dx > 0)
 | 
						|
            dir = NE | E | SE;
 | 
						|
        else if (dx < 0)
 | 
						|
            dir = NW | W | SW;
 | 
						|
        else if (dy > 0)
 | 
						|
            dir = SE | S | SW;
 | 
						|
        else if (dy < 0)
 | 
						|
            dir = NE | N | NW;
 | 
						|
        else
 | 
						|
            dir = UNDEFINED;    /* shouldn't happen */
 | 
						|
    }
 | 
						|
    else {                      /* compute angle and set appropriate flags */
 | 
						|
        double r;
 | 
						|
        int i1, i2;
 | 
						|
 | 
						|
        r = atan2(dy, dx);
 | 
						|
        /* find direction.
 | 
						|
         *
 | 
						|
         * Add 360° to avoid r become negative since C has no well-defined
 | 
						|
         * modulo for such cases. Then divide by 45° to get the octant
 | 
						|
         * number,  e.g.
 | 
						|
         *          0 <= r <= 1 is [0-45]°
 | 
						|
         *          1 <= r <= 2 is [45-90]°
 | 
						|
         *          etc.
 | 
						|
         * But we add extra 90° to match up with our N, S, etc. defines up
 | 
						|
         * there, rest stays the same.
 | 
						|
         */
 | 
						|
        r = (r + (M_PI * 2.5)) / (M_PI / 4);
 | 
						|
        /* this intends to flag 2 directions (45 degrees),
 | 
						|
         * except on very well-aligned mickeys. */
 | 
						|
        i1 = (int) (r + 0.1) % 8;
 | 
						|
        i2 = (int) (r + 0.9) % 8;
 | 
						|
        if (i1 < 0 || i1 > 7 || i2 < 0 || i2 > 7)
 | 
						|
            dir = UNDEFINED;    /* shouldn't happen */
 | 
						|
        else
 | 
						|
            dir = (1 << i1 | 1 << i2);
 | 
						|
    }
 | 
						|
    return dir;
 | 
						|
}
 | 
						|
 | 
						|
#define DIRECTION_CACHE_RANGE 5
 | 
						|
#define DIRECTION_CACHE_SIZE (DIRECTION_CACHE_RANGE*2+1)
 | 
						|
 | 
						|
/* cache DoGetDirection().
 | 
						|
 * To avoid excessive use of direction calculation, cache the values for
 | 
						|
 * [-5..5] for both x/y. Anything outside of that is calcualted on the fly.
 | 
						|
 *
 | 
						|
 * @return A bitmask for N, NE, S, SE, etc. indicating the directions for
 | 
						|
 * this movement.
 | 
						|
 */
 | 
						|
static int
 | 
						|
GetDirection(int dx, int dy)
 | 
						|
{
 | 
						|
    static int cache[DIRECTION_CACHE_SIZE][DIRECTION_CACHE_SIZE];
 | 
						|
    int dir;
 | 
						|
 | 
						|
    if (abs(dx) <= DIRECTION_CACHE_RANGE && abs(dy) <= DIRECTION_CACHE_RANGE) {
 | 
						|
        /* cacheable */
 | 
						|
        dir = cache[DIRECTION_CACHE_RANGE + dx][DIRECTION_CACHE_RANGE + dy];
 | 
						|
        if (dir == 0) {
 | 
						|
            dir = DoGetDirection(dx, dy);
 | 
						|
            cache[DIRECTION_CACHE_RANGE + dx][DIRECTION_CACHE_RANGE + dy] = dir;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        /* non-cacheable */
 | 
						|
        dir = DoGetDirection(dx, dy);
 | 
						|
    }
 | 
						|
 | 
						|
    return dir;
 | 
						|
}
 | 
						|
 | 
						|
#undef DIRECTION_CACHE_RANGE
 | 
						|
#undef DIRECTION_CACHE_SIZE
 | 
						|
 | 
						|
/* convert offset (age) to array index */
 | 
						|
#define TRACKER_INDEX(s, d) (((s)->num_tracker + (s)->cur_tracker - (d)) % (s)->num_tracker)
 | 
						|
#define TRACKER(s, d) &(s)->tracker[TRACKER_INDEX(s,d)]
 | 
						|
 | 
						|
/**
 | 
						|
 * Add the delta motion to each tracker, then reset the latest tracker to
 | 
						|
 * 0/0 and set it as the current one.
 | 
						|
 */
 | 
						|
static inline void
 | 
						|
FeedTrackers(DeviceVelocityPtr vel, double dx, double dy, int cur_t)
 | 
						|
{
 | 
						|
    int n;
 | 
						|
 | 
						|
    for (n = 0; n < vel->num_tracker; n++) {
 | 
						|
        vel->tracker[n].dx += dx;
 | 
						|
        vel->tracker[n].dy += dy;
 | 
						|
    }
 | 
						|
    n = (vel->cur_tracker + 1) % vel->num_tracker;
 | 
						|
    vel->tracker[n].dx = 0.0;
 | 
						|
    vel->tracker[n].dy = 0.0;
 | 
						|
    vel->tracker[n].time = cur_t;
 | 
						|
    vel->tracker[n].dir = GetDirection(dx, dy);
 | 
						|
    DebugAccelF("(dix prtacc) motion [dx: %i dy: %i dir:%i diff: %i]\n",
 | 
						|
                dx, dy, vel->tracker[n].dir,
 | 
						|
                cur_t - vel->tracker[vel->cur_tracker].time);
 | 
						|
    vel->cur_tracker = n;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * calc velocity for given tracker, with
 | 
						|
 * velocity scaling.
 | 
						|
 * This assumes linear motion.
 | 
						|
 */
 | 
						|
static double
 | 
						|
CalcTracker(const MotionTracker * tracker, int cur_t)
 | 
						|
{
 | 
						|
    double dist = sqrt(tracker->dx * tracker->dx + tracker->dy * tracker->dy);
 | 
						|
    int dtime = cur_t - tracker->time;
 | 
						|
 | 
						|
    if (dtime > 0)
 | 
						|
        return dist / dtime;
 | 
						|
    else
 | 
						|
        return 0;               /* synonymous for NaN, since we're not C99 */
 | 
						|
}
 | 
						|
 | 
						|
/* find the most plausible velocity. That is, the most distant
 | 
						|
 * (in time) tracker which isn't too old, the movement vector was
 | 
						|
 * in the same octant, and where the velocity is within an
 | 
						|
 * acceptable range to the inital velocity.
 | 
						|
 *
 | 
						|
 * @return The tracker's velocity or 0 if the above conditions are unmet
 | 
						|
 */
 | 
						|
static double
 | 
						|
QueryTrackers(DeviceVelocityPtr vel, int cur_t)
 | 
						|
{
 | 
						|
    int offset, dir = UNDEFINED, used_offset = -1, age_ms;
 | 
						|
 | 
						|
    /* initial velocity: a low-offset, valid velocity */
 | 
						|
    double initial_velocity = 0, result = 0, velocity_diff;
 | 
						|
    double velocity_factor = vel->corr_mul * vel->const_acceleration;   /* premultiply */
 | 
						|
 | 
						|
    /* loop from current to older data */
 | 
						|
    for (offset = 1; offset < vel->num_tracker; offset++) {
 | 
						|
        MotionTracker *tracker = TRACKER(vel, offset);
 | 
						|
        double tracker_velocity;
 | 
						|
 | 
						|
        age_ms = cur_t - tracker->time;
 | 
						|
 | 
						|
        /* bail out if data is too old and protect from overrun */
 | 
						|
        if (age_ms >= vel->reset_time || age_ms < 0) {
 | 
						|
            DebugAccelF("(dix prtacc) query: tracker too old\n");
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        /*
 | 
						|
         * this heuristic avoids using the linear-motion velocity formula
 | 
						|
         * in CalcTracker() on motion that isn't exactly linear. So to get
 | 
						|
         * even more precision we could subdivide as a final step, so possible
 | 
						|
         * non-linearities are accounted for.
 | 
						|
         */
 | 
						|
        dir &= tracker->dir;
 | 
						|
        if (dir == 0) {         /* we've changed octant of movement (e.g. NE → NW) */
 | 
						|
            DebugAccelF("(dix prtacc) query: no longer linear\n");
 | 
						|
            /* instead of breaking it we might also inspect the partition after,
 | 
						|
             * but actual improvement with this is probably rare. */
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        tracker_velocity = CalcTracker(tracker, cur_t) * velocity_factor;
 | 
						|
 | 
						|
        if ((initial_velocity == 0 || offset <= vel->initial_range) &&
 | 
						|
            tracker_velocity != 0) {
 | 
						|
            /* set initial velocity and result */
 | 
						|
            result = initial_velocity = tracker_velocity;
 | 
						|
            used_offset = offset;
 | 
						|
        }
 | 
						|
        else if (initial_velocity != 0 && tracker_velocity != 0) {
 | 
						|
            velocity_diff = fabs(initial_velocity - tracker_velocity);
 | 
						|
 | 
						|
            if (velocity_diff > vel->max_diff &&
 | 
						|
                velocity_diff / (initial_velocity + tracker_velocity) >=
 | 
						|
                vel->max_rel_diff) {
 | 
						|
                /* we're not in range, quit - it won't get better. */
 | 
						|
                DebugAccelF("(dix prtacc) query: tracker too different:"
 | 
						|
                            " old %2.2f initial %2.2f diff: %2.2f\n",
 | 
						|
                            tracker_velocity, initial_velocity, velocity_diff);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            /* we're in range with the initial velocity,
 | 
						|
             * so this result is likely better
 | 
						|
             * (it contains more information). */
 | 
						|
            result = tracker_velocity;
 | 
						|
            used_offset = offset;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (offset == vel->num_tracker) {
 | 
						|
        DebugAccelF("(dix prtacc) query: last tracker in effect\n");
 | 
						|
        used_offset = vel->num_tracker - 1;
 | 
						|
    }
 | 
						|
    if (used_offset >= 0) {
 | 
						|
#ifdef PTRACCEL_DEBUGGING
 | 
						|
        MotionTracker *tracker = TRACKER(vel, used_offset);
 | 
						|
 | 
						|
        DebugAccelF("(dix prtacc) result: offset %i [dx: %i dy: %i diff: %i]\n",
 | 
						|
                    used_offset, tracker->dx, tracker->dy,
 | 
						|
                    cur_t - tracker->time);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
#undef TRACKER_INDEX
 | 
						|
#undef TRACKER
 | 
						|
 | 
						|
/**
 | 
						|
 * Perform velocity approximation based on 2D 'mickeys' (mouse motion delta).
 | 
						|
 * return true if non-visible state reset is suggested
 | 
						|
 */
 | 
						|
BOOL
 | 
						|
ProcessVelocityData2D(DeviceVelocityPtr vel, double dx, double dy, int time)
 | 
						|
{
 | 
						|
    double velocity;
 | 
						|
 | 
						|
    vel->last_velocity = vel->velocity;
 | 
						|
 | 
						|
    FeedTrackers(vel, dx, dy, time);
 | 
						|
 | 
						|
    velocity = QueryTrackers(vel, time);
 | 
						|
 | 
						|
    vel->velocity = velocity;
 | 
						|
    return velocity == 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * this flattens significant ( > 1) mickeys a little bit for more steady
 | 
						|
 * constant-velocity response
 | 
						|
 */
 | 
						|
static inline double
 | 
						|
ApplySimpleSoftening(double prev_delta, double delta)
 | 
						|
{
 | 
						|
    double result = delta;
 | 
						|
 | 
						|
    if (delta < -1.0 || delta > 1.0) {
 | 
						|
        if (delta > prev_delta)
 | 
						|
            result -= 0.5;
 | 
						|
        else if (delta < prev_delta)
 | 
						|
            result += 0.5;
 | 
						|
    }
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Soften the delta based on previous deltas stored in vel.
 | 
						|
 *
 | 
						|
 * @param[in,out] fdx Delta X, modified in-place.
 | 
						|
 * @param[in,out] fdx Delta Y, modified in-place.
 | 
						|
 */
 | 
						|
static void
 | 
						|
ApplySoftening(DeviceVelocityPtr vel, double *fdx, double *fdy)
 | 
						|
{
 | 
						|
    if (vel->use_softening) {
 | 
						|
        *fdx = ApplySimpleSoftening(vel->last_dx, *fdx);
 | 
						|
        *fdy = ApplySimpleSoftening(vel->last_dy, *fdy);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ApplyConstantDeceleration(DeviceVelocityPtr vel, double *fdx, double *fdy)
 | 
						|
{
 | 
						|
    *fdx *= vel->const_acceleration;
 | 
						|
    *fdy *= vel->const_acceleration;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * compute the acceleration for given velocity and enforce min_acceleration
 | 
						|
 */
 | 
						|
double
 | 
						|
BasicComputeAcceleration(DeviceIntPtr dev,
 | 
						|
                         DeviceVelocityPtr vel,
 | 
						|
                         double velocity, double threshold, double acc)
 | 
						|
{
 | 
						|
 | 
						|
    double result;
 | 
						|
 | 
						|
    result = vel->Profile(dev, vel, velocity, threshold, acc);
 | 
						|
 | 
						|
    /* enforce min_acceleration */
 | 
						|
    if (result < vel->min_acceleration)
 | 
						|
        result = vel->min_acceleration;
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Compute acceleration. Takes into account averaging, nv-reset, etc.
 | 
						|
 * If the velocity has changed, an average is taken of 6 velocity factors:
 | 
						|
 * current velocity, last velocity and 4 times the average between the two.
 | 
						|
 */
 | 
						|
static double
 | 
						|
ComputeAcceleration(DeviceIntPtr dev,
 | 
						|
                    DeviceVelocityPtr vel, double threshold, double acc)
 | 
						|
{
 | 
						|
    double result;
 | 
						|
 | 
						|
    if (vel->velocity <= 0) {
 | 
						|
        DebugAccelF("(dix ptracc) profile skipped\n");
 | 
						|
        /*
 | 
						|
         * If we have no idea about device velocity, don't pretend it.
 | 
						|
         */
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (vel->average_accel && vel->velocity != vel->last_velocity) {
 | 
						|
        /* use simpson's rule to average acceleration between
 | 
						|
         * current and previous velocity.
 | 
						|
         * Though being the more natural choice, it causes a minor delay
 | 
						|
         * in comparison, so it can be disabled. */
 | 
						|
        result =
 | 
						|
            BasicComputeAcceleration(dev, vel, vel->velocity, threshold, acc);
 | 
						|
        result +=
 | 
						|
            BasicComputeAcceleration(dev, vel, vel->last_velocity, threshold,
 | 
						|
                                     acc);
 | 
						|
        result +=
 | 
						|
            4.0f * BasicComputeAcceleration(dev, vel,
 | 
						|
                                            (vel->last_velocity +
 | 
						|
                                             vel->velocity) / 2,
 | 
						|
                                            threshold,
 | 
						|
                                            acc);
 | 
						|
        result /= 6.0f;
 | 
						|
        DebugAccelF("(dix ptracc) profile average [%.2f ... %.2f] is %.3f\n",
 | 
						|
                    vel->velocity, vel->last_velocity, result);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        result = BasicComputeAcceleration(dev, vel,
 | 
						|
                                          vel->velocity, threshold, acc);
 | 
						|
        DebugAccelF("(dix ptracc) profile sample [%.2f] is %.3f\n",
 | 
						|
                    vel->velocity, res);
 | 
						|
    }
 | 
						|
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************
 | 
						|
 *  Acceleration functions and profiles
 | 
						|
 ****************************************/
 | 
						|
 | 
						|
/**
 | 
						|
 * Polynomial function similar previous one, but with f(1) = 1
 | 
						|
 */
 | 
						|
static double
 | 
						|
PolynomialAccelerationProfile(DeviceIntPtr dev,
 | 
						|
                              DeviceVelocityPtr vel,
 | 
						|
                              double velocity, double ignored, double acc)
 | 
						|
{
 | 
						|
    return pow(velocity, (acc - 1.0) * 0.5);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * returns acceleration for velocity.
 | 
						|
 * This profile selects the two functions like the old scheme did
 | 
						|
 */
 | 
						|
static double
 | 
						|
ClassicProfile(DeviceIntPtr dev,
 | 
						|
               DeviceVelocityPtr vel,
 | 
						|
               double velocity, double threshold, double acc)
 | 
						|
{
 | 
						|
    if (threshold > 0) {
 | 
						|
        return SimpleSmoothProfile(dev, vel, velocity, threshold, acc);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        return PolynomialAccelerationProfile(dev, vel, velocity, 0, acc);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Power profile
 | 
						|
 * This has a completely smooth transition curve, i.e. no jumps in the
 | 
						|
 * derivatives.
 | 
						|
 *
 | 
						|
 * This has the expense of overall response dependency on min-acceleration.
 | 
						|
 * In effect, min_acceleration mimics const_acceleration in this profile.
 | 
						|
 */
 | 
						|
static double
 | 
						|
PowerProfile(DeviceIntPtr dev,
 | 
						|
             DeviceVelocityPtr vel,
 | 
						|
             double velocity, double threshold, double acc)
 | 
						|
{
 | 
						|
    double vel_dist;
 | 
						|
 | 
						|
    acc = (acc - 1.0) * 0.1f + 1.0;     /* without this, acc of 2 is unuseable */
 | 
						|
 | 
						|
    if (velocity <= threshold)
 | 
						|
        return vel->min_acceleration;
 | 
						|
    vel_dist = velocity - threshold;
 | 
						|
    return (pow(acc, vel_dist)) * vel->min_acceleration;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * just a smooth function in [0..1] -> [0..1]
 | 
						|
 *  - point symmetry at 0.5
 | 
						|
 *  - f'(0) = f'(1) = 0
 | 
						|
 *  - starts faster than a sinoid
 | 
						|
 *  - smoothness C1 (Cinf if you dare to ignore endpoints)
 | 
						|
 */
 | 
						|
static inline double
 | 
						|
CalcPenumbralGradient(double x)
 | 
						|
{
 | 
						|
    x *= 2.0f;
 | 
						|
    x -= 1.0f;
 | 
						|
    return 0.5f + (x * sqrt(1.0 - x * x) + asin(x)) / M_PI;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * acceleration function similar to classic accelerated/unaccelerated,
 | 
						|
 * but with smooth transition in between (and towards zero for adaptive dec.).
 | 
						|
 */
 | 
						|
static double
 | 
						|
SimpleSmoothProfile(DeviceIntPtr dev,
 | 
						|
                    DeviceVelocityPtr vel,
 | 
						|
                    double velocity, double threshold, double acc)
 | 
						|
{
 | 
						|
    if (velocity < 1.0f)
 | 
						|
        return CalcPenumbralGradient(0.5 + velocity * 0.5) * 2.0f - 1.0f;
 | 
						|
    if (threshold < 1.0f)
 | 
						|
        threshold = 1.0f;
 | 
						|
    if (velocity <= threshold)
 | 
						|
        return 1;
 | 
						|
    velocity /= threshold;
 | 
						|
    if (velocity >= acc)
 | 
						|
        return acc;
 | 
						|
    else
 | 
						|
        return 1.0f + (CalcPenumbralGradient(velocity / acc) * (acc - 1.0f));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * This profile uses the first half of the penumbral gradient as a start
 | 
						|
 * and then scales linearly.
 | 
						|
 */
 | 
						|
static double
 | 
						|
SmoothLinearProfile(DeviceIntPtr dev,
 | 
						|
                    DeviceVelocityPtr vel,
 | 
						|
                    double velocity, double threshold, double acc)
 | 
						|
{
 | 
						|
    double res, nv;
 | 
						|
 | 
						|
    if (acc > 1.0f)
 | 
						|
        acc -= 1.0f;            /*this is so acc = 1 is no acceleration */
 | 
						|
    else
 | 
						|
        return 1.0f;
 | 
						|
 | 
						|
    nv = (velocity - threshold) * acc * 0.5f;
 | 
						|
 | 
						|
    if (nv < 0) {
 | 
						|
        res = 0;
 | 
						|
    }
 | 
						|
    else if (nv < 2) {
 | 
						|
        res = CalcPenumbralGradient(nv * 0.25f) * 2.0f;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        nv -= 2.0f;
 | 
						|
        res = nv * 2.0f / M_PI  /* steepness of gradient at 0.5 */
 | 
						|
            + 1.0f;             /* gradient crosses 2|1 */
 | 
						|
    }
 | 
						|
    res += vel->min_acceleration;
 | 
						|
    return res;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * From 0 to threshold, the response graduates smoothly from min_accel to
 | 
						|
 * acceleration. Beyond threshold it is exactly the specified acceleration.
 | 
						|
 */
 | 
						|
static double
 | 
						|
SmoothLimitedProfile(DeviceIntPtr dev,
 | 
						|
                     DeviceVelocityPtr vel,
 | 
						|
                     double velocity, double threshold, double acc)
 | 
						|
{
 | 
						|
    double res;
 | 
						|
 | 
						|
    if (velocity >= threshold || threshold == 0.0f)
 | 
						|
        return acc;
 | 
						|
 | 
						|
    velocity /= threshold;      /* should be [0..1[ now */
 | 
						|
 | 
						|
    res = CalcPenumbralGradient(velocity) * (acc - vel->min_acceleration);
 | 
						|
 | 
						|
    return vel->min_acceleration + res;
 | 
						|
}
 | 
						|
 | 
						|
static double
 | 
						|
LinearProfile(DeviceIntPtr dev,
 | 
						|
              DeviceVelocityPtr vel,
 | 
						|
              double velocity, double threshold, double acc)
 | 
						|
{
 | 
						|
    return acc * velocity;
 | 
						|
}
 | 
						|
 | 
						|
static double
 | 
						|
NoProfile(DeviceIntPtr dev,
 | 
						|
          DeviceVelocityPtr vel, double velocity, double threshold, double acc)
 | 
						|
{
 | 
						|
    return 1.0f;
 | 
						|
}
 | 
						|
 | 
						|
static PointerAccelerationProfileFunc
 | 
						|
GetAccelerationProfile(DeviceVelocityPtr vel, int profile_num)
 | 
						|
{
 | 
						|
    switch (profile_num) {
 | 
						|
    case AccelProfileClassic:
 | 
						|
        return ClassicProfile;
 | 
						|
    case AccelProfileDeviceSpecific:
 | 
						|
        return vel->deviceSpecificProfile;
 | 
						|
    case AccelProfilePolynomial:
 | 
						|
        return PolynomialAccelerationProfile;
 | 
						|
    case AccelProfileSmoothLinear:
 | 
						|
        return SmoothLinearProfile;
 | 
						|
    case AccelProfileSimple:
 | 
						|
        return SimpleSmoothProfile;
 | 
						|
    case AccelProfilePower:
 | 
						|
        return PowerProfile;
 | 
						|
    case AccelProfileLinear:
 | 
						|
        return LinearProfile;
 | 
						|
    case AccelProfileSmoothLimited:
 | 
						|
        return SmoothLimitedProfile;
 | 
						|
    case AccelProfileNone:
 | 
						|
        return NoProfile;
 | 
						|
    default:
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Set the profile by number.
 | 
						|
 * Intended to make profiles exchangeable at runtime.
 | 
						|
 * If you created a profile, give it a number here and in the header to
 | 
						|
 * make it selectable. In case some profile-specific init is needed, here
 | 
						|
 * would be a good place, since FreeVelocityData() also calls this with
 | 
						|
 * PROFILE_UNINITIALIZE.
 | 
						|
 *
 | 
						|
 * returns FALSE if profile number is unavailable, TRUE otherwise.
 | 
						|
 */
 | 
						|
int
 | 
						|
SetAccelerationProfile(DeviceVelocityPtr vel, int profile_num)
 | 
						|
{
 | 
						|
    PointerAccelerationProfileFunc profile;
 | 
						|
 | 
						|
    profile = GetAccelerationProfile(vel, profile_num);
 | 
						|
 | 
						|
    if (profile == NULL && profile_num != PROFILE_UNINITIALIZE)
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    /* Here one could free old profile-private data */
 | 
						|
    free(vel->profile_private);
 | 
						|
    vel->profile_private = NULL;
 | 
						|
    /* Here one could init profile-private data */
 | 
						|
    vel->Profile = profile;
 | 
						|
    vel->statistics.profile_number = profile_num;
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/**********************************************
 | 
						|
 * driver interaction
 | 
						|
 **********************************************/
 | 
						|
 | 
						|
/**
 | 
						|
 * device-specific profile
 | 
						|
 *
 | 
						|
 * The device-specific profile is intended as a hook for a driver
 | 
						|
 * which may want to provide an own acceleration profile.
 | 
						|
 * It should not rely on profile-private data, instead
 | 
						|
 * it should do init/uninit in the driver (ie. with DEVICE_INIT and friends).
 | 
						|
 * Users may override or choose it.
 | 
						|
 */
 | 
						|
void
 | 
						|
SetDeviceSpecificAccelerationProfile(DeviceVelocityPtr vel,
 | 
						|
                                     PointerAccelerationProfileFunc profile)
 | 
						|
{
 | 
						|
    if (vel)
 | 
						|
        vel->deviceSpecificProfile = profile;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Use this function to obtain a DeviceVelocityPtr for a device. Will return NULL if
 | 
						|
 * the predictable acceleration scheme is not in effect.
 | 
						|
 */
 | 
						|
DeviceVelocityPtr
 | 
						|
GetDevicePredictableAccelData(DeviceIntPtr dev)
 | 
						|
{
 | 
						|
    /*sanity check */
 | 
						|
    if (!dev) {
 | 
						|
        ErrorF("[dix] accel: DeviceIntPtr was NULL");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (dev->valuator &&
 | 
						|
        dev->valuator->accelScheme.AccelSchemeProc ==
 | 
						|
        acceleratePointerPredictable &&
 | 
						|
        dev->valuator->accelScheme.accelData != NULL) {
 | 
						|
 | 
						|
        return ((PredictableAccelSchemePtr)
 | 
						|
                dev->valuator->accelScheme.accelData)->vel;
 | 
						|
    }
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/********************************
 | 
						|
 *  acceleration schemes
 | 
						|
 *******************************/
 | 
						|
 | 
						|
/**
 | 
						|
 * Modifies valuators in-place.
 | 
						|
 * This version employs a velocity approximation algorithm to
 | 
						|
 * enable fine-grained predictable acceleration profiles.
 | 
						|
 */
 | 
						|
void
 | 
						|
acceleratePointerPredictable(DeviceIntPtr dev, ValuatorMask *val, CARD32 evtime)
 | 
						|
{
 | 
						|
    double dx = 0, dy = 0;
 | 
						|
    DeviceVelocityPtr velocitydata = GetDevicePredictableAccelData(dev);
 | 
						|
    Bool soften = TRUE;
 | 
						|
 | 
						|
    if (valuator_mask_num_valuators(val) == 0 || !velocitydata)
 | 
						|
        return;
 | 
						|
 | 
						|
    if (velocitydata->statistics.profile_number == AccelProfileNone &&
 | 
						|
        velocitydata->const_acceleration == 1.0f) {
 | 
						|
        return;                 /*we're inactive anyway, so skip the whole thing. */
 | 
						|
    }
 | 
						|
 | 
						|
    if (valuator_mask_isset(val, 0)) {
 | 
						|
        dx = valuator_mask_get_double(val, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    if (valuator_mask_isset(val, 1)) {
 | 
						|
        dy = valuator_mask_get_double(val, 1);
 | 
						|
    }
 | 
						|
 | 
						|
    if (dx != 0.0 || dy != 0.0) {
 | 
						|
        /* reset non-visible state? */
 | 
						|
        if (ProcessVelocityData2D(velocitydata, dx, dy, evtime)) {
 | 
						|
            soften = FALSE;
 | 
						|
        }
 | 
						|
 | 
						|
        if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
 | 
						|
            double mult;
 | 
						|
 | 
						|
            /* invoke acceleration profile to determine acceleration */
 | 
						|
            mult = ComputeAcceleration(dev, velocitydata,
 | 
						|
                                       dev->ptrfeed->ctrl.threshold,
 | 
						|
                                       (double) dev->ptrfeed->ctrl.num /
 | 
						|
                                       (double) dev->ptrfeed->ctrl.den);
 | 
						|
 | 
						|
            if (mult != 1.0f || velocitydata->const_acceleration != 1.0f) {
 | 
						|
                if (mult > 1.0f && soften)
 | 
						|
                    ApplySoftening(velocitydata, &dx, &dy);
 | 
						|
                ApplyConstantDeceleration(velocitydata, &dx, &dy);
 | 
						|
 | 
						|
                if (dx != 0.0)
 | 
						|
                    valuator_mask_set_double(val, 0, mult * dx);
 | 
						|
                if (dy != 0.0)
 | 
						|
                    valuator_mask_set_double(val, 1, mult * dy);
 | 
						|
                DebugAccelF("pos (%i | %i) delta x:%.3f y:%.3f\n", mult * dx,
 | 
						|
                            mult * dy);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /* remember last motion delta (for softening/slow movement treatment) */
 | 
						|
    velocitydata->last_dx = dx;
 | 
						|
    velocitydata->last_dy = dy;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Originally a part of xf86PostMotionEvent; modifies valuators
 | 
						|
 * in-place. Retained mostly for embedded scenarios.
 | 
						|
 */
 | 
						|
void
 | 
						|
acceleratePointerLightweight(DeviceIntPtr dev,
 | 
						|
                             ValuatorMask *val, CARD32 ignored)
 | 
						|
{
 | 
						|
    double mult = 0.0, tmpf;
 | 
						|
    double dx = 0.0, dy = 0.0;
 | 
						|
 | 
						|
    if (valuator_mask_isset(val, 0)) {
 | 
						|
        dx = valuator_mask_get(val, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    if (valuator_mask_isset(val, 1)) {
 | 
						|
        dy = valuator_mask_get(val, 1);
 | 
						|
    }
 | 
						|
 | 
						|
    if (valuator_mask_num_valuators(val) == 0)
 | 
						|
        return;
 | 
						|
 | 
						|
    if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
 | 
						|
        /* modeled from xf86Events.c */
 | 
						|
        if (dev->ptrfeed->ctrl.threshold) {
 | 
						|
            if ((fabs(dx) + fabs(dy)) >= dev->ptrfeed->ctrl.threshold) {
 | 
						|
                if (dx != 0.0) {
 | 
						|
                    tmpf = (dx * (double) (dev->ptrfeed->ctrl.num)) /
 | 
						|
                        (double) (dev->ptrfeed->ctrl.den);
 | 
						|
                    valuator_mask_set_double(val, 0, tmpf);
 | 
						|
                }
 | 
						|
 | 
						|
                if (dy != 0.0) {
 | 
						|
                    tmpf = (dy * (double) (dev->ptrfeed->ctrl.num)) /
 | 
						|
                        (double) (dev->ptrfeed->ctrl.den);
 | 
						|
                    valuator_mask_set_double(val, 1, tmpf);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            mult = pow(dx * dx + dy * dy,
 | 
						|
                       ((double) (dev->ptrfeed->ctrl.num) /
 | 
						|
                        (double) (dev->ptrfeed->ctrl.den) - 1.0) / 2.0) / 2.0;
 | 
						|
            if (dx != 0.0)
 | 
						|
                valuator_mask_set_double(val, 0, mult * dx);
 | 
						|
            if (dy != 0.0)
 | 
						|
                valuator_mask_set_double(val, 1, mult * dy);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |