[PATCH] dix: refactor pointer acceleration

Simon Thum simon.thum at gmx.de
Sun Feb 8 08:21:09 PST 2009


The algorithm is split in a 2D-specific and a general part.
This potentially allows to accelerate more than just screen motion.
A state machine is intoduced to make code more explicit and readable.
It also improves handling of 'phase 1' mickeys when axial correction
kicks in (corner case).
---
 dix/ptrveloc.c     |  144 +++++++++++++++++++++++++++++++++------------------
 include/ptrveloc.h |    2 +-
 2 files changed, 94 insertions(+), 52 deletions(-)

diff --git a/dix/ptrveloc.c b/dix/ptrveloc.c
index dcf91df..30e0207 100644
--- a/dix/ptrveloc.c
+++ b/dix/ptrveloc.c
@@ -81,7 +81,8 @@ SimpleSmoothProfile(DeviceVelocityPtr pVel, float velocity,
                     float threshold, float acc);
 static PointerAccelerationProfileFunc
 GetAccelerationProfile(DeviceVelocityPtr s, int profile_num);
-
+static short
+ProcessVelocityData(DeviceVelocityPtr s, float distance, int diff);
 
 /*#define PTRACCEL_DEBUGGING*/
 
@@ -519,38 +520,35 @@ GetAxis(int dx, int dy){
             return 1;
         if(dy == 1 || dy == -1)
             return 2;
-        return -1;
-    }else{
-        return -1;
     }
+    return -1;
 }
 
 
 /**
- * Perform velocity approximation
+ * Perform velocity approximation based on 2D 'mickeys' (mouse motion delta).
  * return true if non-visible state reset is suggested
  */
 static short
-ProcessVelocityData(
+ProcessVelocityData2D(
     DeviceVelocityPtr s,
     int dx,
     int dy,
     int time)
 {
-    float cvelocity;
+    float distance;
 
     int diff = time - s->lrm_time;
     int cur_ax, last_ax;
-    short reset = (diff >= s->reset_time);
+    BOOL reset;
 
-    /* remember last round's result */
-    s->last_velocity = s->velocity;
     cur_ax = GetAxis(dx, dy);
     last_ax = GetAxis(s->last_dx, s->last_dy);
 
-    if(cur_ax != last_ax && cur_ax != -1 && last_ax != -1 && !reset){
+    if(cur_ax != last_ax && cur_ax != -1 && last_ax != -1 &&
+	    diff < s->reset_time){
         /* correct for the error induced when diagonal movements are
-           reported as alternating axis mickeys */
+           reported as alternating axis-aligned mickeys */
         dx += s->last_dx;
         dy += s->last_dy;
         diff += s->last_diff;
@@ -560,6 +558,33 @@ ProcessVelocityData(
         s->last_diff = diff;
     }
 
+    distance = (float)sqrt(dx*dx + dy*dy);
+
+    s->lrm_time = time;
+
+    reset = ProcessVelocityData(s, distance, diff);
+
+    if(reset)
+	s->last_diff = 1;
+    return reset;
+}
+
+/**
+ * Perform velocity approximation given a one-dimensional delta.
+ * return true if non-visible state reset is suggested
+ */
+static short
+ProcessVelocityData(
+    DeviceVelocityPtr s,
+    float distance,
+    int diff)
+{
+    float cvelocity;
+    int phase;
+
+    /* remember previous result */
+    s->last_velocity = s->velocity;
+
     /*
      * cvelocity is not a real velocity yet, more a motion delta. constant
      * acceleration is multiplied here to make the velocity an on-screen
@@ -567,9 +592,7 @@ ProcessVelocityData(
      * make multiple devices with widely varying ConstantDecelerations respond
      * similar to acceleration controls.
      */
-    cvelocity = (float)sqrt(dx*dx + dy*dy) * s->const_acceleration;
-
-    s->lrm_time = time;
+    cvelocity = distance * s->const_acceleration;
 
     if (s->reset_time < 0 || diff < 0) { /* reset disabled or timer overrun? */
         /* simply set velocity from current movement, no reset. */
@@ -577,6 +600,24 @@ ProcessVelocityData(
         return FALSE;
     }
 
+    /* try to determine 'phase', i.e. whether we process the first(0),
+     * second(1) or any following motion event of a stroke(2) */
+    if(diff >= s->reset_time && cvelocity < 10){
+	phase = 0;
+    }else{
+	switch(s->last_phase){
+	case 0:
+	case 1:
+	    phase = s->last_phase + 1;
+	    break;
+	default:
+	    phase = 2;
+	    break;
+	}
+    }
+    s->last_phase = phase;
+    DebugAccelF("(dix ptracc) phase: %i\n", phase);
+
     if (diff == 0)
         diff = 1; /* prevent div-by-zero, though it shouldn't happen anyway*/
 
@@ -584,47 +625,47 @@ ProcessVelocityData(
        so we multiply by some per-device adjustable factor) */
     cvelocity = cvelocity * s->corr_mul / (float)diff;
 
-    /* short-circuit: when nv-reset the rest can be skipped */
-    if(reset == TRUE){
+    switch(phase){
+    case 0:
 	/*
-	 * we don't really have a velocity here, since diff includes inactive
-	 * time. This is dealt with in ComputeAcceleration.
+	 * First event of a stroke.
+	 * We don't really have a velocity here, since diff includes inactive
+	 * time. This is dealt with in ComputeAcceleration. Here we cancel out
+	 * remnants from previous strokes which the user is presumably
+	 * not aware of (non-visible state reset).
 	 */
 	StuffFilterChain(s, cvelocity);
 	s->velocity = s->last_velocity = cvelocity;
-	s->last_reset = TRUE;
 	DebugAccelF("(dix ptracc) non-visible state reset\n");
 	return TRUE;
-    }
-
-    if(s->last_reset == TRUE){
+    case 1:
 	/*
 	 * when here, we're probably processing the second mickey of a starting
 	 * stroke. This happens to be the first time we can reasonably pretend
 	 * that cvelocity is an actual velocity. Thus, to opt precision, we
 	 * stuff that into the filter chain.
 	 */
-	s->last_reset = FALSE;
 	DebugAccelF("(dix ptracc) after-reset vel:%.3f\n", cvelocity);
 	StuffFilterChain(s, cvelocity);
 	s->velocity = cvelocity;
 	return FALSE;
+    default:
+	/* normal operarion: feed into filter chain */
+	FeedFilterChain(s, cvelocity, diff);
+
+	/* perform coupling and decide final value */
+	s->velocity = QueryFilterChain(s, cvelocity);
+
+	DebugAccelF(
+	       "(dix ptracc) guess: vel=%.3f diff=%d   %i|%i|%i|%i|%i|%i|%i|%i|%i\n",
+	       s->velocity, diff,
+	       s->statistics.filter_usecount[0], s->statistics.filter_usecount[1],
+	       s->statistics.filter_usecount[2], s->statistics.filter_usecount[3],
+	       s->statistics.filter_usecount[4], s->statistics.filter_usecount[5],
+	       s->statistics.filter_usecount[6], s->statistics.filter_usecount[7],
+	       s->statistics.filter_usecount[8]);
+	return FALSE;
     }
-
-    /* feed into filter chain */
-    FeedFilterChain(s, cvelocity, diff);
-
-    /* perform coupling and decide final value */
-    s->velocity = QueryFilterChain(s, cvelocity);
-
-    DebugAccelF("(dix ptracc) guess: vel=%.3f diff=%d   %i|%i|%i|%i|%i|%i|%i|%i|%i\n",
-           s->velocity, diff,
-           s->statistics.filter_usecount[0], s->statistics.filter_usecount[1],
-           s->statistics.filter_usecount[2], s->statistics.filter_usecount[3],
-           s->statistics.filter_usecount[4], s->statistics.filter_usecount[5],
-           s->statistics.filter_usecount[6], s->statistics.filter_usecount[7],
-           s->statistics.filter_usecount[8]);
-    return FALSE;
 }
 
 
@@ -696,7 +737,7 @@ ComputeAcceleration(
     float acc){
     float res;
 
-    if(vel->last_reset){
+    if(vel->last_phase == 0){
 	DebugAccelF("(dix ptracc) profile skipped\n");
         /*
          * This is intended to override the first estimate of a stroke,
@@ -1009,6 +1050,7 @@ acceleratePointerPredictable(
     DeviceVelocityPtr velocitydata =
 	(DeviceVelocityPtr) pDev->valuator->accelScheme.accelData;
     float fdx, fdy; /* no need to init */
+    Bool soften = TRUE;
 
     if (!num_valuators || !valuators || !velocitydata)
         return;
@@ -1023,16 +1065,15 @@ acceleratePointerPredictable(
     }
 
     if (dx || dy){
-        /* reset nonvisible state? */
-        if (ProcessVelocityData(velocitydata, dx , dy, evtime)) {
-            /* set to center of pixel. makes sense as long as there are no
-             * means of passing on sub-pixel values.
+        /* reset non-visible state? */
+        if (ProcessVelocityData2D(velocitydata, dx , dy, evtime)) {
+            /* if nv-reset: set to center of pixel.
+             * makes sense as long as there are no means of passing on
+             * sub-pixel values to apps(XI2?). If you remove it, make
+             * sure suitable rounding is applied below.
              */
             pDev->last.remainder[0] = pDev->last.remainder[1] = 0.5f;
-            /* prevent softening (somewhat quirky solution,
-            as it depends on the algorithm) */
-            velocitydata->last_dx = dx;
-            velocitydata->last_dy = dy;
+            soften = FALSE;
         }
 
         if (pDev->ptrfeed && pDev->ptrfeed->ctrl.num) {
@@ -1044,9 +1085,10 @@ acceleratePointerPredictable(
 
             if(mult != 1.0 || velocitydata->const_acceleration != 1.0) {
                 ApplySofteningAndConstantDeceleration( velocitydata,
-                                                       dx, dy,
-                                                       &fdx, &fdy,
-                                                       mult > 1.0);
+						       dx, dy,
+						       &fdx, &fdy,
+						       (mult > 1.0) && soften);
+
                 if (dx) {
                     pDev->last.remainder[0] = mult * fdx + pDev->last.remainder[0];
                     *px = (int)pDev->last.remainder[0];
diff --git a/include/ptrveloc.h b/include/ptrveloc.h
index 096dea8..f9933c9 100644
--- a/include/ptrveloc.h
+++ b/include/ptrveloc.h
@@ -83,7 +83,7 @@ typedef struct _DeviceVelocityRec {
     int     lrm_time;       /* time the last motion event was processed  */
     int     last_dx, last_dy; /* last motion delta */
     int     last_diff;      /* last time-difference */
-    Bool    last_reset;     /* whether a nv-reset occurred just before */
+    int     last_phase;     /* phase of last/current estimate */
     float   corr_mul;       /* config: multiply this into velocity */
     float   const_acceleration;  /* config: (recipr.) const deceleration */
     float   min_acceleration;    /* config: minimum acceleration */
-- 
1.6.0.6


--------------020705070200040408000400--


More information about the xorg-devel mailing list