mirror of https://github.com/encounter/SDL.git
Added a hint SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE to control whether to use system mouse acceleration on raw relative motion.
This is currently only implemented on Windows, and "Enhanced pointer precision" mode is not quite correct.
This commit is contained in:
parent
3ce3594e38
commit
92b3c53c92
|
@ -1106,6 +1106,17 @@ extern "C" {
|
|||
*/
|
||||
#define SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE "SDL_MOUSE_RELATIVE_SPEED_SCALE"
|
||||
|
||||
/**
|
||||
* \brief A variable controlling whether the system mouse acceleration curve is used for relative mouse motion.
|
||||
*
|
||||
* This variable can be set to the following values:
|
||||
* "0" - Relative mouse motion will be unscaled (the default)
|
||||
* "1" - Relative mouse motion will be scaled using the system mouse acceleration curve.
|
||||
*
|
||||
* If SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE is set, that will override the system speed scale.
|
||||
*/
|
||||
#define SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE "SDL_MOUSE_RELATIVE_SYSTEM_SCALE"
|
||||
|
||||
/**
|
||||
* \brief A variable controlling whether a motion event should be generated for mouse warping in relative mode.
|
||||
*
|
||||
|
|
|
@ -83,8 +83,10 @@ SDL_MouseNormalSpeedScaleChanged(void *userdata, const char *name, const char *o
|
|||
SDL_Mouse *mouse = (SDL_Mouse *)userdata;
|
||||
|
||||
if (hint && *hint) {
|
||||
mouse->enable_normal_speed_scale = SDL_TRUE;
|
||||
mouse->normal_speed_scale = (float)SDL_atof(hint);
|
||||
} else {
|
||||
mouse->enable_normal_speed_scale = SDL_FALSE;
|
||||
mouse->normal_speed_scale = 1.0f;
|
||||
}
|
||||
}
|
||||
|
@ -95,12 +97,22 @@ SDL_MouseRelativeSpeedScaleChanged(void *userdata, const char *name, const char
|
|||
SDL_Mouse *mouse = (SDL_Mouse *)userdata;
|
||||
|
||||
if (hint && *hint) {
|
||||
mouse->enable_relative_speed_scale = SDL_TRUE;
|
||||
mouse->relative_speed_scale = (float)SDL_atof(hint);
|
||||
} else {
|
||||
mouse->enable_relative_speed_scale = SDL_FALSE;
|
||||
mouse->relative_speed_scale = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDLCALL
|
||||
SDL_MouseRelativeSystemScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
|
||||
{
|
||||
SDL_Mouse *mouse = (SDL_Mouse *)userdata;
|
||||
|
||||
mouse->enable_relative_system_scale = SDL_GetStringBoolean(hint, SDL_FALSE);
|
||||
}
|
||||
|
||||
static void SDLCALL
|
||||
SDL_TouchMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
|
||||
{
|
||||
|
@ -189,6 +201,9 @@ SDL_MouseInit(void)
|
|||
SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE,
|
||||
SDL_MouseRelativeSpeedScaleChanged, mouse);
|
||||
|
||||
SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE,
|
||||
SDL_MouseRelativeSystemScaleChanged, mouse);
|
||||
|
||||
SDL_AddHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS,
|
||||
SDL_TouchMouseEventsChanged, mouse);
|
||||
|
||||
|
@ -344,7 +359,10 @@ SDL_SendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int
|
|||
static int
|
||||
GetScaledMouseDelta(float scale, int value, float *accum)
|
||||
{
|
||||
if (scale != 1.0f) {
|
||||
if (value && scale != 1.0f) {
|
||||
if ((value > 0) != (*accum > 0)) {
|
||||
*accum = 0.0f;
|
||||
}
|
||||
*accum += scale * value;
|
||||
if (*accum >= 0.0f) {
|
||||
value = (int)SDL_floor(*accum);
|
||||
|
@ -356,6 +374,100 @@ GetScaledMouseDelta(float scale, int value, float *accum)
|
|||
return value;
|
||||
}
|
||||
|
||||
static float
|
||||
CalculateSystemScale(SDL_Mouse *mouse, int *x, int *y)
|
||||
{
|
||||
int i;
|
||||
int n = mouse->num_system_scale_values;
|
||||
float *v = mouse->system_scale_values;
|
||||
float speed, coef, scale;
|
||||
|
||||
/* If we're using a single scale value, return that */
|
||||
if (n == 1) {
|
||||
return v[0];
|
||||
}
|
||||
|
||||
speed = SDL_sqrtf((float)(*x * *x) + (*y * *y));
|
||||
for (i = 0; i < (n - 2); i += 2) {
|
||||
if (speed < v[i + 2]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == (n - 2)) {
|
||||
scale = v[n - 1];
|
||||
} else if (speed <= v[i]) {
|
||||
scale = v[i + 1];
|
||||
} else {
|
||||
coef = (speed - v[i]) / (v[i + 2] - v[i]);
|
||||
scale = v[i + 1] + (coef * (v[i + 3] - v[i + 1]));
|
||||
}
|
||||
SDL_Log("speed = %.2f, scale = %.2f\n", speed, scale);
|
||||
return scale;
|
||||
}
|
||||
|
||||
/* You can set either a single scale, or a set of {speed, scale} values in ascending order */
|
||||
int
|
||||
SDL_SetMouseSystemScale(int num_values, const float *values)
|
||||
{
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
float *v;
|
||||
|
||||
if (num_values == mouse->num_system_scale_values &&
|
||||
SDL_memcmp(values, mouse->system_scale_values, num_values * sizeof(*values)) == 0) {
|
||||
/* Nothing has changed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (num_values < 1) {
|
||||
return SDL_SetError("You must have at least one scale value");
|
||||
}
|
||||
|
||||
if (num_values > 1) {
|
||||
/* Validate the values */
|
||||
int i;
|
||||
|
||||
if (num_values < 4 || (num_values % 2) != 0) {
|
||||
return SDL_SetError("You must pass a set of {speed, scale} values");
|
||||
}
|
||||
|
||||
for (i = 0; i < (num_values - 2); i += 2) {
|
||||
if (values[i] >= values[i + 2]) {
|
||||
return SDL_SetError("Speed values must be in ascending order");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v = (float *)SDL_realloc(mouse->system_scale_values, num_values * sizeof(*values));
|
||||
if (!v) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memcpy(v, values, num_values * sizeof(*values));
|
||||
|
||||
mouse->num_system_scale_values = num_values;
|
||||
mouse->system_scale_values = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
GetScaledMouseDeltas(SDL_Mouse *mouse, int *x, int *y)
|
||||
{
|
||||
if (mouse->relative_mode) {
|
||||
if (mouse->enable_relative_speed_scale) {
|
||||
*x = GetScaledMouseDelta(mouse->relative_speed_scale, *x, &mouse->scale_accum_x);
|
||||
*y = GetScaledMouseDelta(mouse->relative_speed_scale, *y, &mouse->scale_accum_y);
|
||||
} else if (mouse->enable_relative_system_scale && mouse->num_system_scale_values > 0) {
|
||||
float relative_system_scale = CalculateSystemScale(mouse, x, y);
|
||||
*x = GetScaledMouseDelta(relative_system_scale, *x, &mouse->scale_accum_x);
|
||||
*y = GetScaledMouseDelta(relative_system_scale, *y, &mouse->scale_accum_y);
|
||||
}
|
||||
} else {
|
||||
if (mouse->enable_normal_speed_scale) {
|
||||
*x = GetScaledMouseDelta(mouse->normal_speed_scale, *x, &mouse->scale_accum_x);
|
||||
*y = GetScaledMouseDelta(mouse->normal_speed_scale, *y, &mouse->scale_accum_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y)
|
||||
{
|
||||
|
@ -405,13 +517,7 @@ SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relativ
|
|||
}
|
||||
|
||||
if (relative) {
|
||||
if (mouse->relative_mode) {
|
||||
x = GetScaledMouseDelta(mouse->relative_speed_scale, x, &mouse->scale_accum_x);
|
||||
y = GetScaledMouseDelta(mouse->relative_speed_scale, y, &mouse->scale_accum_y);
|
||||
} else {
|
||||
x = GetScaledMouseDelta(mouse->normal_speed_scale, x, &mouse->scale_accum_x);
|
||||
y = GetScaledMouseDelta(mouse->normal_speed_scale, y, &mouse->scale_accum_y);
|
||||
}
|
||||
GetScaledMouseDeltas(mouse, &x, &y);
|
||||
xrel = x;
|
||||
yrel = y;
|
||||
x = (mouse->last_x + xrel);
|
||||
|
@ -818,6 +924,9 @@ SDL_MouseQuit(void)
|
|||
SDL_DelHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE,
|
||||
SDL_MouseRelativeSpeedScaleChanged, mouse);
|
||||
|
||||
SDL_DelHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE,
|
||||
SDL_MouseRelativeSystemScaleChanged, mouse);
|
||||
|
||||
SDL_DelHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS,
|
||||
SDL_TouchMouseEventsChanged, mouse);
|
||||
|
||||
|
|
|
@ -92,8 +92,13 @@ typedef struct
|
|||
SDL_bool relative_mode;
|
||||
SDL_bool relative_mode_warp;
|
||||
SDL_bool relative_mode_warp_motion;
|
||||
SDL_bool enable_normal_speed_scale;
|
||||
float normal_speed_scale;
|
||||
SDL_bool enable_relative_speed_scale;
|
||||
float relative_speed_scale;
|
||||
SDL_bool enable_relative_system_scale;
|
||||
int num_system_scale_values;
|
||||
float *system_scale_values;
|
||||
float scale_accum_x;
|
||||
float scale_accum_y;
|
||||
Uint32 double_click_time;
|
||||
|
@ -141,6 +146,9 @@ extern void SDL_SetMouseFocus(SDL_Window * window);
|
|||
/* Update the mouse capture window */
|
||||
extern int SDL_UpdateMouseCapture(SDL_bool force_release);
|
||||
|
||||
/* You can set either a single scale, or a set of {speed, scale} values in sorted order */
|
||||
extern int SDL_SetMouseSystemScale(int num_values, const float *values);
|
||||
|
||||
/* Send a mouse motion event */
|
||||
extern int SDL_SendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y);
|
||||
|
||||
|
|
|
@ -1628,6 +1628,13 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SETTINGCHANGE:
|
||||
if (wParam == SPI_SETMOUSE || wParam == SPI_SETMOUSESPEED) {
|
||||
WIN_UpdateMouseSystemScale();
|
||||
}
|
||||
break;
|
||||
|
||||
#endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
|
||||
}
|
||||
|
||||
|
|
|
@ -363,6 +363,8 @@ WIN_InitMouse(_THIS)
|
|||
SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
|
||||
|
||||
SDL_blank_cursor = WIN_CreateBlankCursor();
|
||||
|
||||
WIN_UpdateMouseSystemScale();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -379,6 +381,109 @@ WIN_QuitMouse(_THIS)
|
|||
}
|
||||
}
|
||||
|
||||
/* For a great description of how the enhanced mouse curve works, see:
|
||||
* https://superuser.com/questions/278362/windows-mouse-acceleration-curve-smoothmousexcurve-and-smoothmouseycurve
|
||||
* http://www.esreality.com/?a=post&id=1846538/
|
||||
*/
|
||||
static SDL_bool
|
||||
LoadFiveFixedPointFloats(BYTE *bytes, float *values)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 5; ++i) {
|
||||
float fraction = (float)((Uint16) bytes[1] << 8 | bytes[0]) / 65535.0f;
|
||||
float value = (float)(((Uint16)bytes[3] << 8) | bytes[2]) + fraction;
|
||||
*values++ = value;
|
||||
bytes += 8;
|
||||
}
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
WIN_SetEnhancedMouseScale(int mouse_speed)
|
||||
{
|
||||
float scale = (float) mouse_speed / 10.0f;
|
||||
HKEY hKey;
|
||||
DWORD dwType = REG_BINARY;
|
||||
BYTE value[40];
|
||||
DWORD length = sizeof(value);
|
||||
int i;
|
||||
float xpoints[5];
|
||||
float ypoints[5];
|
||||
float scale_points[10];
|
||||
const int dpi = 96; // FIXME, how do we handle different monitors with different DPI?
|
||||
const float display_factor = 3.5f * (150.0f / dpi);
|
||||
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Mouse", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
||||
if (RegQueryValueExW(hKey, L"SmoothMouseXCurve", 0, &dwType, value, &length) == ERROR_SUCCESS &&
|
||||
LoadFiveFixedPointFloats(value, xpoints) &&
|
||||
RegQueryValueExW(hKey, L"SmoothMouseYCurve", 0, &dwType, value, &length) == ERROR_SUCCESS &&
|
||||
LoadFiveFixedPointFloats(value, ypoints)) {
|
||||
for (i = 0; i < 5; ++i) {
|
||||
float gain;
|
||||
if (xpoints[i] > 0.0f) {
|
||||
gain = (ypoints[i] / xpoints[i]) * scale;
|
||||
} else {
|
||||
gain = 0.0f;
|
||||
}
|
||||
scale_points[i * 2] = xpoints[i];
|
||||
scale_points[i * 2 + 1] = gain / display_factor;
|
||||
//SDL_Log("Point %d = %f,%f\n", i, scale_points[i * 2], scale_points[i * 2 + 1]);
|
||||
}
|
||||
SDL_SetMouseSystemScale(SDL_arraysize(scale_points), scale_points);
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
WIN_SetLinearMouseScale(int mouse_speed)
|
||||
{
|
||||
static float mouse_speed_scale[] = {
|
||||
0.0f,
|
||||
1 / 32.0f,
|
||||
1 / 16.0f,
|
||||
1 / 8.0f,
|
||||
2 / 8.0f,
|
||||
3 / 8.0f,
|
||||
4 / 8.0f,
|
||||
5 / 8.0f,
|
||||
6 / 8.0f,
|
||||
7 / 8.0f,
|
||||
1.0f,
|
||||
1.25f,
|
||||
1.5f,
|
||||
1.75f,
|
||||
2.0f,
|
||||
2.25f,
|
||||
2.5f,
|
||||
2.75f,
|
||||
3.0f,
|
||||
3.25f,
|
||||
3.5f
|
||||
};
|
||||
|
||||
if (mouse_speed > 0 && mouse_speed < SDL_arraysize(mouse_speed_scale)) {
|
||||
SDL_SetMouseSystemScale(1, &mouse_speed_scale[mouse_speed]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WIN_UpdateMouseSystemScale()
|
||||
{
|
||||
int mouse_speed;
|
||||
int params[3] = { 0, 0, 0 };
|
||||
|
||||
if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &mouse_speed, 0) &&
|
||||
SystemParametersInfo(SPI_GETMOUSE, 0, params, 0)) {
|
||||
if (params[2]) {
|
||||
WIN_SetEnhancedMouseScale(mouse_speed);
|
||||
} else {
|
||||
WIN_SetLinearMouseScale(mouse_speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_WINDOWS */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
|
|
|
@ -29,6 +29,7 @@ extern HCURSOR SDL_cursor;
|
|||
extern void WIN_InitMouse(_THIS);
|
||||
extern void WIN_QuitMouse(_THIS);
|
||||
extern void WIN_SetCursorPos(int x, int y);
|
||||
extern void WIN_UpdateMouseSystemScale();
|
||||
|
||||
#endif /* SDL_windowsmouse_h_ */
|
||||
|
||||
|
|
Loading…
Reference in New Issue