Edit

IABSD.fr/xenocara/xserver/dix/ptrveloc.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2010-07-27 19:02:24
    Hash : 95d684a0
    Message : Update to xserver 1.8. Tested by many. Ok oga@, todd@.

  • xserver/dix/ptrveloc.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 <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 */
    int
    SetAccelerationProfile(DeviceVelocityPtr vel, int profile_num);
    static float
    SimpleSmoothProfile(DeviceIntPtr dev, DeviceVelocityPtr vel, float velocity,
                        float threshold, float acc);
    static PointerAccelerationProfileFunc
    GetAccelerationProfile(DeviceVelocityPtr vel, int profile_num);
    
    /*#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)
    
    /* number of properties for predictable acceleration */
    #define NPROPS_PREDICTABLE_ACCEL 4
    
    /**
     * Init 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
     */
    void
    FreeVelocityData(DeviceVelocityPtr vel){
        xfree(vel->tracker);
        SetAccelerationProfile(vel, PROFILE_UNINITIALIZE);
    }
    
    
    /*
     *  dix uninit helper, called through scheme
     */
    void
    AccelerationDefaultCleanup(DeviceIntPtr dev)
    {
        /*sanity check*/
        if( dev->valuator->accelScheme.AccelSchemeProc == acceleratePointerPredictable
                && dev->valuator->accelScheme.accelData != NULL){
            dev->valuator->accelScheme.AccelSchemeProc = NULL;
            FreeVelocityData(dev->valuator->accelScheme.accelData);
            xfree(dev->valuator->accelScheme.accelData);
            dev->valuator->accelScheme.accelData = NULL;
            DeletePredictableAccelerationProperties(dev);
        }
    }
    
    
    /*************************
     * 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 int AccelPropHandlerPrivateKeyIndex;
    DevPrivateKey AccelPropHandlerPrivateKey = &AccelPropHandlerPrivateKeyIndex;
    
    BOOL
    InitializePredictableAccelerationProperties(DeviceIntPtr dev)
    {
        DeviceVelocityPtr  vel = GetDevicePredictableAccelData(dev);
        long *prop_handlers;
    
        if(!vel)
    	return FALSE;
        prop_handlers = xalloc(NPROPS_PREDICTABLE_ACCEL * sizeof(long));
    
        prop_handlers[0] = AccelInitProfileProperty(dev, vel);
        prop_handlers[1] = AccelInitDecelProperty(dev, vel);
        prop_handlers[2] = AccelInitAdaptDecelProperty(dev, vel);
        prop_handlers[3] = AccelInitScaleProperty(dev, vel);
    
        dixSetPrivate(&dev->devPrivates, AccelPropHandlerPrivateKey,
                      prop_handlers);
    
        return TRUE;
    }
    
    BOOL
    DeletePredictableAccelerationProperties(DeviceIntPtr dev)
    {
        Atom prop;
        long *prop_handlers;
        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);
    
        prop_handlers = dixLookupPrivate(&dev->devPrivates,
                                         AccelPropHandlerPrivateKey);
        dixSetPrivate(&dev->devPrivates, AccelPropHandlerPrivateKey, NULL);
    
        for (i = 0; prop_handlers && i < NPROPS_PREDICTABLE_ACCEL; i++)
            XIUnregisterPropertyHandler(dev, prop_handlers[i]);
        xfree(prop_handlers);
    
        return TRUE;
    }
    
    /*********************
     * Tracking logic
     ********************/
    
    void
    InitTrackers(DeviceVelocityPtr vel, int ntracker)
    {
        if(ntracker < 1){
    	ErrorF("(dix ptracc) invalid number of trackers\n");
    	return;
        }
        xfree(vel->tracker);
        vel->tracker = (MotionTrackerPtr)xalloc(ntracker * sizeof(MotionTracker));
        memset(vel->tracker, 0, ntracker * sizeof(MotionTracker));
        vel->num_tracker = ntracker;
    }
    
    /**
     * return a bit field of possible directions.
     * 0 = N, 2 = E, 4 = S, 6 = W, in-between is as you guess.
     * 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.
     */
    static int
    DoGetDirection(int dx, int dy){
        float r;
        int i1, i2;
        /* on insignificant mickeys, flag 135 degrees */
        if(abs(dx) < 2 && abs(dy < 2)){
    	/* first check diagonal cases */
    	if(dx > 0 && dy > 0)
    	    return 4+8+16;
    	if(dx > 0 && dy < 0)
    	    return 1+2+4;
    	if(dx < 0 && dy < 0)
    	    return 1+128+64;
    	if(dx < 0 && dy > 0)
    	    return 16+32+64;
            /* check axis-aligned directions */
    	if(dx > 0)
                return 2+4+8; /*E*/
            if(dx < 0)
                return 128+64+32; /*W*/
            if(dy > 0)
                return 32+16+8; /*S*/
            if(dy < 0)
                return 128+1+2; /*N*/
            return 255; /* shouldn't happen */
        }
        /* else, compute angle and set appropriate flags */
    #ifdef _ISOC99_SOURCE
        r = atan2f(dy, dx);
    #else
        r = atan2(dy, dx);
    #endif
        /* find direction. We avoid r to become negative,
         * since C has no well-defined modulo for such cases. */
        r = (r+(M_PI*2.5))/(M_PI/4);
        /* this intends to flag 2 directions (90 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)
    	return 255; /* shouldn't happen */
        return 1 << i1 | 1 << i2;
    }
    
    #define DIRECTION_CACHE_RANGE 5
    #define DIRECTION_CACHE_SIZE (DIRECTION_CACHE_RANGE*2+1)
    
    /* cache DoGetDirection(). */
    static int
    GetDirection(int dx, int dy){
        static int cache[DIRECTION_CACHE_SIZE][DIRECTION_CACHE_SIZE];
        int i;
        if (abs(dx) <= DIRECTION_CACHE_RANGE &&
    	abs(dy) <= DIRECTION_CACHE_RANGE) {
    	/* cacheable */
    	i = cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy];
    	if(i != 0){
    	    return i;
    	}else{
    	    i = DoGetDirection(dx, dy);
    	    cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy] = i;
    	    return i;
    	}
        }else{
    	/* non-cacheable */
    	return DoGetDirection(dx, dy);
        }
    }
    
    #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)
    
    static inline void
    FeedTrackers(DeviceVelocityPtr vel, int dx, int 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;
        vel->tracker[n].dy = 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 float
    CalcTracker(DeviceVelocityPtr vel, int offset, int cur_t){
        int index = TRACKER_INDEX(vel, offset);
        float dist = sqrt(  vel->tracker[index].dx * vel->tracker[index].dx
                          + vel->tracker[index].dy * vel->tracker[index].dy);
        int dtime = cur_t - vel->tracker[index].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, beyond a linear partition,
     * or simply too much off initial velocity.
     *
     * May return 0.
     */
    static float
    QueryTrackers(DeviceVelocityPtr vel, int cur_t){
        int n, offset, dir = 255, i = -1, age_ms;
        /* initial velocity: a low-offset, valid velocity */
        float iveloc = 0, res = 0, tmp, vdiff;
        float vfac =  vel->corr_mul * vel->const_acceleration; /* premultiply */
        /* loop from current to older data */
        for(offset = 1; offset < vel->num_tracker; offset++){
    	n = TRACKER_INDEX(vel, offset);
    
    	age_ms = cur_t - vel->tracker[n].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 &= vel->tracker[n].dir;
    	if(dir == 0){
    	    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;
    	}
    
    	tmp = CalcTracker(vel, offset, cur_t) * vfac;
    
    	if ((iveloc == 0 || offset <= vel->initial_range) && tmp != 0) {
    	    /* set initial velocity and result */
    	    res = iveloc = tmp;
    	    i = offset;
    	} else if (iveloc != 0 && tmp != 0) {
    	    vdiff = fabs(iveloc - tmp);
    	    if (vdiff <= vel->max_diff ||
    		vdiff/(iveloc + tmp) < vel->max_rel_diff) {
    		/* we're in range with the initial velocity,
    		 * so this result is likely better
    		 * (it contains more information). */
    		res = tmp;
    		i = offset;
    	    }else{
    		/* 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",
    		            tmp, iveloc, vdiff);
    		break;
    	    }
    	}
        }
        if(offset == vel->num_tracker){
    	DebugAccelF("(dix prtacc) query: last tracker in effect\n");
    	i = vel->num_tracker-1;
        }
        if(i>=0){
            n = TRACKER_INDEX(vel, i);
    	DebugAccelF("(dix prtacc) result: offset %i [dx: %i dy: %i diff: %i]\n",
    	            i,
    	            vel->tracker[n].dx,
    	            vel->tracker[n].dy,
    	            cur_t - vel->tracker[n].time);
        }
        return res;
    }
    
    #undef TRACKER_INDEX
    
    /**
     * Perform velocity approximation based on 2D 'mickeys' (mouse motion delta).
     * return true if non-visible state reset is suggested
     */
    short
    ProcessVelocityData2D(
        DeviceVelocityPtr vel,
        int dx,
        int dy,
        int time)
    {
        float 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 float
    ApplySimpleSoftening(int od, int d)
    {
        float res = d;
        if (d <= 1 && d >= -1)
            return res;
        if (d > od)
            res -= 0.5;
        else if (d < od)
            res += 0.5;
        return res;
    }
    
    
    static void
    ApplySofteningAndConstantDeceleration(
            DeviceVelocityPtr vel,
            int dx,
            int dy,
            float* fdx,
            float* fdy,
            short do_soften)
    {
        if (do_soften && vel->use_softening) {
            *fdx = ApplySimpleSoftening(vel->last_dx, dx);
            *fdy = ApplySimpleSoftening(vel->last_dy, dy);
        } else {
            *fdx = dx;
            *fdy = dy;
        }
    
        *fdx *= vel->const_acceleration;
        *fdy *= vel->const_acceleration;
    }
    
    /*
     * compute the acceleration for given velocity and enforce min_acceleartion
     */
    float
    BasicComputeAcceleration(
        DeviceIntPtr dev,
        DeviceVelocityPtr vel,
        float velocity,
        float threshold,
        float acc){
    
        float 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.
     */
    static float
    ComputeAcceleration(
        DeviceIntPtr dev,
        DeviceVelocityPtr vel,
        float threshold,
        float acc){
        float res;
    
        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. */
    	res = BasicComputeAcceleration(
    	          dev, vel, vel->velocity, threshold, acc);
    	res += BasicComputeAcceleration(
    	          dev, vel, vel->last_velocity, threshold, acc);
    	res += 4.0f * BasicComputeAcceleration(dev, vel,
    	                   (vel->last_velocity + vel->velocity) / 2,
    	                   threshold, acc);
    	res /= 6.0f;
    	DebugAccelF("(dix ptracc) profile average [%.2f ... %.2f] is %.3f\n",
    	            vel->velocity, vel->last_velocity, res);
            return res;
        }else{
    	res = BasicComputeAcceleration(dev, vel,
    	                               vel->velocity, threshold, acc);
    	DebugAccelF("(dix ptracc) profile sample [%.2f] is %.3f\n",
                   vel->velocity, res);
    	return res;
        }
    }
    
    
    /*****************************************
     *  Acceleration functions and profiles
     ****************************************/
    
    /**
     * Polynomial function similar previous one, but with f(1) = 1
     */
    static float
    PolynomialAccelerationProfile(
        DeviceIntPtr dev,
        DeviceVelocityPtr vel,
        float velocity,
        float ignored,
        float 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 float
    ClassicProfile(
        DeviceIntPtr dev,
        DeviceVelocityPtr vel,
        float velocity,
        float threshold,
        float 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 float
    PowerProfile(
        DeviceIntPtr dev,
        DeviceVelocityPtr vel,
        float velocity,
        float threshold,
        float acc)
    {
        float 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 float
    CalcPenumbralGradient(float x){
        x *= 2.0f;
        x -= 1.0f;
        return 0.5f + (x * sqrt(1.0f - 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 float
    SimpleSmoothProfile(
        DeviceIntPtr dev,
        DeviceVelocityPtr vel,
        float velocity,
        float threshold,
        float 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 float
    SmoothLinearProfile(
        DeviceIntPtr dev,
        DeviceVelocityPtr vel,
        float velocity,
        float threshold,
        float acc)
    {
        float 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 float
    SmoothLimitedProfile(
        DeviceIntPtr dev,
        DeviceVelocityPtr vel,
        float velocity,
        float threshold,
        float acc)
    {
        float 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 float
    LinearProfile(
        DeviceIntPtr dev,
        DeviceVelocityPtr vel,
        float velocity,
        float threshold,
        float acc)
    {
        return acc * velocity;
    }
    
    static float
    NoProfile(
        DeviceIntPtr dev,
        DeviceVelocityPtr vel,
        float velocity,
        float threshold,
        float 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;
    
        if(vel->profile_private != NULL){
            /* Here one could free old profile-private data */
            xfree(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 (DeviceVelocityPtr)dev->valuator->accelScheme.accelData;
        }
        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,
        int first_valuator,
        int num_valuators,
        int *valuators,
        int evtime)
    {
        float mult = 0.0;
        int dx = 0, dy = 0;
        int *px = NULL, *py = NULL;
        DeviceVelocityPtr velocitydata =
    	(DeviceVelocityPtr) dev->valuator->accelScheme.accelData;
        float fdx, fdy, tmp; /* no need to init */
        Bool soften = TRUE;
    
        if (!num_valuators || !valuators || !velocitydata)
            return;
    
        if (velocitydata->statistics.profile_number == AccelProfileNone &&
    	velocitydata->const_acceleration == 1.0f) {
    	return; /*we're inactive anyway, so skip the whole thing.*/
        }
    
        if (first_valuator == 0) {
            dx = valuators[0];
            px = &valuators[0];
        }
        if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) {
            dy = valuators[1 - first_valuator];
            py = &valuators[1 - first_valuator];
        }
    
        if (dx || dy){
            /* reset non-visible state? */
            if (ProcessVelocityData2D(velocitydata, dx , dy, evtime)) {
                soften = FALSE;
            }
    
            if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
                /* invoke acceleration profile to determine acceleration */
                mult = ComputeAcceleration (dev, velocitydata,
    					dev->ptrfeed->ctrl.threshold,
    					(float)dev->ptrfeed->ctrl.num /
    					(float)dev->ptrfeed->ctrl.den);
    
                if(mult != 1.0 || velocitydata->const_acceleration != 1.0) {
                    ApplySofteningAndConstantDeceleration( velocitydata,
    						       dx, dy,
    						       &fdx, &fdy,
    						       (mult > 1.0) && soften);
    
                    if (dx) {
                        tmp = mult * fdx + dev->last.remainder[0];
                        /* Since it may not be apparent: lrintf() does not offer
                         * strong statements about rounding; however because we
                         * process each axis conditionally, there's no danger
                         * of a toggling remainder. Its lack of guarantees likely
                         * makes it faster on the average target. */
                        *px = lrintf(tmp);
                        dev->last.remainder[0] = tmp - (float)*px;
                    }
                    if (dy) {
                        tmp = mult * fdy + dev->last.remainder[1];
                        *py = lrintf(tmp);
                        dev->last.remainder[1] = tmp - (float)*py;
                    }
                    DebugAccelF("pos (%i | %i) remainders x: %.3f y: %.3f delta x:%.3f y:%.3f\n",
                                *px, *py, dev->last.remainder[0], dev->last.remainder[1], fdx, fdy);
                }
            }
        }
        /* 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,
        int first_valuator,
        int num_valuators,
        int *valuators,
        int ignored)
    {
        float mult = 0.0;
        int dx = 0, dy = 0;
        int *px = NULL, *py = NULL;
    
        if (!num_valuators || !valuators)
            return;
    
        if (first_valuator == 0) {
            dx = valuators[0];
            px = &valuators[0];
        }
        if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) {
            dy = valuators[1 - first_valuator];
            py = &valuators[1 - first_valuator];
        }
    
        if (!dx && !dy)
            return;
    
        if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
            /* modeled from xf86Events.c */
            if (dev->ptrfeed->ctrl.threshold) {
                if ((abs(dx) + abs(dy)) >= dev->ptrfeed->ctrl.threshold) {
                    dev->last.remainder[0] = ((float)dx *
                                                 (float)(dev->ptrfeed->ctrl.num)) /
                                                 (float)(dev->ptrfeed->ctrl.den) +
                                                dev->last.remainder[0];
                    if (px) {
                        *px = (int)dev->last.remainder[0];
                        dev->last.remainder[0] = dev->last.remainder[0] -
                                                    (float)(*px);
                    }
    
                    dev->last.remainder[1] = ((float)dy *
                                                 (float)(dev->ptrfeed->ctrl.num)) /
                                                 (float)(dev->ptrfeed->ctrl.den) +
                                                dev->last.remainder[1];
                    if (py) {
                        *py = (int)dev->last.remainder[1];
                        dev->last.remainder[1] = dev->last.remainder[1] -
                                                    (float)(*py);
                    }
                }
            }
            else {
    	    mult = pow((float)dx * (float)dx + (float)dy * (float)dy,
                           ((float)(dev->ptrfeed->ctrl.num) /
                            (float)(dev->ptrfeed->ctrl.den) - 1.0) /
                           2.0) / 2.0;
                if (dx) {
                    dev->last.remainder[0] = mult * (float)dx +
                                                dev->last.remainder[0];
                    *px = (int)dev->last.remainder[0];
                    dev->last.remainder[0] = dev->last.remainder[0] -
                                                (float)(*px);
                }
                if (dy) {
                    dev->last.remainder[1] = mult * (float)dy +
                                                dev->last.remainder[1];
                    *py = (int)dev->last.remainder[1];
                    dev->last.remainder[1] = dev->last.remainder[1] -
                                                (float)(*py);
                }
            }
        }
    }