Skip to content

Commit

Permalink
video: Refresh Windows display list on WM_DISPLAYCHANGE
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
slouken committed Oct 14, 2020
1 parent a558409 commit b9cbea3
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 8 deletions.
7 changes: 7 additions & 0 deletions src/video/windows/SDL_windowsevents.c
Expand Up @@ -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);
Expand Down
56 changes: 49 additions & 7 deletions src/video/windows/SDL_windowsmodes.c
Expand Up @@ -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;
Expand All @@ -155,13 +156,26 @@ 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;
}
SDL_memcpy(displaydata->DeviceName, info->szDevice,
sizeof(displaydata->DeviceName));
displaydata->MonitorHandle = hMonitor;
displaydata->IsValid = SDL_TRUE;

SDL_zero(display);
device.cb = sizeof(device);
Expand All @@ -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;

Expand All @@ -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);
}
}

Expand All @@ -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);
Expand All @@ -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");
Expand Down Expand Up @@ -395,6 +411,32 @@ WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
return 0;
}

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)
{
Expand Down
4 changes: 3 additions & 1 deletion src/video/windows/SDL_windowsmodes.h
Expand Up @@ -25,8 +25,9 @@

typedef struct
{
TCHAR DeviceName[32];
WCHAR DeviceName[32];
HMONITOR MonitorHandle;
SDL_bool IsValid;
} SDL_DisplayData;

typedef struct
Expand All @@ -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_ */
Expand Down

0 comments on commit b9cbea3

Please sign in to comment.