diff -b -U 5 hw/xfree86/common_old/xf86Xinput.c hw/xfree86/common/xf86Xinput.c --- hw/xfree86/common_old/xf86Xinput.c 2007-01-23 06:39:16.000000000 +0100 +++ hw/xfree86/common/xf86Xinput.c 2007-03-19 22:12:51.000000000 +0100 @@ -115,10 +115,178 @@ /****************************************************************************** * macros *****************************************************************************/ #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 */ + + /* perform coupling */ + if(s->use_coupling && cvelocity > 0 && fabs(s->velocity-cvelocity) > 1.0){ + div = fabs(cvelocity - s->velocity) / (s->velocity + cvelocity); + if(div > s->coupling){ /* if divergence is too high... */ + /* set current velocity as weighted to catch up quickly */ + s->velocity = cvelocity; + } + } + /* 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 -- * * Control proc for the integer feedback that controls the always @@ -229,10 +397,11 @@ */ _X_EXPORT void xf86ProcessCommonOptions(LocalDevicePtr local, pointer list) { + float tempf; if (xf86SetBoolOption(list, "AlwaysCore", 0) || xf86SetBoolOption(list, "SendCoreEvents", 0)) { local->flags |= XI86_ALWAYS_CORE; xf86Msg(X_CONFIG, "%s: always reports core events\n", local->name); } @@ -252,15 +421,60 @@ } else { xf86Msg(X_CONFIG, "%s: doesn't report drag events\n", local->name); } 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 = xf86SetRealOption(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 = xf86SetRealOption(list, "VelocityCoupling", 0.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; /* 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 = xf86SetRealOption(list, "VelocityScale", 10); + + 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); + } } /*********************************************************************** * * xf86XinputFinalizeInit -- @@ -273,12 +487,12 @@ void xf86XinputFinalizeInit(DeviceIntPtr dev) { 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"); } else { local->always_core_feedback = dev->intfeed; @@ -857,10 +1071,12 @@ if (fromDIX) xf86EventQueue.pDequeueScreen = pScreen; } + + /* * convenient functions to post events */ _X_EXPORT void @@ -874,21 +1090,22 @@ int loop; xEvent xE[2]; 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); Bool is_shared = xf86ShareCorePointer(device); Bool drag = xf86SendDragEvents(device); ValuatorClassPtr val = device->valuator; int valuator[6]; 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; int num; @@ -921,55 +1138,66 @@ valuator[loop%6] = va_arg(var,int); if (loop % 6 == 5 || loop == num_valuators - 1) { num = loop % 6 + 1; + velocitydata = &local->velocitydata; /* * Adjust first two relative valuators */ if (!is_absolute && num_valuators >= 2 && loop_start == 0) { 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]; + if ((dx || dy) && device->ptrfeed) { + apxvelocity = velocitydata->velocity; - 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 (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)); + } + + /* 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 = mult * (float)dx + local->dxremaind; + local->dxremaind = acc * fdx + local->dxremaind; valuator[0] = (int)local->dxremaind; local->dxremaind = local->dxremaind - (float)valuator[0]; } if (dy) { - local->dyremaind = mult * (float)dy + local->dyremaind; + 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 -b -U 5 hw/xfree86/common_old/xf86Xinput.h hw/xfree86/common/xf86Xinput.h --- hw/xfree86/common_old/xf86Xinput.h 2007-01-23 06:39:16.000000000 +0100 +++ hw/xfree86/common/xf86Xinput.h 2007-03-19 15:33:36.000000000 +0100 @@ -110,10 +110,28 @@ pointer module; int refCount; } 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 { struct _LocalDeviceRec *next; char * name; @@ -152,10 +170,11 @@ IntegerFeedbackPtr always_core_feedback; IDevPtr conf_idev; InputDriverPtr drv; pointer module; pointer options; + MouseVelocityData velocitydata; } LocalDeviceRec, *LocalDevicePtr, InputInfoRec, *InputInfoPtr; typedef struct _DeviceAssocRec { char * config_section_name;