SDL: testwm2: Fix video modes menu hit detection when highdpi or logical size used (#4936)

From 0d98793693fa2e428a578efbf3e761aea2d0b4dd Mon Sep 17 00:00:00 2001
From: Eric Wasylishen <[EMAIL REDACTED]>
Date: Tue, 9 Nov 2021 22:03:42 -0700
Subject: [PATCH] testwm2: Fix video modes menu hit detection when highdpi or
 logical size used (#4936)

* SDLTest_CommonDrawWindowInfo: log SDL_RenderGetScale, SDL_RenderGetLogicalSize

* testwm2: fix video modes menu hit detection in High DPI cases

- also when logical size is specified, e.g.
  `--logical 640x480 --resizable --allow-highdpi`

* add function to determine logical coordinates of renderer point when given window point

* change since to the targeted milestone

* fix typo

* rename for consistency

* Change logical coordinate type to float, since we can render with floating point precision.

* add function to convert logical to window coordinates

* testwm2: use new SDL_RenderWindowToLogical

* SDL_render.c: alternate SDL_RenderWindowToLogical/SDL_RenderLogicalToWindow

Co-authored-by: John Blat <johnblat64@protonmail.com>
Co-authored-by: John Blat <47202511+johnblat64@users.noreply.github.com>
---
 include/SDL_render.h              | 42 +++++++++++++++++++++++++++++++
 src/dynapi/SDL_dynapi_overrides.h |  2 ++
 src/dynapi/SDL_dynapi_procs.h     |  2 ++
 src/render/SDL_render.c           | 36 ++++++++++++++++++++++++++
 src/test/SDL_test_common.c        | 12 +++++++++
 test/testwm2.c                    | 11 ++++++--
 6 files changed, 103 insertions(+), 2 deletions(-)

diff --git a/include/SDL_render.h b/include/SDL_render.h
index a74fc129e3..97ccac5c6a 100644
--- a/include/SDL_render.h
+++ b/include/SDL_render.h
@@ -979,6 +979,48 @@ extern DECLSPEC int SDLCALL SDL_RenderSetScale(SDL_Renderer * renderer,
 extern DECLSPEC void SDLCALL SDL_RenderGetScale(SDL_Renderer * renderer,
                                                float *scaleX, float *scaleY);
 
+/**
+ * Get logical coordinates of point in renderer when given real coordinates of point in window.
+ * Logical coordinates will differ from real coordinates when render is scaled and logical renderer size set
+ * 
+ * \param renderer the renderer from which the logical coordinates should be calcualted
+ * \param windowX the real X coordinate in the window
+ * \param windowY the real Y coordinate in the window
+ * \param logicalX the pointer filled with the logical x coordinate
+ * \param logicalY the pointer filled with the logical y coordinate
+ *  
+ * \since This function is available since SDL 2.0.20.
+ * 
+ * \sa SDL_RenderGetScale
+ * \sa SDL_RenderSetScale
+ * \sa SDL_RenderGetLogicalSize
+ * \sa SDL_RenderSetLogicalSize
+ */
+extern DECLSPEC void SDLCALL SDL_RenderWindowToLogical(SDL_Renderer * renderer, 
+                                                            int windowX, int windowY, 
+                                                            float *logicalX, float *logicalY);
+                                                  
+                                                  /**
+ * Get real coordinates of point in window when given logical coordinates of point in renderer.
+ * Logical coordinates will differ from real coordinates when render is scaled and logical renderer size set
+ * 
+ * \param renderer the renderer from which the window coordinates should be calculated
+ * \param logicalX the logical x coordinate
+ * \param logicalY the logical y coordinate
+ * \param windowX the pointer filled with the real X coordinate in the window
+ * \param windowY the pointer filled with the real Y coordinate in the window
+ 
+ *  
+ * \since This function is available since SDL 2.0.20.
+ * 
+ * \sa SDL_RenderGetScale
+ * \sa SDL_RenderSetScale
+ * \sa SDL_RenderGetLogicalSize
+ * \sa SDL_RenderSetLogicalSize
+ */
+extern DECLSPEC void SDLCALL SDL_RenderLogicalToWindow(SDL_Renderer * renderer, 
+                                                            float logicalX, float logicalY,
+                                                            int *windowX, int *windowY);
 /**
  * Set the color used for drawing operations (Rect, Line and Clear).
  *
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 20d9e702ed..9077bb00c5 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -844,3 +844,5 @@
 #define SDL_hid_get_indexed_string SDL_hid_get_indexed_string_REAL
 #define SDL_SetWindowMouseRect SDL_SetWindowMouseRect_REAL
 #define SDL_GetWindowMouseRect SDL_GetWindowMouseRect_REAL
+#define SDL_RenderWindowToLogical SDL_RenderWindowToLogical_REAL
+#define SDL_RenderLogicalToWindow SDL_RenderLogicalToWindow_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index d2c1d13d27..93be9fbb20 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -913,3 +913,5 @@ SDL_DYNAPI_PROC(int,SDL_hid_get_serial_number_string,(SDL_hid_device *a, wchar_t
 SDL_DYNAPI_PROC(int,SDL_hid_get_indexed_string,(SDL_hid_device *a, int b, wchar_t *c, size_t d),(a,b,c,d),return)
 SDL_DYNAPI_PROC(int,SDL_SetWindowMouseRect,(SDL_Window *a, const SDL_Rect *b),(a,b),return)
 SDL_DYNAPI_PROC(const SDL_Rect*,SDL_GetWindowMouseRect,(SDL_Window *a),(a),return)
+SDL_DYNAPI_PROC(void,SDL_RenderWindowToLogical,(SDL_Renderer *a, int b, int c, float *d, float *e),(a,b,c,d,e),)
+SDL_DYNAPI_PROC(void,SDL_RenderLogicalToWindow,(SDL_Renderer *a, float b, float c, int *d, int *e),(a,b,c,d,e),)
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 1a0add99fa..5bca652fd5 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -2491,6 +2491,42 @@ SDL_RenderGetScale(SDL_Renderer * renderer, float *scaleX, float *scaleY)
     }
 }
 
+void
+SDL_RenderWindowToLogical(SDL_Renderer * renderer, int windowX, int windowY, float *logicalX, float *logicalY)
+{
+    float window_physical_x, window_physical_y;
+
+    CHECK_RENDERER_MAGIC(renderer, );
+
+    window_physical_x = ((float) windowX) / renderer->dpi_scale.x;
+    window_physical_y = ((float) windowY) / renderer->dpi_scale.y;
+
+    if (logicalX) {
+        *logicalX = (window_physical_x - renderer->viewport.x) / renderer->scale.x;
+    }
+    if (logicalY) {
+        *logicalY = (window_physical_y - renderer->viewport.y) / renderer->scale.y;
+    }
+}
+
+void
+SDL_RenderLogicalToWindow(SDL_Renderer * renderer, float logicalX, float logicalY, int *windowX, int *windowY)
+{
+    float window_physical_x, window_physical_y;
+
+    CHECK_RENDERER_MAGIC(renderer, );
+
+    window_physical_x = (logicalX * renderer->scale.x) + renderer->viewport.x;
+    window_physical_y = (logicalY * renderer->scale.y) + renderer->viewport.y;
+
+    if (windowX) {
+        *windowX = (int)(window_physical_x * renderer->dpi_scale.x);
+    }
+    if (windowY) {
+        *windowY = (int)(window_physical_y * renderer->dpi_scale.y);
+    }
+}
+
 int
 SDL_SetRenderDrawColor(SDL_Renderer * renderer,
                        Uint8 r, Uint8 g, Uint8 b, Uint8 a)
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index 6dcbee0221..3b6effbb98 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -2234,6 +2234,7 @@ SDLTest_CommonDrawWindowInfo(SDL_Renderer * renderer, SDL_Window * window, int *
     SDL_Rect rect;
     SDL_DisplayMode mode;
     float ddpi, hdpi, vdpi;
+    float scaleX, scaleY;
     Uint32 flags;
     const int windowDisplayIndex = SDL_GetWindowDisplayIndex(window);
     SDL_RendererInfo info;
@@ -2276,6 +2277,17 @@ SDLTest_CommonDrawWindowInfo(SDL_Renderer * renderer, SDL_Window * window, int *
     SDLTest_DrawString(renderer, 0, textY, text);
     textY += lineHeight;
 
+    SDL_RenderGetScale(renderer, &scaleX, &scaleY);
+    SDL_snprintf(text, sizeof(text), "SDL_RenderGetScale: %f,%f",
+                 scaleX, scaleY);
+    SDLTest_DrawString(renderer, 0, textY, text);
+    textY += lineHeight;
+
+    SDL_RenderGetLogicalSize(renderer, &w, &h);
+    SDL_snprintf(text, sizeof(text), "SDL_RenderGetLogicalSize: %dx%d", w, h);
+    SDLTest_DrawString(renderer, 0, textY, text);
+    textY += lineHeight;
+
     /* Window */
 
     SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
diff --git a/test/testwm2.c b/test/testwm2.c
index 03530e5862..86b89a95a6 100644
--- a/test/testwm2.c
+++ b/test/testwm2.c
@@ -52,7 +52,7 @@ quit(int rc)
 
 /* Draws the modes menu, and stores the mode index under the mouse in highlighted_mode */
 static void
-draw_modes_menu(SDL_Window* window, SDL_Renderer* renderer, SDL_Rect viewport)
+draw_modes_menu(SDL_Window *window, SDL_Renderer *renderer, SDL_Rect viewport)
 {
     SDL_DisplayMode mode;
     char text[1024];
@@ -68,7 +68,14 @@ draw_modes_menu(SDL_Window* window, SDL_Renderer* renderer, SDL_Rect viewport)
 
     /* Get mouse position */
     if (SDL_GetMouseFocus() == window) {
-        SDL_GetMouseState(&mouse_pos.x, &mouse_pos.y);
+        int window_x, window_y;
+        float logical_x, logical_y;
+
+        SDL_GetMouseState(&window_x, &window_y);
+        SDL_RenderWindowToLogical(renderer, window_x, window_y, &logical_x, &logical_y);
+
+        mouse_pos.x = (int)logical_x;
+        mouse_pos.y = (int)logical_y;
     }
 
     x = 0;