SDL: Allocate displays as an array of pointers instead of an array of objects

From 68a4bb01e0d549bfa1f2418dd9463f62980eb210 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 1 Aug 2023 18:48:35 -0700
Subject: [PATCH] Allocate displays as an array of pointers instead of an array
 of objects

This fixes current_mode from pointing at the wrong data when displays are moved around
---
 src/video/SDL_sysvideo.h              |   2 +-
 src/video/SDL_video.c                 | 109 ++++++++++++--------------
 src/video/android/SDL_androidvideo.c  |   2 +-
 src/video/cocoa/SDL_cocoaevents.m     |   2 +-
 src/video/cocoa/SDL_cocoamodes.m      |   2 +-
 src/video/cocoa/SDL_cocoawindow.m     |   4 +-
 src/video/n3ds/SDL_n3dsvideo.c        |   1 -
 src/video/uikit/SDL_uikitmodes.m      |   2 +-
 src/video/wayland/SDL_waylandvideo.c  |   2 +-
 src/video/wayland/SDL_waylandwindow.c |   2 +-
 src/video/windows/SDL_windowsmodes.c  |  18 ++---
 src/video/x11/SDL_x11messagebox.c     |   4 +-
 src/video/x11/SDL_x11modes.c          |   2 +-
 src/video/x11/SDL_x11video.c          |   2 +-
 14 files changed, 74 insertions(+), 80 deletions(-)

diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index d4eedd2a6a8a..034d802b0031 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -363,7 +363,7 @@ struct SDL_VideoDevice
     SDL_Window *wakeup_window;
     SDL_Mutex *wakeup_lock; /* Initialized only if WaitEventTimeout/SendWakeupEvent are supported */
     int num_displays;
-    SDL_VideoDisplay *displays;
+    SDL_VideoDisplay **displays;
     SDL_Window *windows;
     SDL_Window *grabbed_window;
     Uint8 window_magic;
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 7cfe08da00cb..f3637f1ce04a 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -642,57 +642,51 @@ SDL_DisplayID SDL_AddBasicVideoDisplay(const SDL_DisplayMode *desktop_mode)
 
 SDL_DisplayID SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send_event)
 {
-    SDL_VideoDisplay *displays, *new_display;
-    SDL_DisplayID id = 0;
-
-    displays = (SDL_VideoDisplay *)SDL_malloc((_this->num_displays + 1) * sizeof(*displays));
-    if (displays) {
-        int i;
-
-        if (_this->displays) {
-            /* The display list may contain self-referential pointers to the desktop mode. */
-            SDL_memcpy(displays, _this->displays, _this->num_displays * sizeof(*displays));
-            for (i = 0; i < _this->num_displays; ++i) {
-                if (displays[i].current_mode == &_this->displays[i].desktop_mode) {
-                    displays[i].current_mode = &displays[i].desktop_mode;
-                }
-            }
+    SDL_VideoDisplay **displays, *new_display;
+    SDL_DisplayID id;
+    int i;
 
-            SDL_free(_this->displays);
-        }
+    new_display = (SDL_VideoDisplay *)SDL_malloc(sizeof(*new_display));
+    if (!new_display) {
+        SDL_OutOfMemory();
+        return 0;
+    }
 
-        _this->displays = displays;
-        id = _this->next_object_id++;
-        new_display = &displays[_this->num_displays++];
+    displays = (SDL_VideoDisplay **)SDL_realloc(_this->displays, (_this->num_displays + 1) * sizeof(*displays));
+    if (!displays) {
+        SDL_OutOfMemory();
+        SDL_free(new_display);
+        return 0;
+    }
+    _this->displays = displays;
+    _this->displays[_this->num_displays++] = new_display;
 
-        SDL_memcpy(new_display, display, sizeof(*new_display));
-        new_display->id = id;
-        new_display->device = _this;
-        if (display->name) {
-            new_display->name = SDL_strdup(display->name);
-        } else {
-            char name[32];
+    id = _this->next_object_id++;
+    SDL_memcpy(new_display, display, sizeof(*new_display));
+    new_display->id = id;
+    new_display->device = _this;
+    if (display->name) {
+        new_display->name = SDL_strdup(display->name);
+    } else {
+        char name[32];
 
-            SDL_itoa(id, name, 10);
-            new_display->name = SDL_strdup(name);
-        }
-        if (new_display->content_scale == 0.0f) {
-            new_display->content_scale = 1.0f;
-        }
+        SDL_itoa(id, name, 10);
+        new_display->name = SDL_strdup(name);
+    }
+    if (new_display->content_scale == 0.0f) {
+        new_display->content_scale = 1.0f;
+    }
 
-        new_display->desktop_mode.displayID = id;
-        new_display->current_mode = &new_display->desktop_mode;
-        SDL_FinalizeDisplayMode(&new_display->desktop_mode);
+    new_display->desktop_mode.displayID = id;
+    new_display->current_mode = &new_display->desktop_mode;
+    SDL_FinalizeDisplayMode(&new_display->desktop_mode);
 
-        for (i = 0; i < new_display->num_fullscreen_modes; ++i) {
-            new_display->fullscreen_modes[i].displayID = id;
-        }
+    for (i = 0; i < new_display->num_fullscreen_modes; ++i) {
+        new_display->fullscreen_modes[i].displayID = id;
+    }
 
-        if (send_event) {
-            SDL_SendDisplayEvent(new_display, SDL_EVENT_DISPLAY_CONNECTED, 0);
-        }
-    } else {
-        SDL_OutOfMemory();
+    if (send_event) {
+        SDL_SendDisplayEvent(new_display, SDL_EVENT_DISPLAY_CONNECTED, 0);
     }
     return id;
 }
@@ -715,7 +709,7 @@ void SDL_DelVideoDisplay(SDL_DisplayID displayID, SDL_bool send_event)
         return;
     }
 
-    display = &_this->displays[display_index];
+    display = _this->displays[display_index];
 
     if (send_event) {
         SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_DISCONNECTED, 0);
@@ -727,6 +721,7 @@ void SDL_DelVideoDisplay(SDL_DisplayID displayID, SDL_bool send_event)
     display->desktop_mode.driverdata = NULL;
     SDL_free(display->driverdata);
     display->driverdata = NULL;
+    SDL_free(display);
 
     if (display_index < (_this->num_displays - 1)) {
         SDL_memmove(&_this->displays[display_index], &_this->displays[display_index + 1], (_this->num_displays - display_index - 1) * sizeof(_this->displays[display_index]));
@@ -755,7 +750,7 @@ SDL_DisplayID *SDL_GetDisplays(int *count)
         }
 
         for (i = 0; i < _this->num_displays; ++i) {
-            displays[i] = _this->displays[i].id;
+            displays[i] = _this->displays[i]->id;
         }
         displays[i] = 0;
     } else {
@@ -776,7 +771,7 @@ SDL_VideoDisplay *SDL_GetVideoDisplay(SDL_DisplayID displayID)
     if (display_index < 0) {
         return NULL;
     }
-    return &_this->displays[display_index];
+    return _this->displays[display_index];
 }
 
 SDL_VideoDisplay *SDL_GetVideoDisplayForWindow(SDL_Window *window)
@@ -790,7 +785,7 @@ SDL_DisplayID SDL_GetPrimaryDisplay(void)
         SDL_UninitializedVideo();
         return 0;
     }
-    return _this->displays[0].id;
+    return _this->displays[0]->id;
 }
 
 int SDL_GetDisplayIndex(SDL_DisplayID displayID)
@@ -802,7 +797,7 @@ int SDL_GetDisplayIndex(SDL_DisplayID displayID)
     }
 
     for (display_index = 0; display_index < _this->num_displays; ++display_index) {
-        if (displayID == _this->displays[display_index].id) {
+        if (displayID == _this->displays[display_index]->id) {
             return display_index;
         }
     }
@@ -853,7 +848,7 @@ int SDL_GetDisplayBounds(SDL_DisplayID displayID, SDL_Rect *rect)
         rect->x = 0;
         rect->y = 0;
     } else {
-        SDL_GetDisplayBounds(_this->displays[SDL_GetDisplayIndex(displayID) - 1].id, rect);
+        SDL_GetDisplayBounds(_this->displays[SDL_GetDisplayIndex(displayID) - 1]->id, rect);
         rect->x += rect->w;
     }
     rect->w = display->current_mode->w;
@@ -1256,7 +1251,7 @@ static SDL_DisplayID GetDisplayForRect(int x, int y, int w, int h)
 
     if (_this) {
         for (i = 0; i < _this->num_displays; ++i) {
-            SDL_VideoDisplay *display = &_this->displays[i];
+            SDL_VideoDisplay *display = _this->displays[i];
             SDL_Rect display_rect;
             SDL_GetDisplayBounds(display->id, &display_rect);
 
@@ -1390,14 +1385,14 @@ static void SDL_CheckWindowDisplayChanged(SDL_Window *window)
         /* Sanity check our fullscreen windows */
         display_index = SDL_GetDisplayIndex(displayID);
         for (i = 0; i < _this->num_displays; ++i) {
-            SDL_VideoDisplay *display = &_this->displays[i];
+            SDL_VideoDisplay *display = _this->displays[i];
 
             if (display->fullscreen_window == window) {
                 if (display_index != i) {
                     if (display_index < 0) {
                         display_index = i;
                     } else {
-                        SDL_VideoDisplay *new_display = &_this->displays[display_index];
+                        SDL_VideoDisplay *new_display = _this->displays[display_index];
 
                         /* The window was moved to a different display */
                         if (new_display->fullscreen_window &&
@@ -1489,7 +1484,7 @@ static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
         }
     } else {
         for (i = 0; i < _this->num_displays; ++i) {
-            display = &_this->displays[i];
+            display = _this->displays[i];
             if (display->fullscreen_window == window) {
                 break;
             }
@@ -1528,7 +1523,7 @@ static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
             }
         } else if (fullscreen && window->last_fullscreen_exclusive_display && !window->fullscreen_exclusive) {
             for (i = 0; i < _this->num_displays; ++i) {
-                SDL_VideoDisplay *last_display = &_this->displays[i];
+                SDL_VideoDisplay *last_display = _this->displays[i];
                 if (last_display->fullscreen_window == window) {
                     SDL_SetDisplayModeForDisplay(last_display, NULL);
                     if (_this->SetWindowFullscreen) {
@@ -1584,7 +1579,7 @@ static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
 
     /* Restore the video mode on other displays if needed */
     for (i = 0; i < _this->num_displays; ++i) {
-        SDL_VideoDisplay *other = &_this->displays[i];
+        SDL_VideoDisplay *other = _this->displays[i];
         if (other != display && other->fullscreen_window == window) {
             SDL_SetDisplayModeForDisplay(other, NULL);
             if (_this->SetWindowFullscreen) {
@@ -3733,7 +3728,7 @@ void SDL_VideoQuit(void)
     _this->VideoQuit(_this);
 
     for (i = _this->num_displays; i--; ) {
-        SDL_VideoDisplay *display = &_this->displays[i];
+        SDL_VideoDisplay *display = _this->displays[i];
         SDL_DelVideoDisplay(display->id, SDL_FALSE);
     }
     if (_this->displays) {
diff --git a/src/video/android/SDL_androidvideo.c b/src/video/android/SDL_androidvideo.c
index 021607ce5d3a..ede5b1c9631c 100644
--- a/src/video/android/SDL_androidvideo.c
+++ b/src/video/android/SDL_androidvideo.c
@@ -268,7 +268,7 @@ void Android_SendResize(SDL_Window *window)
     */
     SDL_VideoDevice *device = SDL_GetVideoDevice();
     if (device && device->num_displays > 0) {
-        SDL_VideoDisplay *display = &device->displays[0];
+        SDL_VideoDisplay *display = device->displays[0];
         SDL_DisplayMode desktop_mode;
 
         SDL_zero(desktop_mode);
diff --git a/src/video/cocoa/SDL_cocoaevents.m b/src/video/cocoa/SDL_cocoaevents.m
index 5e0c3e49c0e1..221a61d91bb2 100644
--- a/src/video/cocoa/SDL_cocoaevents.m
+++ b/src/video/cocoa/SDL_cocoaevents.m
@@ -255,7 +255,7 @@ - (void)focusSomeWindow:(NSNotification *)aNotification
         SDL_Window *window = device->windows;
         int i;
         for (i = 0; i < device->num_displays; ++i) {
-            SDL_Window *fullscreen_window = device->displays[i].fullscreen_window;
+            SDL_Window *fullscreen_window = device->displays[i]->fullscreen_window;
             if (fullscreen_window) {
                 if (fullscreen_window->flags & SDL_WINDOW_MINIMIZED) {
                     SDL_RestoreWindow(fullscreen_window);
diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m
index e6b90b85a84a..34e9c2d5b0c6 100644
--- a/src/video/cocoa/SDL_cocoamodes.m
+++ b/src/video/cocoa/SDL_cocoamodes.m
@@ -520,7 +520,7 @@ void Cocoa_QuitModes(SDL_VideoDevice *_this)
     int i, j;
 
     for (i = 0; i < _this->num_displays; ++i) {
-        SDL_VideoDisplay *display = &_this->displays[i];
+        SDL_VideoDisplay *display = _this->displays[i];
         SDL_DisplayModeData *mode;
 
         if (display->current_mode->driverdata != display->desktop_mode.driverdata) {
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 3bdcb22ebf6f..10071cdf0465 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -2467,9 +2467,9 @@ SDL_DisplayID Cocoa_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *wind
             displayid = [[screen.deviceDescription objectForKey:@"NSScreenNumber"] unsignedIntValue];
 
             for (i = 0; i < _this->num_displays; i++) {
-                SDL_DisplayData *displaydata = _this->displays[i].driverdata;
+                SDL_DisplayData *displaydata = _this->displays[i]->driverdata;
                 if (displaydata != NULL && displaydata->display == displayid) {
-                    return _this->displays[i].id;
+                    return _this->displays[i]->id;
                 }
             }
         }
diff --git a/src/video/n3ds/SDL_n3dsvideo.c b/src/video/n3ds/SDL_n3dsvideo.c
index eb669fe09bd7..74b52071a072 100644
--- a/src/video/n3ds/SDL_n3dsvideo.c
+++ b/src/video/n3ds/SDL_n3dsvideo.c
@@ -48,7 +48,6 @@ struct SDL_DisplayData
 
 static void N3DS_DeleteDevice(SDL_VideoDevice *device)
 {
-    SDL_free(device->displays);
     SDL_free(device->driverdata);
     SDL_free(device);
 }
diff --git a/src/video/uikit/SDL_uikitmodes.m b/src/video/uikit/SDL_uikitmodes.m
index 06141034cc01..bc13c0df8df7 100644
--- a/src/video/uikit/SDL_uikitmodes.m
+++ b/src/video/uikit/SDL_uikitmodes.m
@@ -392,7 +392,7 @@ void UIKit_QuitModes(SDL_VideoDevice *_this)
     int i, j;
     @autoreleasepool {
         for (i = 0; i < _this->num_displays; i++) {
-            SDL_VideoDisplay *display = &_this->displays[i];
+            SDL_VideoDisplay *display = _this->displays[i];
 
             UIKit_FreeDisplayModeData(&display->desktop_mode);
             for (j = 0; j < display->num_fullscreen_modes; j++) {
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 2cd4495e48a6..cec877530ce5 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -935,7 +935,7 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
     Wayland_FiniMouse(data);
 
     for (i = _this->num_displays - 1; i >= 0; --i) {
-        SDL_VideoDisplay *display = &_this->displays[i];
+        SDL_VideoDisplay *display = _this->displays[i];
         Wayland_free_display(display);
     }
 
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index 58650e651e63..442224f21a3d 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -2154,7 +2154,7 @@ int Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window)
     if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
         int i;
         for (i = 0; i < _this->num_displays; i++) {
-            float scale = _this->displays[i].driverdata->scale_factor;
+            float scale = _this->displays[i]->driverdata->scale_factor;
             data->windowed_scale_factor = SDL_max(data->windowed_scale_factor, scale);
         }
     }
diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c
index 5868e9a5edf3..df80ba3f6f59 100644
--- a/src/video/windows/SDL_windowsmodes.c
+++ b/src/video/windows/SDL_windowsmodes.c
@@ -352,7 +352,7 @@ static void WIN_AddDisplay(SDL_VideoDevice *_this, HMONITOR hMonitor, const MONI
     // ready to be added to allow any displays that we can't fully query to be
     // removed
     for (i = 0; i < _this->num_displays; ++i) {
-        SDL_DisplayData *driverdata = _this->displays[i].driverdata;
+        SDL_DisplayData *driverdata = _this->displays[i]->driverdata;
         if (SDL_wcscmp(driverdata->DeviceName, info->szDevice) == 0) {
             SDL_bool moved = (index != i);
             SDL_bool changed_bounds = SDL_FALSE;
@@ -368,11 +368,11 @@ static void WIN_AddDisplay(SDL_VideoDevice *_this, HMONITOR hMonitor, const MONI
             }
 
             if (moved) {
-                SDL_VideoDisplay tmp;
+                SDL_VideoDisplay *tmp;
 
-                SDL_memcpy(&tmp, &_this->displays[index], sizeof(tmp));
-                SDL_memcpy(&_this->displays[index], &_this->displays[i], sizeof(tmp));
-                SDL_memcpy(&_this->displays[i], &tmp, sizeof(tmp));
+                tmp = _this->displays[index];
+                _this->displays[index] = _this->displays[i];
+                _this->displays[i] = tmp;
                 i = index;
             }
 
@@ -380,7 +380,7 @@ static void WIN_AddDisplay(SDL_VideoDevice *_this, HMONITOR hMonitor, const MONI
             driverdata->state = DisplayUnchanged;
 
             if (!_this->setting_display_mode) {
-                SDL_VideoDisplay *existing_display = &_this->displays[i];
+                SDL_VideoDisplay *existing_display = _this->displays[i];
                 SDL_Rect bounds;
 
                 SDL_ResetFullscreenDisplayModes(existing_display);
@@ -654,7 +654,7 @@ void WIN_RefreshDisplays(SDL_VideoDevice *_this)
     // Mark all displays as potentially invalid to detect
     // entries that have actually been removed
     for (i = 0; i < _this->num_displays; ++i) {
-        SDL_DisplayData *driverdata = _this->displays[i].driverdata;
+        SDL_DisplayData *driverdata = _this->displays[i]->driverdata;
         driverdata->state = DisplayRemoved;
     }
 
@@ -665,7 +665,7 @@ void WIN_RefreshDisplays(SDL_VideoDevice *_this)
     // Delete any entries still marked as invalid, iterate
     // in reverse as each delete takes effect immediately
     for (i = _this->num_displays - 1; i >= 0; --i) {
-        SDL_VideoDisplay *display = &_this->displays[i];
+        SDL_VideoDisplay *display = _this->displays[i];
         SDL_DisplayData *driverdata = display->driverdata;
         if (driverdata->state == DisplayRemoved) {
             SDL_DelVideoDisplay(display->id, SDL_TRUE);
@@ -674,7 +674,7 @@ void WIN_RefreshDisplays(SDL_VideoDevice *_this)
 
     // Send events for any newly added displays
     for (i = 0; i < _this->num_displays; ++i) {
-        SDL_VideoDisplay *display = &_this->displays[i];
+        SDL_VideoDisplay *display = _this->displays[i];
         SDL_DisplayData *driverdata = display->driverdata;
         if (driverdata->state == DisplayAdded) {
             SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_CONNECTED, 0);
diff --git a/src/video/x11/SDL_x11messagebox.c b/src/video/x11/SDL_x11messagebox.c
index d2ce6640d90c..cc197227ece4 100644
--- a/src/video/x11/SDL_x11messagebox.c
+++ b/src/video/x11/SDL_x11messagebox.c
@@ -467,8 +467,8 @@ static int X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data)
         X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
     } else {
         const SDL_VideoDevice *dev = SDL_GetVideoDevice();
-        if ((dev) && (dev->displays) && (dev->num_displays > 0)) {
-            const SDL_VideoDisplay *dpy = &dev->displays[0];
+        if (dev && dev->displays && dev->num_displays > 0) {
+            const SDL_VideoDisplay *dpy = dev->displays[0];
             const SDL_DisplayData *dpydata = dpy->driverdata;
             x = dpydata->x + ((dpy->current_mode->w - data->dialog_width) / 2);
             y = dpydata->y + ((dpy->current_mode->h - data->dialog_height) / 3);
diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c
index 5bf2372721be..e8c0ad331445 100644
--- a/src/video/x11/SDL_x11modes.c
+++ b/src/video/x11/SDL_x11modes.c
@@ -99,7 +99,7 @@ static void UpdateDisplayContentScale(float scale)
 
     if (viddevice) {
         for (i = 0; i < viddevice->num_displays; ++i) {
-            SDL_SetDisplayContentScale(&viddevice->displays[i], scale);
+            SDL_SetDisplayContentScale(viddevice->displays[i], scale);
         }
     }
 }
diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index 1016a7abe038..8b14e55c45cc 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -84,7 +84,7 @@ static int X11_SafetyNetErrHandler(Display *d, XErrorEvent *e)
         if (device != NULL) {
             int i;
             for (i = 0; i < device->num_displays; i++) {
-                SDL_VideoDisplay *display = &device->displays[i];
+                SDL_VideoDisplay *display = device->displays[i];
                 if (SDL_GetCurrentDisplayMode(display->id) != SDL_GetDesktopDisplayMode(display->id)) {
                     X11_SetDisplayMode(device, display, &display->desktop_mode);
                 }