#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>
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);
#ifdef PTRACCEL_DEBUGGING
#define DebugAccelF ErrorF
#else
#define DebugAccelF(...)
#endif
#define PROFILE_UNINITIALIZE (-100)
void
InitVelocityData(DeviceVelocityPtr vel)
{
memset(vel, 0, sizeof(DeviceVelocityRec));
vel->corr_mul = 10.0;
vel->const_acceleration = 1.0;
vel->reset_time = 300;
vel->use_softening = 1;
vel->min_acceleration = 1.0;
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);
}
void
FreeVelocityData(DeviceVelocityPtr vel){
free(vel->tracker);
SetAccelerationProfile(vel, PROFILE_UNINITIALIZE);
}
void
AccelerationDefaultCleanup(DeviceIntPtr dev)
{
if( dev->valuator->accelScheme.AccelSchemeProc == acceleratePointerPredictable
&& dev->valuator->accelScheme.accelData != NULL){
dev->valuator->accelScheme.AccelSchemeProc = NULL;
FreeVelocityData(dev->valuator->accelScheme.accelData);
free(dev->valuator->accelScheme.accelData);
dev->valuator->accelScheme.accelData = NULL;
DeletePredictableAccelerationProperties(dev);
}
}
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);
}
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);
}
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);
}
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);
}
BOOL
InitializePredictableAccelerationProperties(DeviceIntPtr dev)
{
DeviceVelocityPtr vel = GetDevicePredictableAccelData(dev);
if(!vel)
return FALSE;
vel->prop_handlers[0] = AccelInitProfileProperty(dev, vel);
vel->prop_handlers[1] = AccelInitDecelProperty(dev, vel);
vel->prop_handlers[2] = AccelInitAdaptDecelProperty(dev, vel);
vel->prop_handlers[3] = AccelInitScaleProperty(dev, vel);
return TRUE;
}
BOOL
DeletePredictableAccelerationProperties(DeviceIntPtr dev)
{
DeviceVelocityPtr vel;
Atom prop;
int i;
prop = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
XIDeleteDeviceProperty(dev, prop, FALSE);
prop = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
XIDeleteDeviceProperty(dev, prop, FALSE);
prop = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
XIDeleteDeviceProperty(dev, prop, FALSE);
prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
XIDeleteDeviceProperty(dev, prop, FALSE);
vel = GetDevicePredictableAccelData(dev);
for (i = 0; vel && i < NPROPS_PREDICTABLE_ACCEL; i++)
if (vel->prop_handlers[i])
XIUnregisterPropertyHandler(dev, vel->prop_handlers[i]);
return TRUE;
}
void
InitTrackers(DeviceVelocityPtr vel, int ntracker)
{
if(ntracker < 1){
ErrorF("(dix ptracc) invalid number of trackers\n");
return;
}
free(vel->tracker);
vel->tracker = (MotionTrackerPtr)malloc(ntracker * sizeof(MotionTracker));
memset(vel->tracker, 0, ntracker * sizeof(MotionTracker));
vel->num_tracker = ntracker;
}
static int
DoGetDirection(int dx, int dy){
float r;
int i1, i2;
if(abs(dx) < 2 && abs(dy) < 2){
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;
if(dx > 0)
return 2+4+8;
if(dx < 0)
return 128+64+32;
if(dy > 0)
return 32+16+8;
if(dy < 0)
return 128+1+2;
return 255;
}
#ifdef _ISOC99_SOURCE
r = atan2f(dy, dx);
#else
r = atan2(dy, dx);
#endif
r = (r+(M_PI*2.5))/(M_PI/4);
i1 = (int)(r+0.1) % 8;
i2 = (int)(r+0.9) % 8;
if(i1 < 0 || i1 > 7 || i2 < 0 || i2 > 7)
return 255;
return 1 << i1 | 1 << i2;
}
#define DIRECTION_CACHE_RANGE 5
#define DIRECTION_CACHE_SIZE (DIRECTION_CACHE_RANGE*2+1)
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) {
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{
return DoGetDirection(dx, dy);
}
}
#undef DIRECTION_CACHE_RANGE
#undef DIRECTION_CACHE_SIZE
#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;
}
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;
}
static float
QueryTrackers(DeviceVelocityPtr vel, int cur_t){
int n, offset, dir = 255, i = -1, age_ms;
float iveloc = 0, res = 0, tmp, vdiff;
float vfac = vel->corr_mul * vel->const_acceleration;
for(offset = 1; offset < vel->num_tracker; offset++){
n = TRACKER_INDEX(vel, offset);
age_ms = cur_t - vel->tracker[n].time;
if (age_ms >= vel->reset_time || age_ms < 0) {
DebugAccelF("(dix prtacc) query: tracker too old\n");
break;
}
dir &= vel->tracker[n].dir;
if(dir == 0){
DebugAccelF("(dix prtacc) query: no longer linear\n");
break;
}
tmp = CalcTracker(vel, offset, cur_t) * vfac;
if ((iveloc == 0 || offset <= vel->initial_range) && tmp != 0) {
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) {
res = tmp;
i = offset;
}else{
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
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;
}
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;
}
float
BasicComputeAcceleration(
DeviceIntPtr dev,
DeviceVelocityPtr vel,
float velocity,
float threshold,
float acc){
float result;
result = vel->Profile(dev, vel, velocity, threshold, acc);
if (result < vel->min_acceleration)
result = vel->min_acceleration;
return result;
}
static float
ComputeAcceleration(
DeviceIntPtr dev,
DeviceVelocityPtr vel,
float threshold,
float acc){
float res;
if(vel->velocity <= 0){
DebugAccelF("(dix ptracc) profile skipped\n");
return 1;
}
if(vel->average_accel && vel->velocity != vel->last_velocity){
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;
}
}
static float
PolynomialAccelerationProfile(
DeviceIntPtr dev,
DeviceVelocityPtr vel,
float velocity,
float ignored,
float acc)
{
return pow(velocity, (acc - 1.0) * 0.5);
}
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);
}
}
static float
PowerProfile(
DeviceIntPtr dev,
DeviceVelocityPtr vel,
float velocity,
float threshold,
float acc)
{
float vel_dist;
acc = (acc-1.0) * 0.1f + 1.0;
if (velocity <= threshold)
return vel->min_acceleration;
vel_dist = velocity - threshold;
return (pow(acc, vel_dist)) * vel->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(
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));
}
static float
SmoothLinearProfile(
DeviceIntPtr dev,
DeviceVelocityPtr vel,
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 += vel->min_acceleration;
return res;
}
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;
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;
}
}
int
SetAccelerationProfile(
DeviceVelocityPtr vel,
int profile_num)
{
PointerAccelerationProfileFunc profile;
profile = GetAccelerationProfile(vel, profile_num);
if(profile == NULL && profile_num != PROFILE_UNINITIALIZE)
return FALSE;
free(vel->profile_private);
vel->profile_private = NULL;
vel->Profile = profile;
vel->statistics.profile_number = profile_num;
return TRUE;
}
void
SetDeviceSpecificAccelerationProfile(
DeviceVelocityPtr vel,
PointerAccelerationProfileFunc profile)
{
if(vel)
vel->deviceSpecificProfile = profile;
}
DeviceVelocityPtr
GetDevicePredictableAccelData(
DeviceIntPtr dev)
{
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;
}
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;
Bool soften = TRUE;
if (!num_valuators || !valuators || !velocitydata)
return;
if (velocitydata->statistics.profile_number == AccelProfileNone &&
velocitydata->const_acceleration == 1.0f) {
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 (ProcessVelocityData2D(velocitydata, dx , dy, evtime)) {
soften = FALSE;
}
if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
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];
*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);
}
}
}
velocitydata->last_dx = dx;
velocitydata->last_dy = dy;
}
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) {
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);
}
}
}
}