From 0bfe0497f398d0027db522ac7c1853327c7fc638 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Fri, 14 Mar 2025 11:50:25 -0400 Subject: [PATCH] video: Add a hint to disable auto mode switching if an exclusive fullscreen window moves between displays The existing behavior helps clients that don't expect exclusive fullscreen windows to move by maintaining a consistent size and mode, however, some are aware that this can occur and want to handle mode selection themselves. Add a hint to disable auto mode switching when an exclusive fullscreen window moves to accommodate this use case, and don't override fullscreen changes that may occur in an event watcher between the display changed event being posted and SDL running the display changed handler, as the mode switch may have already been handled there by the client. --- include/SDL3/SDL_hints.h | 19 +++++++++++++++++++ src/events/SDL_windowevents.c | 1 + src/video/SDL_sysvideo.h | 1 + src/video/SDL_video.c | 19 +++++++++++++++---- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index 2793d09421..2b073b6abe 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -3405,6 +3405,25 @@ extern "C" { */ #define SDL_HINT_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY "SDL_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY" +/** + * A variable controlling whether SDL will attempt to automatically set the + * destination display to a mode most closely matching that of the previous + * display if an exclusive fullscreen window is moved onto it. + * + * The variable can be set to the following values: + * + * - "0": SDL will not attempt to automatically set a matching mode on the destination display. + * If an exclusive fullscreen window is moved to a new display, the window will become + * fullscreen desktop. + * - "1": SDL will attempt to automatically set a mode on the destination display that most closely + * matches the mode of the display that the exclusive fullscreen window was previously on. (default) + * + * This hint can be set anytime. + * + * \since This hint is available since SDL 3.2.10. + */ +#define SDL_HINT_VIDEO_MATCH_EXCLUSIVE_MODE_ON_MOVE "SDL_VIDEO_MATCH_EXCLUSIVE_MODE_ON_MOVE" + /** * A variable controlling whether fullscreen windows are minimized when they * lose focus. diff --git a/src/events/SDL_windowevents.c b/src/events/SDL_windowevents.c index e20cd3ab0d..fa5488a640 100644 --- a/src/events/SDL_windowevents.c +++ b/src/events/SDL_windowevents.c @@ -188,6 +188,7 @@ bool SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent, int data if (data1 == 0 || (SDL_DisplayID)data1 == window->last_displayID) { return false; } + window->update_fullscreen_on_display_changed = true; window->last_displayID = (SDL_DisplayID)data1; break; case SDL_EVENT_WINDOW_OCCLUDED: diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 6da8bd2e18..cf856b096d 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -103,6 +103,7 @@ struct SDL_Window bool restore_on_show; // Child was hidden recursively by the parent, restore when shown. bool last_position_pending; // This should NOT be cleared by the backend, as it is used for fullscreen positioning. bool last_size_pending; // This should be cleared by the backend if the new size cannot be applied. + bool update_fullscreen_on_display_changed; bool is_destroying; bool is_dropping; // drag/drop in progress, expecting SDL_SendDropComplete(). diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 3ed49941e4..a940cb8c7c 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1854,6 +1854,7 @@ bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, b CHECK_WINDOW_MAGIC(window, false); window->fullscreen_exclusive = false; + window->update_fullscreen_on_display_changed = false; // If we are in the process of hiding don't go back to fullscreen if (window->is_destroying || window->is_hiding) { @@ -3940,16 +3941,26 @@ void SDL_OnWindowHidden(SDL_Window *window) void SDL_OnWindowDisplayChanged(SDL_Window *window) { - if (window->flags & SDL_WINDOW_FULLSCREEN) { - SDL_DisplayID displayID = SDL_GetDisplayForWindowPosition(window); + // Don't run this if a fullscreen change was made in an event watcher callback in response to a display changed event. + if (window->update_fullscreen_on_display_changed && (window->flags & SDL_WINDOW_FULLSCREEN)) { + const bool auto_mode_switch = SDL_GetHintBoolean(SDL_HINT_VIDEO_MATCH_EXCLUSIVE_MODE_ON_MOVE, true); - if (window->requested_fullscreen_mode.w != 0 || window->requested_fullscreen_mode.h != 0) { + if (auto_mode_switch && (window->requested_fullscreen_mode.w != 0 || window->requested_fullscreen_mode.h != 0)) { + SDL_DisplayID displayID = SDL_GetDisplayForWindowPosition(window); bool include_high_density_modes = false; if (window->requested_fullscreen_mode.pixel_density > 1.0f) { include_high_density_modes = true; } - SDL_GetClosestFullscreenDisplayMode(displayID, window->requested_fullscreen_mode.w, window->requested_fullscreen_mode.h, window->requested_fullscreen_mode.refresh_rate, include_high_density_modes, &window->current_fullscreen_mode); + const bool found_match = SDL_GetClosestFullscreenDisplayMode(displayID, window->requested_fullscreen_mode.w, window->requested_fullscreen_mode.h, + window->requested_fullscreen_mode.refresh_rate, include_high_density_modes, &window->current_fullscreen_mode); + + // If a mode without matching dimensions was not found, just go to fullscreen desktop. + if (!found_match || + window->requested_fullscreen_mode.w != window->current_fullscreen_mode.w || + window->requested_fullscreen_mode.h != window->current_fullscreen_mode.h) { + SDL_zero(window->current_fullscreen_mode); + } } else { SDL_zero(window->current_fullscreen_mode); }