Skip to content

Commit

Permalink
refactor to platform-specific callback
Browse files Browse the repository at this point in the history
  • Loading branch information
expikr committed Nov 8, 2024
1 parent 03e74d0 commit 8349f5b
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 180 deletions.
102 changes: 4 additions & 98 deletions src/events/SDL_mouse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
8 changes: 3 additions & 5 deletions src/events/SDL_mouse_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
166 changes: 89 additions & 77 deletions src/video/windows/SDL_windowsmouse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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, &params, 0)) {
data->enhanced = params[2] ? true : false;
if (data->enhanced) WIN_SetEnhancedMouseScale(data);
}
}

#endif // SDL_VIDEO_DRIVER_WINDOWS

0 comments on commit 8349f5b

Please sign in to comment.