bin/113749: [PATCH] moused(8) -a drops movements due to rounding errors

Oliver Fromme olli at secnetix.de
Sat Jun 16 11:20:03 UTC 2007


>Number:         113749
>Category:       bin
>Synopsis:       [PATCH] moused(8) -a drops movements due to rounding errors
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Jun 16 11:20:03 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator:     Oliver Fromme
>Release:        RELENG_6 and HEAD
>Organization:
secnetix GmbH & Co. KG
		http://www.secnetix.de/bsd
>Environment:

The problem described here exists for as long as moused(8)
exists.  It existed before the recent addition of the -A
option and is unrelated to it, but the -A option inherited
the problem and is also affected.

The patch presented below applies to HEAD (moused.c 1.78)
and RELENG_6 (moused.c 1.70.2.4).

>Description:

The problem was first reported by Harald Schmalzbauer in
this thread on the -current ML, and I have been able to
reproduce it:

http://lists.freebsd.org/pipermail/freebsd-current/2007-June/073694.html      

When the -a option (linear acceleration) is used with an
acceleration value less than one (i.e. to slow a mouse
down because it has a very high resolution), rounding
errors occur:  Slow movements are not reported at all,
movement information is dropped.  You have to move the
mouse at a certain minimum speed to get any movement of
the pointer on the screen at all.

The culprit is this code in moused.c:

        mouse.u.data.x = action2.dx * rodent.accelx;
        mouse.u.data.y = action2.dy * rodent.accely;

mouse.u.data.* is the movement reported to the application
(e.g. Xorg), action2.d* is the movement reported by the
device, and rodent.accel* is the acceleration factor set
by the -a option (e.g. 0.3 to reduce the speed to 30%).

All those variables are ints, except for the accel* values
which are floats.  So if you move the mouse slowly so the
device reports only 1, 2 or 3 units of movement, they will
all be rounded down to zero:  3 * 0.3 = 0.9 = (int) 0.
Therefore, with an acceleration value of 0.3, you have to
move the mouse fast enough so it constantly reports at
least four units of movement to get any movement of the
pointer at all.

Of course, the solution is to calculate and accumulate the
rounding error and take it into account upon the next
movement.  That's what the following patch does, both for
the -a option (linear acceleration) and for the new -A
option (dynamic acceleration).

>How-To-Repeat:

Run moused(8) with some low -a value (less than 1), e.g. 0.3,
and move the mouse slowly.

>Fix:

As mentioned above, this patch applies to both HEAD
(moused.c 1.78) and RELENG_6 (moused.c 1.70.2.4).
It should be applied in the directory src/usr.sbin/moused.

Harald Schmalzbauer confirmed that this patch fixes the
problem for him.

If the patch got somehow mangled during transmission of
the PR e-mail, it is also available from this URL:

http://www.secnetix.de/~olli/tmp/moused.v2.patch

--- moused.c.orig	Sun Jun  3 15:36:04 2007
+++ moused.c	Sat Jun 16 09:58:10 2007
@@ -400,6 +400,8 @@
     float accely;		/* Acceleration in the Y axis */
     float expoaccel;		/* Exponential acceleration */
     float expoffset;		/* Movement offset for exponential accel. */
+    float remainx;		/* Remainder on X and Y axis, respectively... */
+    float remainy;		/*    ... to compensate for rounding errors. */
     int scrollthreshold;	/* Movement distance before virtual scrolling */
 } rodent = {
     .flags = 0,
@@ -421,6 +423,8 @@
     .accely = 1.0,
     .expoaccel = 1.0,
     .expoffset = 1.0,
+    .remainx = 0.0,
+    .remainy = 0.0,
     .scrollthreshold = DFLT_SCROLLTHRESHOLD,
 };
 
@@ -500,6 +504,7 @@
 
 /* function prototypes */
 
+static void	linacc(int, int, int*, int*);
 static void	expoacc(int, int, int*, int*);
 static void	moused(void);
 static void	hup(int sig);
@@ -956,7 +961,33 @@
 }
 
 /*
+ * Function to calculate linear acceleration.
+ *
+ * If there are any rounding errors, the remainder
+ * is stored in the remainx and remainy variables
+ * and taken into account upon the next movement.
+ */
+
+static void
+linacc(int dx, int dy, int *movex, int *movey)
+{
+    float fdx, fdy;
+
+    if (dx == 0 && dy == 0) {
+	*movex = *movey = 0;
+	return;
+    }
+    fdx = dx * rodent.accelx + rodent.remainx;
+    fdy = dy * rodent.accely + rodent.remainy;
+    *movex = lround(fdx);
+    *movey = lround(fdy);
+    rodent.remainx = fdx - *movex;
+    rodent.remainy = fdy - *movey;
+}
+
+/*
  * Function to calculate exponential acceleration.
+ * (Also includes linear acceleration if enabled.)
  *
  * In order to give a smoother behaviour, we record the four
  * most recent non-zero movements and use their average value
@@ -979,8 +1010,12 @@
     length = (length + lastlength[0] + lastlength[1] + lastlength[2]) / 4;
     lbase = length / rodent.expoffset;
     accel = powf(lbase, rodent.expoaccel) / lbase;
-    *movex = lroundf(fdx * accel);
-    *movey = lroundf(fdy * accel);
+    fdx = fdx * accel + rodent.remainx;
+    fdy = fdy * accel + rodent.remainy;
+    *movex = lroundf(fdx);
+    *movey = lroundf(fdy);
+    rodent.remainx = fdx - *movex;
+    rodent.remainy = fdy - *movey;
     lastlength[2] = lastlength[1];
     lastlength[1] = lastlength[0];
     lastlength[0] = length;	/* Insert new average, not original length! */
@@ -1250,8 +1285,8 @@
 			    &mouse.u.data.x, &mouse.u.data.y);
 		    }
 		    else {
-			mouse.u.data.x = action2.dx * rodent.accelx;
-			mouse.u.data.y = action2.dy * rodent.accely;
+			linacc(action2.dx, action2.dy,
+			    &mouse.u.data.x, &mouse.u.data.y);
 		    }
 		    mouse.u.data.z = action2.dz;
 		    if (debug < 2)
@@ -1266,8 +1301,8 @@
 			&mouse.u.data.x, &mouse.u.data.y);
 		}
 		else {
-		    mouse.u.data.x = action2.dx * rodent.accelx;
-		    mouse.u.data.y = action2.dy * rodent.accely;
+		    linacc(action2.dx, action2.dy,
+			&mouse.u.data.x, &mouse.u.data.y);
 		}
 		mouse.u.data.z = action2.dz;
 		if (debug < 2)
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list