You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
8.7 KiB
284 lines
8.7 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: newmouse.c
|
|
*
|
|
* Copyright (c) 1985 - 2000, Microsoft Corporation
|
|
*
|
|
* Implements new mouse acceleration algorithm.
|
|
*
|
|
* History:
|
|
* 10-12-2000 JasonSch Adapted code from StevieB
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#ifdef SUBPIXEL_MOUSE
|
|
|
|
/*
|
|
* Globals.
|
|
*/
|
|
BOOL gbNewMouseInit;
|
|
|
|
#define MIN_REFRESH 60
|
|
#define MIN_RESOLUTION 96
|
|
|
|
/*
|
|
* Constants for FixedPoint Math.
|
|
*/
|
|
#define FP_SHIFT 16 // number of binary decimal digits
|
|
#define FP_SCALE 65536 // (2^(32-FP_SHIFT)), used during conversion of
|
|
// floats
|
|
#define FP_MASK 0x0000ffff // mask to retrieve the remainder
|
|
|
|
#define FIXP2INT(n) ((INT)((n) >> FP_SHIFT))
|
|
#define FIXP_REM(n) ((n) & FP_MASK)
|
|
#define INT2FIXP(n) ((((FIXPOINT)n) << FP_SHIFT))
|
|
|
|
/*
|
|
* This function divides two fixed point numbers and returns the result.
|
|
* Notice how the final result is shifted back.
|
|
*/
|
|
__inline FIXPOINT Div_Fixed(FIXPOINT f1, FIXPOINT f2)
|
|
{
|
|
return ((f1 << FP_SHIFT) / f2);
|
|
}
|
|
|
|
/*
|
|
* This function mulitplies two fixed point numbers and returns the result.
|
|
* Notice how the final result is shifted back.
|
|
*/
|
|
__inline FIXPOINT Mul_Fixed(FIXPOINT f1, FIXPOINT f2)
|
|
{
|
|
return (f1 * f2) >> FP_SHIFT;
|
|
}
|
|
|
|
/*
|
|
* This function adds two fixed point numbers and returns the result.
|
|
* Notice how no shifting is necessary.
|
|
*/
|
|
__inline FIXPOINT Add_Fixed(FIXPOINT f1, FIXPOINT f2)
|
|
{
|
|
return f1 + f2;
|
|
}
|
|
|
|
/*
|
|
* Build the curves at boot and when the user changes the setting in the UI.
|
|
* The algorithm uses the speedscale, the screenscale, and mousescale to
|
|
* interpolate the new curves.
|
|
*/
|
|
VOID
|
|
BuildMouseAccelerationCurve(
|
|
PMONITOR pMonitor)
|
|
{
|
|
int i, res, vrefresh;
|
|
HDC hdc;
|
|
FIXPOINT ScreenScale, SpeedScale;
|
|
/*
|
|
* 229376 is 3.5 in FP. This is strong magic, so don't sweat it too much!
|
|
* Ideally we'd calculate this number, but USB mice don't report their
|
|
* refresh rate, which we'd need:
|
|
*
|
|
* MouseScale = Div_Fixed(INT2FIXP(MouseRefresh), INT2FIXP(MouseDPI));
|
|
*/
|
|
FIXPOINT MouseScale = 229376;
|
|
|
|
if (!gbNewMouseInit) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Dividing by 10 is somewhat ad hoc -- this divisor controls the
|
|
* overall "height" of the curves, but does not affect the shape.
|
|
*/
|
|
SpeedScale = INT2FIXP(gMouseSensitivity) / 10;
|
|
|
|
hdc = GreCreateDisplayDC(pMonitor->hDev, DCTYPE_DIRECT, FALSE);
|
|
res = GreGetDeviceCaps(hdc, LOGPIXELSX);
|
|
if (res < MIN_RESOLUTION) {
|
|
/*
|
|
* While there is no evidence that display drivers can return bogus
|
|
* values for the resolution, we have no reason to think they won't
|
|
* (see below). So we clamp the value to a reasonable minimum.
|
|
*/
|
|
RIPMSG2(RIP_WARNING,
|
|
"GreGetDeviceCaps(0x%p, LOGPIXELSX) returned 0n%d", hdc, res);
|
|
res = MIN_RESOLUTION;
|
|
}
|
|
|
|
/*
|
|
* Some video cards lie to us and tell us the refresh rate is 1. There are
|
|
* probably others that lie in different ways, so let's make sure there's
|
|
* some sane mimimum value, or the mouse will be entirely too slow.
|
|
* ALL YOUR REFRESH ARE BELONG TO US!
|
|
*/
|
|
vrefresh = GreGetDeviceCaps(hdc, VREFRESH);
|
|
if (vrefresh < MIN_REFRESH) {
|
|
vrefresh = MIN_REFRESH;
|
|
}
|
|
ScreenScale = INT2FIXP(vrefresh) / res;
|
|
GreDeleteDC(hdc);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pMonitor->xTxf); i++) {
|
|
pMonitor->yTxf[i] = Mul_Fixed(Mul_Fixed(gDefyTxf[i], ScreenScale), SpeedScale);
|
|
pMonitor->xTxf[i] = Mul_Fixed(gDefxTxf[i], MouseScale);
|
|
}
|
|
|
|
/*
|
|
* Build the new curves in a slope-intercept format.
|
|
*/
|
|
for (i = 1; i < ARRAY_SIZE(pMonitor->xTxf); i++) {
|
|
/*
|
|
* Make sure we don't divide by zero (this could happen if bogus values
|
|
* are in the registry).
|
|
*/
|
|
if ((pMonitor->xTxf[i] - pMonitor->xTxf[i-1]) == 0) {
|
|
RIPMSG1(RIP_ERROR, "Bad data in registry for new mouse (i = %d)", i);
|
|
pMonitor->slope[i-1] = pMonitor->yint[i-1] = 0;
|
|
continue;
|
|
}
|
|
|
|
pMonitor->slope[i-1] = Div_Fixed(pMonitor->yTxf[i] - pMonitor->yTxf[i-1], pMonitor->xTxf[i] - pMonitor->xTxf[i-1]);
|
|
pMonitor->yint[i-1] = pMonitor->yTxf[i-1] - Mul_Fixed(pMonitor->slope[i-1], pMonitor->xTxf[i-1]);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DoNewMouseAccel(
|
|
INT *dx,
|
|
INT *dy)
|
|
{
|
|
static FIXPOINT fpDxAcc = 0, fpDyAcc = 0;
|
|
static int i_last = 0;
|
|
int i = 0;
|
|
PMONITOR pMonitor = _MonitorFromPoint(gptCursorAsync, MONITOR_DEFAULTTOPRIMARY);
|
|
FIXPOINT accel, fpDxyMag;
|
|
|
|
/*
|
|
* Convert Mouse X and Y to FixedPoint.
|
|
*/
|
|
FIXPOINT fpDx = INT2FIXP(*dx);
|
|
FIXPOINT fpDy = INT2FIXP(*dy);
|
|
|
|
/*
|
|
* During TS operations it's possible for a mouse move to be queued up but
|
|
* for gpDispInfo->pMonitorFirst/Primary to be NULL. Let's try not to fault
|
|
* in that case. Windows Bug #413159.
|
|
*/
|
|
if (pMonitor == NULL) {
|
|
RIPMSG0(RIP_WARNING, "Ignoring mouse movement w/ no monitor set.");
|
|
return;
|
|
}
|
|
|
|
// Get the magnitude
|
|
fpDxyMag = max(abs(fpDx), abs(fpDy)) + (min(abs(fpDx), abs(fpDy)) / 2);
|
|
|
|
/*
|
|
* Ensure we don't divide by 0.
|
|
*/
|
|
if (fpDxyMag != 0) {
|
|
/*
|
|
* Find the position MagXY from the interpolate acceleration curves.
|
|
* It's possible we won't find one so we'll just use the biggest (i.e.,
|
|
* the last entry in the array).
|
|
*/
|
|
while (i < (ARRAY_SIZE(pMonitor->xTxf) - 1) && fpDxyMag > pMonitor->xTxf[i]) {
|
|
++i;
|
|
}
|
|
--i;
|
|
|
|
|
|
accel = Div_Fixed(Add_Fixed(Mul_Fixed(pMonitor->slope[i], fpDxyMag), pMonitor->yint[i]), fpDxyMag);
|
|
|
|
/*
|
|
* If change of slope from last time then average the accel value using
|
|
* i_last and the current i.
|
|
*/
|
|
if (i_last != i) {
|
|
accel = (accel + Div_Fixed((Mul_Fixed(pMonitor->slope[i_last], fpDxyMag) + pMonitor->yint[i_last]), fpDxyMag)) / 2;
|
|
i_last = i;
|
|
}
|
|
|
|
/*
|
|
* Calculate the multiplier for the mouse data.
|
|
*/
|
|
fpDx = Mul_Fixed(accel, fpDx) + fpDxAcc;
|
|
fpDy = Mul_Fixed(accel, fpDy) + fpDyAcc;
|
|
|
|
/*
|
|
* Store the remainder of the calculated X and Y. This gets added in
|
|
* next time.
|
|
*/
|
|
fpDxAcc = FIXP_REM(fpDx);
|
|
fpDyAcc = FIXP_REM(fpDy);
|
|
|
|
/*
|
|
* Convert back to integer.
|
|
*/
|
|
*dx = FIXP2INT(fpDx);
|
|
*dy = FIXP2INT(fpDy);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ReadDefaultAccelerationCurves(
|
|
PUNICODE_STRING pProfileUserName)
|
|
{
|
|
FIXPOINT xTxf[SM_POINT_CNT], yTxf[SM_POINT_CNT];
|
|
DWORD cbSizeX, cbSizeY;
|
|
|
|
/*
|
|
* The default curves will reside in the .DEFAULT user profile but can be
|
|
* overridden on a per-user basis.
|
|
*/
|
|
cbSizeX = FastGetProfileValue(pProfileUserName,
|
|
PMAP_MOUSE,
|
|
(LPWSTR)STR_SMOOTHMOUSEXCURVE,
|
|
NULL,
|
|
(LPBYTE)xTxf,
|
|
sizeof(xTxf),
|
|
0);
|
|
|
|
cbSizeY = FastGetProfileValue(pProfileUserName,
|
|
PMAP_MOUSE,
|
|
(LPWSTR)STR_SMOOTHMOUSEYCURVE,
|
|
NULL,
|
|
(LPBYTE)yTxf,
|
|
sizeof(yTxf),
|
|
0);
|
|
|
|
/*
|
|
* Check if we successfully read the correct amount of data from both keys.
|
|
* If not, and we're reading the .DEFAULT profile, copy in the default
|
|
* values.
|
|
*/
|
|
if (cbSizeX == sizeof(xTxf) && cbSizeY == sizeof(yTxf)) {
|
|
RtlCopyMemory(gDefxTxf, xTxf, sizeof(xTxf));
|
|
RtlCopyMemory(gDefyTxf, yTxf, sizeof(yTxf));
|
|
} else if (!gbNewMouseInit) {
|
|
/*
|
|
* Default values.
|
|
*/
|
|
static FIXPOINT _xTxf[SM_POINT_CNT] = {0x0, 0x6E15, 0x14000, 0x3DC29, 0x280000};
|
|
static FIXPOINT _yTxf[SM_POINT_CNT] = {0x0, 0x15EB8, 0x54CCD, 0x184CCD, 0x2380000};
|
|
|
|
RtlCopyMemory(gDefxTxf, _xTxf, sizeof(_xTxf));
|
|
RtlCopyMemory(gDefyTxf, _yTxf, sizeof(_yTxf));
|
|
}
|
|
|
|
gbNewMouseInit = TRUE;
|
|
}
|
|
|
|
VOID
|
|
ResetMouseAccelerationCurves(
|
|
VOID)
|
|
{
|
|
PMONITOR pMonitor = gpDispInfo->pMonitorFirst;
|
|
|
|
CheckCritIn();
|
|
|
|
for (; pMonitor != NULL; pMonitor = pMonitor->pMonitorNext) {
|
|
BuildMouseAccelerationCurve(pMonitor);
|
|
}
|
|
}
|
|
|
|
#endif // SUBPIXEL_MOUSE
|