SDL: fix errenous delta when warping

From eaef956a9ab46f98baadf7d4281211045a8a4e88 Mon Sep 17 00:00:00 2001
From: expikr <[EMAIL REDACTED]>
Date: Wed, 13 Nov 2024 23:58:27 +0800
Subject: [PATCH] fix errenous delta when warping

The following changes do not alter any logic:
- rename x/ydelta fields to x/y_accu to better reflect what it actually is about
- coalesce the logic for modifying internal state to one spot, branch based on whether the input was a move or a warp

The following changes alter the logic:
- put the x/y_accu addition under the relative branch only, warps should not add to the accumulation buffer by any definition
- (MAIN FIX) when the destination window desires relative mode, warp events should be dropped if SDL_MOUSE_RELATIVE_WARP_MOTION is false, or have x/yrel set to zero if true
---
 src/events/SDL_mouse.c   | 54 +++++++++++++++++++++-------------------
 src/events/SDL_mouse_c.h |  4 +--
 2 files changed, 30 insertions(+), 28 deletions(-)

diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c
index 1d9fc8a4102c1..f00c6ad72018d 100644
--- a/src/events/SDL_mouse.c
+++ b/src/events/SDL_mouse.c
@@ -739,6 +739,7 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL
     SDL_Mouse *mouse = SDL_GetMouse();
     float xrel = 0.0f;
     float yrel = 0.0f;
+    bool window_is_relative = mouse->focus && (mouse->focus->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE);
 
     if ((!mouse->relative_mode || mouse->warp_emulation_active) && mouseID != SDL_TOUCH_MOUSEID) {
         // We're not in relative mode, so all mouse events are global mouse events
@@ -794,7 +795,6 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL
         ConstrainMousePosition(mouse, window, &x, &y);
     } else {
         ConstrainMousePosition(mouse, window, &x, &y);
-
         if (mouse->has_position) {
             xrel = x - mouse->last_x;
             yrel = y - mouse->last_y;
@@ -814,25 +814,30 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL
         yrel = 0.0f;
     }
 
-    if (mouse->has_position) {
-        // Update internal mouse coordinates
-        if (!mouse->relative_mode) {
+    { // modify internal state
+        if (relative) {
+            if (mouse->has_position) {
+                mouse->x += xrel;
+                mouse->y += yrel;
+                ConstrainMousePosition(mouse, window, &mouse->x, &mouse->y);
+            } else {
+                mouse->x = x;
+                mouse->y = y;
+            }
+            mouse->last_x = mouse->x;
+            mouse->last_y = mouse->y;
+            mouse->x_accu += xrel;
+            mouse->y_accu += yrel;
+        } else {
+            // Use unclamped values if we're getting events outside the window
             mouse->x = x;
             mouse->y = y;
-        } else {
-            mouse->x += xrel;
-            mouse->y += yrel;
-            ConstrainMousePosition(mouse, window, &mouse->x, &mouse->y);
+            mouse->last_x = x;
+            mouse->last_y = y;
         }
-    } else {
-        mouse->x = x;
-        mouse->y = y;
         mouse->has_position = true;
     }
 
-    mouse->xdelta += xrel;
-    mouse->ydelta += yrel;
-
     // Move the mouse cursor, if needed
     if (mouse->cursor_shown && !mouse->relative_mode &&
         mouse->MoveCursor && mouse->cur_cursor) {
@@ -841,6 +846,11 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL
 
     // Post the event, if desired
     if (SDL_EventEnabled(SDL_EVENT_MOUSE_MOTION)) {
+        if (!relative && window_is_relative) {
+            if (!mouse->relative_mode_warp_motion) return;
+            xrel = 0.0f;
+            yrel = 0.0f;
+        }
         SDL_Event event;
         event.type = SDL_EVENT_MOUSE_MOTION;
         event.common.timestamp = timestamp;
@@ -855,14 +865,6 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL
         event.motion.yrel = yrel;
         SDL_PushEvent(&event);
     }
-    if (relative) {
-        mouse->last_x = mouse->x;
-        mouse->last_y = mouse->y;
-    } else {
-        // Use unclamped values if we're getting events outside the window
-        mouse->last_x = x;
-        mouse->last_y = y;
-    }
 }
 
 static SDL_MouseInputSource *GetMouseInputSource(SDL_Mouse *mouse, SDL_MouseID mouseID, bool down, Uint8 button)
@@ -1178,13 +1180,13 @@ SDL_MouseButtonFlags SDL_GetRelativeMouseState(float *x, float *y)
     SDL_Mouse *mouse = SDL_GetMouse();
 
     if (x) {
-        *x = mouse->xdelta;
+        *x = mouse->x_accu;
     }
     if (y) {
-        *y = mouse->ydelta;
+        *y = mouse->y_accu;
     }
-    mouse->xdelta = 0.0f;
-    mouse->ydelta = 0.0f;
+    mouse->x_accu = 0.0f;
+    mouse->y_accu = 0.0f;
     return SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, true);
 }
 
diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h
index 993e2ae160bfa..9c324b4e74490 100644
--- a/src/events/SDL_mouse_c.h
+++ b/src/events/SDL_mouse_c.h
@@ -86,8 +86,8 @@ typedef struct
     SDL_Window *focus;
     float x;
     float y;
-    float xdelta;
-    float ydelta;
+    float x_accu;
+    float y_accu;
     float last_x, last_y; // the last reported x and y coordinates
     bool has_position;
     bool relative_mode;