|
|
/****************************** 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
|