ptrveloc.c revision 9ace9065
14642e01fSmrg/*
24642e01fSmrg *
36747b715Smrg * Copyright © 2006-2009 Simon Thum             simon dot thum at gmx dot de
44642e01fSmrg *
54642e01fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
64642e01fSmrg * copy of this software and associated documentation files (the "Software"),
74642e01fSmrg * to deal in the Software without restriction, including without limitation
84642e01fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
94642e01fSmrg * and/or sell copies of the Software, and to permit persons to whom the
104642e01fSmrg * Software is furnished to do so, subject to the following conditions:
114642e01fSmrg *
124642e01fSmrg * The above copyright notice and this permission notice (including the next
134642e01fSmrg * paragraph) shall be included in all copies or substantial portions of the
144642e01fSmrg * Software.
154642e01fSmrg *
164642e01fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
174642e01fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
184642e01fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
194642e01fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
204642e01fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
214642e01fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
224642e01fSmrg * DEALINGS IN THE SOFTWARE.
234642e01fSmrg */
244642e01fSmrg
254642e01fSmrg#ifdef HAVE_DIX_CONFIG_H
264642e01fSmrg#include <dix-config.h>
274642e01fSmrg#endif
284642e01fSmrg
294642e01fSmrg#include <math.h>
304642e01fSmrg#include <ptrveloc.h>
316747b715Smrg#include <exevents.h>
326747b715Smrg#include <X11/Xatom.h>
336747b715Smrg
346747b715Smrg#include <xserver-properties.h>
354642e01fSmrg
364642e01fSmrg/*****************************************************************************
374642e01fSmrg * Predictable pointer acceleration
384642e01fSmrg *
396747b715Smrg * 2006-2009 by Simon Thum (simon [dot] thum [at] gmx de)
404642e01fSmrg *
414642e01fSmrg * Serves 3 complementary functions:
424642e01fSmrg * 1) provide a sophisticated ballistic velocity estimate to improve
434642e01fSmrg *    the relation between velocity (of the device) and acceleration
444642e01fSmrg * 2) make arbitrary acceleration profiles possible
454642e01fSmrg * 3) decelerate by two means (constant and adaptive) if enabled
464642e01fSmrg *
474642e01fSmrg * Important concepts are the
484642e01fSmrg *
494642e01fSmrg * - Scheme
504642e01fSmrg *      which selects the basic algorithm
514642e01fSmrg *      (see devices.c/InitPointerAccelerationScheme)
524642e01fSmrg * - Profile
534642e01fSmrg *      which returns an acceleration
544642e01fSmrg *      for a given velocity
554642e01fSmrg *
566747b715Smrg *  The profile can be selected by the user at runtime.
576747b715Smrg *  The classic profile is intended to cleanly perform old-style
584642e01fSmrg *  function selection (threshold =/!= 0)
594642e01fSmrg *
604642e01fSmrg ****************************************************************************/
614642e01fSmrg
624642e01fSmrg/* fwds */
634642e01fSmrgint
646747b715SmrgSetAccelerationProfile(DeviceVelocityPtr vel, int profile_num);
654642e01fSmrgstatic float
666747b715SmrgSimpleSmoothProfile(DeviceIntPtr dev, DeviceVelocityPtr vel, float velocity,
674642e01fSmrg                    float threshold, float acc);
686747b715Smrgstatic PointerAccelerationProfileFunc
696747b715SmrgGetAccelerationProfile(DeviceVelocityPtr vel, int profile_num);
704642e01fSmrg
714642e01fSmrg/*#define PTRACCEL_DEBUGGING*/
724642e01fSmrg
734642e01fSmrg#ifdef PTRACCEL_DEBUGGING
744642e01fSmrg#define DebugAccelF ErrorF
754642e01fSmrg#else
764642e01fSmrg#define DebugAccelF(...) /* */
774642e01fSmrg#endif
784642e01fSmrg
794642e01fSmrg/********************************
806747b715Smrg *  Init/Uninit
814642e01fSmrg *******************************/
824642e01fSmrg
836747b715Smrg/* some int which is not a profile number */
846747b715Smrg#define PROFILE_UNINITIALIZE (-100)
856747b715Smrg
866747b715Smrg
874642e01fSmrg/**
884642e01fSmrg * Init struct so it should match the average case
894642e01fSmrg */
904642e01fSmrgvoid
916747b715SmrgInitVelocityData(DeviceVelocityPtr vel)
924642e01fSmrg{
936747b715Smrg    memset(vel, 0, sizeof(DeviceVelocityRec));
946747b715Smrg
956747b715Smrg    vel->corr_mul = 10.0;      /* dots per 10 milisecond should be usable */
966747b715Smrg    vel->const_acceleration = 1.0;   /* no acceleration/deceleration  */
976747b715Smrg    vel->reset_time = 300;
986747b715Smrg    vel->use_softening = 1;
996747b715Smrg    vel->min_acceleration = 1.0; /* don't decelerate */
1006747b715Smrg    vel->max_rel_diff = 0.2;
1016747b715Smrg    vel->max_diff = 1.0;
1026747b715Smrg    vel->initial_range = 2;
1036747b715Smrg    vel->average_accel = TRUE;
1046747b715Smrg    SetAccelerationProfile(vel, AccelProfileClassic);
1056747b715Smrg    InitTrackers(vel, 16);
1064642e01fSmrg}
1074642e01fSmrg
1084642e01fSmrg
1094642e01fSmrg/**
1104642e01fSmrg * Clean up
1114642e01fSmrg */
1126747b715Smrgvoid
1136747b715SmrgFreeVelocityData(DeviceVelocityPtr vel){
1146747b715Smrg    free(vel->tracker);
1156747b715Smrg    SetAccelerationProfile(vel, PROFILE_UNINITIALIZE);
1164642e01fSmrg}
1174642e01fSmrg
1184642e01fSmrg
1194642e01fSmrg/*
1204642e01fSmrg *  dix uninit helper, called through scheme
1214642e01fSmrg */
1224642e01fSmrgvoid
1236747b715SmrgAccelerationDefaultCleanup(DeviceIntPtr dev)
1244642e01fSmrg{
1254642e01fSmrg    /*sanity check*/
1266747b715Smrg    if( dev->valuator->accelScheme.AccelSchemeProc == acceleratePointerPredictable
1276747b715Smrg            && dev->valuator->accelScheme.accelData != NULL){
1286747b715Smrg        dev->valuator->accelScheme.AccelSchemeProc = NULL;
1296747b715Smrg        FreeVelocityData(dev->valuator->accelScheme.accelData);
1306747b715Smrg        free(dev->valuator->accelScheme.accelData);
1316747b715Smrg        dev->valuator->accelScheme.accelData = NULL;
1326747b715Smrg        DeletePredictableAccelerationProperties(dev);
1334642e01fSmrg    }
1344642e01fSmrg}
1354642e01fSmrg
1366747b715Smrg
1376747b715Smrg/*************************
1386747b715Smrg * Input property support
1396747b715Smrg ************************/
1404642e01fSmrg
1414642e01fSmrg/**
1426747b715Smrg * choose profile
1436747b715Smrg */
1446747b715Smrgstatic int
1456747b715SmrgAccelSetProfileProperty(DeviceIntPtr dev, Atom atom,
1466747b715Smrg                        XIPropertyValuePtr val, BOOL checkOnly)
1474642e01fSmrg{
1486747b715Smrg    DeviceVelocityPtr vel;
1496747b715Smrg    int profile, *ptr = &profile;
1506747b715Smrg    int rc;
1516747b715Smrg    int nelem = 1;
1526747b715Smrg
1536747b715Smrg    if (atom != XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER))
1546747b715Smrg        return Success;
1556747b715Smrg
1566747b715Smrg    vel = GetDevicePredictableAccelData(dev);
1576747b715Smrg    if (!vel)
1586747b715Smrg        return BadValue;
1596747b715Smrg    rc = XIPropToInt(val, &nelem, &ptr);
1606747b715Smrg
1616747b715Smrg    if(checkOnly)
1626747b715Smrg    {
1636747b715Smrg        if (rc)
1646747b715Smrg            return rc;
1656747b715Smrg
1666747b715Smrg        if (GetAccelerationProfile(vel, profile) == NULL)
1676747b715Smrg            return BadValue;
1686747b715Smrg    } else
1696747b715Smrg	SetAccelerationProfile(vel, profile);
1706747b715Smrg
1716747b715Smrg    return Success;
1724642e01fSmrg}
1734642e01fSmrg
1746747b715Smrgstatic long
1756747b715SmrgAccelInitProfileProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
1764642e01fSmrg{
1776747b715Smrg    int profile = vel->statistics.profile_number;
1786747b715Smrg    Atom prop_profile_number = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
1794642e01fSmrg
1806747b715Smrg    XIChangeDeviceProperty(dev, prop_profile_number, XA_INTEGER, 32,
1816747b715Smrg                           PropModeReplace, 1, &profile, FALSE);
1826747b715Smrg    XISetDevicePropertyDeletable(dev, prop_profile_number, FALSE);
1836747b715Smrg    return XIRegisterPropertyHandler(dev, AccelSetProfileProperty, NULL, NULL);
1844642e01fSmrg}
1854642e01fSmrg
1866747b715Smrg/**
1876747b715Smrg * constant deceleration
1886747b715Smrg */
1896747b715Smrgstatic int
1906747b715SmrgAccelSetDecelProperty(DeviceIntPtr dev, Atom atom,
1916747b715Smrg                      XIPropertyValuePtr val, BOOL checkOnly)
1924642e01fSmrg{
1936747b715Smrg    DeviceVelocityPtr vel;
1946747b715Smrg    float v, *ptr = &v;
1956747b715Smrg    int rc;
1966747b715Smrg    int nelem = 1;
1976747b715Smrg
1986747b715Smrg    if (atom != XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION))
1996747b715Smrg        return Success;
2006747b715Smrg
2016747b715Smrg    vel = GetDevicePredictableAccelData(dev);
2026747b715Smrg    if (!vel)
2036747b715Smrg        return BadValue;
2046747b715Smrg    rc = XIPropToFloat(val, &nelem, &ptr);
2056747b715Smrg
2066747b715Smrg    if(checkOnly)
2076747b715Smrg    {
2086747b715Smrg        if (rc)
2096747b715Smrg            return rc;
2106747b715Smrg	return (v >= 1.0f) ? Success : BadValue;
2114642e01fSmrg    }
2126747b715Smrg
2136747b715Smrg    if(v >= 1.0f)
2146747b715Smrg	vel->const_acceleration = 1/v;
2156747b715Smrg
2166747b715Smrg    return Success;
2176747b715Smrg}
2186747b715Smrg
2196747b715Smrgstatic long
2206747b715SmrgAccelInitDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
2216747b715Smrg{
2226747b715Smrg    float fval = 1.0/vel->const_acceleration;
2236747b715Smrg    Atom prop_const_decel = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
2246747b715Smrg    XIChangeDeviceProperty(dev, prop_const_decel,
2256747b715Smrg                           XIGetKnownProperty(XATOM_FLOAT), 32,
2266747b715Smrg                           PropModeReplace, 1, &fval, FALSE);
2276747b715Smrg    XISetDevicePropertyDeletable(dev, prop_const_decel, FALSE);
2286747b715Smrg    return XIRegisterPropertyHandler(dev, AccelSetDecelProperty, NULL, NULL);
2294642e01fSmrg}
2304642e01fSmrg
2314642e01fSmrg
2324642e01fSmrg/**
2336747b715Smrg * adaptive deceleration
2344642e01fSmrg */
2356747b715Smrgstatic int
2366747b715SmrgAccelSetAdaptDecelProperty(DeviceIntPtr dev, Atom atom,
2376747b715Smrg                           XIPropertyValuePtr val, BOOL checkOnly)
2384642e01fSmrg{
2396747b715Smrg    DeviceVelocityPtr veloc;
2406747b715Smrg    float v, *ptr = &v;
2416747b715Smrg    int rc;
2426747b715Smrg    int nelem = 1;
2436747b715Smrg
2446747b715Smrg    if (atom != XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION))
2456747b715Smrg        return Success;
2466747b715Smrg
2476747b715Smrg    veloc = GetDevicePredictableAccelData(dev);
2486747b715Smrg    if (!veloc)
2496747b715Smrg        return BadValue;
2506747b715Smrg    rc = XIPropToFloat(val, &nelem, &ptr);
2516747b715Smrg
2526747b715Smrg    if(checkOnly)
2536747b715Smrg    {
2546747b715Smrg        if (rc)
2556747b715Smrg            return rc;
2566747b715Smrg	return (v >= 1.0f) ? Success : BadValue;
2574642e01fSmrg    }
2586747b715Smrg
2596747b715Smrg    if(v >= 1.0f)
2606747b715Smrg	veloc->min_acceleration = 1/v;
2616747b715Smrg
2626747b715Smrg    return Success;
2634642e01fSmrg}
2644642e01fSmrg
2656747b715Smrgstatic long
2666747b715SmrgAccelInitAdaptDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
2676747b715Smrg{
2686747b715Smrg    float fval = 1.0/vel->min_acceleration;
2696747b715Smrg    Atom prop_adapt_decel = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
2704642e01fSmrg
2716747b715Smrg    XIChangeDeviceProperty(dev, prop_adapt_decel, XIGetKnownProperty(XATOM_FLOAT), 32,
2726747b715Smrg                           PropModeReplace, 1, &fval, FALSE);
2736747b715Smrg    XISetDevicePropertyDeletable(dev, prop_adapt_decel, FALSE);
2746747b715Smrg    return XIRegisterPropertyHandler(dev, AccelSetAdaptDecelProperty, NULL, NULL);
2756747b715Smrg}
2766747b715Smrg
2776747b715Smrg
2786747b715Smrg/**
2796747b715Smrg * velocity scaling
2806747b715Smrg */
2816747b715Smrgstatic int
2826747b715SmrgAccelSetScaleProperty(DeviceIntPtr dev, Atom atom,
2836747b715Smrg                      XIPropertyValuePtr val, BOOL checkOnly)
2844642e01fSmrg{
2856747b715Smrg    DeviceVelocityPtr vel;
2866747b715Smrg    float v, *ptr = &v;
2876747b715Smrg    int rc;
2886747b715Smrg    int nelem = 1;
2896747b715Smrg
2906747b715Smrg    if (atom != XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING))
2916747b715Smrg        return Success;
2924642e01fSmrg
2936747b715Smrg    vel = GetDevicePredictableAccelData(dev);
2946747b715Smrg    if (!vel)
2956747b715Smrg        return BadValue;
2966747b715Smrg    rc = XIPropToFloat(val, &nelem, &ptr);
2976747b715Smrg
2986747b715Smrg    if (checkOnly)
2996747b715Smrg    {
3006747b715Smrg        if (rc)
3016747b715Smrg            return rc;
3026747b715Smrg
3036747b715Smrg        return (v > 0) ? Success : BadValue;
3044642e01fSmrg    }
3056747b715Smrg
3066747b715Smrg    if(v > 0)
3076747b715Smrg	vel->corr_mul = v;
3086747b715Smrg
3096747b715Smrg    return Success;
3104642e01fSmrg}
3114642e01fSmrg
3126747b715Smrgstatic long
3136747b715SmrgAccelInitScaleProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
3146747b715Smrg{
3156747b715Smrg    float fval = vel->corr_mul;
3166747b715Smrg    Atom prop_velo_scale = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
3174642e01fSmrg
3186747b715Smrg    XIChangeDeviceProperty(dev, prop_velo_scale, XIGetKnownProperty(XATOM_FLOAT), 32,
3196747b715Smrg                           PropModeReplace, 1, &fval, FALSE);
3206747b715Smrg    XISetDevicePropertyDeletable(dev, prop_velo_scale, FALSE);
3216747b715Smrg    return XIRegisterPropertyHandler(dev, AccelSetScaleProperty, NULL, NULL);
3224642e01fSmrg}
3234642e01fSmrg
3246747b715SmrgBOOL
3256747b715SmrgInitializePredictableAccelerationProperties(DeviceIntPtr dev)
3264642e01fSmrg{
3276747b715Smrg    DeviceVelocityPtr  vel = GetDevicePredictableAccelData(dev);
3284642e01fSmrg
3296747b715Smrg    if(!vel)
3306747b715Smrg	return FALSE;
3314642e01fSmrg
3326747b715Smrg    vel->prop_handlers[0] = AccelInitProfileProperty(dev, vel);
3336747b715Smrg    vel->prop_handlers[1] = AccelInitDecelProperty(dev, vel);
3346747b715Smrg    vel->prop_handlers[2] = AccelInitAdaptDecelProperty(dev, vel);
3356747b715Smrg    vel->prop_handlers[3] = AccelInitScaleProperty(dev, vel);
3364642e01fSmrg
3376747b715Smrg    return TRUE;
3386747b715Smrg}
3394642e01fSmrg
3406747b715SmrgBOOL
3416747b715SmrgDeletePredictableAccelerationProperties(DeviceIntPtr dev)
3426747b715Smrg{
3436747b715Smrg    DeviceVelocityPtr  vel;
3446747b715Smrg    Atom prop;
3456747b715Smrg    int i;
3466747b715Smrg
3476747b715Smrg    prop = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
3486747b715Smrg    XIDeleteDeviceProperty(dev, prop, FALSE);
3496747b715Smrg    prop = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
3506747b715Smrg    XIDeleteDeviceProperty(dev, prop, FALSE);
3516747b715Smrg    prop = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
3526747b715Smrg    XIDeleteDeviceProperty(dev, prop, FALSE);
3536747b715Smrg    prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
3546747b715Smrg    XIDeleteDeviceProperty(dev, prop, FALSE);
3556747b715Smrg
3566747b715Smrg    vel = GetDevicePredictableAccelData(dev);
3576747b715Smrg    for (i = 0; vel && i < NPROPS_PREDICTABLE_ACCEL; i++)
3586747b715Smrg	if (vel->prop_handlers[i])
3596747b715Smrg	    XIUnregisterPropertyHandler(dev, vel->prop_handlers[i]);
3604642e01fSmrg
3616747b715Smrg    return TRUE;
3624642e01fSmrg}
3634642e01fSmrg
3646747b715Smrg/*********************
3656747b715Smrg * Tracking logic
3666747b715Smrg ********************/
3674642e01fSmrg
3686747b715Smrgvoid
3696747b715SmrgInitTrackers(DeviceVelocityPtr vel, int ntracker)
3706747b715Smrg{
3716747b715Smrg    if(ntracker < 1){
3726747b715Smrg	ErrorF("(dix ptracc) invalid number of trackers\n");
3736747b715Smrg	return;
3744642e01fSmrg    }
3756747b715Smrg    free(vel->tracker);
3766747b715Smrg    vel->tracker = (MotionTrackerPtr)malloc(ntracker * sizeof(MotionTracker));
3776747b715Smrg    memset(vel->tracker, 0, ntracker * sizeof(MotionTracker));
3786747b715Smrg    vel->num_tracker = ntracker;
3794642e01fSmrg}
3804642e01fSmrg
3814642e01fSmrg/**
3826747b715Smrg * return a bit field of possible directions.
3836747b715Smrg * 0 = N, 2 = E, 4 = S, 6 = W, in-between is as you guess.
3846747b715Smrg * There's no reason against widening to more precise directions (<45 degrees),
3856747b715Smrg * should it not perform well. All this is needed for is sort out non-linear
3866747b715Smrg * motion, so precision isn't paramount. However, one should not flag direction
3876747b715Smrg * too narrow, since it would then cut the linear segment to zero size way too
3886747b715Smrg * often.
3894642e01fSmrg */
3906747b715Smrgstatic int
3916747b715SmrgDoGetDirection(int dx, int dy){
3926747b715Smrg    float r;
3936747b715Smrg    int i1, i2;
3946747b715Smrg    /* on insignificant mickeys, flag 135 degrees */
3959ace9065Smrg    if(abs(dx) < 2 && abs(dy) < 2){
3966747b715Smrg	/* first check diagonal cases */
3976747b715Smrg	if(dx > 0 && dy > 0)
3986747b715Smrg	    return 4+8+16;
3996747b715Smrg	if(dx > 0 && dy < 0)
4006747b715Smrg	    return 1+2+4;
4016747b715Smrg	if(dx < 0 && dy < 0)
4026747b715Smrg	    return 1+128+64;
4036747b715Smrg	if(dx < 0 && dy > 0)
4046747b715Smrg	    return 16+32+64;
4056747b715Smrg        /* check axis-aligned directions */
4066747b715Smrg	if(dx > 0)
4076747b715Smrg            return 2+4+8; /*E*/
4086747b715Smrg        if(dx < 0)
4096747b715Smrg            return 128+64+32; /*W*/
4106747b715Smrg        if(dy > 0)
4116747b715Smrg            return 32+16+8; /*S*/
4126747b715Smrg        if(dy < 0)
4136747b715Smrg            return 128+1+2; /*N*/
4146747b715Smrg        return 255; /* shouldn't happen */
4154642e01fSmrg    }
4166747b715Smrg    /* else, compute angle and set appropriate flags */
4176747b715Smrg#ifdef _ISOC99_SOURCE
4186747b715Smrg    r = atan2f(dy, dx);
4196747b715Smrg#else
4206747b715Smrg    r = atan2(dy, dx);
4216747b715Smrg#endif
4226747b715Smrg    /* find direction. We avoid r to become negative,
4236747b715Smrg     * since C has no well-defined modulo for such cases. */
4246747b715Smrg    r = (r+(M_PI*2.5))/(M_PI/4);
4256747b715Smrg    /* this intends to flag 2 directions (90 degrees),
4266747b715Smrg     * except on very well-aligned mickeys. */
4276747b715Smrg    i1 = (int)(r+0.1) % 8;
4286747b715Smrg    i2 = (int)(r+0.9) % 8;
4296747b715Smrg    if(i1 < 0 || i1 > 7 || i2 < 0 || i2 > 7)
4306747b715Smrg	return 255; /* shouldn't happen */
4316747b715Smrg    return 1 << i1 | 1 << i2;
4326747b715Smrg}
4334642e01fSmrg
4346747b715Smrg#define DIRECTION_CACHE_RANGE 5
4356747b715Smrg#define DIRECTION_CACHE_SIZE (DIRECTION_CACHE_RANGE*2+1)
4366747b715Smrg
4376747b715Smrg/* cache DoGetDirection(). */
4386747b715Smrgstatic int
4396747b715SmrgGetDirection(int dx, int dy){
4406747b715Smrg    static int cache[DIRECTION_CACHE_SIZE][DIRECTION_CACHE_SIZE];
4416747b715Smrg    int i;
4426747b715Smrg    if (abs(dx) <= DIRECTION_CACHE_RANGE &&
4436747b715Smrg	abs(dy) <= DIRECTION_CACHE_RANGE) {
4446747b715Smrg	/* cacheable */
4456747b715Smrg	i = cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy];
4466747b715Smrg	if(i != 0){
4476747b715Smrg	    return i;
4486747b715Smrg	}else{
4496747b715Smrg	    i = DoGetDirection(dx, dy);
4506747b715Smrg	    cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy] = i;
4516747b715Smrg	    return i;
4526747b715Smrg	}
4536747b715Smrg    }else{
4546747b715Smrg	/* non-cacheable */
4556747b715Smrg	return DoGetDirection(dx, dy);
4564642e01fSmrg    }
4576747b715Smrg}
4584642e01fSmrg
4596747b715Smrg#undef DIRECTION_CACHE_RANGE
4606747b715Smrg#undef DIRECTION_CACHE_SIZE
4614642e01fSmrg
4624642e01fSmrg
4636747b715Smrg/* convert offset (age) to array index */
4646747b715Smrg#define TRACKER_INDEX(s, d) (((s)->num_tracker + (s)->cur_tracker - (d)) % (s)->num_tracker)
4656747b715Smrg
4666747b715Smrgstatic inline void
4676747b715SmrgFeedTrackers(DeviceVelocityPtr vel, int dx, int dy, int cur_t)
4686747b715Smrg{
4696747b715Smrg    int n;
4706747b715Smrg    for(n = 0; n < vel->num_tracker; n++){
4716747b715Smrg	vel->tracker[n].dx += dx;
4726747b715Smrg	vel->tracker[n].dy += dy;
4734642e01fSmrg    }
4746747b715Smrg    n = (vel->cur_tracker + 1) % vel->num_tracker;
4756747b715Smrg    vel->tracker[n].dx = 0;
4766747b715Smrg    vel->tracker[n].dy = 0;
4776747b715Smrg    vel->tracker[n].time = cur_t;
4786747b715Smrg    vel->tracker[n].dir = GetDirection(dx, dy);
4796747b715Smrg    DebugAccelF("(dix prtacc) motion [dx: %i dy: %i dir:%i diff: %i]\n",
4806747b715Smrg                dx, dy, vel->tracker[n].dir,
4816747b715Smrg                cur_t - vel->tracker[vel->cur_tracker].time);
4826747b715Smrg    vel->cur_tracker = n;
4836747b715Smrg}
4846747b715Smrg
4856747b715Smrg/**
4866747b715Smrg * calc velocity for given tracker, with
4876747b715Smrg * velocity scaling.
4886747b715Smrg * This assumes linear motion.
4896747b715Smrg */
4906747b715Smrgstatic float
4916747b715SmrgCalcTracker(DeviceVelocityPtr vel, int offset, int cur_t){
4926747b715Smrg    int index = TRACKER_INDEX(vel, offset);
4936747b715Smrg    float dist = sqrt(  vel->tracker[index].dx * vel->tracker[index].dx
4946747b715Smrg                      + vel->tracker[index].dy * vel->tracker[index].dy);
4956747b715Smrg    int dtime = cur_t - vel->tracker[index].time;
4966747b715Smrg    if(dtime > 0)
4976747b715Smrg	return dist / dtime;
4986747b715Smrg    else
4996747b715Smrg	return 0;/* synonymous for NaN, since we're not C99 */
5006747b715Smrg}
5016747b715Smrg
5026747b715Smrg/* find the most plausible velocity. That is, the most distant
5036747b715Smrg * (in time) tracker which isn't too old, beyond a linear partition,
5046747b715Smrg * or simply too much off initial velocity.
5056747b715Smrg *
5066747b715Smrg * May return 0.
5076747b715Smrg */
5086747b715Smrgstatic float
5096747b715SmrgQueryTrackers(DeviceVelocityPtr vel, int cur_t){
5106747b715Smrg    int n, offset, dir = 255, i = -1, age_ms;
5116747b715Smrg    /* initial velocity: a low-offset, valid velocity */
5126747b715Smrg    float iveloc = 0, res = 0, tmp, vdiff;
5136747b715Smrg    float vfac =  vel->corr_mul * vel->const_acceleration; /* premultiply */
5146747b715Smrg    /* loop from current to older data */
5156747b715Smrg    for(offset = 1; offset < vel->num_tracker; offset++){
5166747b715Smrg	n = TRACKER_INDEX(vel, offset);
5176747b715Smrg
5186747b715Smrg	age_ms = cur_t - vel->tracker[n].time;
5196747b715Smrg
5206747b715Smrg	/* bail out if data is too old and protect from overrun */
5216747b715Smrg	if (age_ms >= vel->reset_time || age_ms < 0) {
5226747b715Smrg	    DebugAccelF("(dix prtacc) query: tracker too old\n");
5236747b715Smrg	    break;
5246747b715Smrg	}
5254642e01fSmrg
5264642e01fSmrg	/*
5276747b715Smrg	 * this heuristic avoids using the linear-motion velocity formula
5286747b715Smrg	 * in CalcTracker() on motion that isn't exactly linear. So to get
5296747b715Smrg	 * even more precision we could subdivide as a final step, so possible
5306747b715Smrg	 * non-linearities are accounted for.
5314642e01fSmrg	 */
5326747b715Smrg	dir &= vel->tracker[n].dir;
5336747b715Smrg	if(dir == 0){
5346747b715Smrg	    DebugAccelF("(dix prtacc) query: no longer linear\n");
5356747b715Smrg	    /* instead of breaking it we might also inspect the partition after,
5366747b715Smrg	     * but actual improvement with this is probably rare. */
5376747b715Smrg	    break;
5386747b715Smrg	}
5396747b715Smrg
5406747b715Smrg	tmp = CalcTracker(vel, offset, cur_t) * vfac;
5416747b715Smrg
5426747b715Smrg	if ((iveloc == 0 || offset <= vel->initial_range) && tmp != 0) {
5436747b715Smrg	    /* set initial velocity and result */
5446747b715Smrg	    res = iveloc = tmp;
5456747b715Smrg	    i = offset;
5466747b715Smrg	} else if (iveloc != 0 && tmp != 0) {
5476747b715Smrg	    vdiff = fabs(iveloc - tmp);
5486747b715Smrg	    if (vdiff <= vel->max_diff ||
5496747b715Smrg		vdiff/(iveloc + tmp) < vel->max_rel_diff) {
5506747b715Smrg		/* we're in range with the initial velocity,
5516747b715Smrg		 * so this result is likely better
5526747b715Smrg		 * (it contains more information). */
5536747b715Smrg		res = tmp;
5546747b715Smrg		i = offset;
5556747b715Smrg	    }else{
5566747b715Smrg		/* we're not in range, quit - it won't get better. */
5576747b715Smrg		DebugAccelF("(dix prtacc) query: tracker too different:"
5586747b715Smrg		            " old %2.2f initial %2.2f diff: %2.2f\n",
5596747b715Smrg		            tmp, iveloc, vdiff);
5606747b715Smrg		break;
5616747b715Smrg	    }
5626747b715Smrg	}
5636747b715Smrg    }
5646747b715Smrg    if(offset == vel->num_tracker){
5656747b715Smrg	DebugAccelF("(dix prtacc) query: last tracker in effect\n");
5666747b715Smrg	i = vel->num_tracker-1;
5676747b715Smrg    }
5686747b715Smrg    if(i>=0){
5696747b715Smrg        n = TRACKER_INDEX(vel, i);
5706747b715Smrg	DebugAccelF("(dix prtacc) result: offset %i [dx: %i dy: %i diff: %i]\n",
5716747b715Smrg	            i,
5726747b715Smrg	            vel->tracker[n].dx,
5736747b715Smrg	            vel->tracker[n].dy,
5746747b715Smrg	            cur_t - vel->tracker[n].time);
5754642e01fSmrg    }
5766747b715Smrg    return res;
5776747b715Smrg}
5784642e01fSmrg
5796747b715Smrg#undef TRACKER_INDEX
5804642e01fSmrg
5816747b715Smrg/**
5826747b715Smrg * Perform velocity approximation based on 2D 'mickeys' (mouse motion delta).
5836747b715Smrg * return true if non-visible state reset is suggested
5846747b715Smrg */
5856747b715Smrgshort
5866747b715SmrgProcessVelocityData2D(
5876747b715Smrg    DeviceVelocityPtr vel,
5886747b715Smrg    int dx,
5896747b715Smrg    int dy,
5906747b715Smrg    int time)
5916747b715Smrg{
5926747b715Smrg    float velocity;
5934642e01fSmrg
5946747b715Smrg    vel->last_velocity = vel->velocity;
5956747b715Smrg
5966747b715Smrg    FeedTrackers(vel, dx, dy, time);
5974642e01fSmrg
5986747b715Smrg    velocity = QueryTrackers(vel, time);
5996747b715Smrg
6006747b715Smrg    vel->velocity = velocity;
6016747b715Smrg    return velocity == 0;
6026747b715Smrg}
6034642e01fSmrg
6044642e01fSmrg/**
6054642e01fSmrg * this flattens significant ( > 1) mickeys a little bit for more steady
6064642e01fSmrg * constant-velocity response
6074642e01fSmrg */
6084642e01fSmrgstatic inline float
6094642e01fSmrgApplySimpleSoftening(int od, int d)
6104642e01fSmrg{
6114642e01fSmrg    float res = d;
6124642e01fSmrg    if (d <= 1 && d >= -1)
6134642e01fSmrg        return res;
6144642e01fSmrg    if (d > od)
6154642e01fSmrg        res -= 0.5;
6164642e01fSmrg    else if (d < od)
6174642e01fSmrg        res += 0.5;
6184642e01fSmrg    return res;
6194642e01fSmrg}
6204642e01fSmrg
6214642e01fSmrg
6224642e01fSmrgstatic void
6234642e01fSmrgApplySofteningAndConstantDeceleration(
6246747b715Smrg        DeviceVelocityPtr vel,
6254642e01fSmrg        int dx,
6264642e01fSmrg        int dy,
6274642e01fSmrg        float* fdx,
6284642e01fSmrg        float* fdy,
6294642e01fSmrg        short do_soften)
6304642e01fSmrg{
6316747b715Smrg    if (do_soften && vel->use_softening) {
6326747b715Smrg        *fdx = ApplySimpleSoftening(vel->last_dx, dx);
6336747b715Smrg        *fdy = ApplySimpleSoftening(vel->last_dy, dy);
6344642e01fSmrg    } else {
6354642e01fSmrg        *fdx = dx;
6364642e01fSmrg        *fdy = dy;
6374642e01fSmrg    }
6384642e01fSmrg
6396747b715Smrg    *fdx *= vel->const_acceleration;
6406747b715Smrg    *fdy *= vel->const_acceleration;
6414642e01fSmrg}
6424642e01fSmrg
6434642e01fSmrg/*
6444642e01fSmrg * compute the acceleration for given velocity and enforce min_acceleartion
6454642e01fSmrg */
6466747b715Smrgfloat
6474642e01fSmrgBasicComputeAcceleration(
6486747b715Smrg    DeviceIntPtr dev,
6496747b715Smrg    DeviceVelocityPtr vel,
6504642e01fSmrg    float velocity,
6514642e01fSmrg    float threshold,
6524642e01fSmrg    float acc){
6534642e01fSmrg
6544642e01fSmrg    float result;
6556747b715Smrg    result = vel->Profile(dev, vel, velocity, threshold, acc);
6564642e01fSmrg
6574642e01fSmrg    /* enforce min_acceleration */
6586747b715Smrg    if (result < vel->min_acceleration)
6596747b715Smrg	result = vel->min_acceleration;
6604642e01fSmrg    return result;
6614642e01fSmrg}
6624642e01fSmrg
6634642e01fSmrg/**
6644642e01fSmrg * Compute acceleration. Takes into account averaging, nv-reset, etc.
6654642e01fSmrg */
6664642e01fSmrgstatic float
6674642e01fSmrgComputeAcceleration(
6686747b715Smrg    DeviceIntPtr dev,
6694642e01fSmrg    DeviceVelocityPtr vel,
6704642e01fSmrg    float threshold,
6714642e01fSmrg    float acc){
6724642e01fSmrg    float res;
6734642e01fSmrg
6746747b715Smrg    if(vel->velocity <= 0){
6754642e01fSmrg	DebugAccelF("(dix ptracc) profile skipped\n");
6764642e01fSmrg        /*
6776747b715Smrg         * If we have no idea about device velocity, don't pretend it.
6784642e01fSmrg         */
6794642e01fSmrg	return 1;
6804642e01fSmrg    }
6814642e01fSmrg
6824642e01fSmrg    if(vel->average_accel && vel->velocity != vel->last_velocity){
6834642e01fSmrg	/* use simpson's rule to average acceleration between
6844642e01fSmrg	 * current and previous velocity.
6854642e01fSmrg	 * Though being the more natural choice, it causes a minor delay
6864642e01fSmrg	 * in comparison, so it can be disabled. */
6876747b715Smrg	res = BasicComputeAcceleration(
6886747b715Smrg	          dev, vel, vel->velocity, threshold, acc);
6896747b715Smrg	res += BasicComputeAcceleration(
6906747b715Smrg	          dev, vel, vel->last_velocity, threshold, acc);
6916747b715Smrg	res += 4.0f * BasicComputeAcceleration(dev, vel,
6924642e01fSmrg	                   (vel->last_velocity + vel->velocity) / 2,
6934642e01fSmrg	                   threshold, acc);
6944642e01fSmrg	res /= 6.0f;
6954642e01fSmrg	DebugAccelF("(dix ptracc) profile average [%.2f ... %.2f] is %.3f\n",
6964642e01fSmrg	            vel->velocity, vel->last_velocity, res);
6974642e01fSmrg        return res;
6984642e01fSmrg    }else{
6996747b715Smrg	res = BasicComputeAcceleration(dev, vel,
7006747b715Smrg	                               vel->velocity, threshold, acc);
7014642e01fSmrg	DebugAccelF("(dix ptracc) profile sample [%.2f] is %.3f\n",
7024642e01fSmrg               vel->velocity, res);
7034642e01fSmrg	return res;
7044642e01fSmrg    }
7054642e01fSmrg}
7064642e01fSmrg
7074642e01fSmrg
7084642e01fSmrg/*****************************************
7094642e01fSmrg *  Acceleration functions and profiles
7104642e01fSmrg ****************************************/
7114642e01fSmrg
7124642e01fSmrg/**
7134642e01fSmrg * Polynomial function similar previous one, but with f(1) = 1
7144642e01fSmrg */
7154642e01fSmrgstatic float
7164642e01fSmrgPolynomialAccelerationProfile(
7176747b715Smrg    DeviceIntPtr dev,
7186747b715Smrg    DeviceVelocityPtr vel,
7194642e01fSmrg    float velocity,
7204642e01fSmrg    float ignored,
7214642e01fSmrg    float acc)
7224642e01fSmrg{
7234642e01fSmrg   return pow(velocity, (acc - 1.0) * 0.5);
7244642e01fSmrg}
7254642e01fSmrg
7264642e01fSmrg
7274642e01fSmrg/**
7284642e01fSmrg * returns acceleration for velocity.
7294642e01fSmrg * This profile selects the two functions like the old scheme did
7304642e01fSmrg */
7314642e01fSmrgstatic float
7324642e01fSmrgClassicProfile(
7336747b715Smrg    DeviceIntPtr dev,
7346747b715Smrg    DeviceVelocityPtr vel,
7354642e01fSmrg    float velocity,
7364642e01fSmrg    float threshold,
7374642e01fSmrg    float acc)
7384642e01fSmrg{
7396747b715Smrg    if (threshold > 0) {
7406747b715Smrg	return SimpleSmoothProfile (dev,
7416747b715Smrg	                            vel,
7424642e01fSmrg	                            velocity,
7434642e01fSmrg                                    threshold,
7444642e01fSmrg                                    acc);
7454642e01fSmrg    } else {
7466747b715Smrg	return PolynomialAccelerationProfile (dev,
7476747b715Smrg	                                      vel,
7484642e01fSmrg	                                      velocity,
7494642e01fSmrg                                              0,
7504642e01fSmrg                                              acc);
7514642e01fSmrg    }
7524642e01fSmrg}
7534642e01fSmrg
7544642e01fSmrg
7554642e01fSmrg/**
7564642e01fSmrg * Power profile
7574642e01fSmrg * This has a completely smooth transition curve, i.e. no jumps in the
7584642e01fSmrg * derivatives.
7594642e01fSmrg *
7604642e01fSmrg * This has the expense of overall response dependency on min-acceleration.
7614642e01fSmrg * In effect, min_acceleration mimics const_acceleration in this profile.
7624642e01fSmrg */
7634642e01fSmrgstatic float
7644642e01fSmrgPowerProfile(
7656747b715Smrg    DeviceIntPtr dev,
7666747b715Smrg    DeviceVelocityPtr vel,
7674642e01fSmrg    float velocity,
7684642e01fSmrg    float threshold,
7694642e01fSmrg    float acc)
7704642e01fSmrg{
7714642e01fSmrg    float vel_dist;
7724642e01fSmrg
7734642e01fSmrg    acc = (acc-1.0) * 0.1f + 1.0; /* without this, acc of 2 is unuseable */
7744642e01fSmrg
7754642e01fSmrg    if (velocity <= threshold)
7766747b715Smrg        return vel->min_acceleration;
7774642e01fSmrg    vel_dist = velocity - threshold;
7786747b715Smrg    return (pow(acc, vel_dist)) * vel->min_acceleration;
7794642e01fSmrg}
7804642e01fSmrg
7814642e01fSmrg
7824642e01fSmrg/**
7834642e01fSmrg * just a smooth function in [0..1] -> [0..1]
7844642e01fSmrg *  - point symmetry at 0.5
7854642e01fSmrg *  - f'(0) = f'(1) = 0
7864642e01fSmrg *  - starts faster than a sinoid
7874642e01fSmrg *  - smoothness C1 (Cinf if you dare to ignore endpoints)
7884642e01fSmrg */
7894642e01fSmrgstatic inline float
7904642e01fSmrgCalcPenumbralGradient(float x){
7914642e01fSmrg    x *= 2.0f;
7924642e01fSmrg    x -= 1.0f;
7934642e01fSmrg    return 0.5f + (x * sqrt(1.0f - x*x) + asin(x))/M_PI;
7944642e01fSmrg}
7954642e01fSmrg
7964642e01fSmrg
7974642e01fSmrg/**
7984642e01fSmrg * acceleration function similar to classic accelerated/unaccelerated,
7994642e01fSmrg * but with smooth transition in between (and towards zero for adaptive dec.).
8004642e01fSmrg */
8014642e01fSmrgstatic float
8024642e01fSmrgSimpleSmoothProfile(
8036747b715Smrg    DeviceIntPtr dev,
8046747b715Smrg    DeviceVelocityPtr vel,
8054642e01fSmrg    float velocity,
8064642e01fSmrg    float threshold,
8074642e01fSmrg    float acc)
8084642e01fSmrg{
8094642e01fSmrg    if(velocity < 1.0f)
8104642e01fSmrg        return CalcPenumbralGradient(0.5 + velocity*0.5) * 2.0f - 1.0f;
8114642e01fSmrg    if(threshold < 1.0f)
8124642e01fSmrg        threshold = 1.0f;
8134642e01fSmrg    if (velocity <= threshold)
8144642e01fSmrg        return 1;
8154642e01fSmrg    velocity /= threshold;
8164642e01fSmrg    if (velocity >= acc)
8174642e01fSmrg        return acc;
8184642e01fSmrg    else
8194642e01fSmrg        return 1.0f + (CalcPenumbralGradient(velocity/acc) * (acc - 1.0f));
8204642e01fSmrg}
8214642e01fSmrg
8224642e01fSmrg
8234642e01fSmrg/**
8244642e01fSmrg * This profile uses the first half of the penumbral gradient as a start
8254642e01fSmrg * and then scales linearly.
8264642e01fSmrg */
8274642e01fSmrgstatic float
8284642e01fSmrgSmoothLinearProfile(
8296747b715Smrg    DeviceIntPtr dev,
8306747b715Smrg    DeviceVelocityPtr vel,
8314642e01fSmrg    float velocity,
8324642e01fSmrg    float threshold,
8334642e01fSmrg    float acc)
8344642e01fSmrg{
8354642e01fSmrg    float res, nv;
8364642e01fSmrg
8374642e01fSmrg    if(acc > 1.0f)
8384642e01fSmrg        acc -= 1.0f; /*this is so acc = 1 is no acceleration */
8394642e01fSmrg    else
8404642e01fSmrg        return 1.0f;
8414642e01fSmrg
8424642e01fSmrg    nv = (velocity - threshold) * acc * 0.5f;
8434642e01fSmrg
8444642e01fSmrg    if(nv < 0){
8454642e01fSmrg        res = 0;
8464642e01fSmrg    }else if(nv < 2){
8474642e01fSmrg        res = CalcPenumbralGradient(nv*0.25f)*2.0f;
8484642e01fSmrg    }else{
8494642e01fSmrg        nv -= 2.0f;
8504642e01fSmrg        res = nv * 2.0f / M_PI  /* steepness of gradient at 0.5 */
8514642e01fSmrg              + 1.0f; /* gradient crosses 2|1 */
8524642e01fSmrg    }
8536747b715Smrg    res += vel->min_acceleration;
8544642e01fSmrg    return res;
8554642e01fSmrg}
8564642e01fSmrg
8574642e01fSmrg
8586747b715Smrg/**
8596747b715Smrg * From 0 to threshold, the response graduates smoothly from min_accel to
8606747b715Smrg * acceleration. Beyond threshold it is exactly the specified acceleration.
8616747b715Smrg */
8626747b715Smrgstatic float
8636747b715SmrgSmoothLimitedProfile(
8646747b715Smrg    DeviceIntPtr dev,
8656747b715Smrg    DeviceVelocityPtr vel,
8666747b715Smrg    float velocity,
8676747b715Smrg    float threshold,
8686747b715Smrg    float acc)
8696747b715Smrg{
8706747b715Smrg    float res;
8716747b715Smrg
8726747b715Smrg    if(velocity >= threshold || threshold == 0.0f)
8736747b715Smrg	return acc;
8746747b715Smrg
8756747b715Smrg    velocity /= threshold; /* should be [0..1[ now */
8766747b715Smrg
8776747b715Smrg    res = CalcPenumbralGradient(velocity) * (acc - vel->min_acceleration);
8786747b715Smrg
8796747b715Smrg    return vel->min_acceleration + res;
8806747b715Smrg}
8816747b715Smrg
8826747b715Smrg
8834642e01fSmrgstatic float
8844642e01fSmrgLinearProfile(
8856747b715Smrg    DeviceIntPtr dev,
8866747b715Smrg    DeviceVelocityPtr vel,
8874642e01fSmrg    float velocity,
8884642e01fSmrg    float threshold,
8894642e01fSmrg    float acc)
8904642e01fSmrg{
8914642e01fSmrg    return acc * velocity;
8924642e01fSmrg}
8934642e01fSmrg
8946747b715Smrgstatic float
8956747b715SmrgNoProfile(
8966747b715Smrg    DeviceIntPtr dev,
8976747b715Smrg    DeviceVelocityPtr vel,
8986747b715Smrg    float velocity,
8996747b715Smrg    float threshold,
9006747b715Smrg    float acc)
9016747b715Smrg{
9026747b715Smrg    return 1.0f;
9036747b715Smrg}
9044642e01fSmrg
9056747b715Smrgstatic PointerAccelerationProfileFunc
9066747b715SmrgGetAccelerationProfile(
9076747b715Smrg    DeviceVelocityPtr vel,
9084642e01fSmrg    int profile_num)
9094642e01fSmrg{
9104642e01fSmrg    switch(profile_num){
9114642e01fSmrg        case AccelProfileClassic:
9126747b715Smrg            return ClassicProfile;
9134642e01fSmrg        case AccelProfileDeviceSpecific:
9146747b715Smrg            return vel->deviceSpecificProfile;
9154642e01fSmrg        case AccelProfilePolynomial:
9166747b715Smrg            return PolynomialAccelerationProfile;
9174642e01fSmrg        case AccelProfileSmoothLinear:
9186747b715Smrg            return SmoothLinearProfile;
9194642e01fSmrg        case AccelProfileSimple:
9206747b715Smrg            return SimpleSmoothProfile;
9214642e01fSmrg        case AccelProfilePower:
9226747b715Smrg            return PowerProfile;
9234642e01fSmrg        case AccelProfileLinear:
9246747b715Smrg            return LinearProfile;
9256747b715Smrg        case AccelProfileSmoothLimited:
9266747b715Smrg            return SmoothLimitedProfile;
9276747b715Smrg        case AccelProfileNone:
9286747b715Smrg            return NoProfile;
9294642e01fSmrg        default:
9306747b715Smrg            return NULL;
9314642e01fSmrg    }
9326747b715Smrg}
9336747b715Smrg
9346747b715Smrg/**
9356747b715Smrg * Set the profile by number.
9366747b715Smrg * Intended to make profiles exchangeable at runtime.
9376747b715Smrg * If you created a profile, give it a number here and in the header to
9386747b715Smrg * make it selectable. In case some profile-specific init is needed, here
9396747b715Smrg * would be a good place, since FreeVelocityData() also calls this with
9406747b715Smrg * PROFILE_UNINITIALIZE.
9416747b715Smrg *
9426747b715Smrg * returns FALSE if profile number is unavailable, TRUE otherwise.
9436747b715Smrg */
9446747b715Smrgint
9456747b715SmrgSetAccelerationProfile(
9466747b715Smrg    DeviceVelocityPtr vel,
9476747b715Smrg    int profile_num)
9486747b715Smrg{
9496747b715Smrg    PointerAccelerationProfileFunc profile;
9506747b715Smrg    profile = GetAccelerationProfile(vel, profile_num);
9516747b715Smrg
9526747b715Smrg    if(profile == NULL && profile_num != PROFILE_UNINITIALIZE)
9536747b715Smrg	return FALSE;
9546747b715Smrg
9559ace9065Smrg    /* Here one could free old profile-private data */
9569ace9065Smrg    free(vel->profile_private);
9579ace9065Smrg    vel->profile_private = NULL;
9584642e01fSmrg    /* Here one could init profile-private data */
9596747b715Smrg    vel->Profile = profile;
9606747b715Smrg    vel->statistics.profile_number = profile_num;
9614642e01fSmrg    return TRUE;
9624642e01fSmrg}
9634642e01fSmrg
9644642e01fSmrg/**********************************************
9654642e01fSmrg * driver interaction
9664642e01fSmrg **********************************************/
9674642e01fSmrg
9684642e01fSmrg
9694642e01fSmrg/**
9704642e01fSmrg * device-specific profile
9714642e01fSmrg *
9724642e01fSmrg * The device-specific profile is intended as a hook for a driver
9734642e01fSmrg * which may want to provide an own acceleration profile.
9744642e01fSmrg * It should not rely on profile-private data, instead
9754642e01fSmrg * it should do init/uninit in the driver (ie. with DEVICE_INIT and friends).
9764642e01fSmrg * Users may override or choose it.
9774642e01fSmrg */
9786747b715Smrgvoid
9794642e01fSmrgSetDeviceSpecificAccelerationProfile(
9806747b715Smrg        DeviceVelocityPtr vel,
9814642e01fSmrg        PointerAccelerationProfileFunc profile)
9824642e01fSmrg{
9836747b715Smrg    if(vel)
9846747b715Smrg	vel->deviceSpecificProfile = profile;
9854642e01fSmrg}
9864642e01fSmrg
9874642e01fSmrg/**
9884642e01fSmrg * Use this function to obtain a DeviceVelocityPtr for a device. Will return NULL if
9894642e01fSmrg * the predictable acceleration scheme is not in effect.
9904642e01fSmrg */
9916747b715SmrgDeviceVelocityPtr
9924642e01fSmrgGetDevicePredictableAccelData(
9936747b715Smrg	DeviceIntPtr dev)
9944642e01fSmrg{
9954642e01fSmrg    /*sanity check*/
9966747b715Smrg    if(!dev){
9974642e01fSmrg	ErrorF("[dix] accel: DeviceIntPtr was NULL");
9984642e01fSmrg	return NULL;
9994642e01fSmrg    }
10006747b715Smrg    if( dev->valuator &&
10016747b715Smrg	dev->valuator->accelScheme.AccelSchemeProc ==
10024642e01fSmrg	    acceleratePointerPredictable &&
10036747b715Smrg	dev->valuator->accelScheme.accelData != NULL){
10044642e01fSmrg
10056747b715Smrg	return (DeviceVelocityPtr)dev->valuator->accelScheme.accelData;
10064642e01fSmrg    }
10074642e01fSmrg    return NULL;
10084642e01fSmrg}
10094642e01fSmrg
10104642e01fSmrg/********************************
10114642e01fSmrg *  acceleration schemes
10124642e01fSmrg *******************************/
10134642e01fSmrg
10144642e01fSmrg/**
10154642e01fSmrg * Modifies valuators in-place.
10164642e01fSmrg * This version employs a velocity approximation algorithm to
10174642e01fSmrg * enable fine-grained predictable acceleration profiles.
10184642e01fSmrg */
10194642e01fSmrgvoid
10204642e01fSmrgacceleratePointerPredictable(
10216747b715Smrg    DeviceIntPtr dev,
10224642e01fSmrg    int first_valuator,
10234642e01fSmrg    int num_valuators,
10244642e01fSmrg    int *valuators,
10254642e01fSmrg    int evtime)
10264642e01fSmrg{
10274642e01fSmrg    float mult = 0.0;
10284642e01fSmrg    int dx = 0, dy = 0;
10294642e01fSmrg    int *px = NULL, *py = NULL;
10304642e01fSmrg    DeviceVelocityPtr velocitydata =
10316747b715Smrg	(DeviceVelocityPtr) dev->valuator->accelScheme.accelData;
10326747b715Smrg    float fdx, fdy, tmp; /* no need to init */
10336747b715Smrg    Bool soften = TRUE;
10344642e01fSmrg
10354642e01fSmrg    if (!num_valuators || !valuators || !velocitydata)
10364642e01fSmrg        return;
10374642e01fSmrg
10386747b715Smrg    if (velocitydata->statistics.profile_number == AccelProfileNone &&
10396747b715Smrg	velocitydata->const_acceleration == 1.0f) {
10406747b715Smrg	return; /*we're inactive anyway, so skip the whole thing.*/
10416747b715Smrg    }
10426747b715Smrg
10434642e01fSmrg    if (first_valuator == 0) {
10444642e01fSmrg        dx = valuators[0];
10454642e01fSmrg        px = &valuators[0];
10464642e01fSmrg    }
10474642e01fSmrg    if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) {
10484642e01fSmrg        dy = valuators[1 - first_valuator];
10494642e01fSmrg        py = &valuators[1 - first_valuator];
10504642e01fSmrg    }
10514642e01fSmrg
10524642e01fSmrg    if (dx || dy){
10536747b715Smrg        /* reset non-visible state? */
10546747b715Smrg        if (ProcessVelocityData2D(velocitydata, dx , dy, evtime)) {
10556747b715Smrg            soften = FALSE;
10564642e01fSmrg        }
10574642e01fSmrg
10586747b715Smrg        if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
10594642e01fSmrg            /* invoke acceleration profile to determine acceleration */
10606747b715Smrg            mult = ComputeAcceleration (dev, velocitydata,
10616747b715Smrg					dev->ptrfeed->ctrl.threshold,
10626747b715Smrg					(float)dev->ptrfeed->ctrl.num /
10636747b715Smrg					(float)dev->ptrfeed->ctrl.den);
10644642e01fSmrg
10654642e01fSmrg            if(mult != 1.0 || velocitydata->const_acceleration != 1.0) {
10664642e01fSmrg                ApplySofteningAndConstantDeceleration( velocitydata,
10676747b715Smrg						       dx, dy,
10686747b715Smrg						       &fdx, &fdy,
10696747b715Smrg						       (mult > 1.0) && soften);
10706747b715Smrg
10714642e01fSmrg                if (dx) {
10726747b715Smrg                    tmp = mult * fdx + dev->last.remainder[0];
10736747b715Smrg                    /* Since it may not be apparent: lrintf() does not offer
10746747b715Smrg                     * strong statements about rounding; however because we
10756747b715Smrg                     * process each axis conditionally, there's no danger
10766747b715Smrg                     * of a toggling remainder. Its lack of guarantees likely
10776747b715Smrg                     * makes it faster on the average target. */
10786747b715Smrg                    *px = lrintf(tmp);
10796747b715Smrg                    dev->last.remainder[0] = tmp - (float)*px;
10804642e01fSmrg                }
10814642e01fSmrg                if (dy) {
10826747b715Smrg                    tmp = mult * fdy + dev->last.remainder[1];
10836747b715Smrg                    *py = lrintf(tmp);
10846747b715Smrg                    dev->last.remainder[1] = tmp - (float)*py;
10854642e01fSmrg                }
10866747b715Smrg                DebugAccelF("pos (%i | %i) remainders x: %.3f y: %.3f delta x:%.3f y:%.3f\n",
10876747b715Smrg                            *px, *py, dev->last.remainder[0], dev->last.remainder[1], fdx, fdy);
10884642e01fSmrg            }
10894642e01fSmrg        }
10904642e01fSmrg    }
10914642e01fSmrg    /* remember last motion delta (for softening/slow movement treatment) */
10924642e01fSmrg    velocitydata->last_dx = dx;
10934642e01fSmrg    velocitydata->last_dy = dy;
10944642e01fSmrg}
10954642e01fSmrg
10964642e01fSmrg
10974642e01fSmrg
10984642e01fSmrg/**
10994642e01fSmrg * Originally a part of xf86PostMotionEvent; modifies valuators
11004642e01fSmrg * in-place. Retained mostly for embedded scenarios.
11014642e01fSmrg */
11024642e01fSmrgvoid
11034642e01fSmrgacceleratePointerLightweight(
11046747b715Smrg    DeviceIntPtr dev,
11054642e01fSmrg    int first_valuator,
11064642e01fSmrg    int num_valuators,
11074642e01fSmrg    int *valuators,
11084642e01fSmrg    int ignored)
11094642e01fSmrg{
11104642e01fSmrg    float mult = 0.0;
11114642e01fSmrg    int dx = 0, dy = 0;
11124642e01fSmrg    int *px = NULL, *py = NULL;
11134642e01fSmrg
11144642e01fSmrg    if (!num_valuators || !valuators)
11154642e01fSmrg        return;
11164642e01fSmrg
11174642e01fSmrg    if (first_valuator == 0) {
11184642e01fSmrg        dx = valuators[0];
11194642e01fSmrg        px = &valuators[0];
11204642e01fSmrg    }
11214642e01fSmrg    if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) {
11224642e01fSmrg        dy = valuators[1 - first_valuator];
11234642e01fSmrg        py = &valuators[1 - first_valuator];
11244642e01fSmrg    }
11254642e01fSmrg
11264642e01fSmrg    if (!dx && !dy)
11274642e01fSmrg        return;
11284642e01fSmrg
11296747b715Smrg    if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
11304642e01fSmrg        /* modeled from xf86Events.c */
11316747b715Smrg        if (dev->ptrfeed->ctrl.threshold) {
11326747b715Smrg            if ((abs(dx) + abs(dy)) >= dev->ptrfeed->ctrl.threshold) {
11336747b715Smrg                dev->last.remainder[0] = ((float)dx *
11346747b715Smrg                                             (float)(dev->ptrfeed->ctrl.num)) /
11356747b715Smrg                                             (float)(dev->ptrfeed->ctrl.den) +
11366747b715Smrg                                            dev->last.remainder[0];
11374642e01fSmrg                if (px) {
11386747b715Smrg                    *px = (int)dev->last.remainder[0];
11396747b715Smrg                    dev->last.remainder[0] = dev->last.remainder[0] -
11404642e01fSmrg                                                (float)(*px);
11414642e01fSmrg                }
11424642e01fSmrg
11436747b715Smrg                dev->last.remainder[1] = ((float)dy *
11446747b715Smrg                                             (float)(dev->ptrfeed->ctrl.num)) /
11456747b715Smrg                                             (float)(dev->ptrfeed->ctrl.den) +
11466747b715Smrg                                            dev->last.remainder[1];
11474642e01fSmrg                if (py) {
11486747b715Smrg                    *py = (int)dev->last.remainder[1];
11496747b715Smrg                    dev->last.remainder[1] = dev->last.remainder[1] -
11504642e01fSmrg                                                (float)(*py);
11514642e01fSmrg                }
11524642e01fSmrg            }
11534642e01fSmrg        }
11544642e01fSmrg        else {
11554642e01fSmrg	    mult = pow((float)dx * (float)dx + (float)dy * (float)dy,
11566747b715Smrg                       ((float)(dev->ptrfeed->ctrl.num) /
11576747b715Smrg                        (float)(dev->ptrfeed->ctrl.den) - 1.0) /
11584642e01fSmrg                       2.0) / 2.0;
11594642e01fSmrg            if (dx) {
11606747b715Smrg                dev->last.remainder[0] = mult * (float)dx +
11616747b715Smrg                                            dev->last.remainder[0];
11626747b715Smrg                *px = (int)dev->last.remainder[0];
11636747b715Smrg                dev->last.remainder[0] = dev->last.remainder[0] -
11644642e01fSmrg                                            (float)(*px);
11654642e01fSmrg            }
11664642e01fSmrg            if (dy) {
11676747b715Smrg                dev->last.remainder[1] = mult * (float)dy +
11686747b715Smrg                                            dev->last.remainder[1];
11696747b715Smrg                *py = (int)dev->last.remainder[1];
11706747b715Smrg                dev->last.remainder[1] = dev->last.remainder[1] -
11714642e01fSmrg                                            (float)(*py);
11724642e01fSmrg            }
11734642e01fSmrg        }
11744642e01fSmrg    }
11754642e01fSmrg}
1176