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:
Sam Lantinga
2022-08-22 16:25:25 -07:00
parent 3ce3594e38
commit 92b3c53c92
6 changed files with 249 additions and 8 deletions

View File

@@ -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);

View File

@@ -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);