diff -Bbc hw/xfree86/common_old/xf86Xinput.c hw/xfree86/common/xf86Xinput.c *** hw/xfree86/common_old/xf86Xinput.c Fri Oct 21 19:06:13 2005 --- hw/xfree86/common/xf86Xinput.c Sat Oct 7 16:50:13 2006 *************** *** 119,124 **** --- 119,273 ---- *****************************************************************************/ #define ENQUEUE(e) xf86eqEnqueue((e)) + + + + /****************************************************************************** + * 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) + { + 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; //TODO: init a lookup for common pow() values here + 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; + } + + + // 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 + s->velocity = 0; + reset = TRUE; + } + + if (diff == 0) + diff = 1; //prevent div-by-zero, though it shouldn't happen anyway + else if (diff > s->reset_time) + diff = s->reset_time; + //translate velocity to dots/ms (somewhat untractable in integers, so we multiply by some adjustable factor) + cvelocity = cvelocity * s->corr_mul / (float)diff; + s->lrm_velocity = cvelocity; + 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 help 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; + } + + //the name says it all + static inline void + ApplySofteningAndDeceleration(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 + 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; + }*/ + + //steady function similar to Classic + static float + SimpleAccelerationFunc(float velocity, float threshold, float acc) + { + 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 steady response + static float + PolynomialAccelerationFunc(float velocity, float ignored, float acc) + { + return pow(velocity, (acc - 1.0) * 0.5); + } + + + /*********************************************************************** * * xf86AlwaysCoreControl -- *************** *** 233,238 **** --- 382,388 ---- xf86ProcessCommonOptions(LocalDevicePtr local, pointer list) { + float tempf; if (xf86SetBoolOption(list, "AlwaysCore", 0) || xf86SetBoolOption(list, "SendCoreEvents", 0)) { local->flags |= XI86_ALWAYS_CORE; *************** *** 261,266 **** --- 410,462 ---- 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,284 **** { LocalDevicePtr local = (LocalDevicePtr)dev->public.devicePrivate; ! local->dxremaind = 0.0; ! local->dyremaind = 0.0; if (InitIntegerFeedbackClassDeviceStruct(dev, xf86AlwaysCoreControl) == FALSE) { ErrorF("Unable to init integer feedback for always core feature\n"); --- 473,480 ---- { LocalDevicePtr local = (LocalDevicePtr)dev->public.devicePrivate; ! local->dxremaind = 0.5f; ! local->dyremaind = 0.5f; if (InitIntegerFeedbackClassDeviceStruct(dev, xf86AlwaysCoreControl) == FALSE) { ErrorF("Unable to init integer feedback for always core feature\n"); *************** *** 875,880 **** --- 1073,1079 ---- 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); *************** *** 885,891 **** int oldaxis[6]; int *axisvals; int dx = 0, dy = 0; ! float mult; int x, y; int loop_start; int i; --- 1084,1090 ---- int oldaxis[6]; int *axisvals; int dx = 0, dy = 0; ! float acc, apxvelocity, fdx, fdy; int x, y; int loop_start; int i; *************** *** 922,927 **** --- 1121,1127 ---- if (loop % 6 == 5 || loop == num_valuators - 1) { num = loop % 6 + 1; + velocitydata = &local->velocitydata; /* * Adjust first two relative valuators */ *************** *** 930,972 **** dx = valuator[0]; dy = valuator[1]; /* * 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 (dy) { ! local->dyremaind = mult * (float)dy + local->dyremaind; valuator[1] = (int)local->dyremaind; local->dyremaind = local->dyremaind - (float)valuator[1]; } } - DBG(6, ErrorF("xf86PostMotionEvent acceleration v0=%d v1=%d\n", - valuator[0], valuator[1])); } /* * Map current position back to device space in case --- 1130,1181 ---- 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 ((dx || dy) && device->ptrfeed) { ! apxvelocity = velocitydata->velocity; ! if (device->ptrfeed->ctrl.num) { ! // Calc Acceleration for current velocity ! if (device->ptrfeed->ctrl.threshold) { //select acceleration style ! //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)); ! } ! ! //do at least this much acceleration (or deceleration) ! if (acc < velocitydata->min_acceleration) ! acc = velocitydata->min_acceleration; ! ! ! if(acc != 1.0 || velocitydata->const_acceleration != 1.0) { ! ApplySofteningAndDeceleration(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) devicev=(%d, %d)\n", valuator[0], valuator[1], apxvelocity, acc, fdx, fdy, dx, dy)); + } } } + //remember last motion delta (for softening) + velocitydata->last_dx = dx; + velocitydata->last_dy = dy; /* * Map current position back to device space in case diff -Bbc hw/xfree86/common_old/xf86Xinput.h hw/xfree86/common/xf86Xinput.h *** hw/xfree86/common_old/xf86Xinput.h Wed Aug 24 11:18:32 2005 --- hw/xfree86/common/xf86Xinput.h Thu Oct 5 14:03:15 2006 *************** *** 113,118 **** --- 113,135 ---- } InputDriverRec, *InputDriverPtr; #endif + + //Contains all Data needed to implement the 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 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,160 **** --- 172,178 ---- InputDriverPtr drv; pointer module; pointer options; + MouseVelocityData velocitydata; } LocalDeviceRec, *LocalDevicePtr, InputInfoRec, *InputInfoPtr; typedef struct _DeviceAssocRec