Commit 74ec7cabdbbac7ad20335b894e80b15e58133241

Sam Lantinga 2018-06-18T13:13:56

Fixed race condition where Android touch events could get scaled by a render target's viewport

diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 1d15af3..b1bcc46 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -132,6 +132,16 @@ SDL_GetRenderDriverInfo(int index, SDL_RendererInfo * info)
 #endif
 }
 
+static void GetWindowViewportValues(SDL_Renderer *renderer, int *logical_w, int *logical_h, SDL_Rect *viewport, SDL_FPoint *scale)
+{
+    SDL_LockMutex(renderer->target_mutex);
+    *logical_w = renderer->target ? renderer->logical_w_backup : renderer->logical_w;
+    *logical_h = renderer->target ? renderer->logical_h_backup : renderer->logical_h;
+    *viewport = renderer->target ? renderer->viewport_backup : renderer->viewport;
+    *scale = renderer->target ? renderer->scale_backup : renderer->scale;
+    SDL_UnlockMutex(renderer->target_mutex);
+}
+
 static int SDLCALL
 SDL_RendererEventWatch(void *userdata, SDL_Event *event)
 {
@@ -197,35 +207,51 @@ 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 -= (int)(renderer->viewport.x * renderer->dpi_scale.x);
-            event->motion.y -= (int)(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 * renderer->dpi_scale.x)));
-            } else if (event->motion.xrel < 0) {
-                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 * renderer->dpi_scale.y)));
-            } else if (event->motion.yrel < 0) {
-                event->motion.yrel = SDL_min(-1, (int)(event->motion.yrel / (renderer->scale.y * renderer->dpi_scale.y)));
+        if (window == renderer->window) {
+            int logical_w, logical_h;
+            SDL_Rect viewport;
+            SDL_FPoint scale;
+            GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
+            if (logical_w) {
+                event->motion.x -= (int)(viewport.x * renderer->dpi_scale.x);
+                event->motion.y -= (int)(viewport.y * renderer->dpi_scale.y);
+                event->motion.x = (int)(event->motion.x / (scale.x * renderer->dpi_scale.x));
+                event->motion.y = (int)(event->motion.y / (scale.y * renderer->dpi_scale.y));
+                if (event->motion.xrel > 0) {
+                    event->motion.xrel = SDL_max(1, (int)(event->motion.xrel / (scale.x * renderer->dpi_scale.x)));
+                } else if (event->motion.xrel < 0) {
+                    event->motion.xrel = SDL_min(-1, (int)(event->motion.xrel / (scale.x * renderer->dpi_scale.x)));
+                }
+                if (event->motion.yrel > 0) {
+                    event->motion.yrel = SDL_max(1, (int)(event->motion.yrel / (scale.y * renderer->dpi_scale.y)));
+                } else if (event->motion.yrel < 0) {
+                    event->motion.yrel = SDL_min(-1, (int)(event->motion.yrel / (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 -= (int)(renderer->viewport.x * renderer->dpi_scale.x);
-            event->button.y -= (int)(renderer->viewport.y * renderer->dpi_scale.y);
-            event->button.x = (int)(event->button.x / (renderer->scale.x * renderer->dpi_scale.x));
-            event->button.y = (int)(event->button.y / (renderer->scale.y * renderer->dpi_scale.y));
+        if (window == renderer->window) {
+            int logical_w, logical_h;
+            SDL_Rect viewport;
+            SDL_FPoint scale;
+            GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
+            if (logical_w) {
+                event->button.x -= (int)(viewport.x * renderer->dpi_scale.x);
+                event->button.y -= (int)(viewport.y * renderer->dpi_scale.y);
+                event->button.x = (int)(event->button.x / (scale.x * renderer->dpi_scale.x));
+                event->button.y = (int)(event->button.y / (scale.y * renderer->dpi_scale.y));
+            }
         }
     } else if (event->type == SDL_FINGERDOWN ||
                event->type == SDL_FINGERUP ||
                event->type == SDL_FINGERMOTION) {
-        if (renderer->logical_w) {
+        int logical_w, logical_h;
+        SDL_Rect viewport;
+        SDL_FPoint scale;
+        GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
+        if (logical_w) {
             int w = 1;
             int h = 1;
             SDL_GetRendererOutputSize(renderer, &w, &h);
@@ -233,18 +259,18 @@ SDL_RendererEventWatch(void *userdata, SDL_Event *event)
             event->tfinger.x *= (w - 1);
             event->tfinger.y *= (h - 1);
 
-            event->tfinger.x -= (renderer->viewport.x * renderer->dpi_scale.x);
-            event->tfinger.y -= (renderer->viewport.y * renderer->dpi_scale.y);
-            event->tfinger.x = (event->tfinger.x / (renderer->scale.x * renderer->dpi_scale.x));
-            event->tfinger.y = (event->tfinger.y / (renderer->scale.y * renderer->dpi_scale.y));
+            event->tfinger.x -= (viewport.x * renderer->dpi_scale.x);
+            event->tfinger.y -= (viewport.y * renderer->dpi_scale.y);
+            event->tfinger.x = (event->tfinger.x / (scale.x * renderer->dpi_scale.x));
+            event->tfinger.y = (event->tfinger.y / (scale.y * renderer->dpi_scale.y));
 
-            if (renderer->logical_w > 1) {
-                event->tfinger.x = event->tfinger.x / (renderer->logical_w - 1);
+            if (logical_w > 1) {
+                event->tfinger.x = event->tfinger.x / (logical_w - 1);
             } else {
                 event->tfinger.x = 0.5f;
             }
-            if (renderer->logical_h > 1) {
-                event->tfinger.y = event->tfinger.y / (renderer->logical_h - 1);
+            if (logical_h > 1) {
+                event->tfinger.y = event->tfinger.y / (logical_h - 1);
             } else {
                 event->tfinger.y = 0.5f;
             }
@@ -345,6 +371,7 @@ SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
     if (renderer) {
         renderer->magic = &renderer_magic;
         renderer->window = window;
+        renderer->target_mutex = SDL_CreateMutex();
         renderer->scale.x = 1.0f;
         renderer->scale.y = 1.0f;
         renderer->dpi_scale.x = 1.0f;
@@ -392,6 +419,7 @@ SDL_CreateSoftwareRenderer(SDL_Surface * surface)
 
     if (renderer) {
         renderer->magic = &renderer_magic;
+        renderer->target_mutex = SDL_CreateMutex();
         renderer->scale.x = 1.0f;
         renderer->scale.y = 1.0f;
 
@@ -1203,6 +1231,8 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
         }
     }
 
+    SDL_LockMutex(renderer->target_mutex);
+
     if (texture && !renderer->target) {
         /* Make a backup of the viewport */
         renderer->viewport_backup = renderer->viewport;
@@ -1215,6 +1245,7 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
     renderer->target = texture;
 
     if (renderer->SetRenderTarget(renderer, texture) < 0) {
+        SDL_UnlockMutex(renderer->target_mutex);
         return -1;
     }
 
@@ -1237,6 +1268,9 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
         renderer->logical_w = renderer->logical_w_backup;
         renderer->logical_h = renderer->logical_h_backup;
     }
+
+    SDL_UnlockMutex(renderer->target_mutex);
+
     if (renderer->UpdateViewport(renderer) < 0) {
         return -1;
     }
@@ -2106,6 +2140,10 @@ SDL_DestroyRenderer(SDL_Renderer * renderer)
     /* It's no longer magical... */
     renderer->magic = NULL;
 
+    /* Free the target mutex */
+    SDL_DestroyMutex(renderer->target_mutex);
+    renderer->target_mutex = NULL;
+
     /* Free the renderer instance */
     renderer->DestroyRenderer(renderer);
 }
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index 037124d..940bebc 100644
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -25,6 +25,7 @@
 
 #include "SDL_render.h"
 #include "SDL_events.h"
+#include "SDL_mutex.h"
 #include "SDL_yuv_sw_c.h"
 
 /* The SDL 2D rendering system */
@@ -172,6 +173,7 @@ struct SDL_Renderer
     /* The list of textures */
     SDL_Texture *textures;
     SDL_Texture *target;
+    SDL_mutex *target_mutex;
 
     Uint8 r, g, b, a;                   /**< Color for drawing operations values */
     SDL_BlendMode blendMode;            /**< The drawing blend mode */