SDL: Removed mouse warping for local mice and improved warp handling for mouse over RDP

From a1fabca162091b50d6f7dd71879d028319e09d80 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 14 Oct 2021 16:52:21 -0700
Subject: [PATCH] Removed mouse warping for local mice and improved warp
 handling for mouse over RDP

---
 src/video/windows/SDL_windowsevents.c | 155 +++++---------------------
 src/video/windows/SDL_windowswindow.c |  33 +++++-
 2 files changed, 55 insertions(+), 133 deletions(-)

diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 60f87d764f..f6b80802c8 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -508,79 +508,6 @@ static SDL_MOUSE_EVENT_SOURCE GetMouseMessageSource()
     return SDL_MOUSE_EVENT_SOURCE_MOUSE;
 }
 
-static void
-GetDisplayBoundsForPoint(int x, int y, RECT *bounds)
-{
-    SDL_VideoDevice *_this = SDL_GetVideoDevice();
-    int i, dist;
-    int closest = -1;
-    int closest_dist = 0x7FFFFFFF;
-    SDL_Point point;
-    SDL_Point delta;
-    SDL_Rect rect;
-
-    point.x = x;
-    point.y = y;
-    for (i = 0; i < _this->num_displays; ++i) {
-        SDL_GetDisplayBounds(i, &rect);
-        if (SDL_EnclosePoints(&point, 1, &rect, NULL)) {
-            WIN_RectToRECT(&rect, bounds);
-            return;
-        }
-
-        delta.x = point.x - (rect.x + rect.w / 2);
-        delta.y = point.y - (rect.y + rect.h / 2);
-        dist = (delta.x*delta.x + delta.y*delta.y);
-        if (dist < closest_dist) {
-            closest = i;
-            closest_dist = dist;
-            WIN_RectToRECT(&rect, bounds);
-        }
-    }
-    if (closest < 0) {
-        bounds->left = 0;
-        bounds->right = GetSystemMetrics(SM_CXSCREEN) - 1;
-        bounds->top = 0;
-        bounds->bottom = GetSystemMetrics(SM_CYSCREEN) - 1;
-    }
-}
-
-static void
-WarpWithinBoundsRect(int x, int y, RECT *bounds)
-{
-    if (x < bounds->left || x > bounds->right || y < bounds->top || y > bounds->bottom) {
-        const int MIN_BOUNDS_SIZE = 32;
-        int boundsWidth = (bounds->right - bounds->left) + 1;
-        int boundsHeight = (bounds->bottom - bounds->top) + 1;
-        if (boundsWidth >= MIN_BOUNDS_SIZE && boundsHeight >= MIN_BOUNDS_SIZE) {
-            /* Warp back to the opposite side, assuming more motion in the current direction */
-            int targetLeft = bounds->right - (boundsWidth * 3) / 4;
-            int targetRight = bounds->left + (boundsWidth * 3) / 4;
-            int targetTop = bounds->bottom - (boundsHeight * 3) / 4;
-            int targetBottom = bounds->top + (boundsHeight * 3) / 4;
-            int warpX;
-            int warpY;
-
-            if (x < bounds->left) {
-                warpX = targetRight;
-            } else if (x > bounds->right) {
-                warpX = targetLeft;
-            } else {
-                warpX = SDL_clamp(x, targetLeft, targetRight);
-            }
-
-            if (y < bounds->top) {
-                warpY = targetBottom;
-            } else if (y > bounds->bottom) {
-                warpY = targetTop;
-            } else {
-                warpY = SDL_clamp(y, targetTop, targetBottom);
-            }
-            WIN_SetCursorPos(warpX, warpY);
-        }
-    }
-}
-
 static SDL_WindowData *
 WIN_GetWindowDataFromHWND(HWND hwnd)
 {
@@ -818,35 +745,8 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
                     RAWMOUSE* rawmouse = &inp.data.mouse;
 
                     if ((rawmouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
-                        POINT pt;
-
                         SDL_SendMouseMotion(data->window, mouseID, 1, (int)rawmouse->lLastX, (int)rawmouse->lLastY);
 
-                        /* Make sure that the mouse doesn't hover over notifications and so forth */
-                        if (GetCursorPos(&pt)) {
-                            int x = pt.x;
-                            int y = pt.y;
-                            RECT screenRect;
-                            RECT hwndRect;
-                            RECT boundsRect;
-
-                            /* Calculate screen rect */
-                            GetDisplayBoundsForPoint(x, y, &screenRect);
-
-                            /* Calculate client rect */
-                            GetClientRect(hwnd, &hwndRect);
-                            ClientToScreen(hwnd, (LPPOINT) & hwndRect);
-                            ClientToScreen(hwnd, (LPPOINT) & hwndRect + 1);
-
-                            /* Calculate bounds rect */
-                            IntersectRect(&boundsRect, &screenRect, &hwndRect);
-                            InflateRect(&boundsRect, -SAFE_AREA_X, -SAFE_AREA_Y);
-
-                            if (!data->in_title_click && !data->focus_click_pending) {
-                                WarpWithinBoundsRect(x, y, &boundsRect);
-                            }
-                        }
-
                     } else if (rawmouse->lLastX || rawmouse->lLastY) {
                         /* This is absolute motion, either using a tablet or mouse over RDP
 
@@ -876,33 +776,34 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
                         relY = (int)(y - data->last_raw_mouse_position.y);
 
                         if (remote_desktop) {
-                            RECT screenRect;
-                            RECT hwndRect;
-                            RECT boundsRect;
-                            int boundsWidth, boundsHeight;
-
-                            /* Calculate screen rect */
-                            GetDisplayBoundsForPoint(x, y, &screenRect);
-
-                            /* Calculate client rect */
-                            GetClientRect(hwnd, &hwndRect);
-                            ClientToScreen(hwnd, (LPPOINT) & hwndRect);
-                            ClientToScreen(hwnd, (LPPOINT) & hwndRect + 1);
-
-                            /* Calculate bounds rect */
-                            IntersectRect(&boundsRect, &screenRect, &hwndRect);
-                            InflateRect(&boundsRect, -SAFE_AREA_X, -SAFE_AREA_Y);
-                            boundsWidth = (boundsRect.right - boundsRect.left) + 1;
-                            boundsHeight = (boundsRect.bottom - boundsRect.top) + 1;
-
-                            if ((boundsWidth > 0 && SDL_abs(relX) > (boundsWidth / 2)) ||
-                                (boundsHeight > 0 && SDL_abs(relY) > (boundsHeight / 2))) {
-                                /* Expected motion for warping below, ignore this */
-                            } else {
-                                SDL_SendMouseMotion(data->window, mouseID, 1, relX, relY);
-
-                                if (!data->in_title_click && !data->focus_click_pending) {
-                                    WarpWithinBoundsRect(x, y, &boundsRect);
+                            if (!data->in_title_click && !data->focus_click_pending) {
+                                static int wobble;
+                                float floatX = (float)x / w;
+                                float floatY = (float)y / h;
+
+                                /* See if the mouse is at the edge of the screen, or in the RDP title bar area */
+                                if (floatX <= 0.01f || floatX >= 0.99f || floatY <= 0.01f || floatY >= 0.99f || y < 32) {
+                                    /* Wobble the cursor position so it's not ignored if the last warp didn't have any effect */
+                                    RECT rect = data->cursor_clipped_rect;
+                                    int warpX = rect.left + ((rect.right - rect.left) / 2) + wobble;
+                                    int warpY = rect.top + ((rect.bottom - rect.top) / 2);
+
+                                    WIN_SetCursorPos(warpX, warpY);
+
+                                    ++wobble;
+                                    if (wobble > 1) {
+                                        wobble = -1;
+                                    }
+                                } else {
+                                    /* Send relative motion if we didn't warp last frame (had good position data)
+                                       We also sometimes get large deltas due to coalesced mouse motion and warping,
+                                       so ignore those.
+                                     */
+                                    const int MAX_RELATIVE_MOTION = (h / 6);
+                                    if (SDL_abs(relX) < MAX_RELATIVE_MOTION &&
+                                        SDL_abs(relY) < MAX_RELATIVE_MOTION) {
+                                        SDL_SendMouseMotion(data->window, mouseID, 1, relX, relY);
+                                    }
                                 }
                             }
                         } else {
diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c
index 0cfc006a22..b6e9dc3d93 100644
--- a/src/video/windows/SDL_windowswindow.c
+++ b/src/video/windows/SDL_windowswindow.c
@@ -974,12 +974,33 @@ WIN_UpdateClipCursor(SDL_Window *window)
 
     if ((mouse->relative_mode || (window->flags & SDL_WINDOW_MOUSE_GRABBED)) &&
         (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
-        if (GetClientRect(data->hwnd, &rect) && !IsRectEmpty(&rect)) {
-            ClientToScreen(data->hwnd, (LPPOINT) & rect);
-            ClientToScreen(data->hwnd, (LPPOINT) & rect + 1);
-            if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) {
-                if (ClipCursor(&rect)) {
-                    data->cursor_clipped_rect = rect;
+        if (mouse->relative_mode && !mouse->relative_mode_warp) {
+            if (GetWindowRect(data->hwnd, &rect)) {
+                LONG cx, cy;
+
+                cx = (rect.left + rect.right) / 2;
+                cy = (rect.top + rect.bottom) / 2;
+
+                /* Make an absurdly small clip rect */
+                rect.left = cx - 1;
+                rect.right = cx + 1;
+                rect.top = cy - 1;
+                rect.bottom = cy + 1;
+
+                if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) {
+                    if (ClipCursor(&rect)) {
+                        data->cursor_clipped_rect = rect;
+                    }
+                }
+            }
+        } else {
+            if (GetClientRect(data->hwnd, &rect) && !IsRectEmpty(&rect)) {
+                ClientToScreen(data->hwnd, (LPPOINT) & rect);
+                ClientToScreen(data->hwnd, (LPPOINT) & rect + 1);
+                if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) {
+                    if (ClipCursor(&rect)) {
+                        data->cursor_clipped_rect = rect;
+                    }
                 }
             }
         }