sdl12-compat: Track fake window mouse position in relative mode.

From bcc2df0f41fbebda3cf95182949f338076e64938 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Fri, 26 Feb 2021 12:43:39 -0500
Subject: [PATCH] Track fake window mouse position in relative mode.

This makes Wolf4SDL's input usable in fullscreen, which wants to
grab/hide the mouse (what SDL2 calls "relative mode"), but it also
uses SDL_GetMouseState() and SDL_WarpMouse() to keep centering the
mouse and querying for mouse position relative to that center point.

Fixes #16.
---
 src/SDL12_compat.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 43 insertions(+), 3 deletions(-)

diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c
index 2a20c70..2070c70 100644
--- a/src/SDL12_compat.c
+++ b/src/SDL12_compat.c
@@ -757,6 +757,8 @@ static Uint8 EventStates[SDL12_NUMEVENTS];
 static int SwapInterval = 0;
 static JoystickOpenedItem JoystickOpenList[16];
 static Uint8 KeyState[SDLK12_LAST];
+static SDL_bool MouseInputIsRelative = SDL_FALSE;
+static SDL_Point MousePositionWhenRelative = { 0, 0 };
 
 // !!! FIXME: need a mutex for the event queue.
 #define SDL12_MAXEVENTS 128
@@ -1744,7 +1746,12 @@ static Uint8 MouseButtonState20to12(const Uint32 state20)
 DECLSPEC Uint8 SDLCALL
 SDL_GetMouseState(int *x, int *y)
 {
-    return MouseButtonState20to12(SDL20_GetMouseState(x, y));
+    const Uint8 buttons = MouseButtonState20to12(SDL20_GetMouseState(x, y));
+    if (MouseInputIsRelative) {
+        if (x) { *x = MousePositionWhenRelative.x; }
+        if (y) { *y = MousePositionWhenRelative.y; }
+    }
+    return buttons;
 }
 
 DECLSPEC Uint8 SDLCALL
@@ -2211,6 +2218,20 @@ EventFilter20to12(void *data, SDL_Event *event20)
             event12.motion.y = (Uint16) event20->motion.y;
             event12.motion.xrel = (Sint16) event20->motion.xrel;
             event12.motion.yrel = (Sint16) event20->motion.yrel;
+            if (MouseInputIsRelative) {
+                /* in relative mode, clamp fake absolute position to the window dimensions. */
+                #define ADJUST_RELATIVE(axis, rel, dim) { \
+                    MousePositionWhenRelative.axis += event20->motion.rel; \
+                    if (MousePositionWhenRelative.axis <= 0) { \
+                        MousePositionWhenRelative.axis = 0; \
+                    } else if (MousePositionWhenRelative.axis >= VideoSurface12->dim) { \
+                        MousePositionWhenRelative.axis = (VideoSurface12->dim - 1); \
+                    } \
+                }
+                ADJUST_RELATIVE(x, xrel, w);
+                ADJUST_RELATIVE(y, yrel, h);
+                #undef ADJUST_RELATIVE
+            }
             break;
 
         case SDL_MOUSEBUTTONDOWN:
@@ -2891,6 +2912,11 @@ EndVidModeCreate(void)
         SDL20_FreeSurface(VideoConvertSurface20);
         VideoConvertSurface20 = NULL;
     }
+
+    MouseInputIsRelative = SDL_FALSE;
+    MousePositionWhenRelative.x = 0;
+    MousePositionWhenRelative.y = 0;
+
     return NULL;
 }
 
@@ -3638,7 +3664,16 @@ UpdateRelativeMouseMode(void)
 {
     // in SDL 1.2, hiding+grabbing the cursor was like SDL2's relative mouse mode.
     if (VideoWindow20) {
-        SDL20_SetRelativeMouseMode((VideoWindowGrabbed && VideoCursorHidden) ? SDL_TRUE : SDL_FALSE);
+        const SDL_bool enable = (VideoWindowGrabbed && VideoCursorHidden) ? SDL_TRUE : SDL_FALSE;
+        if (MouseInputIsRelative != enable) {
+            MouseInputIsRelative = enable;
+            if (MouseInputIsRelative) {
+                // reset position, we'll have to track it ourselves in SDL_MOUSEMOTION events, since 1.2
+                //  would give you window coordinates, even in relative mode.
+                SDL_GetMouseState(&MousePositionWhenRelative.x, &MousePositionWhenRelative.y);
+            }
+            SDL20_SetRelativeMouseMode(MouseInputIsRelative);
+        }
     }
 }
 
@@ -3676,7 +3711,12 @@ SDL_WM_GrabInput(SDL12_GrabMode mode)
 DECLSPEC void SDLCALL
 SDL_WarpMouse(Uint16 x, Uint16 y)
 {
-    SDL20_WarpMouseInWindow(VideoWindow20, x, y);
+    if (MouseInputIsRelative) {  /* we have to track this ourselves, in case app calls SDL_GetMouseState(). */
+        MousePositionWhenRelative.x = (int) x;
+        MousePositionWhenRelative.y = (int) y;
+    } else {
+        SDL20_WarpMouseInWindow(VideoWindow20, x, y);
+    }
 }
 
 DECLSPEC Uint8 SDLCALL