SDL: Added SDL_DISPLAYEVENT_MOVED to detect when display positioning changes

From 264da8c127f8bf8930fa62f106763855efbf2a93 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 8 Dec 2022 12:46:13 -0800
Subject: [PATCH] Added SDL_DISPLAYEVENT_MOVED to detect when display
 positioning changes

---
 WhatsNew.txt                         |  8 ++++++
 include/SDL_video.h                  |  3 +-
 src/video/windows/SDL_windowsmodes.c | 42 ++++++++++++++++++++++------
 src/video/windows/SDL_windowsmodes.h |  1 +
 4 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/WhatsNew.txt b/WhatsNew.txt
index 1f95f059257e..14c37c1d5a62 100644
--- a/WhatsNew.txt
+++ b/WhatsNew.txt
@@ -1,6 +1,14 @@
 
 This is a list of major changes in SDL's version history.
 
+---------------------------------------------------------------------------
+2.28.0:
+---------------------------------------------------------------------------
+
+General:
+* Added a display event SDL_DISPLAYEVENT_MOVED which is sent when the primary monitor changes or displays change position relative to each other
+
+
 ---------------------------------------------------------------------------
 2.26.0:
 ---------------------------------------------------------------------------
diff --git a/include/SDL_video.h b/include/SDL_video.h
index b2f1509f73bd..25ce98aa6f8e 100644
--- a/include/SDL_video.h
+++ b/include/SDL_video.h
@@ -187,7 +187,8 @@ typedef enum
     SDL_DISPLAYEVENT_NONE,          /**< Never used */
     SDL_DISPLAYEVENT_ORIENTATION,   /**< Display orientation has changed to data1 */
     SDL_DISPLAYEVENT_CONNECTED,     /**< Display has been added to the system */
-    SDL_DISPLAYEVENT_DISCONNECTED   /**< Display has been removed from the system */
+    SDL_DISPLAYEVENT_DISCONNECTED,  /**< Display has been removed from the system */
+    SDL_DISPLAYEVENT_MOVED          /**< Display has changed position */
 } SDL_DisplayEventID;
 
 /**
diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c
index 8250232819a0..8748f26def77 100644
--- a/src/video/windows/SDL_windowsmodes.c
+++ b/src/video/windows/SDL_windowsmodes.c
@@ -294,9 +294,9 @@ static char *WIN_GetDisplayNameVista(const WCHAR *deviceName)
     return NULL;
 }
 
-static SDL_bool WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *info, SDL_bool send_event)
+static void WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *info, int *display_index, SDL_bool send_event)
 {
-    int i;
+    int i, index = *display_index;
     SDL_VideoDisplay display;
     SDL_DisplayData *displaydata;
     SDL_DisplayMode mode;
@@ -307,7 +307,7 @@ static SDL_bool WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *i
 #endif
 
     if (!WIN_GetDisplayMode(_this, info->szDevice, ENUM_CURRENT_SETTINGS, &mode, &orientation)) {
-        return SDL_FALSE;
+        return;
     }
 
     // Prevent adding duplicate displays. Do this after we know the display is
@@ -316,22 +316,41 @@ static SDL_bool WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *i
     for (i = 0; i < _this->num_displays; ++i) {
         SDL_DisplayData *driverdata = (SDL_DisplayData *)_this->displays[i].driverdata;
         if (SDL_wcscmp(driverdata->DeviceName, info->szDevice) == 0) {
+            SDL_bool moved = (index != i);
+
+            if (moved) {
+                SDL_VideoDisplay tmp;
+
+                SDL_assert(index < _this->num_displays);
+                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));
+                i = index;
+            }
+
             driverdata->MonitorHandle = hMonitor;
             driverdata->IsValid = SDL_TRUE;
 
             if (!_this->setting_display_mode) {
+                SDL_Rect bounds;
+
                 SDL_ResetDisplayModes(i);
                 SDL_SetCurrentDisplayMode(&_this->displays[i], &mode);
                 SDL_SetDesktopDisplayMode(&_this->displays[i], &mode);
+                if (WIN_GetDisplayBounds(_this, &_this->displays[i], &bounds) == 0) {
+                    if (SDL_memcmp(&driverdata->bounds, &bounds, sizeof(bounds)) != 0 || moved) {
+                        SDL_SendDisplayEvent(&_this->displays[i], SDL_DISPLAYEVENT_MOVED, 0);
+                    }
+                }
                 SDL_SendDisplayEvent(&_this->displays[i], SDL_DISPLAYEVENT_ORIENTATION, orientation);
             }
-            return SDL_FALSE;
+            goto done;
         }
     }
 
     displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata));
     if (displaydata == NULL) {
-        return SDL_FALSE;
+        return;
     }
     SDL_memcpy(displaydata->DeviceName, info->szDevice, sizeof(displaydata->DeviceName));
     displaydata->MonitorHandle = hMonitor;
@@ -351,15 +370,21 @@ static SDL_bool WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *i
     display.desktop_mode = mode;
     display.current_mode = mode;
     display.orientation = orientation;
+    display.device = _this;
     display.driverdata = displaydata;
-    SDL_AddVideoDisplay(&display, send_event);
+    WIN_GetDisplayBounds(_this, &display, &displaydata->bounds);
+    index = SDL_AddVideoDisplay(&display, send_event);
+    SDL_assert(index == *display_index);
     SDL_free(display.name);
-    return SDL_TRUE;
+
+done:
+    *display_index += 1;
 }
 
 typedef struct _WIN_AddDisplaysData
 {
     SDL_VideoDevice *video_device;
+    int display_index;
     SDL_bool send_event;
     SDL_bool want_primary;
 } WIN_AddDisplaysData;
@@ -379,7 +404,7 @@ static BOOL CALLBACK WIN_AddDisplaysCallback(HMONITOR hMonitor,
         const SDL_bool is_primary = ((info.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY);
 
         if (is_primary == data->want_primary) {
-            WIN_AddDisplay(data->video_device, hMonitor, &info, data->send_event);
+            WIN_AddDisplay(data->video_device, hMonitor, &info, &data->display_index, data->send_event);
         }
     }
 
@@ -391,6 +416,7 @@ static void WIN_AddDisplays(_THIS, SDL_bool send_event)
 {
     WIN_AddDisplaysData callback_data;
     callback_data.video_device = _this;
+    callback_data.display_index = 0;
     callback_data.send_event = send_event;
 
     callback_data.want_primary = SDL_TRUE;
diff --git a/src/video/windows/SDL_windowsmodes.h b/src/video/windows/SDL_windowsmodes.h
index fd586ed81252..c687fc957cb0 100644
--- a/src/video/windows/SDL_windowsmodes.h
+++ b/src/video/windows/SDL_windowsmodes.h
@@ -28,6 +28,7 @@ typedef struct
     WCHAR DeviceName[32];
     HMONITOR MonitorHandle;
     SDL_bool IsValid;
+    SDL_Rect bounds;
 } SDL_DisplayData;
 
 typedef struct