From 4fd778119b85626d06e74dbc38c56881a0cd1508 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Wed, 25 Oct 2023 15:09:55 -0400
Subject: [PATCH] video: Implement asynchronous windowing
SDL window size, state, and position functions have been considered immediate, with their effects assuming to have taken effect upon successful return of the function. However, several windowing systems handle these requests asynchronously, resulting in the functions blocking until the changes have taken effect, potentially for long periods of time. Additionally, some windowing systems treat these as requests, and can potentially deny or fulfill the request in a manner differently than the application expects, such as not allowing a window to be positioned or sized beyond desktop borders, prohibiting fullscreen, and so on.
With these changes, applications can make requests of the window manager that do not block, with the understanding that an associated event will be sent if the request is fulfilled. Currently, size, position, maximize, minimize, and fullscreen calls are handled as asynchronous requests, with events being returned if the request is honored. If the application requires that the change take effect immediately, it can call the new SDL_SyncWindow function, which will attempt to block until the request is fulfilled, or some arbitrary timeout period elapses, the duration of which depends not only on the windowing system, but on the operation requested as well (e.g. a 100ms timeout is fine for most X11 events, but maximizing a window can take considerably longer for some reason). There is also a new hint 'SDL_VIDEO_SYNC_ALL_WINDOW_OPS' that will mimic the old behavior by synchronizing after every window operation with, again, the understanding that using this may result in the associated calls blocking for a relatively long period.
The deferred model also results in the window size and position getters not reporting false coordinates anymore, as they only forward what the window manager reports vs allowing applications to set arbitrary values, and fullscreen enter/leave events that were initiated via the window manager update the window state appropriately, where they didn't before.
Care was taken to ensure that order of operations is maintained, and that requests are not ignored or dropped. This does require some implicit internal synchronization in the various backends if many requests are made in a short period, as some state and behavior depends on other bits of state that need to be known at that particular point in time, but this isn't something that typical applications will hit, unless they are sending a lot of window state in a short time as the tests do.
The automated tests developed to test the previous behavior also resulted in previously undefined behavior being defined and normalized across platforms, particularly when it comes to the sizing and positioning of windows when they are in a fixed-size state, such as maximized or fullscreen. Size and position requests made when the window is not in a movable or resizable state will be deferred until it can be applied, so no requests are lost. These changes fix another long-standing issue with renderers recreating maximized windows, where the original non-maximized size was lost, resulting in the window being restored to the wrong size. All automated video tests pass across all platforms.
Overall, the "make a request/get an event" model better reflects how most windowing systems work, and some backends avoid spending significant time blocking while waiting for operations to complete.
---
docs/README-migration.md | 15 +
include/SDL3/SDL_events.h | 2 +
include/SDL3/SDL_hints.h | 19 +
include/SDL3/SDL_video.h | 141 +++++-
src/dynapi/SDL_dynapi.sym | 1 +
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
src/events/SDL_events.c | 2 +
src/events/SDL_windowevents.c | 22 +
src/render/opengl/SDL_render_gl.c | 1 +
src/render/opengles2/SDL_render_gles2.c | 1 +
src/test/SDL_test_common.c | 9 +
src/video/SDL_sysvideo.h | 43 +-
src/video/SDL_video.c | 461 +++++++++--------
src/video/android/SDL_androidvideo.c | 2 +
src/video/android/SDL_androidwindow.c | 3 +-
src/video/android/SDL_androidwindow.h | 2 +-
src/video/cocoa/SDL_cocoamodes.m | 4 +
src/video/cocoa/SDL_cocoavideo.m | 5 +-
src/video/cocoa/SDL_cocoawindow.h | 20 +-
src/video/cocoa/SDL_cocoawindow.m | 518 +++++++++++++-------
src/video/dummy/SDL_nullvideo.c | 13 +
src/video/emscripten/SDL_emscriptenevents.c | 15 +-
src/video/emscripten/SDL_emscriptenvideo.c | 30 +-
src/video/haiku/SDL_bwindow.cc | 15 +-
src/video/haiku/SDL_bwindow.h | 2 +-
src/video/kmsdrm/SDL_kmsdrmvideo.c | 4 +-
src/video/kmsdrm/SDL_kmsdrmvideo.h | 2 +-
src/video/offscreen/SDL_offscreenvideo.c | 1 +
src/video/offscreen/SDL_offscreenwindow.c | 5 +
src/video/offscreen/SDL_offscreenwindow.h | 1 +
src/video/qnx/SDL_qnxvideo.c | 7 +-
src/video/uikit/SDL_uikitwindow.h | 2 +-
src/video/uikit/SDL_uikitwindow.m | 3 +-
src/video/wayland/SDL_waylandvideo.c | 7 +-
src/video/wayland/SDL_waylandwindow.c | 495 +++++++++++--------
src/video/wayland/SDL_waylandwindow.h | 27 +-
src/video/windows/SDL_windowsevents.c | 53 +-
src/video/windows/SDL_windowsvideo.c | 3 +-
src/video/windows/SDL_windowswindow.c | 142 ++++--
src/video/windows/SDL_windowswindow.h | 12 +-
src/video/winrt/SDL_winrtvideo.cpp | 14 +-
src/video/x11/SDL_x11events.c | 104 +++-
src/video/x11/SDL_x11modes.c | 11 +
src/video/x11/SDL_x11modes.h | 2 +
src/video/x11/SDL_x11video.c | 4 +-
src/video/x11/SDL_x11window.c | 407 ++++++++-------
src/video/x11/SDL_x11window.h | 21 +-
test/testautomation_video.c | 514 +++++++++++++++++--
49 files changed, 2190 insertions(+), 999 deletions(-)
diff --git a/docs/README-migration.md b/docs/README-migration.md
index e6f434701d9f..3b7a90a2f59a 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -1358,6 +1358,21 @@ The following symbols have been renamed:
* SDL_WINDOW_ALLOW_HIGHDPI => SDL_WINDOW_HIGH_PIXEL_DENSITY
* SDL_WINDOW_INPUT_GRABBED => SDL_WINDOW_MOUSE_GRABBED
+The following window operations are now considered to be asynchronous requests and should not be assumed to succeed unless
+a corresponding event has been received:
+* SDL_SetWindowSize() (SDL_EVENT_WINDOW_RESIZED)
+* SDL_SetWindowPosition() (SDL_EVENT_WINDOW_MOVED)
+* SDL_MinimizeWindow() (SDL_EVENT_WINDOW_MINIMIZED)
+* SDL_MaximizeWindow() (SDL_EVENT_WINDOW_MAXIMIZED)
+* SDL_RestoreWindow() (SDL_EVENT_WINDOW_RESTORED)
+* SDL_SetWindowFullscreen() (SDL_EVENT_WINDOW_ENTER_FULLSCREEN / SDL_EVENT_WINDOW_LEAVE_FULLSCREEN)
+
+If it is required that operations be applied immediately after one of the preceeding calls, the `SDL_SyncWindow()` function
+will attempt to wait until all pending window operations have completed. Be aware that this function can potentially block for
+long periods of time, as it may have to wait for window animations to complete. Also note that windowing systems can deny or
+not precisely obey these requests (e.g. windows may not be allowed to be larger than the usable desktop space or placed
+offscreen), so a corresponding event may never arrive or not contain the expected values.
+
## SDL_vulkan.h
SDL_Vulkan_GetInstanceExtensions() no longer takes a window parameter, and no longer makes the app allocate query/allocate space for the result, instead returning a static const internal string.
diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h
index a49d30cc08ec..1b9edd2bd824 100644
--- a/include/SDL3/SDL_events.h
+++ b/include/SDL3/SDL_events.h
@@ -123,6 +123,8 @@ typedef enum
SDL_EVENT_WINDOW_DISPLAY_CHANGED, /**< Window has been moved to display data1 */
SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED, /**< Window display scale has been changed */
SDL_EVENT_WINDOW_OCCLUDED, /**< The window has been occluded */
+ SDL_EVENT_WINDOW_ENTER_FULLSCREEN, /**< The window has entered fullscreen mode */
+ SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, /**< The window has left fullscreen mode */
SDL_EVENT_WINDOW_DESTROYED, /**< The window with the associated ID is being or has been destroyed. If this message is being handled
in an event watcher, the window handle is still valid and can still be used to retrieve any userdata
associated with the window. Otherwise, the handle has already been destroyed and all resources
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index 184dac3fb1e7..f71ed32f80ff 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -1795,6 +1795,25 @@ extern "C" {
*/
#define SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP "SDL_VIDEO_WAYLAND_EMULATE_MOUSE_WARP"
+/**
+* Set whether all window operations will block until complete.
+*
+* Window systems that run asynchronously may not have the results of window operations that resize or move the window
+* applied immediately upon the return of the requesting function. Setting this hint will cause such operations to block
+* after every call until the pending operation has completed. Setting this to '1' is the equivalent of calling
+* SDL_SyncWindow() after every function call.
+*
+* Be aware that amount of time spent blocking while waiting for window operations to complete can be quite lengthy, as
+* animations may have to complete, which can take upwards of multiple seconds in some cases.
+*
+* This variable can be set to the following values:
+* "0" - Window operations are non-blocking
+* "1" - Window operations will block until completed
+*
+* By default SDL will run in non-blocking mode
+ */
+#define SDL_HINT_VIDEO_SYNC_WINDOW_OPERATIONS "SDL_VIDEO_SYNC_WINDOW_OPERATIONS"
+
/**
* A variable specifying which shader compiler to preload when using the Chrome ANGLE binaries
*
diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h
index 8f9e398bf473..b6a0e608748c 100644
--- a/include/SDL3/SDL_video.h
+++ b/include/SDL3/SDL_video.h
@@ -630,6 +630,15 @@ extern DECLSPEC float SDLCALL SDL_GetWindowDisplayScale(SDL_Window *window);
* change the window size when the window is not fullscreen, use
* SDL_SetWindowSize().
*
+ * If the window is currently in the fullscreen state, this request is asynchronous
+ * on some windowing systems and the new mode dimensions may not be applied
+ * immediately upon the return of this function. If an immediate change is required,
+ * call SDL_SyncWindow() to block until the changes have taken effect.
+ *
+ * When the new mode takes effect, an SDL_EVENT_WINDOW_RESIZED and/or an
+ * SDL_EVENT_WINDOOW_PIXEL_SIZE_CHANGED event will be emitted with the new
+ * mode dimensions.
+ *
* \param window the window to affect
* \param mode a pointer to the display mode to use, which can be NULL for
* desktop mode, or one of the fullscreen modes returned by
@@ -641,6 +650,7 @@ extern DECLSPEC float SDLCALL SDL_GetWindowDisplayScale(SDL_Window *window);
*
* \sa SDL_GetWindowFullscreenMode
* \sa SDL_SetWindowFullscreen
+ * \sa SDL_SyncWindow
*/
extern DECLSPEC int SDLCALL SDL_SetWindowFullscreenMode(SDL_Window *window, const SDL_DisplayMode *mode);
@@ -1070,7 +1080,27 @@ extern DECLSPEC const char *SDLCALL SDL_GetWindowTitle(SDL_Window *window);
extern DECLSPEC int SDLCALL SDL_SetWindowIcon(SDL_Window *window, SDL_Surface *icon);
/**
- * Set the position of a window.
+ * Request that the window's position be set.
+ *
+ * If, at the time of this request, the window is in a fixed-size state such as
+ * maximized, this request may be deferred until the window returns to a resizable
+ * state.
+ *
+ * This can be used to reposition fullscreen-desktop windows onto a different display,
+ * however, exclusive fullscreen windows are locked to a specific display and can
+ * only be repositioned programmatically via SDL_SetWindowFullscreenMode().
+ *
+ * On some windowing systems this request is asynchronous and the new coordinates
+ * may not have have been applied immediately upon the return of this function.
+ * If an immediate change is required, call SDL_SyncWindow() to block until the changes
+ * have taken effect.
+ *
+ * When the window position changes, an SDL_EVENT_WINDOW_MOVED event will be
+ * emitted with the window's new coordinates. Note that the new coordinates may
+ * not match the exact coordinates requested, as some windowing systems can restrict
+ * the position of the window in certain scenarios (e.g. constraining the position
+ * so the window is always within desktop bounds). Additionally, as this is just a
+ * request, it can be denied by the windowing system.
*
* \param window the window to reposition
* \param x the x coordinate of the window, or `SDL_WINDOWPOS_CENTERED` or
@@ -1083,12 +1113,16 @@ extern DECLSPEC int SDLCALL SDL_SetWindowIcon(SDL_Window *window, SDL_Surface *i
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetWindowPosition
+ * \sa SDL_SyncWindow
*/
extern DECLSPEC int SDLCALL SDL_SetWindowPosition(SDL_Window *window, int x, int y);
/**
* Get the position of a window.
*
+ * This is the current position of the window as last reported by the windowing
+ * system.
+ *
* If you do not need the value for one of the positions a NULL may be passed
* in the `x` or `y` parameter.
*
@@ -1105,10 +1139,28 @@ extern DECLSPEC int SDLCALL SDL_SetWindowPosition(SDL_Window *window, int x, int
extern DECLSPEC int SDLCALL SDL_GetWindowPosition(SDL_Window *window, int *x, int *y);
/**
- * Set the size of a window's client area.
+ * Request that the size of a window's client area be set.
+ *
+ * NULL can safely be passed as the `w` or `h` parameter if the width or
+ * height value is not desired.
*
- * This only affects the size of the window when not in fullscreen mode. To
- * change the fullscreen mode of a window, use SDL_SetWindowFullscreenMode()
+ * If, at the time of this request, the window in a fixed-size state, such
+ * as maximized or fullscreen, the request will be deferred until the window
+ * exits this state and becomes resizable again.
+ *
+ * To change the fullscreen mode of a window, use SDL_SetWindowFullscreenMode()
+ *
+ * On some windowing systems, this request is asynchronous and the new window size
+ * may not have have been applied immediately upon the return of this function.
+ * If an immediate change is required, call SDL_SyncWindow() to block until the
+ * changes have taken effect.
+ *
+ * When the window size changes, an SDL_EVENT_WINDOW_RESIZED event will be
+ * emitted with the new window dimensions. Note that the new dimensions may
+ * not match the exact size requested, as some windowing systems can restrict
+ * the window size in certain scenarios (e.g. constraining the size of the content
+ * area to remain within the usable desktop bounds). Additionally, as this is just
+ * a request, it can be denied by the windowing system.
*
* \param window the window to change
* \param w the width of the window, must be > 0
@@ -1120,6 +1172,7 @@ extern DECLSPEC int SDLCALL SDL_GetWindowPosition(SDL_Window *window, int *x, in
*
* \sa SDL_GetWindowSize
* \sa SDL_SetWindowFullscreenMode
+ * \sa SDL_SyncWindow
*/
extern DECLSPEC int SDLCALL SDL_SetWindowSize(SDL_Window *window, int w, int h);
@@ -1363,7 +1416,22 @@ extern DECLSPEC int SDLCALL SDL_HideWindow(SDL_Window *window);
extern DECLSPEC int SDLCALL SDL_RaiseWindow(SDL_Window *window);
/**
- * Make a window as large as possible.
+ * Request that the window be made as large as possible.
+ *
+ * Non-resizable windows can't be maximized. The window must have the
+ * SDL_WINDOW_RESIZABLE flag set, or this will have no effect.
+ *
+ * On some windowing systems this request is asynchronous and the new window state
+ * may not have have been applied immediately upon the return of this function.
+ * If an immediate change is required, call SDL_SyncWindow() to block until the
+ * changes have taken effect.
+ *
+ * When the window state changes, an SDL_EVENT_WINDOW_MAXIMIZED event will be emitted.
+ * Note that, as this is just a request, the windowing system can deny the state change.
+ *
+ * When maximizing a window, whether the constraints set via SDL_SetWindowMaximumSize()
+ * are honored depends on the policy of the window manager. Win32 and macOS enforce the
+ * constraints when maximizing, while X11 and Wayland window managers may vary.
*
* \param window the window to maximize
* \returns 0 on success or a negative error code on failure; call
@@ -1373,11 +1441,20 @@ extern DECLSPEC int SDLCALL SDL_RaiseWindow(SDL_Window *window);
*
* \sa SDL_MinimizeWindow
* \sa SDL_RestoreWindow
+ * \sa SDL_SyncWindow
*/
extern DECLSPEC int SDLCALL SDL_MaximizeWindow(SDL_Window *window);
/**
- * Minimize a window to an iconic representation.
+ * Request that the window be minimized to an iconic representation.
+ *
+ * On some windowing systems this request is asynchronous and the new window state
+ * may not have have been applied immediately upon the return of this function.
+ * If an immediate change is required, call SDL_SyncWindow() to block until the
+ * changes have taken effect.
+ *
+ * When the window state changes, an SDL_EVENT_WINDOW_MINIMIZED event will be emitted.
+ * Note that, as this is just a request, the windowing system can deny the state change.
*
* \param window the window to minimize
* \returns 0 on success or a negative error code on failure; call
@@ -1387,11 +1464,20 @@ extern DECLSPEC int SDLCALL SDL_MaximizeWindow(SDL_Window *window);
*
* \sa SDL_MaximizeWindow
* \sa SDL_RestoreWindow
+ * \sa SDL_SyncWindow
*/
extern DECLSPEC int SDLCALL SDL_MinimizeWindow(SDL_Window *window);
/**
- * Restore the size and position of a minimized or maximized window.
+ * Request that the size and position of a minimized or maximized window be restored.
+ *
+ * On some windowing systems this request is asynchronous and the new window state
+ * may not have have been applied immediately upon the return of this function.
+ * If an immediate change is required, call SDL_SyncWindow() to block until the
+ * changes have taken effect.
+ *
+ * When the window state changes, an SDL_EVENT_WINDOW_RESTORED event will be emitted.
+ * Note that, as this is just a request, the windowing system can deny the state change.
*
* \param window the window to restore
* \returns 0 on success or a negative error code on failure; call
@@ -1401,15 +1487,25 @@ extern DECLSPEC int SDLCALL SDL_MinimizeWindow(SDL_Window *window);
*
* \sa SDL_MaximizeWindow
* \sa SDL_MinimizeWindow
+ * \sa SDL_SyncWindow
*/
extern DECLSPEC int SDLCALL SDL_RestoreWindow(SDL_Window *window);
/**
- * Set a window's fullscreen state.
+ * Request that the window's fullscreen state be changed.
*
* By default a window in fullscreen state uses fullscreen desktop mode, but a
* specific display mode can be set using SDL_SetWindowFullscreenMode().
*
+ * On some windowing systems this request is asynchronous and the new fullscreen
+ * state may not have have been applied immediately upon the return of this function.
+ * If an immediate change is required, call SDL_SyncWindow() to block until the
+ * changes have taken effect.
+ *
+ * When the window state changes, an SDL_EVENT_WINDOW_ENTER_FULLSCREEN or
+ * SDL_EVENT_WINDOW_LEAVE_FULLSCREEN event will be emitted. Note that, as this is
+ * just a request, it can be denied by the windowing system.
+ *
* \param window the window to change
* \param fullscreen SDL_TRUE for fullscreen mode, SDL_FALSE for windowed mode
* \returns 0 on success or a negative error code on failure; call
@@ -1419,9 +1515,38 @@ extern DECLSPEC int SDLCALL SDL_RestoreWindow(SDL_Window *window);
*
* \sa SDL_GetWindowFullscreenMode
* \sa SDL_SetWindowFullscreenMode
+ * \sa SDL_SyncWindow
*/
extern DECLSPEC int SDLCALL SDL_SetWindowFullscreen(SDL_Window *window, SDL_bool fullscreen);
+/**
+ * Block until any pending window state is finalized.
+ *
+ * On asynchronous windowing systems, this acts as a synchronization barrier for
+ * pending window state. It will attempt to wait until any pending window state
+ * has been applied and is guaranteed to return within finite time. Note that for
+ * how long it can potentially block depends on the underlying window system, as
+ * window state changes may involve somewhat lengthy animations that must complete
+ * before the window is in its final requested state.
+ *
+ * On windowing systems where changes are immediate, this does nothing.
+ *
+ * \param window the window for which to wait for the pending state to be applied
+ * \returns 0 on success, a positive value if the operation timed out before the
+ * window was in the requested state, or a negative error code on failure;
+ * call SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_SetWindowSize
+ * \sa SDL_SetWindowPosition
+ * \sa SDL_SetWindowFullscreen
+ * \sa SDL_MinimizeWindow
+ * \sa SDL_MaximizeWindow
+ * \sa SDL_RestoreWindow
+ */
+extern DECLSPEC int SDLCALL SDL_SyncWindow(SDL_Window *window);
+
/**
* Return whether the window has a surface associated with it.
*
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 61bf3069149e..e4747421af62 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -962,6 +962,7 @@ SDL3_0.0.0 {
SDL_GetTouchDeviceName;
SDL_strnstr;
SDL_wcsnstr;
+ SDL_SyncWindow;
# extra symbols go here (don't modify this line)
local: *;
};
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index bc03b31787fd..25d834ce6036 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -987,3 +987,4 @@
#define SDL_GetTouchDeviceName SDL_GetTouchDeviceName_REAL
#define SDL_strnstr SDL_strnstr_REAL
#define SDL_wcsnstr SDL_wcsnstr_REAL
+#define SDL_SyncWindow SDL_SyncWindow_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index ac2c219758d6..e6d79cc035bf 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1012,3 +1012,4 @@ SDL_DYNAPI_PROC(SDL_TouchID*,SDL_GetTouchDevices,(int *a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetTouchDeviceName,(SDL_TouchID a),(a),return)
SDL_DYNAPI_PROC(char*,SDL_strnstr,(const char *a, const char *b, size_t c),(a,b,c),return)
SDL_DYNAPI_PROC(wchar_t*,SDL_wcsnstr,(const wchar_t *a, const wchar_t *b, size_t c),(a,b,c),return)
+SDL_DYNAPI_PROC(int,SDL_SyncWindow,(SDL_Window *a),(a),return)
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index 93588772c20a..ac36e25cc2e7 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -312,6 +312,8 @@ static void SDL_LogEvent(const SDL_Event *event)
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DISPLAY_CHANGED);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_OCCLUDED);
+ SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_ENTER_FULLSCREEN);
+ SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_LEAVE_FULLSCREEN);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DESTROYED);
#undef SDL_WINDOWEVENT_CASE
diff --git a/src/events/SDL_windowevents.c b/src/events/SDL_windowevents.c
index 5a4a235d55d3..268aff882cb6 100644
--- a/src/events/SDL_windowevents.c
+++ b/src/events/SDL_windowevents.c
@@ -71,6 +71,11 @@ int SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent,
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
window->windowed.x = data1;
window->windowed.y = data2;
+
+ if (!(window->flags & SDL_WINDOW_MAXIMIZED) && !window->state_not_floating) {
+ window->floating.x = data1;
+ window->floating.y = data2;
+ }
}
if (data1 == window->x && data2 == window->y) {
return 0;
@@ -82,6 +87,11 @@ int SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent,
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
window->windowed.w = data1;
window->windowed.h = data2;
+
+ if (!(window->flags & SDL_WINDOW_MAXIMIZED) && !window->state_not_floating) {
+ window->floating.w = data1;
+ window->floating.h = data2;
+ }
}
if (data1 == window->w && data2 == window->h) {
SDL_CheckWindowPixelSizeChanged(window);
@@ -153,6 +163,18 @@ int SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent,
}
window->flags |= SDL_WINDOW_OCCLUDED;
break;
+ case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
+ if (window->flags & SDL_WINDOW_FULLSCREEN) {
+ return 0;
+ }
+ window->flags |= SDL_WINDOW_FULLSCREEN;
+ break;
+ case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
+ if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
+ return 0;
+ }
+ window->flags &= ~SDL_WINDOW_FULLSCREEN;
+ break;
default:
break;
}
diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c
index 43c69602b66b..4d04fa7c816b 100644
--- a/src/render/opengl/SDL_render_gl.c
+++ b/src/render/opengl/SDL_render_gl.c
@@ -1777,6 +1777,7 @@ static SDL_Renderer *GL_CreateRenderer(SDL_Window *window, SDL_PropertiesID crea
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
#ifndef SDL_VIDEO_VITA_PVR_OGL
+ SDL_SyncWindow(window);
window_flags = SDL_GetWindowFlags(window);
if (!(window_flags & SDL_WINDOW_OPENGL) ||
profile_mask == SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) {
diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c
index 2e6d6fb1dce9..698ec57a486c 100644
--- a/src/render/opengles2/SDL_render_gles2.c
+++ b/src/render/opengles2/SDL_render_gles2.c
@@ -2088,6 +2088,7 @@ static SDL_Renderer *GLES2_CreateRenderer(SDL_Window *window, SDL_PropertiesID c
goto error;
}
+ SDL_SyncWindow(window);
window_flags = SDL_GetWindowFlags(window);
/* OpenGL ES 3.0 is a superset of OpenGL ES 2.0 */
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index 966c0c016d33..655a8a86e41b 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -1744,6 +1744,15 @@ static void SDLTest_PrintEvent(const SDL_Event *event)
case SDL_EVENT_WINDOW_OCCLUDED:
SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " occluded", event->window.windowID);
break;
+ case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
+ SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " entered fullscreen", event->window.windowID);
+ break;
+ case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
+ SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " left fullscreen", event->window.windowID);
+ break;
+ case SDL_EVENT_WINDOW_DESTROYED:
+ SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " destroyed", event->window.windowID);
+ break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP: {
char modstr[64];
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index df986bdf87e1..8a8816f8c82e 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -53,9 +53,27 @@ struct SDL_Window
SDL_DisplayID last_fullscreen_exclusive_display; /* The last fullscreen_exclusive display */
SDL_DisplayID last_displayID;
- /* Stored position and size for windowed mode */
+ /* Stored position and size for the window in the non-fullscreen state,
+ * including when the window is maximized or tiled.
+ *
+ * This is the size and position to which the window should return when
+ * leaving the fullscreen state.
+ */
SDL_Rect windowed;
+ /* Stored position and size for the window in the base 'floating' state;
+ * when not fullscreen, nor in a state such as maximized or tiled.
+ *
+ * This is the size and position to which the window should return when
+ * it's maximized and SDL_RestoreWindow() is called.
+ */
+ SDL_Rect floating;
+
+ /* Toggle for drivers to indicate that the current window state is
+ * not floating, but may not have any fixed-size flags (e.g. tiled)
+ */
+ SDL_bool state_not_floating;
+
/* Whether or not the initial position was defined */
SDL_bool undefined_x;
SDL_bool undefined_y;
@@ -95,10 +113,9 @@ struct SDL_Window
(((W)->flags & SDL_WINDOW_HIDDEN) == 0) && \
(((W)->flags & SDL_WINDOW_MINIMIZED) == 0))
-#define SDL_WINDOW_IS_POPUP(W) \
- ((((W)->flags & SDL_WINDOW_TOOLTIP) != 0) || \
- (((W)->flags & SDL_WINDOW_POPUP_MENU) != 0)) \
- \
+#define SDL_WINDOW_IS_POPUP(W) \
+ (((W)->flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) != 0)
+
/*
* Define the SDL display structure.
* This corresponds to physical monitors attached to the system.
@@ -128,10 +145,10 @@ struct SDL_VideoDisplay
/* Video device flags */
typedef enum
{
- VIDEO_DEVICE_QUIRK_MODE_SWITCHING_EMULATED = 0x01,
- VIDEO_DEVICE_QUIRK_DISABLE_UNSET_FULLSCREEN_ON_MINIMIZE = 0x02,
- VIDEO_DEVICE_QUIRK_HAS_POPUP_WINDOW_SUPPORT = 0x04,
-} DeviceQuirkFlags;
+ VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED = 0x01,
+ VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT = 0x02,
+ VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS = 0x04
+} DeviceCaps;
struct SDL_VideoDevice
{
@@ -217,7 +234,7 @@ struct SDL_VideoDevice
void (*SetWindowBordered)(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered);
void (*SetWindowResizable)(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool resizable);
void (*SetWindowAlwaysOnTop)(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool on_top);
- void (*SetWindowFullscreen)(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
+ int (*SetWindowFullscreen)(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
void *(*GetWindowICCProfile)(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
SDL_DisplayID (*GetDisplayForWindow)(SDL_VideoDevice *_this, SDL_Window *window);
void (*SetWindowMouseRect)(SDL_VideoDevice *_this, SDL_Window *window);
@@ -230,6 +247,7 @@ struct SDL_VideoDevice
void (*OnWindowEnter)(SDL_VideoDevice *_this, SDL_Window *window);
int (*FlashWindow)(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
int (*SetWindowFocusable)(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable);
+ int (*SyncWindow)(SDL_VideoDevice *_this, SDL_Window *window);
/* * * */
/*
@@ -335,7 +353,7 @@ struct SDL_VideoDevice
size_t num_clipboard_mime_types;
char *primary_selection_text;
SDL_bool setting_display_
(Patch may be truncated, please check the link at the top of this post.)