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); }