So I needed to see my thoughts on paper but my desk was so full of stuff I couldn't make room for pen and paper. Instead I fired up Gimp, and drawing with the mouse worked fine until I realized it's next to impossible to draw diagonal lines that look like lines.
Instead of straight lines, I got waves. The faster I draw the mouse, the deeper the waves. It looked like diagonal mouse motion generated a pair of pointer motion events, one for the X axis and another for Y. And Gimp smoothed that stairstep motion, resulting in waves. Xev(1) confirmed my hypothesis. It turns out wsmouse(4) is breaking the mouse input into multiple events. This isn't necessarily a bug, since it allows for a very small and simple event structure which works without modification as new properties (such as button states, axes, etc.) are added. Now wsmouse generates all the events it can in a loop before waking up the process that waits for these events. So on the receiving side (i.e. in the xenocara ws(4) driver) we can sum all the consecutive X and Y deltas from a single read() before issuing a pointer motion event. This eliminates the stairsteps as long as the events generated by wsmouse fit in the buffers involved. Other approaches would be either extending the event structure, or perhaps adding a new event type that somehow communicates the intended grouping for events that follow/precede (but ugh...). I felt the ws(4) fix was the least intrusive, and it appears to work just fine here. What do you think? This image (drawn in Gimp) illustrates the problem. The last two lines were drawn with my diff applied. http://guu.fi/mousebug.png Index: xenocara/driver/xf86-input-ws/src/ws.c =================================================================== RCS file: /cvs/xenocara/driver/xf86-input-ws/src/ws.c,v retrieving revision 1.57 diff -u -p -r1.57 ws.c --- xenocara/driver/xf86-input-ws/src/ws.c 8 Jul 2012 14:22:03 -0000 1.57 +++ xenocara/driver/xf86-input-ws/src/ws.c 7 Jul 2013 18:33:57 -0000 @@ -474,7 +474,7 @@ wsReadInput(InputInfoPtr pInfo) { WSDevicePtr priv = (WSDevicePtr)pInfo->private; static struct wscons_event eventList[NUMEVENTS]; - int n, c; + int n, c, dx, dy; struct wscons_event *event = eventList; unsigned char *pBuf; @@ -488,10 +488,11 @@ wsReadInput(InputInfoPtr pInfo) if (n == 0) return; + dx = dy = 0; n /= sizeof(struct wscons_event); while (n--) { int buttons = priv->lastButtons; - int dx = 0, dy = 0, dz = 0, dw = 0, ax = 0, ay = 0; + int dz = 0, dw = 0, ax = 0, ay = 0; int zbutton = 0, wbutton = 0; switch (event->type) { @@ -506,11 +507,11 @@ wsReadInput(InputInfoPtr pInfo) buttons)); break; case WSCONS_EVENT_MOUSE_DELTA_X: - dx = event->value; + dx += event->value; DBG(4, ErrorF("Relative X %d\n", event->value)); break; case WSCONS_EVENT_MOUSE_DELTA_Y: - dy = -event->value; + dy -= event->value; DBG(4, ErrorF("Relative Y %d\n", event->value)); break; case WSCONS_EVENT_MOUSE_ABSOLUTE_X: @@ -548,14 +549,18 @@ wsReadInput(InputInfoPtr pInfo) } ++event; - if (dx || dy) { - if (wsWheelEmuFilterMotion(pInfo, dx, dy)) + if ((dx || dy) && event->type != WSCONS_EVENT_MOUSE_DELTA_X && + event->type != WSCONS_EVENT_MOUSE_DELTA_Y) { + int tmpx = dx, tmpy = dy; + dx = dy = 0; + + if (wsWheelEmuFilterMotion(pInfo, tmpx, tmpy)) continue; /* relative motion event */ DBG(3, ErrorF("postMotionEvent dX %d dY %d\n", - dx, dy)); - xf86PostMotionEvent(pInfo->dev, 0, 0, 2, dx, dy); + tmpx, tmpy)); + xf86PostMotionEvent(pInfo->dev, 0, 0, 2, tmpx, tmpy); } if (dz && priv->Z.negative != WS_NOMAP && priv->Z.positive != WS_NOMAP) { @@ -583,9 +588,9 @@ wsReadInput(InputInfoPtr pInfo) ay = tmp; } if (ax) { - dx = ax - priv->old_ax; + int xdelta = ax - priv->old_ax; priv->old_ax = ax; - if (wsWheelEmuFilterMotion(pInfo, dx, 0)) + if (wsWheelEmuFilterMotion(pInfo, xdelta, 0)) continue; /* absolute position event */ @@ -593,15 +598,24 @@ wsReadInput(InputInfoPtr pInfo) xf86PostMotionEvent(pInfo->dev, 1, 0, 1, ax); } if (ay) { - dy = ay - priv->old_ay; + int ydelta = ay - priv->old_ay; priv->old_ay = ay; - if (wsWheelEmuFilterMotion(pInfo, 0, dy)) + if (wsWheelEmuFilterMotion(pInfo, 0, ydelta)) continue; /* absolute position event */ DBG(3, ErrorF("postMotionEvent y %d\n", ay)); xf86PostMotionEvent(pInfo->dev, 1, 1, 1, ay); } + } + if (dx || dy) { + if (wsWheelEmuFilterMotion(pInfo, dx, dy)) + return; + + /* relative motion event */ + DBG(3, ErrorF("postMotionEvent dX %d dY %d\n", + dx, dy)); + xf86PostMotionEvent(pInfo->dev, 0, 0, 2, dx, dy); } return; }