#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include <math.h>
#include <ptrveloc.h>
#include <inputstr.h>
#include <assert.h>
#include <os.h>
static inline void
FeedFilterStage(FilterStagePtr s, float value, int tdiff);
extern void
InitFilterStage(FilterStagePtr s, float rdecay, int lutsize);
void
CleanupFilterChain(DeviceVelocityPtr s);
int
SetAccelerationProfile(DeviceVelocityPtr s, int profile_num);
void
InitFilterChain(DeviceVelocityPtr s, float rdecay, float degression,
int stages, int lutsize);
void
CleanupFilterChain(DeviceVelocityPtr s);
static float
SimpleSmoothProfile(DeviceVelocityPtr pVel, float velocity,
float threshold, float acc);
#ifdef PTRACCEL_DEBUGGING
#define DebugAccelF ErrorF
#else
#define DebugAccelF(...)
#endif
void
InitVelocityData(DeviceVelocityPtr s)
{
memset(s, 0, sizeof(DeviceVelocityRec));
s->corr_mul = 10.0;
s->const_acceleration = 1.0;
s->reset_time = 300;
s->use_softening = 1;
s->min_acceleration = 1.0;
s->coupling = 0.25;
s->average_accel = TRUE;
SetAccelerationProfile(s, AccelProfileClassic);
InitFilterChain(s, (float)1.0/20.0, 1, 1, 40);
}
static void
FreeVelocityData(DeviceVelocityPtr s){
CleanupFilterChain(s);
SetAccelerationProfile(s, -1);
}
void
AccelerationDefaultCleanup(DeviceIntPtr pDev)
{
if( pDev->valuator->accelScheme.AccelSchemeProc == acceleratePointerPredictable
&& pDev->valuator->accelScheme.accelData != NULL){
pDev->valuator->accelScheme.AccelSchemeProc = NULL;
FreeVelocityData(pDev->valuator->accelScheme.accelData);
xfree(pDev->valuator->accelScheme.accelData);
pDev->valuator->accelScheme.accelData = NULL;
}
}
void
InitFilterChain(DeviceVelocityPtr s, float rdecay, float progression, int stages, int lutsize)
{
int fn;
if((stages > 1 && progression < 1.0f) || 0 == progression){
ErrorF("(dix ptracc) invalid filter chain progression specified\n");
return;
}
OsBlockSignals();
for(fn = 0; fn < MAX_VELOCITY_FILTERS; fn++){
if(fn < stages){
InitFilterStage(&s->filters[fn], rdecay, lutsize);
}else{
InitFilterStage(&s->filters[fn], 0, 0);
}
rdecay /= progression;
}
OsReleaseSignals();
}
void
CleanupFilterChain(DeviceVelocityPtr s)
{
int fn;
for(fn = 0; fn < MAX_VELOCITY_FILTERS; fn++)
InitFilterStage(&s->filters[fn], 0, 0);
}
static inline void
StuffFilterChain(DeviceVelocityPtr s, float value)
{
int fn;
for(fn = 0; fn < MAX_VELOCITY_FILTERS; fn++){
if(s->filters[fn].rdecay != 0)
s->filters[fn].current = value;
else break;
}
}
void
InitFilterStage(FilterStagePtr s, float rdecay, int lutsize)
{
int x;
float *newlut;
float *oldlut;
s->fading_lut_size = 0;
if(lutsize > 0){
newlut = xalloc (sizeof(float)* lutsize);
if(!newlut)
return;
for(x = 0; x < lutsize; x++)
newlut[x] = pow(0.5, ((float)x) * rdecay);
}else{
newlut = NULL;
}
oldlut = s->fading_lut;
s->fading_lut = newlut;
s->rdecay = rdecay;
s->fading_lut_size = lutsize;
s->current = 0;
if(oldlut != NULL)
xfree(oldlut);
}
static inline void
FeedFilterChain(DeviceVelocityPtr s, float value, int tdiff)
{
int fn;
for(fn = 0; fn < MAX_VELOCITY_FILTERS; fn++){
if(s->filters[fn].rdecay != 0)
FeedFilterStage(&s->filters[fn], value, tdiff);
else break;
}
}
static inline void
FeedFilterStage(FilterStagePtr s, float value, int tdiff){
float fade;
if(tdiff < s->fading_lut_size)
fade = s->fading_lut[tdiff];
else
fade = pow(0.5, ((float)tdiff) * s->rdecay);
s->current *= fade;
s->current += value * (1.0f - fade);
}
static inline float
QueryFilterChain(
DeviceVelocityPtr s,
float value)
{
int fn, rfn = 0, cfn = -1;
float cur, result = value;
for(fn = 0; fn < MAX_VELOCITY_FILTERS; fn++){
if(0.0f == s->filters[fn].rdecay)
break;
cur = s->filters[fn].current;
if (fabs(value - cur) <= (s->coupling * (value + cur))){
result = cur;
rfn = fn + 1;
} else if(cfn == -1){
cfn = fn;
}
}
s->statistics.filter_usecount[rfn]++;
DebugAccelF("(dix ptracc) result from stage %i, input %.2f, output %.2f\n",
rfn, value, result);
if(cfn != -1)
s->filters[cfn].current = result;
return result;
}
static inline short
GetAxis(int dx, int dy){
if(dx == 0 || dy == 0){
if(dx == 1 || dx == -1)
return 1;
if(dy == 1 || dy == -1)
return 2;
return -1;
}else{
return -1;
}
}
static short
ProcessVelocityData(
DeviceVelocityPtr s,
int dx,
int dy,
int time)
{
float cvelocity;
int diff = time - s->lrm_time;
int cur_ax, last_ax;
short reset = (diff >= s->reset_time);
s->last_velocity = s->velocity;
cur_ax = GetAxis(dx, dy);
last_ax = GetAxis(s->last_dx, s->last_dy);
if(cur_ax != last_ax && cur_ax != -1 && last_ax != -1 && !reset){
dx += s->last_dx;
dy += s->last_dy;
diff += s->last_diff;
s->last_diff = time - s->lrm_time;
DebugAccelF("(dix ptracc) axial correction\n");
}else{
s->last_diff = diff;
}
cvelocity = (float)sqrt(dx*dx + dy*dy) * s->const_acceleration;
s->lrm_time = time;
if (s->reset_time < 0 || diff < 0) {
s->velocity = cvelocity;
return FALSE;
}
if (diff == 0)
diff = 1;
cvelocity = cvelocity * s->corr_mul / (float)diff;
if(reset == TRUE){
StuffFilterChain(s, cvelocity);
s->velocity = s->last_velocity = cvelocity;
s->last_reset = TRUE;
DebugAccelF("(dix ptracc) non-visible state reset\n");
return TRUE;
}
if(s->last_reset == TRUE){
s->last_reset = FALSE;
DebugAccelF("(dix ptracc) after-reset vel:%.3f\n", cvelocity);
StuffFilterChain(s, cvelocity);
s->velocity = cvelocity;
return FALSE;
}
FeedFilterChain(s, cvelocity, diff);
s->velocity = QueryFilterChain(s, cvelocity);
DebugAccelF("(dix ptracc) guess: vel=%.3f diff=%d %i|%i|%i|%i|%i|%i|%i|%i|%i\n",
s->velocity, diff,
s->statistics.filter_usecount[0], s->statistics.filter_usecount[1],
s->statistics.filter_usecount[2], s->statistics.filter_usecount[3],
s->statistics.filter_usecount[4], s->statistics.filter_usecount[5],
s->statistics.filter_usecount[6], s->statistics.filter_usecount[7],
s->statistics.filter_usecount[8]);
return FALSE;
}
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 s,
int dx,
int dy,
float* fdx,
float* fdy,
short do_soften)
{
if (do_soften && s->use_softening) {
*fdx = ApplySimpleSoftening(s->last_dx, dx);
*fdy = ApplySimpleSoftening(s->last_dy, dy);
} else {
*fdx = dx;
*fdy = dy;
}
*fdx *= s->const_acceleration;
*fdy *= s->const_acceleration;
}
static float
BasicComputeAcceleration(
DeviceVelocityPtr pVel,
float velocity,
float threshold,
float acc){
float result;
result = pVel->Profile(pVel, velocity, threshold, acc);
if (result < pVel->min_acceleration)
result = pVel->min_acceleration;
return result;
}
static float
ComputeAcceleration(
DeviceVelocityPtr vel,
float threshold,
float acc){
float res;
if(vel->last_reset){
DebugAccelF("(dix ptracc) profile skipped\n");
return 1;
}
if(vel->average_accel && vel->velocity != vel->last_velocity){
res = BasicComputeAcceleration(vel, vel->velocity, threshold, acc);
res += BasicComputeAcceleration(vel, vel->last_velocity, threshold, acc);
res += 4.0f * BasicComputeAcceleration(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(vel, vel->velocity, threshold, acc);
DebugAccelF("(dix ptracc) profile sample [%.2f] is %.3f\n",
vel->velocity, res);
return res;
}
}
static float
PolynomialAccelerationProfile(
DeviceVelocityPtr pVel,
float velocity,
float ignored,
float acc)
{
return pow(velocity, (acc - 1.0) * 0.5);
}
static float
ClassicProfile(
DeviceVelocityPtr pVel,
float velocity,
float threshold,
float acc)
{
if (threshold) {
return SimpleSmoothProfile (pVel,
velocity,
threshold,
acc);
} else {
return PolynomialAccelerationProfile (pVel,
velocity,
0,
acc);
}
}
static float
PowerProfile(
DeviceVelocityPtr pVel,
float velocity,
float threshold,
float acc)
{
float vel_dist;
acc = (acc-1.0) * 0.1f + 1.0;
if (velocity <= threshold)
return pVel->min_acceleration;
vel_dist = velocity - threshold;
return (pow(acc, vel_dist)) * pVel->min_acceleration;
}
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;
}
static float
SimpleSmoothProfile(
DeviceVelocityPtr pVel,
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));
}
static float
SmoothLinearProfile(
DeviceVelocityPtr pVel,
float velocity,
float threshold,
float acc)
{
float res, nv;
if(acc > 1.0f)
acc -= 1.0f;
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
+ 1.0f;
}
res += pVel->min_acceleration;
return res;
}
static float
LinearProfile(
DeviceVelocityPtr pVel,
float velocity,
float threshold,
float acc)
{
return acc * velocity;
}
_X_EXPORT int
SetAccelerationProfile(
DeviceVelocityPtr s,
int profile_num)
{
PointerAccelerationProfileFunc profile;
switch(profile_num){
case -1:
profile = NULL;
break;
case AccelProfileClassic:
profile = ClassicProfile;
break;
case AccelProfileDeviceSpecific:
if(NULL == s->deviceSpecificProfile)
return FALSE;
profile = s->deviceSpecificProfile;
break;
case AccelProfilePolynomial:
profile = PolynomialAccelerationProfile;
break;
case AccelProfileSmoothLinear:
profile = SmoothLinearProfile;
break;
case AccelProfileSimple:
profile = SimpleSmoothProfile;
break;
case AccelProfilePower:
profile = PowerProfile;
break;
case AccelProfileLinear:
profile = LinearProfile;
break;
case AccelProfileReserved:
default:
return FALSE;
}
if(s->profile_private != NULL){
xfree(s->profile_private);
s->profile_private = NULL;
}
s->Profile = profile;
s->statistics.profile_number = profile_num;
return TRUE;
}
_X_EXPORT void
SetDeviceSpecificAccelerationProfile(
DeviceVelocityPtr s,
PointerAccelerationProfileFunc profile)
{
if(s)
s->deviceSpecificProfile = profile;
}
_X_EXPORT DeviceVelocityPtr
GetDevicePredictableAccelData(
DeviceIntPtr pDev)
{
if(!pDev){
ErrorF("[dix] accel: DeviceIntPtr was NULL");
return NULL;
}
if( pDev->valuator &&
pDev->valuator->accelScheme.AccelSchemeProc ==
acceleratePointerPredictable &&
pDev->valuator->accelScheme.accelData != NULL){
return (DeviceVelocityPtr)pDev->valuator->accelScheme.accelData;
}
return NULL;
}
void
acceleratePointerPredictable(
DeviceIntPtr pDev,
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) pDev->valuator->accelScheme.accelData;
float fdx, fdy;
if (!num_valuators || !valuators || !velocitydata)
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){
if (ProcessVelocityData(velocitydata, dx , dy, evtime)) {
pDev->last.remainder[0] = pDev->last.remainder[1] = 0.5f;
velocitydata->last_dx = dx;
velocitydata->last_dy = dy;
}
if (pDev->ptrfeed && pDev->ptrfeed->ctrl.num) {
mult = ComputeAcceleration (velocitydata,
pDev->ptrfeed->ctrl.threshold,
(float)pDev->ptrfeed->ctrl.num /
(float)pDev->ptrfeed->ctrl.den);
if(mult != 1.0 || velocitydata->const_acceleration != 1.0) {
ApplySofteningAndConstantDeceleration( velocitydata,
dx, dy,
&fdx, &fdy,
mult > 1.0);
if (dx) {
pDev->last.remainder[0] = mult * fdx + pDev->last.remainder[0];
*px = (int)pDev->last.remainder[0];
pDev->last.remainder[0] = pDev->last.remainder[0] - (float)*px;
}
if (dy) {
pDev->last.remainder[1] = mult * fdy + pDev->last.remainder[1];
*py = (int)pDev->last.remainder[1];
pDev->last.remainder[1] = pDev->last.remainder[1] - (float)*py;
}
}
}
}
velocitydata->last_dx = dx;
velocitydata->last_dy = dy;
}
void
acceleratePointerLightweight(
DeviceIntPtr pDev,
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 (pDev->ptrfeed && pDev->ptrfeed->ctrl.num) {
if (pDev->ptrfeed->ctrl.threshold) {
if ((abs(dx) + abs(dy)) >= pDev->ptrfeed->ctrl.threshold) {
pDev->last.remainder[0] = ((float)dx *
(float)(pDev->ptrfeed->ctrl.num)) /
(float)(pDev->ptrfeed->ctrl.den) +
pDev->last.remainder[0];
if (px) {
*px = (int)pDev->last.remainder[0];
pDev->last.remainder[0] = pDev->last.remainder[0] -
(float)(*px);
}
pDev->last.remainder[1] = ((float)dy *
(float)(pDev->ptrfeed->ctrl.num)) /
(float)(pDev->ptrfeed->ctrl.den) +
pDev->last.remainder[1];
if (py) {
*py = (int)pDev->last.remainder[1];
pDev->last.remainder[1] = pDev->last.remainder[1] -
(float)(*py);
}
}
}
else {
mult = pow((float)dx * (float)dx + (float)dy * (float)dy,
((float)(pDev->ptrfeed->ctrl.num) /
(float)(pDev->ptrfeed->ctrl.den) - 1.0) /
2.0) / 2.0;
if (dx) {
pDev->last.remainder[0] = mult * (float)dx +
pDev->last.remainder[0];
*px = (int)pDev->last.remainder[0];
pDev->last.remainder[0] = pDev->last.remainder[0] -
(float)(*px);
}
if (dy) {
pDev->last.remainder[1] = mult * (float)dy +
pDev->last.remainder[1];
*py = (int)pDev->last.remainder[1];
pDev->last.remainder[1] = pDev->last.remainder[1] -
(float)(*py);
}
}
}
}