Commit 9dbe5a96868c8fb96346c20de3728ccf7ba36cec

Sam Lantinga 2017-08-02T13:38:46

Fixed bug 3311 - Broken touch positions with SDL_RenderSetLogicalSize & HIGHDPI on iOS Eric wing Hi, I think I found a bug when using SDL_WINDOW_ALLOW_HIGHDPI with SDL_RenderSetLogicalSize on iOS. I use SDL_RenderSetLogicalSize for all my stuff. I just tried turning on SDL_WINDOW_ALLOW_HIGHDPI on iOS and suddenly all my touch/mouse positions are really broken/far-off-the-mark. I actually don't have a real retina device (still) so I'm seeing this using the iOS simulator with a 6plus template. Attached is a simple test program that can reproduce the problem. It uses RenderSetLogicalSize and draws some moving happy faces (to show the boundaries/space of the LogicalSize and that it is working correctly for that part). When you click/touch, it will draw one more happy face where your button point is. If you comment out SDL_WINDOW_ALLOW_HIGHDPI, everything works as expected. But if you compile with it in, the mouse coordinates seem really far off the mark. (Face appears far up and to the left.) Alex Szpakowski on the mailing list suggests the problem is "I believe this is a bug in SDL_Render?s platform-agnostic mouse coordinate scaling code. It assumes the units of the mouse coordinates are always in pixels, which isn?t the case where high-DPI is involved (regardless of whether iOS is used) ? they?re actually in ?DPI independent? coordinates (which matches the window size, but not the renderer output size)." Additionally, if this is correct, the Mac under Retina is also probably affected too and "as well as any other platform SDL adds high-dpi support for in the future".

diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 1068627..e2fc16e 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -168,29 +168,29 @@ SDL_RendererEventWatch(void *userdata, SDL_Event *event)
     } else if (event->type == SDL_MOUSEMOTION) {
         SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID);
         if (renderer->logical_w && window == renderer->window) {
-            event->motion.x -= renderer->viewport.x;
-            event->motion.y -= renderer->viewport.y;
-            event->motion.x = (int)(event->motion.x / renderer->scale.x);
-            event->motion.y = (int)(event->motion.y / renderer->scale.y);
+            event->motion.x -= (renderer->viewport.x * renderer->dpi_scale.x);
+            event->motion.y -= (renderer->viewport.y * renderer->dpi_scale.y);
+            event->motion.x = (int)(event->motion.x / (renderer->scale.x * renderer->dpi_scale.x));
+            event->motion.y = (int)(event->motion.y / (renderer->scale.y * renderer->dpi_scale.y));
             if (event->motion.xrel > 0) {
-                event->motion.xrel = SDL_max(1, (int)(event->motion.xrel / renderer->scale.x));
+                event->motion.xrel = SDL_max(1, (int)(event->motion.xrel / (renderer->scale.x * renderer->dpi_scale.x)));
             } else if (event->motion.xrel < 0) {
-                event->motion.xrel = SDL_min(-1, (int)(event->motion.xrel / renderer->scale.x));
+                event->motion.xrel = SDL_min(-1, (int)(event->motion.xrel / (renderer->scale.x * renderer->dpi_scale.x)));
             }
             if (event->motion.yrel > 0) {
-                event->motion.yrel = SDL_max(1, (int)(event->motion.yrel / renderer->scale.y));
+                event->motion.yrel = SDL_max(1, (int)(event->motion.yrel / (renderer->scale.y * renderer->dpi_scale.y)));
             } else if (event->motion.yrel < 0) {
-                event->motion.yrel = SDL_min(-1, (int)(event->motion.yrel / renderer->scale.y));
+                event->motion.yrel = SDL_min(-1, (int)(event->motion.yrel / (renderer->scale.y * renderer->dpi_scale.y)));
             }
         }
     } else if (event->type == SDL_MOUSEBUTTONDOWN ||
                event->type == SDL_MOUSEBUTTONUP) {
         SDL_Window *window = SDL_GetWindowFromID(event->button.windowID);
         if (renderer->logical_w && window == renderer->window) {
-            event->button.x -= renderer->viewport.x;
-            event->button.y -= renderer->viewport.y;
-            event->button.x = (int)(event->button.x / renderer->scale.x);
-            event->button.y = (int)(event->button.y / renderer->scale.y);
+            event->motion.x -= (renderer->viewport.x * renderer->dpi_scale.x);
+            event->motion.y -= (renderer->viewport.y * renderer->dpi_scale.y);
+            event->motion.x = (int)(event->motion.x / (renderer->scale.x * renderer->dpi_scale.x));
+            event->motion.y = (int)(event->motion.y / (renderer->scale.y * renderer->dpi_scale.y));
         }
     }
     return 0;
@@ -289,6 +289,18 @@ SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
         renderer->window = window;
         renderer->scale.x = 1.0f;
         renderer->scale.y = 1.0f;
+        renderer->dpi_scale.x = 1.0f;
+        renderer->dpi_scale.y = 1.0f;
+
+        if (window && renderer->GetOutputSize) {
+            int window_w, window_h;
+            int output_w, output_h;
+            if (renderer->GetOutputSize(renderer, &output_w, &output_h) == 0) {
+                SDL_GetWindowSize(renderer->window, &window_w, &window_h);
+                renderer->dpi_scale.x = (float)window_w / output_w;
+                renderer->dpi_scale.y = (float)window_h / output_h;
+            }
+        }
 
         if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN|SDL_WINDOW_MINIMIZED)) {
             renderer->hidden = SDL_TRUE;
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index 4c1ac1b..24ea98a 100644
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -154,6 +154,9 @@ struct SDL_Renderer
     SDL_FPoint scale;
     SDL_FPoint scale_backup;
 
+    /* The pixel to point coordinate scale */
+    SDL_FPoint dpi_scale;
+
     /* The list of textures */
     SDL_Texture *textures;
     SDL_Texture *target;