diff -Bbc common_old/xf86Xinput.c common/xf86Xinput.c
*** common_old/xf86Xinput.c	Fri Oct 21 19:06:13 2005
--- 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 common_old/xf86Xinput.h common/xf86Xinput.h
*** common_old/xf86Xinput.h	Wed Aug 24 11:18:32 2005
--- 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 
