sdl12-compat: Improve relative mouse scaling for OpenGL Logical Scaling

From 23c40739e6d021ec63ba7823598eaacd470a68be Mon Sep 17 00:00:00 2001
From: David Gow <[EMAIL REDACTED]>
Date: Mon, 26 Jul 2021 12:08:47 +0800
Subject: [PATCH] Improve relative mouse scaling for OpenGL Logical Scaling
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

To avoid rounding errors accumulating, keep the remainder from
converting the scaled relative coordinates from float → int around in a
MouseRelativeRemainder variable. This makes it less likely for them to
get out-of-sync with the mouse position in non-relative modes, and
matches what SDL_Render now does.
---
 src/SDL12_compat.c | 32 ++++++++++++++++++++++++++++----
 src/SDL20_syms.h   |  2 ++
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c
index 1354ec3..62b4503 100644
--- a/src/SDL12_compat.c
+++ b/src/SDL12_compat.c
@@ -166,6 +166,11 @@ SDL20_MostSignificantBitIndex32(Uint32 x)
 #endif
 }
 
+/* SDL_truncf needs SDL >=2.0.14 (and breaks Watcom), so copy it here */
+static float SDL20_truncf(float x)
+{
+    return (x < 0.0f) ? SDL20_ceilf(x) : SDL20_floorf(x);
+}
 
 #define SDL12_DEFAULT_REPEAT_DELAY 500
 #define SDL12_DEFAULT_REPEAT_INTERVAL 30
@@ -929,6 +934,7 @@ static JoystickOpenedItem JoystickOpenList[16];
 static Uint8 KeyState[SDLK12_LAST];
 static SDL_bool MouseInputIsRelative = SDL_FALSE;
 static SDL_Point MousePosition = { 0, 0 };
+static SDL_FPoint MouseRelativeRemainder = { 0.f, 0.f };
 static SDL_bool UseMouseRelativeScaling = SDL_FALSE;
 static OpenGLEntryPoints OpenGLFuncs;
 static int OpenGLBlitLockCount = 0;
@@ -2224,11 +2230,13 @@ AdjustOpenGLLogicalScalingPoint(int *x, int *y)
     
 /* Scale a vector (e.g. relative mouse movement) to the logical scaling size */
 static void
-AdjustOpenGLLogicalScalingVector(int *x, int *y)
+AdjustOpenGLLogicalScalingVector(int *x, int *y, float *rx, float *ry)
 {
     SDL_Rect viewport;
     int physical_w, physical_h;
     float scale_x, scale_y;
+    float float_x, float_y;
+    float trunc_x, trunc_y;
 
     /* Don't adjust anything if we're not using Logical Scaling */
     if (!OpenGLLogicalScalingFBO) {
@@ -2242,8 +2250,21 @@ AdjustOpenGLLogicalScalingVector(int *x, int *y)
     scale_x = (float)OpenGLLogicalScalingWidth / viewport.w;
     scale_y = (float)OpenGLLogicalScalingHeight / viewport.h;
 
-    *x = (int) (*x * scale_x);
-    *y = (int) (*y * scale_y);
+    float_x = *x * scale_x + (rx ? *rx : 0.f);
+    float_y = *y * scale_y + (ry ? *ry : 0.f);
+
+    trunc_x = SDL20_truncf(float_x);
+    trunc_y = SDL20_truncf(float_y);
+
+    *x = (int)trunc_x;
+    *y = (int)trunc_y;
+
+    if (rx) {
+        *rx = float_x - trunc_x;
+    }
+    if (ry) {
+        *ry = float_y - trunc_y;
+    }
 }
     
 
@@ -3156,7 +3177,10 @@ EventFilter20to12(void *data, SDL_Event *event20)
             event12.motion.x = (Uint16) event20->motion.x;
             event12.motion.y = (Uint16) event20->motion.y;
             if (UseMouseRelativeScaling) {
-                AdjustOpenGLLogicalScalingVector(&event20->motion.xrel, &event20->motion.yrel);
+                AdjustOpenGLLogicalScalingVector(&event20->motion.xrel,
+                                                 &event20->motion.yrel,
+                                                 &MouseRelativeRemainder.x,
+                                                 &MouseRelativeRemainder.y);
             }
             event12.motion.xrel = (Sint16) event20->motion.xrel;
             event12.motion.yrel = (Sint16) event20->motion.yrel;
diff --git a/src/SDL20_syms.h b/src/SDL20_syms.h
index 36ca624..f1cb785 100644
--- a/src/SDL20_syms.h
+++ b/src/SDL20_syms.h
@@ -308,11 +308,13 @@ SDL20_SYM(int,atoi,(const char *a),(a),return)
 #ifndef SDL12_MATH
 #include <math.h>
 #define SDL20_fabsf fabs
+#define SDL20_ceilf ceil
 #define SDL20_floorf floor
 #define SDL12_MATH
 #endif
 #else
 SDL20_SYM(float,fabsf,(float a),(a),return)
+SDL20_SYM(float,ceilf,(float a),(a),return)
 SDL20_SYM(float,floorf,(float a),(a),return)
 #endif