From 8349f5bd1b6041923e0196a29424a350f45ef2dc Mon Sep 17 00:00:00 2001 From: expikr <77922942+expikr@users.noreply.github.com> Date: Fri, 8 Nov 2024 23:42:18 +0800 Subject: [PATCH] refactor to platform-specific callback --- src/events/SDL_mouse.c | 102 +--------------- src/events/SDL_mouse_c.h | 8 +- src/video/windows/SDL_windowsmouse.c | 166 ++++++++++++++------------- 3 files changed, 96 insertions(+), 180 deletions(-) diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index dc38cf99770f4..305802db88280 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -588,102 +588,14 @@ void SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouse SDL_PrivateSendMouseMotion(timestamp, window, mouseID, relative, x, y); } -static void CalculateSystemScale(SDL_Mouse *mouse, SDL_Window *window, float *x, float *y) -{ - int i; - int n = mouse->num_system_scale_values; - float *v = mouse->system_scale_values; - float speed, slope, scale; - float xabs = SDL_fabsf(*x); - float yabs = SDL_fabsf(*y); - - // If we're using a single scale value, return that - if (n == 1) { - *x *= v[0]; - *y *= v[0]; - } else { - n /= 2; - //speed = SDL_sqrtf((*x * *x) + (*y * *y)); - speed = SDL_max(xabs, yabs) + 0.5f * SDL_min(xabs, yabs); // cursed approximation used by Windows - slope = 0; // initialize so compiler doesn't complain - for (i = 1; i < n; i++) { - slope = (v[1 + 2*i] - v[1 + 2*(i-1)]) / (v[2*i] - v[2*(i-1)]); - if (speed < v[2*i]) { - break; - } - } - scale = v[1 + 2*(i-1)] + (speed - v[2*(i-1)]) * slope; - *x *= scale / speed; - *y *= scale / speed; - } -#ifdef SDL_PLATFORM_WIN32 - { - // On Windows the mouse speed is affected by the content scale - SDL_VideoDisplay *display; - - if (window) { - display = SDL_GetVideoDisplayForWindow(window); - } else { - display = SDL_GetVideoDisplay(SDL_GetPrimaryDisplay()); - } - if (display) { - *x *= display->content_scale; - *y *= display->content_scale; - } - } -#endif -} - -// You can set either a single scale, or a set of {speed, scale} values in ascending order -bool 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 true; - } - - 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 false; - } - SDL_memcpy(v, values, num_values * sizeof(*values)); - - mouse->num_system_scale_values = num_values; - mouse->system_scale_values = v; - return true; -} - -static void GetScaledMouseDeltas(SDL_Mouse *mouse, SDL_Window *window, float *x, float *y) +static void GetScaledMouseDeltas(SDL_Mouse *mouse, SDL_Window *window, float *x, float *y, Uint64 t) { if (mouse->relative_mode) { if (mouse->enable_relative_speed_scale) { *x *= mouse->relative_speed_scale; *y *= mouse->relative_speed_scale; - } else if (mouse->enable_relative_system_scale && mouse->num_system_scale_values > 0) { - CalculateSystemScale(mouse, window, x, y); + } else if (mouse->enable_relative_system_scale && mouse->TransformMouse) { + mouse->TransformMouse(mouse->internal, window, x, y, t); } } else { if (mouse->enable_normal_speed_scale) { @@ -786,7 +698,7 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL } if (relative) { - GetScaledMouseDeltas(mouse, window, &x, &y); + GetScaledMouseDeltas(mouse, window, &x, &y, timestamp); xrel = x; yrel = y; x = (mouse->last_x + xrel); @@ -1111,12 +1023,6 @@ void SDL_QuitMouse(void) } mouse->num_clickstates = 0; - if (mouse->system_scale_values) { - SDL_free(mouse->system_scale_values); - mouse->system_scale_values = NULL; - } - mouse->num_system_scale_values = 0; - SDL_RemoveHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME, SDL_MouseDoubleClickTimeChanged, mouse); diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h index 993e2ae160bfa..9a2eca657f83b 100644 --- a/src/events/SDL_mouse_c.h +++ b/src/events/SDL_mouse_c.h @@ -82,6 +82,9 @@ typedef struct // Get absolute mouse coordinates. (x) and (y) are never NULL and set to zero before call. SDL_MouseButtonFlags (*GetGlobalMouseState)(float *x, float *y); + // Transform mouse input counts + void (*TransformMouse)(void *internal, SDL_Window *window, float *x, float *y, Uint64 t); + // Data common to all mice SDL_Window *focus; float x; @@ -104,8 +107,6 @@ typedef struct bool enable_relative_speed_scale; float relative_speed_scale; bool enable_relative_system_scale; - int num_system_scale_values; - float *system_scale_values; Uint32 double_click_time; int double_click_radius; bool touch_mouse_events; @@ -162,9 +163,6 @@ extern void SDL_SetMouseFocus(SDL_Window *window); // Update the mouse capture window extern bool SDL_UpdateMouseCapture(bool force_release); -// You can set either a single scale, or a set of {speed, scale} values in sorted order -extern bool SDL_SetMouseSystemScale(int num_values, const float *values); - // Send a mouse motion event extern void SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y); diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c index a1714c265b2b5..a5bed659a9db4 100644 --- a/src/video/windows/SDL_windowsmouse.c +++ b/src/video/windows/SDL_windowsmouse.c @@ -47,6 +47,15 @@ struct SDL_CursorData HCURSOR cursor; }; +struct SDL_MouseData +{ + Uint32 xs[5]; + Uint32 ys[5]; + int last_node; + int ptrspeed + bool enhanced; +}; + DWORD SDL_last_warp_time = 0; HCURSOR SDL_cursor = NULL; static SDL_Cursor *SDL_blank_cursor = NULL; @@ -521,6 +530,50 @@ static SDL_MouseButtonFlags WIN_GetGlobalMouseState(float *x, float *y) return result; } +static void WIN_TransformMouse(void *internal, SDL_Window *window, float *x, float *y, Uint64 t) +{ + if (!internal) return; + SDL_MouseData *data = (SDL_MouseData *)internal; + + // On Windows the mouse speed is affected by the content scale + SDL_VideoDisplay *display = window ? SDL_GetVideoDisplayForWindow(window) : SDL_GetVideoDisplay(SDL_GetPrimaryDisplay()); + float content_scale = (display ? display->content_scale : 1.0f) / 65536.0f; + + int ix = (int)*x << 16; + int iy = (int)*y << 16; + + if (!data->enhanced) { // early return if flat scale + int idx = data->ptrspeed; + int mul = SDL_max(SDL_max(idx, (idx - 2) << 2), (idx - 6) << 3); + *x = content_scale * (float)((ix * mul) >> 5); + *y = content_scale * (float)((iy * mul) >> 5); + return; + } + + int *xs = data->xs; + int *ys = data->ys; + int absx = ix < 0 ? -ix : ix; + int absy = iy < 0 ? -iy : iy; + int speed = SDL_max(absx, absy) + (SDL_min(absx, absy) >> 1); // super cursed approximation used by Windows + if (speed == 0) return; + + int i, j, k; + for (i = 1; i < 5; i++) { + j = i; + if (speed < xs[j]) break; + } + k = data->last_node; + data->last_node = j; + j = SDL_min(j, k); + + int slope = (ys[j] - ys[j-1]) / (xs[j] - xs[j-1]); + int inter = ys[i-1] - xs[i-1] * slope; + int scale = slope + inter / speed; // (slope * speed + inter) / speed; + + *x = content_scale * (float)(ix * scale); + *y = content_scale * (float)(iy * scale); +} + void WIN_InitMouse(SDL_VideoDevice *_this) { SDL_Mouse *mouse = SDL_GetMouse(); @@ -548,98 +601,57 @@ void WIN_QuitMouse(SDL_VideoDevice *_this) WIN_FreeCursor(SDL_blank_cursor); SDL_blank_cursor = NULL; } -} -/* 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 bool LoadFiveFixedPointFloats(const 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; + SDL_Mouse *mouse = SDL_GetMouse(); + if (mouse) { + if (mouse->internal) { + SDL_free(mouse->internal); + mouse->internal = NULL; + } } - return true; } -static void WIN_SetEnhancedMouseScale(int mouse_speed) +static void WIN_SetEnhancedMouseScale(SDL_MouseData *data) { - 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 float x_factor = 3.5f; - const float y_factor = (float)USER_DEFAULT_SCREEN_DPI / 120; - - 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) { - scale_points[i * 2 + 0] = xpoints[i] * x_factor; - scale_points[i * 2 + 1] = ypoints[i] * y_factor * scale; + HKEY open_handle; + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Mouse", 0, KEY_READ, &open_handle) == ERROR_SUCCESS) { + Uint64 xs[5]; + Uint64 ys[5]; + DWORD xsize = sizeof(xs); + DWORD ysize = sizeof(ys); + if (RegQueryValueExW(hKey, L"SmoothMouseXCurve", NULL, NULL, (BYTE*)xs, &xsize) == ERROR_SUCCESS + && RegQueryValueExW(hKey, L"SmoothMouseYCurve", NULL, NULL, (BYTE*)ys, &ysize) == ERROR_SUCCESS) { + for (int i = 0; i < 5; i++) { + data->xs[i] = (Uint32)(0xffffffff & ((xs[i] * 7) >> 1)); + data->ys[i] = (Uint32)(0xffffffff & ((ys[i] * data->ptrspeed * USER_DEFAULT_SCREEN_DPI) / (120 * 10))); } - 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]); + RegCloseKey(open_handle); } } void WIN_UpdateMouseSystemScale(void) { - 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); + SDL_Mouse *mouse = SDL_GetMouse(); + if (!mouse) return; + if (!mouse->internal) { + void *internal = SDL_calloc(1, sizeof(SDL_MouseData)); + if (internal) { + mouse->internal = internal; + mouse->TransformMouse = WIN_TransformMouse; } else { - WIN_SetLinearMouseScale(mouse_speed); + mouse->internal = NULL; + mouse->TransformMouse = NULL; + return; } } + SDL_MouseData *data = (SDL_MouseData *)mouse->internal; + SystemParametersInfo(SPI_GETMOUSESPEED, 0, &data->ptrspeed, 0); + int params[3] = { 0, 0, 0 }; + if (SystemParametersInfo(SPI_GETMOUSE, 0, ¶ms, 0)) { + data->enhanced = params[2] ? true : false; + if (data->enhanced) WIN_SetEnhancedMouseScale(data); + } } #endif // SDL_VIDEO_DRIVER_WINDOWS