video: Refresh Windows display list on WM_DISPLAYCHANGE - Displays may have been added, removed or changed and all cached monitor handles are invalidated as a result. - Display events are handled in three steps: 1. Mark all currently know displays as invalid 2. Enumerate all displays, adding new ones and marking known displays as valid 3. Remove all displays still invalid after enumeration - Display connect/disconnect events are sent when displays are added or removed after initial setup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 4ee1b07..ba2a628 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -1057,6 +1057,13 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
}
break;
+ case WM_DISPLAYCHANGE:
+ {
+ // Reacquire displays if any were added or removed
+ WIN_RefreshDisplays(SDL_GetVideoDevice());
+ }
+ break;
+
case WM_NCCALCSIZE:
{
Uint32 window_flags = SDL_GetWindowFlags(data->window);
diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c
index 412f967..d88dcab 100644
--- a/src/video/windows/SDL_windowsmodes.c
+++ b/src/video/windows/SDL_windowsmodes.c
@@ -140,8 +140,9 @@ WIN_GetDisplayMode(_THIS, LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mod
}
static SDL_bool
-WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEX *info)
+WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *info, SDL_bool send_event)
{
+ int i;
SDL_VideoDisplay display;
SDL_DisplayData *displaydata;
SDL_DisplayMode mode;
@@ -155,6 +156,18 @@ WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEX *info)
return SDL_FALSE;
}
+ // Prevent adding duplicate displays. Do this after we know the display is
+ // 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 = (SDL_DisplayData *)_this->displays[i].driverdata;
+ if (SDL_wcscmp(driverdata->DeviceName, info->szDevice) == 0) {
+ driverdata->MonitorHandle = hMonitor;
+ driverdata->IsValid = SDL_TRUE;
+ return SDL_FALSE;
+ }
+ }
+
displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
if (!displaydata) {
return SDL_FALSE;
@@ -162,6 +175,7 @@ WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEX *info)
SDL_memcpy(displaydata->DeviceName, info->szDevice,
sizeof(displaydata->DeviceName));
displaydata->MonitorHandle = hMonitor;
+ displaydata->IsValid = SDL_TRUE;
SDL_zero(display);
device.cb = sizeof(device);
@@ -171,13 +185,14 @@ WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEX *info)
display.desktop_mode = mode;
display.current_mode = mode;
display.driverdata = displaydata;
- SDL_AddVideoDisplay(&display, SDL_FALSE);
+ SDL_AddVideoDisplay(&display, send_event);
SDL_free(display.name);
return SDL_TRUE;
}
typedef struct _WIN_AddDisplaysData {
SDL_VideoDevice *video_device;
+ SDL_bool send_event;
SDL_bool want_primary;
} WIN_AddDisplaysData;
@@ -188,16 +203,16 @@ WIN_AddDisplaysCallback(HMONITOR hMonitor,
LPARAM dwData)
{
WIN_AddDisplaysData *data = (WIN_AddDisplaysData*)dwData;
- MONITORINFOEX info;
+ MONITORINFOEXW info;
SDL_zero(info);
info.cbSize = sizeof(info);
- if (GetMonitorInfo(hMonitor, (LPMONITORINFO)&info) != 0) {
+ if (GetMonitorInfoW(hMonitor, (LPMONITORINFO)&info) != 0) {
const SDL_bool is_primary = ((info.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY);
if (is_primary == data->want_primary) {
- WIN_AddDisplay(data->video_device, hMonitor, &info);
+ WIN_AddDisplay(data->video_device, hMonitor, &info, data->send_event);
}
}
@@ -206,10 +221,11 @@ WIN_AddDisplaysCallback(HMONITOR hMonitor,
}
static void
-WIN_AddDisplays(_THIS)
+WIN_AddDisplays(_THIS, SDL_bool send_event)
{
WIN_AddDisplaysData callback_data;
callback_data.video_device = _this;
+ callback_data.send_event = send_event;
callback_data.want_primary = SDL_TRUE;
EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data);
@@ -221,7 +237,7 @@ WIN_AddDisplays(_THIS)
int
WIN_InitModes(_THIS)
{
- WIN_AddDisplays(_this);
+ WIN_AddDisplays(_this, SDL_FALSE);
if (_this->num_displays == 0) {
return SDL_SetError("No displays available");
@@ -396,6 +412,32 @@ WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
}
void
+WIN_RefreshDisplays(_THIS)
+{
+ int i;
+
+ // 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 = (SDL_DisplayData *)_this->displays[i].driverdata;
+ driverdata->IsValid = SDL_FALSE;
+ }
+
+ // Enumerate displays to add any new ones and mark still
+ // connected entries as valid
+ WIN_AddDisplays(_this, SDL_TRUE);
+
+ // 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_DisplayData *driverdata = (SDL_DisplayData *)_this->displays[i].driverdata;
+ if (driverdata->IsValid == SDL_FALSE) {
+ SDL_DelVideoDisplay(i);
+ }
+ }
+}
+
+void
WIN_QuitModes(_THIS)
{
/* All fullscreen windows should have restored modes by now */
diff --git a/src/video/windows/SDL_windowsmodes.h b/src/video/windows/SDL_windowsmodes.h
index a89159a..4317242 100644
--- a/src/video/windows/SDL_windowsmodes.h
+++ b/src/video/windows/SDL_windowsmodes.h
@@ -25,8 +25,9 @@
typedef struct
{
- TCHAR DeviceName[32];
+ WCHAR DeviceName[32];
HMONITOR MonitorHandle;
+ SDL_bool IsValid;
} SDL_DisplayData;
typedef struct
@@ -40,6 +41,7 @@ extern int WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rec
extern int WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi);
extern void WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display);
extern int WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
+extern void WIN_RefreshDisplays(_THIS);
extern void WIN_QuitModes(_THIS);
#endif /* SDL_windowsmodes_h_ */