diff -Naur hw/xfree86/common_old/xf86Xinput.c hw/xfree86/common/xf86Xinput.c --- hw/xfree86/common_old/xf86Xinput.c 2006-07-05 18:31:40.000000000 +0000 +++ hw/xfree86/common/xf86Xinput.c 2006-10-23 04:24:36.000000000 +0000 @@ -119,6 +119,172 @@ *****************************************************************************/ #define ENQUEUE(e) xf86eqEnqueue((e)) +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + + +/****************************************************************************** + * Mouse softening fns + * + * Serves 3 complementary functions: + * 1) provide a sophisticated ballistic velocity guess to give better acceleration + * 2) slightly soften out mouse data when acceleration is applied + * 3) decelerate by two means (constant and adaptive) if enabled + *****************************************************************************/ + +static void +InitVelocityData(MouseVelocityPtr s, float rdecay) +{ + int x; + s->lrm_time = 0; + s->velocity = 0; + s->lrm_velocity = 0; + s->corr_mul = 10.0; /* dots per 10 milisecond is a usable form */ + s->const_acceleration = 1.0; /* no acceleration/deceleration */ + s->rdecay = rdecay; + s->reset_time = 0; + s->last_dx = 0; + s->last_dy = 0; + s->use_softening = 1; + s->min_acceleration = 1.0; + s->use_coupling = 1; + s->coupling = 0.3; + + for(x = 0; x < ARRAY_SIZE(s->fading_lut); x++) + s->fading_lut[x] = pow(0.5, ((float)x) * s->rdecay); +} + + +/* Perform velocity guessing + return true if non-visible state reset is suggested */ +static inline short +ProcessVelocityData(MouseVelocityPtr s, int dx, int dy, int time) +{ + int diff = time - s->lrm_time; + float cvelocity = (float)sqrt(dx*dx + dy*dy) * s->const_acceleration; /* not a real velocity yet, more a motion delta */ + float fade, div; + short reset = FALSE; + + s->lrm_time = time; + + if (s->reset_time < 0 || diff < 0) { /* disabled or timer overrun? */ + s->velocity = cvelocity; /* simply set velocity from current movement, no reset. */ + return 0; + } + if (diff >= s->reset_time) { /* no movement for some time, suggest to reset nonvisible state */ + diff = s->reset_time; + reset = TRUE; + } + + if (diff == 0) + diff = 1; /* prevent div-by-zero, though it shouldn't happen anyway */ + + /* translate velocity to dots/ms (somewhat untractable in integers, so we multiply by some per-mouse adjustable factor) */ + cvelocity = cvelocity * s->corr_mul / (float)diff; + s->lrm_velocity = cvelocity; + + /* short-circuit: when nv-reset the rest may be skipped */ + if(reset == TRUE){ + s->velocity = cvelocity; + return TRUE; + } + + /* find fading factor; crucial it matches definition in InitVelocityData */ + if(diff < ARRAY_SIZE(s->fading_lut)) + fade = s->fading_lut[diff]; + else + fade = pow(0.5, ((float)diff) * s->rdecay); /* plot [x=0:10] 0.5**x, (0.5**x)/log(0.5) [its integral] for rationale */ + + s->velocity *= fade; /* fade out old velocity info */ + s->velocity += cvelocity * (1.0f - fade); /* and add up current */ + + if(s->use_coupling && !reset && cvelocity != 0 && fabs(s->velocity - cvelocity) > 1.0){ + div = fabs(1.0f - (s->velocity / cvelocity)); + if(div > s->coupling){ /* if divergence is too high... */ + s->velocity = cvelocity; /* set current velocity as weighted to catch up quickly */ + } + } + /* xf86Msg(X_CONFIG, "ProcessVelocityData acceleration guessing: vel=%.3f diff=%d curw=%.4f, fade=%.4f\n", s->velocity, diff, (1.0- fade), fade); */ + return reset; +} + +/* this flattens significant values 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 inline void +ApplySofteningAndConstantDeceleration(MouseVelocityPtr 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; +} + + + +/* return acceleration for velocity/threshold (profile) */ +typedef float (*accelerationFunc)(float velocity, float threshold, float max_acc); + +/* classic X algorithm for threshold > 0, has the disadvantage of discarding all the nice data we have about mouse velocity */ +/*static float +ClassicAccelerationFunc(float velocity, float threshold, float acc) +{ + if (velocity <= threshold) + return 1; + else + return acc; +}*/ + +/* acceleration function similar to Classic, introducting a transition range */ +static float +SimpleAccelerationFunc(float velocity, float threshold, float acc) +{ + if(velocity < 1.0) + return velocity; + if (velocity <= threshold) + return 1; + velocity /= threshold; + if (velocity >= acc) + return acc; + else + return velocity; + +} + + +/* Classic X Polynomial function for threshold = 0, somewhat quirky due to non-unity respone to velocity = 1 (unsteady acceleration) */ +/*static float +ClassicPolynomialAccelerationFunc(float velocity, float ignored, float acc) +{ + return pow(velocity, (acc - 1.0) * 0.5) * 0.5; +}*/ + +/* Polynomial function similar classic one, but with f(1) = 1 */ +static float +PolynomialAccelerationFunc(float velocity, float ignored, float acc) +{ + return pow(velocity, (acc - 1.0) * 0.5); +} + + + /*********************************************************************** * * xf86AlwaysCoreControl -- @@ -233,6 +399,7 @@ xf86ProcessCommonOptions(LocalDevicePtr local, pointer list) { + float tempf; if (xf86SetBoolOption(list, "AlwaysCore", 0) || xf86SetBoolOption(list, "SendCoreEvents", 0)) { local->flags |= XI86_ALWAYS_CORE; @@ -254,13 +421,59 @@ } else { xf86Msg(X_CONFIG, "%s: doesn't report drag events\n", local->name); } - - local->history_size = xf86SetIntOption(list, "HistorySize", 0); + local->history_size = xf86SetIntOption(list, "HistorySize", 0); if (local->history_size > 0) { xf86Msg(X_CONFIG, "%s: has a history of %d motions\n", local->name, local->history_size); } + + /* FIXME These should be reachable by API/xset and/or be read only for (relative) pointing devices not keyboards */ + tempf = xf86SetIntOption(list, "WeightingDecay", 15); + xf86Msg(X_CONFIG, "%s: weighting decay %d ms\n", local->name, (int)tempf); + if(tempf > 0) + tempf = 1.0 / tempf; /* set reciprocal if possible */ + else + tempf = 10000; /* else set fairly high */ + + InitVelocityData(&(local->velocitydata), tempf); + + tempf = xf86SetIntOption(list, "ConstantDeceleration", 1); + if(tempf > 1.0){ + xf86Msg(X_CONFIG, "%s: constant deceleration by %.1f\n", local->name, tempf); + local->velocitydata.const_acceleration = 1.0 / tempf; /* set reciprocal deceleration aka acceleration */ + } + + tempf = xf86SetIntOption(list, "AdaptiveDeceleration", 1); + if(tempf > 1.0){ + xf86Msg(X_CONFIG, "%s: adaptive deceleration by %.1f\n", local->name, tempf); + local->velocitydata.min_acceleration = 1.0 / tempf; /* set minimum acceleration */ + } + + tempf = xf86SetIntOption(list, "VelocityCouplingPerCent", 25); + if(tempf > 0.0){ + xf86Msg(X_CONFIG, "%s: velocity coupling is %d%%\n", local->name, (int)tempf); + local->velocitydata.use_coupling = 1; + local->velocitydata.coupling = tempf / 100.0; /* set max divergence */ + }else{ + local->velocitydata.use_coupling = 0; + xf86Msg(X_CONFIG, "%s: disabled velocity coupling\n", local->name); + } + + /* Read Softening cfg. if deceleration is used, this is expected to provide enough subpixel information so we enable */ + /* Softening by default only if ConstantDeceleration is not used */ + local->velocitydata.use_softening = xf86SetBoolOption(list, "Softening", local->velocitydata.const_acceleration == 1.0); + + local->velocitydata.reset_time = xf86SetIntOption(list, "VelocityReset", 200); + + local->velocitydata.corr_mul = xf86SetIntOption(list, "VelocityScalePerCent", 1000); /* FIXME reading a float preferred */ + local->velocitydata.corr_mul /= 100.0; + + if (xf86SetBoolOption(list, "VelocityGuessing", 1) == 0) { + local->velocitydata.reset_time = -1; /* Disable */ + local->velocitydata.use_softening = 0; + xf86Msg(X_CONFIG, "%s: Disabled velocity guessing\n", local->name); + } } /*********************************************************************** @@ -277,8 +490,8 @@ { LocalDevicePtr local = (LocalDevicePtr)dev->public.devicePrivate; - local->dxremaind = 0.0; - local->dyremaind = 0.0; + local->dxremaind = 0.5f; + local->dyremaind = 0.5f; if (InitIntegerFeedbackClassDeviceStruct(dev, xf86AlwaysCoreControl) == FALSE) { ErrorF("Unable to init integer feedback for always core feature\n"); @@ -861,6 +1074,8 @@ xf86EventQueue.pDequeueScreen = pScreen; } + + /* * convenient functions to post events */ @@ -878,6 +1093,7 @@ deviceKeyButtonPointer *xev = (deviceKeyButtonPointer*) xE; deviceValuator *xv = (deviceValuator*) xev+1; LocalDevicePtr local = (LocalDevicePtr) device->public.devicePrivate; + MouseVelocityPtr velocitydata; char *buff = 0; Time current; Bool is_core = xf86IsCorePointer(device); @@ -888,7 +1104,7 @@ int oldaxis[6]; int *axisvals; int dx = 0, dy = 0; - float mult; + float acc, apxvelocity, fdx, fdy; int x, y; int loop_start; int i; @@ -925,6 +1141,7 @@ if (loop % 6 == 5 || loop == num_valuators - 1) { num = loop % 6 + 1; + velocitydata = &local->velocitydata; /* * Adjust first two relative valuators */ @@ -932,45 +1149,55 @@ dx = valuator[0]; dy = valuator[1]; + + if (ProcessVelocityData(velocitydata, dx , dy, current)) { /* reset nonvisible state? */ + local->dxremaind = local->dyremaind = 0.5f; /* do rounding by pre-adding 0.5 once (see below and xf86XinputFinalizeInit) */ + velocitydata->last_dx = dx; /* prevent softening (somewhat quirky solution, as it depends on the algorithm) */ + velocitydata->last_dy = dy; + } /* * Accelerate - */ - if (device->ptrfeed && device->ptrfeed->ctrl.num) { - /* modeled from xf86Events.c */ - if (device->ptrfeed->ctrl.threshold) { - if ((abs(dx) + abs(dy)) >= device->ptrfeed->ctrl.threshold) { - local->dxremaind = ((float)dx * (float)(device->ptrfeed->ctrl.num)) / - (float)(device->ptrfeed->ctrl.den) + local->dxremaind; - valuator[0] = (int)local->dxremaind; - local->dxremaind = local->dxremaind - (float)valuator[0]; - - local->dyremaind = ((float)dy * (float)(device->ptrfeed->ctrl.num)) / - (float)(device->ptrfeed->ctrl.den) + local->dyremaind; - valuator[1] = (int)local->dyremaind; - local->dyremaind = local->dyremaind - (float)valuator[1]; - } - } - else if (dx || dy) { - mult = pow((float)(dx*dx+dy*dy), - ((float)(device->ptrfeed->ctrl.num) / - (float)(device->ptrfeed->ctrl.den) - 1.0) / - 2.0) / 2.0; - if (dx) { - local->dxremaind = mult * (float)dx + local->dxremaind; - valuator[0] = (int)local->dxremaind; - local->dxremaind = local->dxremaind - (float)valuator[0]; + */ + if ((dx || dy) && device->ptrfeed) { + apxvelocity = velocitydata->velocity; + + if (device->ptrfeed->ctrl.num) { + /* Calc Acceleration for current velocity */ + if (device->ptrfeed->ctrl.threshold) { /* select acceleration profile */ + /* use ClassicAccelerationFunc for old-style jumpy behaviour */ + acc = SimpleAccelerationFunc(apxvelocity, device->ptrfeed->ctrl.threshold, (float)(device->ptrfeed->ctrl.num) / + (float)(device->ptrfeed->ctrl.den)); + }else { + acc = PolynomialAccelerationFunc(apxvelocity, 0, (float)(device->ptrfeed->ctrl.num) / (float)(device->ptrfeed->ctrl.den)); } - if (dy) { - local->dyremaind = mult * (float)dy + local->dyremaind; - valuator[1] = (int)local->dyremaind; - local->dyremaind = local->dyremaind - (float)valuator[1]; + + /* enforce min_acceleration */ + if (acc < velocitydata->min_acceleration) + acc = velocitydata->min_acceleration; + + + if(acc != 1.0 || velocitydata->const_acceleration != 1.0) { + ApplySofteningAndConstantDeceleration(velocitydata, dx, dy, &fdx, &fdy, acc > 1.0); + if (dx) { + local->dxremaind = acc * fdx + local->dxremaind; + valuator[0] = (int)local->dxremaind; + local->dxremaind = local->dxremaind - (float)valuator[0]; + } + if (dy) { + local->dyremaind = acc * fdy + local->dyremaind; + valuator[1] = (int)local->dyremaind; + local->dyremaind = local->dyremaind - (float)valuator[1]; + } + DBG(6, ErrorF /* or xf86Msg */ ("xf86PostMotionEvent acceleration v=(%d, %d) vel=%.3f acc=%.2f appliedv=(%.1f, %.1f)\n", valuator[0], valuator[1], apxvelocity, acc, fdx, fdy)); } - } - DBG(6, ErrorF("xf86PostMotionEvent acceleration v0=%d v1=%d\n", - valuator[0], valuator[1])); + } } + /* remember last motion delta (for softening) */ + velocitydata->last_dx = dx; + velocitydata->last_dy = dy; + /* * Map current position back to device space in case * the cursor was warped diff -Naur hw/xfree86/common_old/xf86Xinput.h hw/xfree86/common/xf86Xinput.h --- hw/xfree86/common_old/xf86Xinput.h 2006-07-05 18:31:40.000000000 +0000 +++ hw/xfree86/common/xf86Xinput.h 2006-10-23 04:24:36.000000000 +0000 @@ -113,6 +113,24 @@ } InputDriverRec, *InputDriverPtr; #endif + +/* Contains all Data needed to implement mouse ballistics */ +typedef struct _MouseVelocityData { + int lrm_time; /* time the last motion event was processed */ + float velocity; /* velocity as guessed by algo (weighted) */ + float lrm_velocity; /* velocity as by last movement event */ + int last_dx, last_dy; /* last motions delta (for cheap softening algo) */ + float fading_lut[40]; /* lookup for fadeout values; 40 saves 99% of cases, bigger won't help much, maximum sane size is reset_time+1 (which can be configured of course!) */ + float corr_mul; /* config: multiply this into velocity; can be used to adujust behaviour, about 2 to 25 is sensible */ + float rdecay; /* config: reciprocal decay ms are between full and half weight (see AddVelocityData) */ + float const_acceleration; /* config: USED FOR CONSTANT DECELERATION ! but stored reciprocal for fmul is usually faster */ + float min_acceleration; /* config: minimum acceleration; normally 1, set lower to enable subpixel precision */ + short reset_time; /* config: reset non-visible state after this number of miliseconds inactivity, -1 means disable velocity guessing */ + short use_softening; /* config: use softening of actual mouse values (if accelerated) */ + short use_coupling; /* config: use coupling of weighted and current velocity */ + float coupling; /* config: maximum divergence to allow weighted velocity to be preferred */ +} MouseVelocityData, *MouseVelocityPtr; + /* This is to input devices what the ScrnInfoRec is to screens. */ typedef struct _LocalDeviceRec { @@ -155,6 +173,7 @@ InputDriverPtr drv; pointer module; pointer options; + MouseVelocityData velocitydata; } LocalDeviceRec, *LocalDevicePtr, InputInfoRec, *InputInfoPtr; typedef struct _DeviceAssocRec