Skip to content

Instantly share code, notes, and snippets.

@DJm00n
Created December 7, 2023 11:08
Show Gist options
  • Save DJm00n/597c44fcf82f2b51cd7ca46ebc819737 to your computer and use it in GitHub Desktop.
Save DJm00n/597c44fcf82f2b51cd7ca46ebc819737 to your computer and use it in GitHub Desktop.
Mouse acceleration
/*****************************************************************************
*
* The algorithm for applying acceleration is:
*
* dxC = dxR
* if A >= 1 and abs(dxR) > T1 then
* dxC = dxR * 2
* if A >= 2 and abs(dxR) > Thres2 then
* dxC = dxR * 4
* end if
* end if
*
* where
* dxR is the raw mouse motion
* dxC is the cooked mouse motion
* A is the acceleration
* T1 is the first threshold
* T2 is the second threshold
*
* Repeat for dy instead of dx.
*
* We can optimize this by simply setting the thresholds to MAXLONG
* if they are disabled; that way, abs(dx) will never exceed it.
*
* The result is the following piecewise linear function:
*
* if 0 < abs(dxR) <= T1: dxC = dxR
* if T1 < abs(dxR) <= T2: dxC = dxR * 2
* if T2 < abs(dxR): dxC = dxR * 4
*
* If you graph this function, you'll see that it's discontinuous!
*
* The inverse mapping of this function is what concerns us.
* It looks like this:
*
* if 0 < abs(dxC) <= T1: dxR = dxC
* if T1 * 2 < abs(dxC) <= T2 * 2: dxR = dxC / 2
* if T2 * 4 < abs(dxC): dxR = dxC / 4
*
* Notice that there are gaps in the graph, so we can fill them in
* any way we want, as long as it isn't blatantly *wrong*. (In the
* case where we are using emulation, it is possible to get relative
* mouse motions that live in the "impossible" limbo zone due to
* clipping.)
*
* if 0 < abs(dxC) <= T1: dxR = dxC
* if T1 < abs(dxC) <= T2 * 2: dxR = dxC / 2
* if T2 * 2 < abs(dxC): dxR = dxC / 4
*
* Therefore: (you knew the punch line was coming)
*
* s_rgiMouseThresh[0] = T1 (or MAXLONG)
* s_rgiMouseThresh[1] = T2 * 2 (or MAXLONG)
*
*
*****************************************************************************/
static int s_rgiMouseThresh[2];
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEm_Mouse_OnMouseChange |
*
* The mouse acceleration changed. Go recompute the
* unacceleration variables.
*
*****************************************************************************/
void EXTERNAL
CEm_Mouse_OnMouseChange(void)
{
int rgi[3]; /* Mouse acceleration information */
/*
* See the huge comment block at the definition of
* s_rgiMouseThresh for an explanation of the math
* that is happening here.
*
* If acceleration is enabled at all...
*/
if (SystemParametersInfo(SPI_GETMOUSE, 0, &rgi, 0) && rgi[2]) {
s_rgiMouseThresh[0] = rgi[0];
if (rgi[2] >= 2) {
s_rgiMouseThresh[1] = rgi[1] * 2;
} else { /* Disable level 2 acceleration */
s_rgiMouseThresh[1] = MAXLONG;
}
} else { /* Disable all acceleration */
s_rgiMouseThresh[0] = MAXLONG;
}
SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_OnMouseChange: ")
TEXT("New accelerations %d / %d"),
s_rgiMouseThresh[0], s_rgiMouseThresh[1]);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEm_Mouse_RemoveAccel |
*
* Remove any acceleration from the mouse motion.
*
* See the huge comment block at s_rgiMouseThresh
* for an explanation of what we are doing.
*
* @parm int | dx |
*
* Change in coordinate, either dx or dy.
*
*****************************************************************************/
int INTERNAL
CEm_Mouse_RemoveAccel(int dx)
{
int x = abs(dx);
if (x > s_rgiMouseThresh[0]) {
dx /= 2;
if (x > s_rgiMouseThresh[1]) {
dx /= 2;
}
}
return dx;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment