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.
main
Frank Praznik 2023-10-25 15:09:55 -04:00 committed by Sam Lantinga
parent ace385a134
commit 4fd778119b
49 changed files with 2207 additions and 1016 deletions

View File

@ -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.

View File

@ -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

View File

@ -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
*

View File

@ -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.
*
* This only affects the size of the window when not in fullscreen mode. To
* change the fullscreen mode of a window, use SDL_SetWindowFullscreenMode()
* NULL can safely be passed as the `w` or `h` parameter if the width or
* height value is not desired.
*
* 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.
*

View File

@ -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: *;
};

View File

@ -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

View File

@ -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)

View File

@ -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

View File

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

View File

@ -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) {

View File

@ -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 */

View File

@ -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];

View File

@ -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_mode;
Uint32 quirk_flags;
Uint32 device_caps;
SDL_SystemTheme system_theme;
/* * * */
@ -469,8 +487,10 @@ extern void SDL_ResetFullscreenDisplayModes(SDL_VideoDisplay *display);
extern void SDL_SetDesktopDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode);
extern void SDL_SetCurrentDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode);
extern void SDL_SetDisplayContentScale(SDL_VideoDisplay *display, float scale);
extern int SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, SDL_DisplayMode *mode);
extern SDL_VideoDisplay *SDL_GetVideoDisplay(SDL_DisplayID display);
extern SDL_VideoDisplay *SDL_GetVideoDisplayForWindow(SDL_Window *window);
extern SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window);
extern int SDL_GetDisplayIndex(SDL_DisplayID displayID);
extern SDL_DisplayData *SDL_GetDisplayDriverData(SDL_DisplayID display);
extern SDL_DisplayData *SDL_GetDisplayDriverDataForWindow(SDL_Window *window);
@ -499,6 +519,7 @@ extern void SDL_OnWindowFocusGained(SDL_Window *window);
extern void SDL_OnWindowFocusLost(SDL_Window *window);
extern void SDL_OnWindowDisplayChanged(SDL_Window *window);
extern void SDL_UpdateWindowGrab(SDL_Window *window);
extern int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen, SDL_bool commit);
extern SDL_Window *SDL_GetToplevelForKeyboardFocus(void);
extern SDL_bool SDL_ShouldAllowTopmost(void);

View File

@ -30,6 +30,7 @@
#include "SDL_rect_c.h"
#include "SDL_video_c.h"
#include "../events/SDL_events_c.h"
#include "../SDL_hints_c.h"
#include "../timer/SDL_timer_c.h"
#include "SDL_video_capture_c.h"
@ -157,27 +158,39 @@ static VideoBootStrap *bootstrap[] = {
#if defined(__MACOS__) && defined(SDL_VIDEO_DRIVER_COCOA)
/* Support for macOS fullscreen spaces */
extern SDL_bool Cocoa_IsWindowInFullscreenSpace(SDL_Window *window);
extern SDL_bool Cocoa_SetWindowFullscreenSpace(SDL_Window *window, SDL_bool state);
extern SDL_bool Cocoa_SetWindowFullscreenSpace(SDL_Window *window, SDL_bool state, SDL_bool blocking);
#endif
static void SDL_CheckWindowDisplayChanged(SDL_Window *window);
static void SDL_CheckWindowDisplayScaleChanged(SDL_Window *window);
/* Convenience functions for reading driver flags */
static SDL_bool ModeSwitchingEmulated(SDL_VideoDevice *_this)
static SDL_bool SDL_ModeSwitchingEmulated(SDL_VideoDevice *_this)
{
if (_this->quirk_flags & VIDEO_DEVICE_QUIRK_MODE_SWITCHING_EMULATED) {
if (_this->device_caps & VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED) {
return SDL_TRUE;
}
return SDL_FALSE;
}
static SDL_bool DisableUnsetFullscreenOnMinimize(SDL_VideoDevice *_this)
static SDL_bool SDL_SendsFullscreenDimensions(SDL_VideoDevice *_this)
{
if (_this->quirk_flags & VIDEO_DEVICE_QUIRK_DISABLE_UNSET_FULLSCREEN_ON_MINIMIZE) {
return SDL_TRUE;
return !!(_this->device_caps & VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS);
}
/* Hint to treat all window ops as synchronous */
static SDL_bool syncHint;
static void SDL_SyncHintWatcher(void *userdata, const char *name, const char *oldValue, const char *newValue)
{
syncHint = SDL_GetStringBoolean(newValue, SDL_FALSE);
}
static void SDL_SyncIfRequired(SDL_Window *window)
{
if (syncHint) {
SDL_SyncWindow(window);
}
return SDL_FALSE;
}
/* Support for framebuffer emulation using an accelerated renderer */
@ -543,6 +556,8 @@ int SDL_VideoInit(const char *driver_name)
return SDL_SetError("The video driver did not add any displays");
}
SDL_AddHintCallback(SDL_HINT_VIDEO_SYNC_WINDOW_OPERATIONS, SDL_SyncHintWatcher, NULL);
/* Disable the screen saver by default. This is a change from <= 2.0.1,
but most things using SDL are games or media players; you wouldn't
want a screensaver to trigger if you're playing exclusively with a
@ -1203,10 +1218,10 @@ const SDL_DisplayMode *SDL_GetCurrentDisplayMode(SDL_DisplayID displayID)
return display->current_mode;
}
static int SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, SDL_DisplayMode *mode)
int SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, SDL_DisplayMode *mode)
{
/* Mode switching is being emulated per-window; nothing to do and cannot fail. */
if (ModeSwitchingEmulated(_this)) {
if (SDL_ModeSwitchingEmulated(_this)) {
return 0;
}
@ -1377,6 +1392,34 @@ static SDL_DisplayID SDL_GetDisplayForWindowPosition(SDL_Window *window)
return displayID;
}
SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window)
{
SDL_DisplayID displayID = 0;
CHECK_WINDOW_MAGIC(window, 0);
/* An explicit fullscreen display overrides all */
if (window->current_fullscreen_mode.displayID) {
displayID = window->current_fullscreen_mode.displayID;
}
/* The floating position is used here as a very common pattern is
* SDL_SetWindowPosition() followed by SDL_SetWindowFullscreen to make the
* window fullscreen desktop on a specific display. If the backend doesn't
* support changing the window position, or the compositor hasn't yet actually
* moved the window, the actual position won't be updated at the time of the
* fullscreen call.
*/
if (!displayID) {
displayID = GetDisplayForRect(window->floating.x, window->floating.y, window->w, window->h);
}
if (!displayID) {
/* Use the primary display for a window if we can't find it anywhere else */
displayID = SDL_GetPrimaryDisplay();
}
return SDL_GetVideoDisplay(displayID);
}
SDL_DisplayID SDL_GetDisplayForWindow(SDL_Window *window)
{
SDL_DisplayID displayID = 0;
@ -1479,7 +1522,7 @@ static void SDL_RestoreMousePosition(SDL_Window *window)
}
}
static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen, SDL_bool commit)
{
SDL_VideoDisplay *display = NULL;
SDL_DisplayMode *mode = NULL;
@ -1496,7 +1539,7 @@ static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
/* Get the correct display for this operation */
if (fullscreen) {
display = SDL_GetVideoDisplayForWindow(window);
display = SDL_GetVideoDisplayForFullscreenWindow(window);
if (!display) {
/* This should never happen, but it did... */
goto done;
@ -1510,7 +1553,7 @@ static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
}
if (!display || i == _this->num_displays) {
/* Already not fullscreen on any display */
goto done;
display = NULL;
}
}
@ -1531,39 +1574,34 @@ static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
if (SDL_strcmp(_this->name, "cocoa") == 0) { /* don't do this for X11, etc */
if (window->is_destroying && !window->last_fullscreen_exclusive_display) {
window->fullscreen_exclusive = SDL_FALSE;
display->fullscreen_window = NULL;
goto done;
}
/* If we're switching between a fullscreen Space and exclusive fullscreen, we need to get back to normal first. */
if (fullscreen && !window->last_fullscreen_exclusive_display && window->fullscreen_exclusive) {
if (!Cocoa_SetWindowFullscreenSpace(window, SDL_FALSE)) {
goto error;
}
} else if (fullscreen && window->last_fullscreen_exclusive_display && !window->fullscreen_exclusive) {
for (i = 0; i < _this->num_displays; ++i) {
SDL_VideoDisplay *last_display = _this->displays[i];
if (last_display->fullscreen_window == window) {
SDL_SetDisplayModeForDisplay(last_display, NULL);
if (_this->SetWindowFullscreen) {
_this->SetWindowFullscreen(_this, window, last_display, SDL_FALSE);
}
last_display->fullscreen_window = NULL;
}
}
}
if (Cocoa_SetWindowFullscreenSpace(window, fullscreen)) {
if (Cocoa_IsWindowInFullscreenSpace(window) != fullscreen) {
goto error;
}
if (fullscreen) {
display->fullscreen_window = window;
} else {
if (display) {
display->fullscreen_window = NULL;
}
goto done;
}
if (commit) {
/* If we're switching between a fullscreen Space and exclusive fullscreen, we need to get back to normal first. */
if (fullscreen && Cocoa_IsWindowInFullscreenSpace(window) && !window->last_fullscreen_exclusive_display && window->fullscreen_exclusive) {
if (!Cocoa_SetWindowFullscreenSpace(window, SDL_FALSE, SDL_TRUE)) {
goto error;
}
} else if (fullscreen && window->last_fullscreen_exclusive_display && !window->fullscreen_exclusive) {
for (i = 0; i < _this->num_displays; ++i) {
SDL_VideoDisplay *last_display = _this->displays[i];
if (last_display->fullscreen_window == window) {
SDL_SetDisplayModeForDisplay(last_display, NULL);
if (_this->SetWindowFullscreen) {
_this->SetWindowFullscreen(_this, window, last_display, SDL_FALSE);
}
last_display->fullscreen_window = NULL;
}
}
}
if (Cocoa_SetWindowFullscreenSpace(window, fullscreen, syncHint)) {
goto done;
}
}
}
#elif defined(__WINRT__) && (NTDDI_VERSION < NTDDI_WIN10)
/* HACK: WinRT 8.x apps can't choose whether or not they are fullscreen
@ -1596,15 +1634,14 @@ static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
}
#endif
/* Restore the video mode on other displays if needed */
for (i = 0; i < _this->num_displays; ++i) {
SDL_VideoDisplay *other = _this->displays[i];
if (other != display && other->fullscreen_window == window) {
SDL_SetDisplayModeForDisplay(other, NULL);
if (_this->SetWindowFullscreen) {
_this->SetWindowFullscreen(_this, window, other, SDL_FALSE);
if (display) {
/* Restore the video mode on other displays if needed */
for (i = 0; i < _this->num_displays; ++i) {
SDL_VideoDisplay *other = _this->displays[i];
if (other != display && other->fullscreen_window == window) {
SDL_SetDisplayModeForDisplay(other, NULL);
other->fullscreen_window = NULL;
}
other->fullscreen_window = NULL;
}
}
@ -1621,75 +1658,109 @@ static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
if (SDL_SetDisplayModeForDisplay(display, mode) < 0) {
goto error;
}
if (_this->SetWindowFullscreen) {
_this->SetWindowFullscreen(_this, window, display, SDL_TRUE);
} else {
resized = SDL_TRUE;
}
display->fullscreen_window = window;
if (commit) {
int ret = 0;
if (_this->SetWindowFullscreen) {
ret = _this->SetWindowFullscreen(_this, window, display, SDL_TRUE);
} else {
resized = SDL_TRUE;
}
if (mode) {
mode_w = mode->w;
mode_h = mode->h;
} else {
mode_w = display->desktop_mode.w;
mode_h = display->desktop_mode.h;
}
#ifdef __ANDROID__
/* Android may not resize the window to exactly what our fullscreen mode is,
* especially on windowed Android environments like the Chromebook or Samsung DeX.
* Given this, we shouldn't use the mode size. Android's SetWindowFullscreen
* will generate the window event for us with the proper final size.
*/
#elif defined(__WIN32__)
/* This is also unnecessary on Win32 (WIN_SetWindowFullscreen calls SetWindowPos,
* WM_WINDOWPOSCHANGED will send SDL_EVENT_WINDOW_RESIZED).
*/
#else
if (window->w != mode_w || window->h != mode_h) {
resized = SDL_TRUE;
}
#endif
if (resized) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, mode_w, mode_h);
} else {
SDL_OnWindowResized(window);
if (ret == 0) {
/* Window is fullscreen immediately upon return. If the driver hasn't already sent the event, do so now. */
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
}
} else if (ret < 0) {
goto error;
}
}
/* Restore the cursor position */
SDL_RestoreMousePosition(window);
if (window->flags & SDL_WINDOW_FULLSCREEN) {
display->fullscreen_window = window;
/* Android may not resize the window to exactly what our fullscreen mode is,
* especially on windowed Android environments like the Chromebook or Samsung DeX.
* Given this, we shouldn't use the mode size. Android's SetWindowFullscreen
* will generate the window event for us with the proper final size.
*
* This is also unnecessary on Cocoa, Wayland, Win32, and X11 (will send SDL_EVENT_WINDOW_RESIZED).
*/
if (!SDL_SendsFullscreenDimensions(_this)) {
if (mode) {
mode_w = mode->w;
mode_h = mode->h;
} else {
mode_w = display->desktop_mode.w;
mode_h = display->desktop_mode.h;
}
if (window->w != mode_w || window->h != mode_h) {
resized = SDL_TRUE;
}
if (resized) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, mode_w, mode_h);
} else {
SDL_OnWindowResized(window);
}
}
/* Restore the cursor position */
SDL_RestoreMousePosition(window);
}
} else {
SDL_bool resized = SDL_FALSE;
/* Restore the desktop mode */
SDL_SetDisplayModeForDisplay(display, NULL);
if (_this->SetWindowFullscreen) {
_this->SetWindowFullscreen(_this, window, display, SDL_FALSE);
} else {
resized = SDL_TRUE;
if (display) {
SDL_SetDisplayModeForDisplay(display, NULL);
}
display->fullscreen_window = NULL;
if (commit) {
int ret = 0;
if (_this->SetWindowFullscreen) {
ret = _this->SetWindowFullscreen(_this, window, display ? display : SDL_GetVideoDisplayForFullscreenWindow(window), SDL_FALSE);
} else {
resized = SDL_TRUE;
}
if (resized) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->windowed.w, window->windowed.h);
} else {
SDL_OnWindowResized(window);
if (ret == 0) {
/* Window left fullscreen immediately upon return. If the driver hasn't already sent the event, do so now. */
if (window->flags & SDL_WINDOW_FULLSCREEN) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
}
} else if (ret < 0) {
goto error;
}
}
/* Restore the cursor position */
SDL_RestoreMousePosition(window);
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
if (display) {
display->fullscreen_window = NULL;
}
if (!SDL_SendsFullscreenDimensions(_this)) {
if (resized) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->windowed.w, window->windowed.h);
} else {
SDL_OnWindowResized(window);
}
}
/* Restore the cursor position */
SDL_RestoreMousePosition(window);
}
}
done:
window->last_fullscreen_exclusive_display = display && window->fullscreen_exclusive ? display->id : 0;
window->last_fullscreen_exclusive_display = display && (window->flags & SDL_WINDOW_FULLSCREEN) && window->fullscreen_exclusive ? display->id : 0;
SDL_SyncIfRequired(window);
return 0;
error:
if (fullscreen) {
/* Something went wrong and the window is no longer fullscreen. */
window->flags &= ~SDL_WINDOW_FULLSCREEN;
SDL_UpdateFullscreenMode(window, SDL_FALSE);
SDL_UpdateFullscreenMode(window, SDL_FALSE, commit);
}
return -1;
}
@ -1705,16 +1776,18 @@ int SDL_SetWindowFullscreenMode(SDL_Window *window, const SDL_DisplayMode *mode)
}
/* Save the mode so we can look up the closest match later */
SDL_memcpy(&window->requested_fullscreen_mode, mode, sizeof(window->requested_fullscreen_mode));
SDL_copyp(&window->requested_fullscreen_mode, mode);
} else {
SDL_zero(window->requested_fullscreen_mode);
}
if (window->flags & SDL_WINDOW_FULLSCREEN) {
SDL_memcpy(&window->current_fullscreen_mode, &window->requested_fullscreen_mode, sizeof(window->current_fullscreen_mode));
if (SDL_WINDOW_FULLSCREEN_VISIBLE(window)) {
SDL_UpdateFullscreenMode(window, SDL_TRUE);
}
/* Copy to the current mode now, in case an asynchronous fullscreen window request
* is in progress. It will be overwritten if a new request is made.
*/
SDL_copyp(&window->current_fullscreen_mode, &window->requested_fullscreen_mode);
if (SDL_WINDOW_FULLSCREEN_VISIBLE(window)) {
SDL_UpdateFullscreenMode(window, SDL_TRUE, SDL_TRUE);
SDL_SyncIfRequired(window);
}
return 0;
@ -1790,18 +1863,19 @@ void SDL_ToggleDragAndDropSupport(void)
static void ApplyWindowFlags(SDL_Window *window, Uint32 flags)
{
if (flags & SDL_WINDOW_MAXIMIZED) {
SDL_MaximizeWindow(window);
}
if (flags & SDL_WINDOW_MINIMIZED) {
SDL_MinimizeWindow(window);
}
if (!(flags & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED))) {
SDL_RestoreWindow(window);
}
if (flags & SDL_WINDOW_MAXIMIZED) {
SDL_MaximizeWindow(window);
}
SDL_SetWindowFullscreen(window, (flags & SDL_WINDOW_FULLSCREEN) != 0);
if (flags & SDL_WINDOW_MINIMIZED) {
SDL_MinimizeWindow(window);
}
if (flags & SDL_WINDOW_MOUSE_GRABBED) {
/* We must specifically call SDL_SetWindowGrab() and not
SDL_SetWindowMouseGrab() here because older applications may use
@ -1908,7 +1982,7 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
}
if ((flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) != 0) {
if (!(_this->quirk_flags & VIDEO_DEVICE_QUIRK_HAS_POPUP_WINDOW_SUPPORT)) {
if (!(_this->device_caps & VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT)) {
SDL_Unsupported();
return NULL;
}
@ -2018,10 +2092,10 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
}
window->magic = &_this->window_magic;
window->id = SDL_GetNextObjectID();
window->windowed.x = window->x = x;
window->windowed.y = window->y = y;
window->windowed.w = window->w = w;
window->windowed.h = window->h = h;
window->floating.x = window->windowed.x = window->x = x;
window->floating.y = window->windowed.y = window->y = y;
window->floating.w = window->windowed.w = window->w = w;
window->floating.h = window->windowed.h = window->h = h;
window->undefined_x = undefined_x;
window->undefined_y = undefined_y;
@ -2089,11 +2163,6 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
}
SDL_FinishWindowCreation(window, flags);
/* If the window was created fullscreen, make sure the display mode matches */
if (SDL_WINDOW_FULLSCREEN_VISIBLE(window)) {
SDL_UpdateFullscreenMode(window, SDL_TRUE);
}
/* Make sure window pixel size is up to date */
SDL_CheckWindowPixelSizeChanged(window);
@ -2238,6 +2307,14 @@ int SDL_RecreateWindow(SDL_Window *window, Uint32 flags)
window->is_destroying = SDL_FALSE;
if (_this->CreateSDLWindow && !(flags & SDL_WINDOW_EXTERNAL)) {
/* Reset the window size to the original floating value, so the
* recreated window has the proper base size.
*/
window->x = window->windowed.x = window->floating.x;
window->y = window->windowed.y = window->floating.y;
window->w = window->windowed.w = window->floating.w;
window->h = window->windowed.h = window->floating.h;
if (_this->CreateSDLWindow(_this, window, 0) < 0) {
if (loaded_opengl) {
SDL_GL_UnloadLibrary();
@ -2320,7 +2397,7 @@ Uint32 SDL_GetWindowFlags(SDL_Window *window)
{
CHECK_WINDOW_MAGIC(window, 0);
return window->flags;
return window->flags | window->pending_flags;
}
int SDL_SetWindowTitle(SDL_Window *window, const char *title)
@ -2410,31 +2487,19 @@ int SDL_SetWindowPosition(SDL_Window *window, int x, int y)
}
}
window->windowed.x = x;
window->windowed.y = y;
window->floating.x = x;
window->floating.y = y;
window->undefined_x = SDL_FALSE;
window->undefined_y = SDL_FALSE;
if (SDL_WINDOW_FULLSCREEN_VISIBLE(window)) {
if (!window->fullscreen_exclusive) {
/* See if we should move to another display */
SDL_DisplayID displayID = GetDisplayForRect(x, y, 1, 1);
if (displayID != original_displayID) {
/* Set the new target display and update the fullscreen mode */
window->current_fullscreen_mode.displayID = displayID;
return SDL_UpdateFullscreenMode(window, SDL_TRUE);
}
}
} else {
window->x = x;
window->y = y;
window->last_displayID = SDL_GetDisplayForWindow(window);
if (_this->SetWindowPosition) {
return _this->SetWindowPosition(_this, window);
if (_this->SetWindowPosition) {
const int ret = _this->SetWindowPosition(_this, window);
if (!ret) {
SDL_SyncIfRequired(window);
}
return ret;
}
return SDL_Unsupported();
}
@ -2511,6 +2576,7 @@ int SDL_SetWindowResizable(SDL_Window *window, SDL_bool resizable)
window->flags |= SDL_WINDOW_RESIZABLE;
} else {
window->flags &= ~SDL_WINDOW_RESIZABLE;
SDL_copyp(&window->windowed, &window->floating);
}
_this->SetWindowResizable(_this, window, want);
}
@ -2561,16 +2627,14 @@ int SDL_SetWindowSize(SDL_Window *window, int w, int h)
h = window->max_h;
}
window->windowed.w = w;
window->windowed.h = h;
window->floating.w = w;
window->floating.h = h;
if (!SDL_WINDOW_FULLSCREEN_VISIBLE(window)) {
window->w = w;
window->h = h;
if (_this->SetWindowSize) {
_this->SetWindowSize(_this, window);
}
if (_this->SetWindowSize) {
_this->SetWindowSize(_this, window);
SDL_SyncIfRequired(window);
} else {
return SDL_Unsupported();
}
return 0;
}
@ -2677,8 +2741,8 @@ int SDL_SetWindowMinimumSize(SDL_Window *window, int min_w, int min_h)
}
/* Ensure that window is not smaller than minimal size */
w = window->min_w ? SDL_max(window->w, window->min_w) : window->w;
h = window->min_h ? SDL_max(window->h, window->min_h) : window->h;
w = window->min_w ? SDL_max(window->floating.w, window->min_w) : window->floating.w;
h = window->min_h ? SDL_max(window->floating.h, window->min_h) : window->floating.h;
return SDL_SetWindowSize(window, w, h);
}
return 0;
@ -2721,8 +2785,8 @@ int SDL_SetWindowMaximumSize(SDL_Window *window, int max_w, int max_h)
}
/* Ensure that window is not larger than maximal size */
w = window->max_w ? SDL_min(window->w, window->max_w) : window->w;
h = window->max_h ? SDL_min(window->h, window->max_h) : window->h;
w = window->max_w ? SDL_min(window->floating.w, window->max_w) : window->floating.w;
h = window->max_h ? SDL_min(window->floating.h, window->max_h) : window->floating.h;
return SDL_SetWindowSize(window, w, h);
}
return 0;
@ -2763,10 +2827,6 @@ int SDL_ShowWindow(SDL_Window *window)
}
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
/* Set window state if we have pending window flags cached */
ApplyWindowFlags(window, window->pending_flags);
window->pending_flags = 0;
/* Restore child windows */
for (child = window->first_child; child; child = child->next_sibling) {
if (!child->restore_on_show && (child->flags & SDL_WINDOW_HIDDEN)) {
@ -2830,8 +2890,12 @@ int SDL_MaximizeWindow(SDL_Window *window)
CHECK_WINDOW_MAGIC(window, -1);
CHECK_WINDOW_NOT_POPUP(window, -1);
if (window->flags & SDL_WINDOW_MAXIMIZED) {
return 0;
if (!_this->MaximizeWindow) {
return SDL_Unsupported();
}
if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
return SDL_SetError("A window without the 'SDL_WINDOW_RESIZABLE' flag can't be maximized");
}
if (window->flags & SDL_WINDOW_HIDDEN) {
@ -2839,29 +2903,18 @@ int SDL_MaximizeWindow(SDL_Window *window)
return 0;
}
/* !!! FIXME: should this check if the window is resizable? */
if (_this->MaximizeWindow) {
_this->MaximizeWindow(_this, window);
}
_this->MaximizeWindow(_this, window);
SDL_SyncIfRequired(window);
return 0;
}
static SDL_bool SDL_CanMinimizeWindow(SDL_Window *window)
{
if (!_this->MinimizeWindow || SDL_WINDOW_IS_POPUP(window)) {
return SDL_FALSE;
}
return SDL_TRUE;
}
int SDL_MinimizeWindow(SDL_Window *window)
{
CHECK_WINDOW_MAGIC(window, -1);
CHECK_WINDOW_NOT_POPUP(window, -1);
if (window->flags & SDL_WINDOW_MINIMIZED) {
return 0;
if (!_this->MinimizeWindow) {
return SDL_Unsupported();
}
if (window->flags & SDL_WINDOW_HIDDEN) {
@ -2869,17 +2922,8 @@ int SDL_MinimizeWindow(SDL_Window *window)
return 0;
}
if (!SDL_CanMinimizeWindow(window)) {
return 0;
}
if (!DisableUnsetFullscreenOnMinimize(_this)) {
SDL_UpdateFullscreenMode(window, SDL_FALSE);
}
if (_this->MinimizeWindow) {
_this->MinimizeWindow(_this, window);
}
_this->MinimizeWindow(_this, window);
SDL_SyncIfRequired(window);
return 0;
}
@ -2888,8 +2932,8 @@ int SDL_RestoreWindow(SDL_Window *window)
CHECK_WINDOW_MAGIC(window, -1);
CHECK_WINDOW_NOT_POPUP(window, -1);
if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
return 0;
if (!_this->RestoreWindow) {
return SDL_Unsupported();
}
if (window->flags & SDL_WINDOW_HIDDEN) {
@ -2897,16 +2941,14 @@ int SDL_RestoreWindow(SDL_Window *window)
return 0;
}
if (_this->RestoreWindow) {
_this->RestoreWindow(_this, window);
}
_this->RestoreWindow(_this, window);
SDL_SyncIfRequired(window);
return 0;
}
int SDL_SetWindowFullscreen(SDL_Window *window, SDL_bool fullscreen)
{
int ret = 0;
Uint32 flags = fullscreen ? SDL_WINDOW_FULLSCREEN : 0;
CHECK_WINDOW_MAGIC(window, -1);
CHECK_WINDOW_NOT_POPUP(window, -1);
@ -2920,19 +2962,12 @@ int SDL_SetWindowFullscreen(SDL_Window *window, SDL_bool fullscreen)
return 0;
}
if (flags == (window->flags & SDL_WINDOW_FULLSCREEN)) {
return 0;
}
/* Clear the previous flags and OR in the new ones */
window->flags = (window->flags & ~SDL_WINDOW_FULLSCREEN) | flags;
if (fullscreen) {
/* Set the current fullscreen mode to the desired mode */
SDL_memcpy(&window->current_fullscreen_mode, &window->requested_fullscreen_mode, sizeof(window->current_fullscreen_mode));
SDL_copyp(&window->current_fullscreen_mode, &window->requested_fullscreen_mode);
}
ret = SDL_UpdateFullscreenMode(window, SDL_WINDOW_FULLSCREEN_VISIBLE(window));
ret = SDL_UpdateFullscreenMode(window, fullscreen, SDL_TRUE);
if (!fullscreen || ret != 0) {
/* Clear the current fullscreen mode. */
@ -2942,6 +2977,17 @@ int SDL_SetWindowFullscreen(SDL_Window *window, SDL_bool fullscreen)
return ret;
}
int SDL_SyncWindow(SDL_Window *window)
{
CHECK_WINDOW_MAGIC(window, -1)
if (_this->SyncWindow) {
return _this->SyncWindow(_this, window);
}
return 0;
}
static SDL_Surface *SDL_CreateWindowFramebuffer(SDL_Window *window)
{
Uint32 format = 0;
@ -3346,12 +3392,15 @@ int SDL_FlashWindow(SDL_Window *window, SDL_FlashOperation operation)
void SDL_OnWindowShown(SDL_Window *window)
{
SDL_OnWindowRestored(window);
/* Set window state if we have pending window flags cached */
ApplyWindowFlags(window, window->pending_flags);
window->pending_flags = 0;
}
void SDL_OnWindowHidden(SDL_Window *window)
{
SDL_UpdateFullscreenMode(window, SDL_FALSE);
/* The window is already hidden at this point, so just change the mode back if necessary. */
SDL_UpdateFullscreenMode(window, SDL_FALSE, SDL_FALSE);
}
void SDL_OnWindowDisplayChanged(SDL_Window *window)
@ -3370,13 +3419,13 @@ void SDL_OnWindowDisplayChanged(SDL_Window *window)
}
if (new_mode) {
SDL_memcpy(&window->current_fullscreen_mode, new_mode, sizeof(window->current_fullscreen_mode));
SDL_copyp(&window->current_fullscreen_mode, new_mode);
} else {
SDL_zero(window->current_fullscreen_mode);
}
if (SDL_WINDOW_FULLSCREEN_VISIBLE(window)) {
SDL_UpdateFullscreenMode(window, SDL_TRUE);
SDL_UpdateFullscreenMode(window, SDL_TRUE, SDL_TRUE);
}
}
@ -3411,8 +3460,8 @@ void SDL_OnWindowPixelSizeChanged(SDL_Window *window)
void SDL_OnWindowMinimized(SDL_Window *window)
{
if (!DisableUnsetFullscreenOnMinimize(_this)) {
SDL_UpdateFullscreenMode(window, SDL_FALSE);
if (window->flags & SDL_WINDOW_FULLSCREEN) {
SDL_UpdateFullscreenMode(window, SDL_FALSE, SDL_FALSE);
}
}
@ -3430,8 +3479,8 @@ void SDL_OnWindowRestored(SDL_Window *window)
*/
/*SDL_RaiseWindow(window);*/
if (SDL_WINDOW_FULLSCREEN_VISIBLE(window)) {
SDL_UpdateFullscreenMode(window, SDL_TRUE);
if (window->flags & SDL_WINDOW_FULLSCREEN) {
SDL_UpdateFullscreenMode(window, SDL_TRUE, SDL_FALSE);
}
}
@ -3488,7 +3537,7 @@ static SDL_bool SDL_ShouldMinimizeOnFocusLoss(SDL_Window *window)
/* Real fullscreen windows should minimize on focus loss so the desktop video mode is restored */
hint = SDL_GetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS);
if (!hint || !*hint || SDL_strcasecmp(hint, "auto") == 0) {
if (window->fullscreen_exclusive && !ModeSwitchingEmulated(_this)) {
if (window->fullscreen_exclusive && !SDL_ModeSwitchingEmulated(_this)) {
return SDL_TRUE;
} else {
return SDL_FALSE;
@ -3548,7 +3597,7 @@ void SDL_DestroyWindow(SDL_Window *window)
}
/* Restore video mode, etc. */
SDL_UpdateFullscreenMode(window, SDL_FALSE);
SDL_UpdateFullscreenMode(window, SDL_FALSE, SDL_TRUE);
if (!(window->flags & SDL_WINDOW_EXTERNAL)) {
SDL_HideWindow(window);
}

View File

@ -155,6 +155,8 @@ static SDL_VideoDevice *Android_CreateDevice(void)
device->GetClipboardText = Android_GetClipboardText;
device->HasClipboardText = Android_HasClipboardText;
device->device_caps = VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
return device;
}

View File

@ -105,7 +105,7 @@ void Android_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
Android_JNI_SetActivityTitle(window->title);
}
void Android_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
int Android_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
{
SDL_LockMutex(Android_ActivityMutex);
@ -154,6 +154,7 @@ void Android_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL
endfunction:
SDL_UnlockMutex(Android_ActivityMutex);
return 0;
}
void Android_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)

View File

@ -28,7 +28,7 @@
extern int Android_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern void Android_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern void Android_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern int Android_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern void Android_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Android_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool resizable);

View File

@ -489,6 +489,8 @@ int Cocoa_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_
CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
CGError result = kCGErrorSuccess;
b_inModeTransition = SDL_TRUE;
/* Fade to black to hide resolution-switching flicker */
if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
@ -508,6 +510,8 @@ int Cocoa_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_
CGReleaseDisplayFadeReservation(fade_token);
}
b_inModeTransition = SDL_FALSE;
if (result != kCGErrorSuccess) {
CG_SetError("CGDisplaySwitchToMode()", result);
return -1;

View File

@ -116,6 +116,7 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void)
device->AcceptDragAndDrop = Cocoa_AcceptDragAndDrop;
device->FlashWindow = Cocoa_FlashWindow;
device->SetWindowFocusable = Cocoa_SetWindowFocusable;
device->SyncWindow = Cocoa_SyncWindow;
#ifdef SDL_VIDEO_OPENGL_CGL
device->GL_LoadLibrary = Cocoa_GL_LoadLibrary;
@ -171,8 +172,8 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void)
device->free = Cocoa_DeleteDevice;
device->quirk_flags = VIDEO_DEVICE_QUIRK_HAS_POPUP_WINDOW_SUPPORT;
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
return device;
}
}

View File

@ -35,10 +35,10 @@
typedef enum
{
PENDING_OPERATION_NONE,
PENDING_OPERATION_ENTER_FULLSCREEN,
PENDING_OPERATION_LEAVE_FULLSCREEN,
PENDING_OPERATION_MINIMIZE
PENDING_OPERATION_NONE = 0x00,
PENDING_OPERATION_ENTER_FULLSCREEN = 0x01,
PENDING_OPERATION_LEAVE_FULLSCREEN = 0x02,
PENDING_OPERATION_MINIMIZE = 0x04
} PendingWindowOperation;
@interface Cocoa_WindowListener : NSResponder <NSWindowDelegate>
@ -54,6 +54,7 @@ typedef enum
BOOL inFullscreenTransition;
PendingWindowOperation pendingWindowOperation;
BOOL isMoving;
BOOL isMiniaturizing;
NSInteger focusClickPending;
float pendingWindowWarpX, pendingWindowWarpY;
BOOL isDragAreaRunning;
@ -130,17 +131,23 @@ typedef enum
@property(nonatomic) NSView *sdlContentView;
@property(nonatomic) NSMutableArray *nscontexts;
@property(nonatomic) SDL_bool created;
@property(nonatomic) SDL_bool inWindowFullscreenTransition;
@property(nonatomic) BOOL in_blocking_transition;
@property(nonatomic) BOOL was_zoomed;
@property(nonatomic) NSInteger window_number;
@property(nonatomic) NSInteger flash_request;
@property(nonatomic) SDL_Window *keyboard_focus;
@property(nonatomic) Cocoa_WindowListener *listener;
@property(nonatomic) SDL_CocoaVideoData *videodata;
@property(nonatomic) SDL_bool send_floating_size;
@property(nonatomic) SDL_bool send_floating_position;
@property(nonatomic) BOOL checking_zoom;
#ifdef SDL_VIDEO_OPENGL_EGL
@property(nonatomic) EGLSurface egl_surface;
#endif
@end
extern SDL_bool b_inModeTransition;
extern int Cocoa_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern void Cocoa_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern int Cocoa_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
@ -159,7 +166,7 @@ extern void Cocoa_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered);
extern void Cocoa_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool resizable);
extern void Cocoa_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool on_top);
extern void Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern int Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern void *Cocoa_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
extern SDL_DisplayID Cocoa_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window);
@ -169,5 +176,6 @@ extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
extern void Cocoa_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept);
extern int Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
extern int Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable);
extern int Cocoa_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
#endif /* SDL_cocoawindow_h_ */

View File

@ -26,20 +26,19 @@
#error SDL for macOS must be built with a 10.9 SDK or above.
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1090 */
#include <float.h> /* For FLT_MAX */
#include <float.h> /* For FLT_MAX */
#include "../SDL_sysvideo.h"
#include "../../events/SDL_dropevents_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_touch_c.h"
#include "../../events/SDL_windowevents_c.h"
#include "../../events/SDL_dropevents_c.h"
#include "../SDL_sysvideo.h"
#include "SDL_cocoavideo.h"
#include "SDL_cocoamouse.h"
#include "SDL_cocoaopengl.h"
#include "SDL_cocoaopengles.h"
#include "SDL_cocoavideo.h"
/* #define DEBUG_COCOAWINDOW */
@ -275,7 +274,7 @@
@end
static Uint64 s_moveHack;
SDL_bool b_inModeTransition;
static CGFloat SqDistanceToRect(const NSPoint *point, const NSRect *rect)
{
@ -300,7 +299,8 @@ static CGFloat SqDistanceToRect(const NSPoint *point, const NSRect *rect)
return delta.x * delta.x + delta.y * delta.y;
}
static NSScreen *ScreenForPoint(const NSPoint *point) {
static NSScreen *ScreenForPoint(const NSPoint *point)
{
NSScreen *screen;
/* Do a quick check first to see if the point lies on a specific screen*/
@ -328,7 +328,8 @@ static NSScreen *ScreenForPoint(const NSPoint *point) {
return screen;
}
static NSScreen *ScreenForRect(const NSRect *rect) {
static NSScreen *ScreenForRect(const NSRect *rect)
{
NSPoint center = NSMakePoint(NSMidX(*rect), NSMidY(*rect));
return ScreenForPoint(&center);
}
@ -548,7 +549,7 @@ static void Cocoa_UpdateClipCursor(SDL_Window *window)
static void Cocoa_SetKeyboardFocus(SDL_Window *window)
{
SDL_Window *topmost = window;
SDL_CocoaWindowData* topmost_data;
SDL_CocoaWindowData *topmost_data;
/* Find the topmost parent */
while (topmost->parent != NULL) {
@ -562,12 +563,37 @@ static void Cocoa_SetKeyboardFocus(SDL_Window *window)
static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
{
NSWindow *nswindow = ((__bridge SDL_CocoaWindowData*)window->driverdata).nswindow;
NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)window->driverdata).nswindow;
if ([nswindow occlusionState] & NSWindowOcclusionStateVisible) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
}
}
static void Cocoa_WaitForMiniaturizable(SDL_Window *window)
{
NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)window->driverdata).nswindow;
NSButton *button = [nswindow standardWindowButton:NSWindowMiniaturizeButton];
if (button) {
int iterations = 0;
while (![button isEnabled] && (iterations < 100)) {
SDL_Delay(10);
SDL_PumpEvents();
iterations++;
}
}
}
static SDL_bool Cocoa_IsZoomed(SDL_Window *window)
{
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->driverdata;
data.checking_zoom = YES;
const SDL_bool ret = [data.nswindow isZoomed];
data.checking_zoom = NO;
return ret;
}
@implementation Cocoa_WindowListener
- (void)listen:(SDL_CocoaWindowData *)data
@ -584,6 +610,7 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
inFullscreenTransition = NO;
pendingWindowOperation = PENDING_OPERATION_NONE;
isMoving = NO;
isMiniaturizing = NO;
isDragAreaRunning = NO;
pendingWindowWarpX = pendingWindowWarpY = FLT_MAX;
@ -591,8 +618,10 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
if ([window delegate] != nil) {
[center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window];
[center addObserver:self selector:@selector(windowWillMove:) name:NSWindowWillMoveNotification object:window];
[center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window];
[center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window];
[center addObserver:self selector:@selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification object:window];
[center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
[center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
[center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
@ -710,9 +739,25 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
return inFullscreenTransition;
}
- (void)clearPendingWindowOperation:(PendingWindowOperation)operation
{
pendingWindowOperation &= ~operation;
}
- (void)addPendingWindowOperation:(PendingWindowOperation)operation
{
pendingWindowOperation = operation;
pendingWindowOperation |= operation;
}
- (BOOL)windowOperationIsPending:(PendingWindowOperation)operation
{
return !!(pendingWindowOperation & operation);
}
- (BOOL)hasPendingWindowOperation
{
return pendingWindowOperation != PENDING_OPERATION_NONE ||
isMiniaturizing || inFullscreenTransition;
}
- (void)close
@ -725,8 +770,10 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
if ([window delegate] != self) {
[center removeObserver:self name:NSWindowDidExposeNotification object:window];
[center removeObserver:self name:NSWindowWillMoveNotification object:window];
[center removeObserver:self name:NSWindowDidMoveNotification object:window];
[center removeObserver:self name:NSWindowDidResizeNotification object:window];
[center removeObserver:self name:NSWindowWillMiniaturizeNotification object:window];
[center removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
[center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
[center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
@ -862,27 +909,11 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
ConvertNSRect(fullscreen, &rect);
if (inFullscreenTransition) {
if (inFullscreenTransition || b_inModeTransition) {
/* We'll take care of this at the end of the transition */
return;
}
if (s_moveHack) {
SDL_bool blockMove = ((SDL_GetTicks() - s_moveHack) < 500);
s_moveHack = 0;
if (blockMove) {
/* Cocoa is adjusting the window in response to a mode change */
SDL_RelativeToGlobalForWindow(window, window->x, window->y, &x, &y );
rect.origin.x = x;
rect.origin.y = y;
ConvertNSRect(fullscreen, &rect);
[nswindow setFrameOrigin:rect.origin];
return;
}
}
x = (int)rect.origin.x;
y = (int)rect.origin.y;
@ -893,6 +924,34 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
}
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize
{
SDL_Window *window = _data.window;
/* XXX: Calling [isZoomed] calls this function, and calling [isZoomed]
* from within this function will recurse until the stack overflows,
* so a recursion guard is required.
*/
if (!_data.checking_zoom) {
_data.checking_zoom = YES;
if ([_data.nswindow isZoomed] && !_data.was_zoomed && _data.send_floating_size) {
NSRect rect;
_data.send_floating_size = NO;
rect.origin.x = window->floating.x;
rect.origin.y = window->floating.y;
rect.size.width = window->floating.w;
rect.size.height = window->floating.h;
ConvertNSRect(SDL_FALSE, &rect);
frameSize = rect.size;
}
_data.checking_zoom = NO;
}
return frameSize;
}
- (void)windowDidResize:(NSNotification *)aNotification
{
SDL_Window *window;
@ -901,7 +960,8 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
int x, y, w, h;
BOOL zoomed;
BOOL fullscreen;
if (inFullscreenTransition) {
if (inFullscreenTransition || b_inModeTransition) {
/* We'll take care of this at the end of the transition */
return;
}
@ -910,7 +970,6 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
focusClickPending = 0;
[self onMovingOrFocusClickPendingStateCleared];
}
window = _data.window;
nswindow = _data.nswindow;
rect = [nswindow contentRectForFrameRect:[nswindow frame]];
@ -923,14 +982,11 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
ScheduleContextUpdates(_data);
/* The window can move during a resize event, such as when maximizing
or resizing from a corner */
SDL_GlobalToRelativeForWindow(window, x, y, &x, &y);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, w, h);
/* isZoomed always returns true if the window is not resizable */
if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
/* isZoomed always returns true if the window is not resizable
* and fullscreen windows are considered zoomed.
*/
if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window) &&
!(window->flags & SDL_WINDOW_FULLSCREEN) && ![self isInFullscreenSpace]) {
zoomed = YES;
} else {
zoomed = NO;
@ -939,7 +995,22 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
} else if (zoomed) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
if ([self windowOperationIsPending:PENDING_OPERATION_MINIMIZE]) {
[nswindow miniaturize:nil];
}
}
/* The window can move during a resize event, such as when maximizing
or resizing from a corner */
SDL_GlobalToRelativeForWindow(window, x, y, &x, &y);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, w, h);
}
- (void)windowWillMiniaturize:(NSNotification *)aNotification
{
isMiniaturizing = YES;
Cocoa_WaitForMiniaturizable(_data.window);
}
- (void)windowDidMiniaturize:(NSNotification *)aNotification
@ -948,16 +1019,23 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
focusClickPending = 0;
[self onMovingOrFocusClickPendingStateCleared];
}
isMiniaturizing = NO;
[self clearPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
}
- (void)windowDidDeminiaturize:(NSNotification *)aNotification
{
/* isZoomed always returns true if the window is not resizable */
if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && [_data.nswindow isZoomed]) {
/* Always send restored before maximized. */
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
/* isZoomed always returns true if the window is not resizable. */
if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(_data.window)) {
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
} else {
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
}
if ([self windowOperationIsPending:PENDING_OPERATION_ENTER_FULLSCREEN]) {
SDL_UpdateFullscreenMode(_data.window, SDL_TRUE, SDL_TRUE);
}
}
@ -1065,6 +1143,8 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
SetWindowStyle(window, (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable));
_data.was_zoomed = !!(window->flags & SDL_WINDOW_MAXIMIZED);
isFullscreenSpace = YES;
inFullscreenTransition = YES;
}
@ -1079,6 +1159,7 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
SetWindowStyle(window, GetWindowStyle(window));
[self clearPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
isFullscreenSpace = NO;
inFullscreenTransition = NO;
@ -1090,16 +1171,24 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
SDL_Window *window = _data.window;
inFullscreenTransition = NO;
[self clearPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
if (pendingWindowOperation == PENDING_OPERATION_LEAVE_FULLSCREEN) {
pendingWindowOperation = PENDING_OPERATION_NONE;
if ([self windowOperationIsPending:PENDING_OPERATION_LEAVE_FULLSCREEN]) {
[self setFullscreenSpace:NO];
} else {
if (window->fullscreen_exclusive) {
[NSMenu setMenuBarVisible:NO];
}
pendingWindowOperation = PENDING_OPERATION_NONE;
/* Don't recurse back into UpdateFullscreenMode() if this was hit in
* a blocking transition, as the caller is already waiting in
* UpdateFullscreenMode().
*/
if (!_data.in_blocking_transition) {
SDL_UpdateFullscreenMode(window, SDL_TRUE, SDL_FALSE);
}
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
/* Force the size change event in case it was delivered earlier
while the window was still animating into place.
*/
@ -1148,7 +1237,6 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
{
SDL_Window *window = _data.window;
NSWindow *nswindow = _data.nswindow;
NSButton *button = nil;
inFullscreenTransition = NO;
@ -1160,17 +1248,35 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
*/
SetWindowStyle(window, GetWindowWindowedStyle(window));
/* Don't recurse back into UpdateFullscreenMode() if this was hit in
* a blocking transition, as the caller is already waiting in
* UpdateFullscreenMode().
*/
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
if (!_data.in_blocking_transition) {
SDL_UpdateFullscreenMode(window, SDL_FALSE, SDL_FALSE);
}
if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) {
[nswindow setLevel:NSFloatingWindowLevel];
} else {
[nswindow setLevel:kCGNormalWindowLevel];
}
if (pendingWindowOperation == PENDING_OPERATION_ENTER_FULLSCREEN) {
pendingWindowOperation = PENDING_OPERATION_NONE;
[self clearPendingWindowOperation:PENDING_OPERATION_LEAVE_FULLSCREEN];
if ([self windowOperationIsPending:PENDING_OPERATION_ENTER_FULLSCREEN]) {
[self setFullscreenSpace:YES];
} else if (pendingWindowOperation == PENDING_OPERATION_MINIMIZE) {
pendingWindowOperation = PENDING_OPERATION_NONE;
} else if ([self windowOperationIsPending:PENDING_OPERATION_MINIMIZE]) {
/* There's some state that isn't quite back to normal when
* windowDidExitFullScreen triggers. For example, the minimize button on
* the title bar doesn't actually enable for another 200 milliseconds or
* so on this MacBook. Camp here and wait for that to happen before
* going on, in case we're exiting fullscreen to minimize, which need
* that window state to be normal before it will work.
*/
Cocoa_WaitForMiniaturizable(_data.window);
[self addPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
[nswindow miniaturize:nil];
} else {
/* Adjust the fullscreen toggle button and readd menu now that we're here. */
@ -1182,62 +1288,34 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
}
[NSMenu setMenuBarVisible:YES];
pendingWindowOperation = PENDING_OPERATION_NONE;
#if 0
/* This fixed bug 3719, which is that changing window size while fullscreen
doesn't take effect when leaving fullscreen, but introduces bug 3809,
which is that a maximized window doesn't go back to normal size when
restored, so this code is disabled until we can properly handle the
beginning and end of maximize and restore.
*/
/* Restore windowed size and position in case it changed while fullscreen */
{
int x, y;
NSRect rect;
SDL_RelativeToGlobalForWindow(window, window->windowed.x, window->windowed.y, x, y);
rect.origin.x = x;
rect.origin.y = y;
rect.size.width = window->windowed.w;
rect.size.height = window->windowed.h;
ConvertNSRect(NO, &rect);
NSRect rect;
rect.origin.x = _data.was_zoomed ? window->windowed.x : window->floating.x;
rect.origin.y = _data.was_zoomed ? window->windowed.y : window->floating.y;
rect.size.width = _data.was_zoomed ? window->windowed.w : window->floating.w;
rect.size.height = _data.was_zoomed ? window->windowed.h : window->floating.h;
ConvertNSRect(NO, &rect);
s_moveHack = 0;
[nswindow setContentSize:rect.size];
[nswindow setFrameOrigin:rect.origin];
s_moveHack = SDL_GetTicks();
}
#endif /* 0 */
_data.send_floating_position = NO;
_data.send_floating_size = NO;
[nswindow setContentSize:rect.size];
[nswindow setFrameOrigin:rect.origin];
/* Force the size change event in case it was delivered earlier
while the window was still animating into place.
* while the window was still animating into place.
*/
window->w = 0;
window->h = 0;
[self windowDidMove:aNotification];
[self windowDidResize:aNotification];
_data.was_zoomed = SDL_FALSE;
/* FIXME: Why does the window get hidden? */
if (!(window->flags & SDL_WINDOW_HIDDEN)) {
Cocoa_ShowWindow(SDL_GetVideoDevice(), window);
}
}
/* There's some state that isn't quite back to normal when
windowDidExitFullScreen triggers. For example, the minimize button on
the titlebar doesn't actually enable for another 200 milliseconds or
so on this MacBook. Camp here and wait for that to happen before
going on, in case we're exiting fullscreen to minimize, which need
that window state to be normal before it will work. */
button = [nswindow standardWindowButton:NSWindowMiniaturizeButton];
if (button) {
int iterations = 0;
while (![button isEnabled] && (iterations < 100)) {
SDL_Delay(10);
SDL_PumpEvents();
iterations++;
}
}
}
- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
@ -1813,7 +1891,7 @@ static int SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow
}
/* isZoomed always returns true if the window is not resizable */
if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window)) {
window->flags |= SDL_WINDOW_MAXIMIZED;
} else {
window->flags &= ~SDL_WINDOW_MAXIMIZED;
@ -2069,36 +2147,49 @@ int Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->driverdata;
NSWindow *nswindow = windata.nswindow;
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
BOOL fullscreen;
Uint64 moveHack;
BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
int x, y;
SDL_RelativeToGlobalForWindow(window, window->x, window->y, &x, &y);
rect.origin.x = x;
rect.origin.y = y;
fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO;
ConvertNSRect(fullscreen, &rect);
/* Position and constrain the popup */
if (SDL_WINDOW_IS_POPUP(window)) {
NSRect screenRect = [ScreenForRect(&rect) frame];
if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) {
rect.origin.x -= (rect.origin.x + rect.size.width) - (screenRect.origin.x + screenRect.size.width);
}
if (rect.origin.y + rect.size.height > screenRect.origin.y + screenRect.size.height) {
rect.origin.y -= (rect.origin.y + rect.size.height) - (screenRect.origin.y + screenRect.size.height);
}
rect.origin.x = SDL_max(rect.origin.x, screenRect.origin.x);
rect.origin.y = SDL_max(rect.origin.y, screenRect.origin.y);
if ([windata.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] ||
[windata.listener isInFullscreenSpaceTransition]) {
Cocoa_SyncWindow(_this, window);
}
moveHack = s_moveHack;
s_moveHack = 0;
[nswindow setFrameOrigin:rect.origin];
s_moveHack = moveHack;
if (!(window->flags & SDL_WINDOW_MAXIMIZED)) {
if (fullscreen) {
SDL_VideoDisplay *display = SDL_GetVideoDisplayForFullscreenWindow(window);
SDL_Rect r;
SDL_GetDisplayBounds(display->id, &r);
ScheduleContextUpdates(windata);
rect.origin.x = r.x;
rect.origin.y = r.y;
} else {
SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, &x, &y);
rect.origin.x = x;
rect.origin.y = y;
}
ConvertNSRect(fullscreen, &rect);
/* Position and constrain the popup */
if (SDL_WINDOW_IS_POPUP(window)) {
NSRect screenRect = [ScreenForRect(&rect) frame];
if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) {
rect.origin.x -= (rect.origin.x + rect.size.width) - (screenRect.origin.x + screenRect.size.width);
}
if (rect.origin.y + rect.size.height > screenRect.origin.y + screenRect.size.height) {
rect.origin.y -= (rect.origin.y + rect.size.height) - (screenRect.origin.y + screenRect.size.height);
}
rect.origin.x = SDL_max(rect.origin.x, screenRect.origin.x);
rect.origin.y = SDL_max(rect.origin.y, screenRect.origin.y);
}
[nswindow setFrameOrigin:rect.origin];
ScheduleContextUpdates(windata);
} else {
windata.send_floating_position = SDL_TRUE;
}
}
return 0;
}
@ -2109,17 +2200,25 @@ void Cocoa_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->driverdata;
NSWindow *nswindow = windata.nswindow;
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
Uint64 moveHack;
rect.size.width = window->w;
rect.size.height = window->h;
rect.size.width = window->floating.w;
rect.size.height = window->floating.h;
moveHack = s_moveHack;
s_moveHack = 0;
[nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES];
s_moveHack = moveHack;
if ([windata.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] ||
[windata.listener isInFullscreenSpaceTransition]) {
Cocoa_SyncWindow(_this, window);
}
ScheduleContextUpdates(windata);
if (!Cocoa_IsZoomed(window)) {
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
[nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES];
ScheduleContextUpdates(windata);
} else if (windata.was_zoomed) {
windata.send_floating_size = SDL_TRUE;
}
} else {
windata.send_floating_size = SDL_TRUE;
}
}
}
@ -2262,9 +2361,17 @@ void Cocoa_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->driverdata;
NSWindow *nswindow = windata.nswindow;
[nswindow zoom:nil];
if ([windata.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] ||
[windata.listener isInFullscreenSpaceTransition]) {
Cocoa_SyncWindow(_this, window);
}
ScheduleContextUpdates(windata);
if (!(window->flags & SDL_WINDOW_FULLSCREEN) &&
![windata.listener isInFullscreenSpaceTransition] &&
![windata.listener isInFullscreenSpace]) {
[nswindow zoom:nil];
ScheduleContextUpdates(windata);
}
}
}
@ -2273,8 +2380,13 @@ void Cocoa_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
@autoreleasepool {
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->driverdata;
NSWindow *nswindow = data.nswindow;
if ([data.listener isInFullscreenSpaceTransition]) {
[data.listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
[data.listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
if ([data.listener isInFullscreenSpace] || (window->flags & SDL_WINDOW_FULLSCREEN)) {
[data.listener addPendingWindowOperation:PENDING_OPERATION_LEAVE_FULLSCREEN];
SDL_UpdateFullscreenMode(window, SDL_FALSE, SDL_TRUE);
} else if ([data.listener isInFullscreenSpaceTransition]) {
[data.listener addPendingWindowOperation:PENDING_OPERATION_LEAVE_FULLSCREEN];
} else {
[nswindow miniaturize:nil];
}
@ -2284,12 +2396,42 @@ void Cocoa_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
void Cocoa_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
@autoreleasepool {
NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)window->driverdata).nswindow;
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->driverdata;
NSWindow *nswindow = data.nswindow;
if ([nswindow isMiniaturized]) {
[nswindow deminiaturize:nil];
} else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
[nswindow zoom:nil];
if ([data.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] ||
[data.listener isInFullscreenSpaceTransition]) {
Cocoa_SyncWindow(_this, window);
}
[data.listener clearPendingWindowOperation:(PENDING_OPERATION_MINIMIZE)];
if (!(window->flags & SDL_WINDOW_FULLSCREEN) &&
![data.listener isInFullscreenSpaceTransition] &&
![data.listener isInFullscreenSpace]) {
if ([nswindow isMiniaturized]) {
[nswindow deminiaturize:nil];
} else if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window)) {
NSRect rect;
/* Update the floating coordinates */
rect.origin.x = window->floating.x;
rect.origin.y = window->floating.y;
/* The floating size will be set in windowWillResize */
[nswindow zoom:nil];
rect.size.width = window->floating.w;
rect.size.height = window->floating.h;
ConvertNSRect(SDL_FALSE, &rect);
if (data.send_floating_position) {
data.send_floating_position = SDL_FALSE;
[nswindow setFrameOrigin:rect.origin];
ScheduleContextUpdates(data);
}
}
}
}
}
@ -2342,7 +2484,7 @@ void Cocoa_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, SDL_
}
}
void Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
int Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
{
@autoreleasepool {
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->driverdata;
@ -2357,6 +2499,11 @@ void Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_V
if (fullscreen) {
SDL_Rect bounds;
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
data.was_zoomed = !!(window->flags & SDL_WINDOW_MAXIMIZED);
}
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
Cocoa_GetDisplayBounds(_this, display, &bounds);
rect.origin.x = bounds.x;
rect.origin.y = bounds.y;
@ -2377,10 +2524,14 @@ void Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_V
[nswindow setStyleMask:NSWindowStyleMaskBorderless];
} else {
NSRect frameRect;
rect.origin.x = window->windowed.x;
rect.origin.y = window->windowed.y;
rect.size.width = window->windowed.w;
rect.size.height = window->windowed.h;
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
rect.origin.x = data.was_zoomed ? window->windowed.x : window->floating.x;
rect.origin.y = data.was_zoomed ? window->windowed.y : window->floating.y;
rect.size.width = data.was_zoomed ? window->windowed.w : window->floating.w;
rect.size.height = data.was_zoomed ? window->windowed.h : window->floating.h;
ConvertNSRect(fullscreen, &rect);
/* The window is not meant to be fullscreen, but its flags might have a
@ -2402,14 +2553,20 @@ void Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_V
[data.sdlContentView setNextResponder:data.listener];
}
s_moveHack = 0;
[nswindow setContentSize:rect.size];
[nswindow setFrameOrigin:rect.origin];
s_moveHack = SDL_GetTicks();
/* When the window style changes the title is cleared */
if (!fullscreen) {
Cocoa_SetWindowTitle(_this, window);
data.was_zoomed = NO;
if ([data.listener windowOperationIsPending:PENDING_OPERATION_MINIMIZE]) {
Cocoa_WaitForMiniaturizable(window);
[data.listener addPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
[nswindow miniaturize:nil];
}
}
if (SDL_ShouldAllowTopmost() && fullscreen) {
@ -2429,6 +2586,8 @@ void Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_V
ScheduleContextUpdates(data);
}
return 0;
}
void *Cocoa_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size)
@ -2579,47 +2738,46 @@ SDL_bool Cocoa_IsWindowInFullscreenSpace(SDL_Window *window)
}
}
SDL_bool Cocoa_SetWindowFullscreenSpace(SDL_Window *window, SDL_bool state)
SDL_bool Cocoa_SetWindowFullscreenSpace(SDL_Window *window, SDL_bool state, SDL_bool blocking)
{
@autoreleasepool {
SDL_bool succeeded = SDL_FALSE;
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->driverdata;
if (data.inWindowFullscreenTransition) {
return SDL_FALSE;
}
data.inWindowFullscreenTransition = SDL_TRUE;
data.in_blocking_transition = blocking;
if ([data.listener setFullscreenSpace:(state ? YES : NO)]) {
const int maxattempts = 3;
int attempt = 0;
while (++attempt <= maxattempts) {
/* Wait for the transition to complete, so application changes
take effect properly (e.g. setting the window size, etc.)
*/
const int limit = 10000;
int count = 0;
while ([data.listener isInFullscreenSpaceTransition]) {
if (++count == limit) {
/* Uh oh, transition isn't completing. Should we assert? */
if (blocking) {
const int maxattempts = 3;
int attempt = 0;
while (++attempt <= maxattempts) {
/* Wait for the transition to complete, so application changes
take effect properly (e.g. setting the window size, etc.)
*/
const int limit = 10000;
int count = 0;
while ([data.listener isInFullscreenSpaceTransition]) {
if (++count == limit) {
/* Uh oh, transition isn't completing. Should we assert? */
break;
}
SDL_Delay(1);
SDL_PumpEvents();
}
if ([data.listener isInFullscreenSpace] == (state ? YES : NO)) {
break;
}
SDL_Delay(1);
SDL_PumpEvents();
}
if ([data.listener isInFullscreenSpace] == (state ? YES : NO)) {
break;
}
/* Try again, the last attempt was interrupted by user gestures */
if (![data.listener setFullscreenSpace:(state ? YES : NO)]) {
break; /* ??? */
/* Try again, the last attempt was interrupted by user gestures */
if (![data.listener setFullscreenSpace:(state ? YES : NO)]) {
break; /* ??? */
}
}
}
/* Return TRUE to prevent non-space fullscreen logic from running */
succeeded = SDL_TRUE;
}
data.inWindowFullscreenTransition = SDL_FALSE;
data.in_blocking_transition = NO;
return succeeded;
}
}
@ -2683,4 +2841,32 @@ int Cocoa_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opa
}
}
int Cocoa_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
int ret = 0;
@autoreleasepool {
/* The timeout needs to be high enough that animated fullscreen
* spaces transitions won't cause it to time out.
*/
Uint64 timeout = SDL_GetTicksNS() + SDL_MS_TO_NS(2000);
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->driverdata;
while (SDL_TRUE) {
SDL_PumpEvents();
if (SDL_GetTicksNS() >= timeout) {
ret = 1;
break;
}
if (![data.listener hasPendingWindowOperation]) {
break;
}
SDL_Delay(10);
}
}
return ret;
}
#endif /* SDL_VIDEO_DRIVER_COCOA */

View File

@ -55,6 +55,17 @@
static int DUMMY_VideoInit(SDL_VideoDevice *_this);
static void DUMMY_VideoQuit(SDL_VideoDevice *_this);
static int DUMMY_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, window->floating.x, window->floating.y);
return 0;
}
static void DUMMY_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h);
}
/* DUMMY driver bootstrap functions */
static int DUMMY_Available(const char *enable_hint)
@ -92,6 +103,8 @@ static SDL_VideoDevice *DUMMY_InternalCreateDevice(const char *enable_hint)
device->VideoInit = DUMMY_VideoInit;
device->VideoQuit = DUMMY_VideoQuit;
device->PumpEvents = DUMMY_PumpEvents;
device->SetWindowSize = DUMMY_SetWindowSize;
device->SetWindowPosition = DUMMY_SetWindowPosition;
device->CreateWindowFramebuffer = SDL_DUMMY_CreateWindowFramebuffer;
device->UpdateWindowFramebuffer = SDL_DUMMY_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = SDL_DUMMY_DestroyWindowFramebuffer;

View File

@ -843,23 +843,16 @@ static EM_BOOL Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboard
static EM_BOOL Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
{
SDL_WindowData *window_data = userData;
SDL_VideoDisplay *display;
if (fullscreenChangeEvent->isFullscreen) {
window_data->window->flags |= window_data->fullscreen_mode_flags;
SDL_SendWindowEvent(window_data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
window_data->fullscreen_mode_flags = 0;
} else {
window_data->window->flags &= ~SDL_WINDOW_FULLSCREEN;
/* reset fullscreen window if the browser left fullscreen */
display = SDL_GetVideoDisplayForWindow(window_data->window);
if (display->fullscreen_window == window_data->window) {
display->fullscreen_window = NULL;
}
SDL_SendWindowEvent(window_data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
}
SDL_UpdateFullscreenMode(window_data->window, fullscreenChangeEvent->isFullscreen, SDL_FALSE);
return 0;
}

View File

@ -44,7 +44,7 @@ static int Emscripten_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, S
static void Emscripten_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
static void Emscripten_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
static void Emscripten_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
static void Emscripten_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
static int Emscripten_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
static void Emscripten_PumpEvents(SDL_VideoDevice *_this);
static void Emscripten_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
@ -247,12 +247,14 @@ static void Emscripten_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
data->pixel_ratio = emscripten_get_device_pixel_ratio();
}
emscripten_set_canvas_element_size(data->canvas_id, window->w * data->pixel_ratio, window->h * data->pixel_ratio);
emscripten_set_canvas_element_size(data->canvas_id, window->floating.w * data->pixel_ratio, window->floating.h * data->pixel_ratio);
/*scale canvas down*/
if (!data->external_size && data->pixel_ratio != 1.0f) {
emscripten_set_element_css_size(data->canvas_id, window->w, window->h);
emscripten_set_element_css_size(data->canvas_id, window->floating.w, window->floating.h);
}
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h);
}
}
@ -284,16 +286,17 @@ static void Emscripten_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
}
static void Emscripten_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
static int Emscripten_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
{
SDL_WindowData *data;
int res = -1;
if (window->driverdata) {
data = window->driverdata;
if (fullscreen) {
EmscriptenFullscreenStrategy strategy;
SDL_bool is_fullscreen_desktop = !window->fullscreen_exclusive;
int res;
strategy.scaleMode = is_fullscreen_desktop ? EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH : EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT;
@ -314,12 +317,17 @@ static void Emscripten_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *w
data->fullscreen_resize = is_fullscreen_desktop;
res = emscripten_request_fullscreen_strategy(data->canvas_id, 1, &strategy);
if (res != EMSCRIPTEN_RESULT_SUCCESS && res != EMSCRIPTEN_RESULT_DEFERRED) {
/* unset flags, fullscreen failed */
window->flags &= ~SDL_WINDOW_FULLSCREEN;
}
} else
emscripten_exit_fullscreen();
} else {
res = emscripten_exit_fullscreen();
}
}
if (res == EMSCRIPTEN_RESULT_SUCCESS) {
return 0;
} else if (res == EMSCRIPTEN_RESULT_DEFERRED) {
return 1;
} else {
return -1;
}
}

View File

@ -92,17 +92,18 @@ void HAIKU_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window * window) {
_ToBeWin(window)->PostMessage(&msg);
}
void HAIKU_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window * window) {
int HAIKU_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window * window) {
BMessage msg(BWIN_MOVE_WINDOW);
msg.AddInt32("window-x", window->x);
msg.AddInt32("window-y", window->y);
msg.AddInt32("window-x", window->floating.x);
msg.AddInt32("window-y", window->floating.y);
_ToBeWin(window)->PostMessage(&msg);
return 0;
}
void HAIKU_SetWindowSize(SDL_VideoDevice *_this, SDL_Window * window) {
BMessage msg(BWIN_RESIZE_WINDOW);
msg.AddInt32("window-w", window->w - 1);
msg.AddInt32("window-h", window->h - 1);
msg.AddInt32("window-w", window->floating.w - 1);
msg.AddInt32("window-h", window->floating.h - 1);
_ToBeWin(window)->PostMessage(&msg);
}
@ -148,13 +149,13 @@ void HAIKU_RestoreWindow(SDL_VideoDevice *_this, SDL_Window * window) {
_ToBeWin(window)->PostMessage(&msg);
}
void HAIKU_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window * window,
int HAIKU_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window * window,
SDL_VideoDisplay * display, SDL_bool fullscreen) {
/* Haiku tracks all video display information */
BMessage msg(BWIN_FULLSCREEN);
msg.AddBool("fullscreen", fullscreen);
_ToBeWin(window)->PostMessage(&msg);
return 0;
}

View File

@ -37,7 +37,7 @@ extern void HAIKU_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void HAIKU_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void HAIKU_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered);
extern void HAIKU_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool resizable);
extern void HAIKU_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern int HAIKU_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern void HAIKU_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed);
extern void HAIKU_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);

View File

@ -1596,13 +1596,13 @@ void KMSDRM_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
KMSDRM_DirtySurfaces(window);
}
}
void KMSDRM_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
int KMSDRM_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
{
SDL_VideoData *viddata = _this->driverdata;
if (!viddata->vulkan_mode) {
KMSDRM_DirtySurfaces(window);
}
return 0;
}
void KMSDRM_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
{

View File

@ -125,7 +125,7 @@ int KMSDRM_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properti
void KMSDRM_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
int KMSDRM_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);
void KMSDRM_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
void KMSDRM_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_bool fullscreen);
int KMSDRM_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_bool fullscreen);
void KMSDRM_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
void KMSDRM_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
void KMSDRM_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);

View File

@ -86,6 +86,7 @@ static SDL_VideoDevice *OFFSCREEN_CreateDevice(void)
/* "Window" */
device->CreateSDLWindow = OFFSCREEN_CreateWindow;
device->DestroyWindow = OFFSCREEN_DestroyWindow;
device->SetWindowSize = OFFSCREEN_SetWindowSize;
return device;
}

View File

@ -23,6 +23,7 @@
#ifdef SDL_VIDEO_DRIVER_OFFSCREEN
#include "../SDL_sysvideo.h"
#include "../../events/SDL_windowevents_c.h"
#include "../SDL_egl_c.h"
#include "SDL_offscreenwindow.h"
@ -82,4 +83,8 @@ void OFFSCREEN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
window->driverdata = NULL;
}
void OFFSCREEN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h);
}
#endif /* SDL_VIDEO_DRIVER_OFFSCREEN */

View File

@ -35,5 +35,6 @@ struct SDL_WindowData
extern int OFFSCREEN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern void OFFSCREEN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void OFFSCREEN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
#endif /* SDL_offscreenwindow_h */

View File

@ -241,12 +241,11 @@ static void setWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
window_impl_t *impl = (window_impl_t *)window->driverdata;
int size[2];
size[0] = window->w;
size[1] = window->h;
size[0] = window->floating.w;
size[1] = window->floating.h;
screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SIZE, size);
screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SOURCE_SIZE,
size);
screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SOURCE_SIZE, size);
}
/**

View File

@ -32,7 +32,7 @@ extern void UIKit_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void UIKit_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void UIKit_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void UIKit_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered);
extern void UIKit_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern int UIKit_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern void UIKit_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed);
extern void UIKit_UpdatePointerLock(SDL_VideoDevice *_this, SDL_Window *window);
extern void UIKit_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);

View File

@ -309,11 +309,12 @@ void UIKit_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_boo
}
}
void UIKit_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
int UIKit_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
{
@autoreleasepool {
UIKit_UpdateWindowBorder(_this, window);
}
return 0;
}
void UIKit_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed)

View File

@ -216,6 +216,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
device->FlashWindow = Wayland_FlashWindow;
device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport;
device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu;
device->SyncWindow = Wayland_SyncWindow;
#ifdef SDL_USE_LIBDBUS
if (SDL_SystemTheme_Init())
@ -239,9 +240,9 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
device->free = Wayland_DeleteDevice;
device->quirk_flags = VIDEO_DEVICE_QUIRK_MODE_SWITCHING_EMULATED |
VIDEO_DEVICE_QUIRK_DISABLE_UNSET_FULLSCREEN_ON_MINIMIZE |
VIDEO_DEVICE_QUIRK_HAS_POPUP_WINDOW_SUPPORT;
device->device_caps = VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED |
VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
return device;
}

View File

@ -182,7 +182,7 @@ static void SetMinMaxDimensions(SDL_Window *window)
SDL_VideoData *viddata = wind->waylandData;
int min_width, min_height, max_width, max_height;
if (window->flags & SDL_WINDOW_FULLSCREEN) {
if ((window->flags & SDL_WINDOW_FULLSCREEN) || wind->fullscreen_deadline_count) {
min_width = 0;
min_height = 0;
max_width = 0;
@ -230,7 +230,7 @@ static void SetMinMaxDimensions(SDL_Window *window)
}
}
static void EnsurePopupPositionIsValid(SDL_Window *window)
static void EnsurePopupPositionIsValid(SDL_Window *window, int *x, int *y)
{
int adj_count = 0;
@ -241,20 +241,20 @@ static void EnsurePopupPositionIsValid(SDL_Window *window)
* can result in behavior ranging from the window being spuriously closed
* to a protocol violation.
*/
if (window->x + window->driverdata->wl_window_width < 0) {
window->x = -window->w;
if (*x + window->driverdata->wl_window_width < 0) {
*x = -window->w;
++adj_count;
}
if (window->y + window->driverdata->wl_window_height < 0) {
window->y = -window->h;
if (*y + window->driverdata->wl_window_height < 0) {
*y = -window->h;
++adj_count;
}
if (window->x > window->parent->driverdata->wl_window_width) {
window->x = window->parent->driverdata->wl_window_width;
if (*x > window->parent->driverdata->wl_window_width) {
*x = window->parent->driverdata->wl_window_width;
++adj_count;
}
if (window->y > window->parent->driverdata->wl_window_height) {
window->y = window->parent->driverdata->wl_window_height;
if (*y > window->parent->driverdata->wl_window_height) {
*y = window->parent->driverdata->wl_window_height;
++adj_count;
}
@ -263,7 +263,7 @@ static void EnsurePopupPositionIsValid(SDL_Window *window)
* must be nudged by 1 to be considered adjacent.
*/
if (adj_count > 1) {
window->x += window->x < 0 ? 1 : -1;
*x += *x < 0 ? 1 : -1;
}
}
@ -282,17 +282,19 @@ static void GetPopupPosition(SDL_Window *popup, int x, int y, int *adj_x, int *a
}
}
static void RepositionPopup(SDL_Window *window)
static void RepositionPopup(SDL_Window *window, SDL_bool use_current_position)
{
SDL_WindowData *wind = window->driverdata;
if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP &&
wind->shell_surface.xdg.roleobj.popup.positioner &&
xdg_popup_get_version(wind->shell_surface.xdg.roleobj.popup.popup) >= XDG_POPUP_REPOSITION_SINCE_VERSION) {
int orig_x = use_current_position ? window->x : window->floating.x;
int orig_y = use_current_position ? window->y : window->floating.y;
int x, y;
EnsurePopupPositionIsValid(window);
GetPopupPosition(window, window->x, window->y, &x, &y);
EnsurePopupPositionIsValid(window, &orig_x, &orig_y);
GetPopupPosition(window, orig_x, orig_y, &x, &y);
xdg_positioner_set_anchor_rect(wind->shell_surface.xdg.roleobj.popup.positioner, 0, 0, window->parent->driverdata->wl_window_width, window->parent->driverdata->wl_window_height);
xdg_positioner_set_size(wind->shell_surface.xdg.roleobj.popup.positioner, wind->wl_window_width, wind->wl_window_height);
xdg_positioner_set_offset(wind->shell_surface.xdg.roleobj.popup.positioner, x, y);
@ -431,7 +433,7 @@ static void ConfigureWindowGeometry(SDL_Window *window)
/* Ensure that child popup windows are still in bounds. */
for (SDL_Window *child = window->first_child; child; child = child->next_sibling) {
RepositionPopup(child);
RepositionPopup(child, SDL_TRUE);
}
if (data->confined_pointer) {
@ -479,6 +481,31 @@ static void CommitLibdecorFrame(SDL_Window *window)
#endif
}
static void fullscreen_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
{
/* Get the window from the ID as it may have been destroyed */
SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data);
SDL_Window *window = SDL_GetWindowFromID(windowID);
if (window && window->driverdata) {
window->driverdata->fullscreen_deadline_count--;
SetMinMaxDimensions(window);
}
wl_callback_destroy(callback);
}
static struct wl_callback_listener fullscreen_deadline_listener = {
fullscreen_deadline_handler
};
static void FlushFullscreenEvents(SDL_Window *window)
{
while (window->driverdata->fullscreen_deadline_count) {
WAYLAND_wl_display_roundtrip(window->driverdata->waylandData->display);
}
}
static void SetFullscreen(SDL_Window *window, struct wl_output *output)
{
SDL_WindowData *wind = window->driverdata;
@ -489,6 +516,8 @@ static void SetFullscreen(SDL_Window *window, struct wl_output *output)
if (!wind->shell_surface.libdecor.frame) {
return; /* Can't do anything yet, wait for ShowWindow */
}
++wind->fullscreen_deadline_count;
if (output) {
Wayland_SetWindowResizable(SDL_GetVideoDevice(), window, SDL_TRUE);
wl_surface_commit(wind->surface);
@ -504,6 +533,7 @@ static void SetFullscreen(SDL_Window *window, struct wl_output *output)
return; /* Can't do anything yet, wait for ShowWindow */
}
++wind->fullscreen_deadline_count;
if (output) {
Wayland_SetWindowResizable(SDL_GetVideoDevice(), window, SDL_TRUE);
wl_surface_commit(wind->surface);
@ -514,8 +544,9 @@ static void SetFullscreen(SDL_Window *window, struct wl_output *output)
}
}
/* Roundtrip to apply the new state. */
WAYLAND_wl_display_roundtrip(viddata->display);
/* Queue a deadline event */
struct wl_callback *cb = wl_display_sync(viddata->display);
wl_callback_add_listener(cb, &fullscreen_deadline_listener, (void *)((uintptr_t)window->id));
}
static void UpdateWindowFullscreen(SDL_Window *window, SDL_bool fullscreen)
@ -524,26 +555,30 @@ static void UpdateWindowFullscreen(SDL_Window *window, SDL_bool fullscreen)
wind->is_fullscreen = fullscreen;
/* If this configure event is coming from a roundtrip after explicitly
* changing the fullscreen state, don't call back into the
* SDL_SetWindowFullscreen() function.
*/
if (wind->in_fullscreen_transition) {
return;
}
if (fullscreen) {
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
wind->in_fullscreen_transition = SDL_TRUE;
SDL_SetWindowFullscreen(window, SDL_TRUE);
wind->in_fullscreen_transition = SDL_FALSE;
SDL_copyp(&window->current_fullscreen_mode, &window->requested_fullscreen_mode);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
SDL_UpdateFullscreenMode(window, SDL_TRUE, SDL_FALSE);
/* Unconditionally set the output for exclusive fullscreen windows when entering
* fullscreen from a compositor event, as where the compositor will actually
* place the fullscreen window is unknown.
*/
if (window->fullscreen_exclusive && !wind->fullscreen_was_positioned) {
SDL_VideoDisplay *disp = SDL_GetVideoDisplay(window->current_fullscreen_mode.displayID);
if (disp) {
wind->fullscreen_was_positioned = SDL_TRUE;
SetFullscreen(window, disp->driverdata->output);
}
}
}
} else {
/* Don't change the fullscreen flags if the window is hidden or being hidden. */
if ((window->flags & SDL_WINDOW_FULLSCREEN) && !window->is_hiding && !(window->flags & SDL_WINDOW_HIDDEN)) {
wind->in_fullscreen_transition = SDL_TRUE;
SDL_SetWindowFullscreen(window, SDL_FALSE);
wind->in_fullscreen_transition = SDL_FALSE;
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
SDL_UpdateFullscreenMode(window, SDL_FALSE, SDL_FALSE);
wind->fullscreen_was_positioned = SDL_FALSE;
}
}
}
@ -638,6 +673,7 @@ static void handle_configure_xdg_toplevel(void *data,
SDL_bool fullscreen = SDL_FALSE;
SDL_bool maximized = SDL_FALSE;
SDL_bool floating = SDL_TRUE;
SDL_bool tiled = SDL_FALSE;
SDL_bool active = SDL_FALSE;
SDL_bool suspended = SDL_FALSE;
wl_array_for_each (state, states) {
@ -657,6 +693,7 @@ static void handle_configure_xdg_toplevel(void *data,
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
case XDG_TOPLEVEL_STATE_TILED_TOP:
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
tiled = SDL_TRUE;
floating = SDL_FALSE;
break;
case XDG_TOPLEVEL_STATE_SUSPENDED:
@ -669,17 +706,36 @@ static void handle_configure_xdg_toplevel(void *data,
UpdateWindowFullscreen(window, fullscreen);
/* Always send a maximized/restore event; if the event is redundant it will
* automatically be discarded (see src/events/SDL_windowevents.c)
*
* No, we do not get minimize events from xdg-shell, however, the minimized
* state can be programmatically set. The meaning of 'minimized' is compositor
* dependent, but in general, we can assume that the flag should remain set until
* the next focused configure event occurs.
*/
if (active || !(window->flags & SDL_WINDOW_MINIMIZED)) {
if (window->flags & SDL_WINDOW_MINIMIZED) {
/* If we were minimized, send a restored event before possibly sending maximized. */
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
}
SDL_SendWindowEvent(window,
(maximized && !fullscreen) ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED,
0, 0);
}
if (!fullscreen) {
/* xdg_toplevel spec states that this is a suggestion.
* Ignore if less than or greater than max/min size.
*/
if (window->flags & SDL_WINDOW_RESIZABLE) {
if ((floating && !wind->floating) || width == 0 || height == 0) {
if ((floating && !wind->floating) ||
width == 0 || height == 0) {
/* This happens when we're being restored from a
* non-floating state, so use the cached floating size here.
*/
width = wind->floating_width;
height = wind->floating_height;
width = window->floating.w;
height = window->floating.h;
}
} else {
/* If we're a fixed-size window, we know our size for sure.
@ -706,50 +762,28 @@ static void handle_configure_xdg_toplevel(void *data,
}
height = SDL_max(height, window->min_h);
}
/* Always send a maximized/restore event; if the event is redundant it will
* automatically be discarded (see src/events/SDL_windowevents.c)
*
* No, we do not get minimize events from xdg-shell, however, the minimized
* state can be programmatically set. The meaning of 'minimized' is compositor
* dependent, but in general, we can assume that the flag should remain set until
* the next focused configure event occurs.
*/
if (active || !(window->flags & SDL_WINDOW_MINIMIZED)) {
SDL_SendWindowEvent(window,
maximized ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED,
0, 0);
}
/* Store current floating dimensions for restoring */
if (floating) {
wind->floating_width = width;
wind->floating_height = height;
}
} else {
/* Unconditionally set the output for exclusive fullscreen windows when entering
* fullscreen from a compositor event, as where the compositor will actually
* place the fullscreen window is unknown.
*/
if (window->fullscreen_exclusive && !wind->fullscreen_was_positioned) {
SDL_VideoDisplay *disp = SDL_GetVideoDisplay(window->current_fullscreen_mode.displayID);
if (disp) {
wind->fullscreen_was_positioned = SDL_TRUE;
xdg_toplevel_set_fullscreen(xdg_toplevel, disp->driverdata->output);
}
}
if (width == 0 || height == 0) {
width = wind->requested_window_width;
height = wind->requested_window_height;
}
}
wind->requested_window_width = width;
wind->requested_window_height = height;
/* Don't update the dimensions if they haven't changed, or they could overwrite
* a new size set programmatically with old dimensions.
*/
if (width != wind->last_configure_width || height != wind->last_configure_height) {
wind->requested_window_width = width;
wind->requested_window_height = height;
}
wind->last_configure_width = width;
wind->last_configure_height = height;
wind->floating = floating;
wind->suspended = suspended;
wind->active = active;
window->state_not_floating = tiled;
if (wind->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_CONFIGURE) {
wind->surface_status = WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME;
}
@ -940,38 +974,28 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
UpdateWindowFullscreen(window, fullscreen);
if (!fullscreen) {
/* Always send a maximized/restore event; if the event is redundant it will
* automatically be discarded (see src/events/SDL_windowevents.c)
*
* No, we do not get minimize events from libdecor, however, the minimized
* state can be programmatically set. The meaning of 'minimized' is compositor
* dependent, but in general, we can assume that the flag should remain set until
* the next focused configure event occurs.
*/
if (active || !(window->flags & SDL_WINDOW_MINIMIZED)) {
SDL_SendWindowEvent(window,
maximized ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED,
0, 0);
/* Always send a maximized/restore event; if the event is redundant it will
* automatically be discarded (see src/events/SDL_windowevents.c)
*
* No, we do not get minimize events from libdecor, however, the minimized
* state can be programmatically set. The meaning of 'minimized' is compositor
* dependent, but in general, we can assume that the flag should remain set until
* the next focused configure event occurs.
*/
if (active || !(window->flags & SDL_WINDOW_MINIMIZED)) {
if (window->flags & SDL_WINDOW_MINIMIZED) {
/* If we were minimized, send a restored event before possibly sending maximized. */
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
}
SDL_SendWindowEvent(window,
(maximized && !fullscreen) ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED,
0, 0);
}
/* For fullscreen or fixed-size windows we know our size.
* Always assume the configure is wrong.
*/
if (fullscreen) {
/* Unconditionally set the output for exclusive fullscreen windows when entering
* fullscreen from a compositor event, as where the compositor will actually
* place the fullscreen window is unknown.
*/
if (window->fullscreen_exclusive && !wind->fullscreen_was_positioned) {
SDL_VideoDisplay *disp = SDL_GetVideoDisplay(window->current_fullscreen_mode.displayID);
if (disp) {
wind->fullscreen_was_positioned = SDL_TRUE;
libdecor_frame_set_fullscreen(frame, disp->driverdata->output);
}
}
/* FIXME: We have been explicitly told to respect the fullscreen size
* parameters here, even though they are known to be wrong on GNOME at
* bare minimum. If this is wrong, don't blame us, we were explicitly
@ -1008,11 +1032,11 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
/* This usually happens when we're being restored from a
* non-floating state, so use the cached floating size here.
*/
width = wind->floating_width;
height = wind->floating_height;
width = window->floating.w;
height = window->floating.h;
} else {
width = window->w;
height = window->h;
width = window->windowed.w;
height = window->windowed.h;
}
}
}
@ -1036,16 +1060,21 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
}
}
/* Store current floating dimensions for restoring */
if (floating) {
wind->floating_width = width;
wind->floating_height = height;
/* Don't update the dimensions if they haven't changed, or they could overwrite
* a new size set programmatically with old dimensions.
*/
if (width != wind->last_configure_width || height != wind->last_configure_height) {
wind->requested_window_width = width;
wind->requested_window_height = height;
}
/* Store the new state. */
wind->last_configure_width = width;
wind->last_configure_height = height;
wind->floating = floating;
wind->suspended = suspended;
wind->active = active;
window->state_not_floating = tiled;
/* Calculate the new window geometry */
wind->requested_window_width = width;
@ -1178,10 +1207,16 @@ static void Wayland_move_window(SDL_Window *window, SDL_DisplayData *driverdata)
* -flibit
*/
SDL_Rect bounds;
SDL_GetDisplayBounds(displays[i], &bounds);
wind->last_displayID = displays[i];
if (wind->shell_surface_type != WAYLAND_SURFACE_XDG_POPUP) {
/* Need to catch up on fullscreen state here, as the video core may try to update
* the fullscreen window, which on Wayland involves a set fullscreen call, which
* can overwrite older pending state.
*/
FlushFullscreenEvents(window);
SDL_GetDisplayBounds(wind->last_displayID, &bounds);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, bounds.x, bounds.y);
}
break;
@ -1347,10 +1382,27 @@ int Wayland_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window,
return SDL_Unsupported();
}
WAYLAND_wl_display_flush(viddata->display);
return 0;
}
static void show_hide_sync_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
{
/* Get the window from the ID as it may have been destroyed */
SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data);
SDL_Window *window = SDL_GetWindowFromID(windowID);
if (window && window->driverdata) {
SDL_WindowData *wind = window->driverdata;
wind->show_hide_sync_required = SDL_FALSE;
}
wl_callback_destroy(callback);
}
static struct wl_callback_listener show_hide_sync_listener = {
show_hide_sync_handler
};
void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_VideoData *c = _this->driverdata;
@ -1372,6 +1424,13 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
}
/* The window was hidden, but the sync point hasn't yet been reached.
* Pump events to avoid a possible protocol violation.
*/
if (data->show_hide_sync_required) {
WAYLAND_wl_display_roundtrip(c->display);
}
data->surface_status = WAYLAND_SURFACE_STATUS_WAITING_FOR_CONFIGURE;
/* Detach any previous buffers before resetting everything, otherwise when
@ -1442,7 +1501,7 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
xdg_positioner_set_size(data->shell_surface.xdg.roleobj.popup.positioner, window->w, window->h);
/* Set the popup initial position */
EnsurePopupPositionIsValid(window);
EnsurePopupPositionIsValid(window, &window->x, &window->y);
GetPopupPosition(window, window->x, window->y, &position_x, &position_y);
xdg_positioner_set_offset(data->shell_surface.xdg.roleobj.popup.positioner, position_x, position_y);
@ -1561,11 +1620,9 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
}
/*
* Roundtrip required to avoid a possible protocol violation when
* HideWindow was called immediately before ShowWindow.
*/
WAYLAND_wl_display_roundtrip(c->display);
data->show_hide_sync_required = SDL_TRUE;
struct wl_callback *cb = wl_display_sync(_this->driverdata->display);
wl_callback_add_listener(cb, &show_hide_sync_listener, (void*)((uintptr_t)window->id));
/* Send an exposure event to signal that the client should draw. */
if (data->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME) {
@ -1620,6 +1677,13 @@ void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
SDL_WindowData *wind = window->driverdata;
SDL_PropertiesID props = SDL_GetWindowProperties(window);
/* The window was shown, but the sync point hasn't yet been reached.
* Pump events to avoid a possible protocol violation.
*/
if (wind->show_hide_sync_required) {
WAYLAND_wl_display_roundtrip(data->display);
}
wind->surface_status = WAYLAND_SURFACE_STATUS_HIDDEN;
if (wind->server_decoration) {
@ -1659,11 +1723,9 @@ void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
}
/*
* Roundtrip required to avoid a possible protocol violation when
* ShowWindow is called immediately after HideWindow.
*/
WAYLAND_wl_display_roundtrip(data->display);
wind->show_hide_sync_required = SDL_TRUE;
struct wl_callback *cb = wl_display_sync(_this->driverdata->display);
wl_callback_add_listener(cb, &show_hide_sync_listener, (void*)((uintptr_t)window->id));
}
static void handle_xdg_activation_done(void *data,
@ -1753,29 +1815,45 @@ int Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOpe
return 0;
}
void Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window,
static void fullscreen_configure_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
{
/* Get the window from the ID as it may have been destroyed */
SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data);
SDL_Window *window = SDL_GetWindowFromID(windowID);
if (window && window->driverdata && window->driverdata->is_fullscreen) {
ConfigureWindowGeometry(window);
CommitLibdecorFrame(window);
}
wl_callback_destroy(callback);
}
static struct wl_callback_listener fullscreen_configure_listener = {
fullscreen_configure_handler
};
int Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window,
SDL_VideoDisplay *display, SDL_bool fullscreen)
{
SDL_WindowData *wind = window->driverdata;
struct wl_output *output = display->driverdata->output;
/* Called from within a configure event or the window is a popup, drop it. */
if (wind->in_fullscreen_transition || wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
if (!fullscreen) {
/* Clear the fullscreen positioned flag. */
wind->fullscreen_was_positioned = SDL_FALSE;
}
return;
if (wind->show_hide_sync_required) {
WAYLAND_wl_display_roundtrip(_this->driverdata->display);
}
/* If we're here, this was called from a higher-level video subsystem function.
* Set the flag to avoid recursively re-entering these functions while changing the
* fullscreen state.
*/
wind->in_fullscreen_transition = SDL_TRUE;
/* Flushing old events pending a new one, ignore this request. */
if (wind->drop_fullscreen_requests) {
return 0;
}
wind->drop_fullscreen_requests = SDL_TRUE;
FlushFullscreenEvents(window);
wind->drop_fullscreen_requests = SDL_FALSE;
/* Don't send redundant fullscreen set/unset events. */
if (wind->is_fullscreen != fullscreen) {
if (fullscreen != wind->is_fullscreen) {
wind->fullscreen_was_positioned = fullscreen;
SetFullscreen(window, fullscreen ? output : NULL);
} else if (wind->is_fullscreen) {
@ -1790,12 +1868,13 @@ void Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window,
wind->fullscreen_was_positioned = SDL_TRUE;
SetFullscreen(window, output);
} else {
ConfigureWindowGeometry(window);
CommitLibdecorFrame(window);
/* Queue a configure event */
struct wl_callback *cb = wl_display_sync(_this->driverdata->display);
wl_callback_add_listener(cb, &fullscreen_configure_listener, (void *)((uintptr_t)window->id));
}
}
wind->in_fullscreen_transition = SDL_FALSE;
return 1;
}
void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
@ -1803,10 +1882,6 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
SDL_VideoData *viddata = _this->driverdata;
SDL_WindowData *wind = window->driverdata;
if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
return;
}
#ifdef HAVE_LIBDECOR_H
if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
if (!wind->shell_surface.libdecor.frame) {
@ -1822,8 +1897,6 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
xdg_toplevel_unset_maximized(wind->shell_surface.xdg.roleobj.toplevel);
}
WAYLAND_wl_display_roundtrip(viddata->display);
}
void Wayland_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered)
@ -1831,10 +1904,6 @@ void Wayland_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_b
SDL_WindowData *wind = window->driverdata;
const SDL_VideoData *viddata = (const SDL_VideoData *)_this->driverdata;
if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
return;
}
#ifdef HAVE_LIBDECOR_H
if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
if (wind->shell_surface.libdecor.frame) {
@ -1879,12 +1948,8 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
SDL_VideoData *viddata = _this->driverdata;
SDL_WindowData *wind = window->driverdata;
if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
return;
}
if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
return;
if (wind->show_hide_sync_required) {
WAYLAND_wl_display_roundtrip(_this->driverdata->display);
}
#ifdef HAVE_LIBDECOR_H
@ -1901,12 +1966,6 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
xdg_toplevel_set_maximized(wind->shell_surface.xdg.roleobj.toplevel);
}
/* Don't roundtrip if this is being called to set the initial state during window creation. */
if (wind->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME ||
wind->surface_status == WAYLAND_SURFACE_STATUS_SHOWN) {
WAYLAND_wl_display_roundtrip(viddata->display);
}
}
void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
@ -1914,16 +1973,14 @@ void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
SDL_VideoData *viddata = _this->driverdata;
SDL_WindowData *wind = window->driverdata;
/* Maximized and minimized flags are mutually exclusive */
window->flags &= ~SDL_WINDOW_MAXIMIZED;
window->flags |= SDL_WINDOW_MINIMIZED;
/* TODO: Check compositor capabilities to see if minimizing is supported */
#ifdef HAVE_LIBDECOR_H
if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
if (!wind->shell_surface.libdecor.frame) {
return; /* Can't do anything yet, wait for ShowWindow */
}
libdecor_frame_set_minimized(wind->shell_surface.libdecor.frame);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
} else
#endif
if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && viddata->shell.xdg) {
@ -1931,12 +1988,7 @@ void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
return; /* Can't do anything yet, wait for ShowWindow */
}
xdg_toplevel_set_minimized(wind->shell_surface.xdg.roleobj.toplevel);
}
/* Don't roundtrip if this is being called to set the initial state during window creation. */
if (wind->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME ||
wind->surface_status == WAYLAND_SURFACE_STATUS_SHOWN) {
WAYLAND_wl_display_roundtrip(viddata->display);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
}
}
@ -2002,7 +2054,7 @@ int Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Propert
}
if (SDL_WINDOW_IS_POPUP(window)) {
EnsurePopupPositionIsValid(window);
EnsurePopupPositionIsValid(window, &window->x, &window->y);
}
data->waylandData = c;
@ -2026,11 +2078,8 @@ int Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Propert
data->requested_window_width = window->w;
data->requested_window_height = window->h;
data->floating_width = window->windowed.w;
data->floating_height = window->windowed.h;
data->surface =
wl_compositor_create_surface(c->compositor);
data->surface = wl_compositor_create_surface(c->compositor);
wl_surface_add_listener(data->surface, &surface_listener, data);
SDL_WAYLAND_register_surface(data->surface);
@ -2084,9 +2133,6 @@ int Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Propert
&fractional_scale_listener, data);
}
/* Moved this call to ShowWindow: wl_surface_commit(data->surface); */
WAYLAND_wl_display_flush(c->display);
/* We may need to create an idle inhibitor for this new window */
Wayland_SuspendScreenSaver(_this);
@ -2137,35 +2183,85 @@ int Wayland_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
return SDL_Unsupported();
}
RepositionPopup(window);
RepositionPopup(window, SDL_FALSE);
return 0;
} else if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR || wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
const int x = window->floating.x;
const int y = window->floating.y;
/* Catch up on any pending state before attempting to change the fullscreen window
* display via a set fullscreen call to make sure the window doesn't have a pending
* leave fullscreen event that it might override.
*/
FlushFullscreenEvents(window);
/* XXX: Need to restore this after the roundtrip, as the requested coordinates might
* have been overwritten by the 'real' coordinates if a display enter/leave event
* occurred.
*
* The common pattern:
*
* SDL_SetWindowPosition();
* SDL_SetWindowFullscreen();
*
* for positioning a desktop fullscreen window won't work without this.
*/
window->floating.x = x;
window->floating.y = y;
if (wind->is_fullscreen) {
SDL_VideoDisplay *display = SDL_GetVideoDisplayForFullscreenWindow(window);
if (display && wind->last_displayID != display->id) {
struct wl_output *output = display->driverdata->output;
SetFullscreen(window, output);
return 0;
}
}
}
return SDL_SetError("wayland cannot position non-popup windows");
}
static void size_event_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
{
/* Get the window from the ID as it may have been destroyed */
SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data);
SDL_Window *window = SDL_GetWindowFromID(windowID);
if (window && window->driverdata) {
SDL_WindowData *wind = window->driverdata;
/* Fullscreen windows do not get explicitly resized, and not strictly
* obeying the size of maximized windows is a protocol violation.
*/
if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED))) {
wind->requested_window_width = wind->pending_size_event.width;
wind->requested_window_height = wind->pending_size_event.height;
ConfigureWindowGeometry(window);
}
/* Always commit, as this may be in response to a min/max limit change. */
CommitLibdecorFrame(window);
}
wl_callback_destroy(callback);
}
static struct wl_callback_listener size_event_listener = {
size_event_handler
};
void Wayland_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *wind = window->driverdata;
/*
* Unconditionally store the floating size, as it will need
* to be applied when returning from a non-floating state.
*/
wind->floating_width = window->windowed.w;
wind->floating_height = window->windowed.h;
/* Queue an event to send the window size. */
struct wl_callback *cb = wl_display_sync(_this->driverdata->display);
/* Fullscreen windows do not get explicitly resized, and not strictly
* obeying the size of maximized windows is a protocol violation.
*/
if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED))) {
wind->requested_window_width = window->windowed.w;
wind->requested_window_height = window->windowed.h;
ConfigureWindowGeometry(window);
}
/* Always commit, as this may be in response to a min/max limit change. */
CommitLibdecorFrame(window);
wind->pending_size_event.width = window->floating.w;
wind->pending_size_event.height = window->floating.h;
wl_callback_add_listener(cb, &size_event_listener, (void*)((uintptr_t)window->id));
}
void Wayland_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h)
@ -2181,29 +2277,22 @@ void Wayland_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, i
void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *wind = window->driverdata;
SDL_VideoData *viddata = _this->driverdata;
const char *title = window->title ? window->title : "";
if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
return;
}
#ifdef HAVE_LIBDECOR_H
if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
if (!wind->shell_surface.libdecor.frame) {
return; /* Can't do anything yet, wait for ShowWindow */
}
if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && wind->shell_surface.libdecor.frame) {
libdecor_frame_set_title(wind->shell_surface.libdecor.frame, title);
} else
#endif
if (viddata->shell.xdg) {
if (wind->shell_surface.xdg.roleobj.toplevel == NULL) {
return; /* Can't do anything yet, wait for ShowWindow */
}
if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && wind->shell_surface.xdg.roleobj.toplevel) {
xdg_toplevel_set_title(wind->shell_surface.xdg.roleobj.toplevel, title);
}
}
WAYLAND_wl_display_flush(viddata->display);
int Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
WAYLAND_wl_display_roundtrip(_this->driverdata->display);
return 0;
}
void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y)

View File

@ -95,9 +95,6 @@ struct SDL_WindowData
struct wp_viewport *draw_viewport;
struct wp_fractional_scale_v1 *fractional_scale;
/* floating dimensions for restoring from maximized and fullscreen */
int floating_width, floating_height;
SDL_AtomicInt swap_interval_ready;
SDL_DisplayData **outputs;
@ -109,18 +106,27 @@ struct SDL_WindowData
float windowed_scale_factor;
float pointer_scale_x;
float pointer_scale_y;
struct
{
int width, height;
} pending_size_event;
int last_configure_width, last_configure_height;
int requested_window_width, requested_window_height;
int drawable_width, drawable_height;
int wl_window_width, wl_window_height;
int system_min_required_width;
int system_min_required_height;
SDL_DisplayID last_displayID;
SDL_bool floating;
SDL_bool suspended;
SDL_bool active;
SDL_bool is_fullscreen;
SDL_bool in_fullscreen_transition;
SDL_bool fullscreen_was_positioned;
int fullscreen_deadline_count;
SDL_bool floating : 1;
SDL_bool suspended : 1;
SDL_bool active : 1;
SDL_bool is_fullscreen : 1;
SDL_bool drop_fullscreen_requests : 1;
SDL_bool fullscreen_was_positioned : 1;
SDL_bool show_hide_sync_required : 1;
SDL_HitTestResult hit_test_result;
};
@ -128,7 +134,7 @@ struct SDL_WindowData
extern void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window,
extern int Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window,
SDL_VideoDisplay *_display,
SDL_bool fullscreen);
extern void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
@ -153,5 +159,6 @@ extern int Wayland_SuspendScreenSaver(SDL_VideoDevice *_this);
extern int Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
extern int Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
extern int Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
#endif /* SDL_waylandwindow_h_ */

View File

@ -1009,11 +1009,38 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
#endif /* WM_GETMINMAXINFO */
case WM_WINDOWPOSCHANGING:
{
WINDOWPOS *windowpos = (WINDOWPOS*)lParam;
if (data->expected_resize) {
returnCode = 0;
}
break;
if (IsIconic(hwnd)) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
} else if (IsZoomed(hwnd)) {
if (data->window->flags & SDL_WINDOW_MINIMIZED) {
/* If going from minimized to maximized, send the restored event first. */
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
}
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
} else {
SDL_bool was_fixed_size = !!(data->window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED));
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
/* Send the stored floating size if moving from a fixed-size to floating state. */
if (was_fixed_size && !(data->window->flags & SDL_WINDOW_FULLSCREEN)) {
int fx, fy, fw, fh;
WIN_AdjustWindowRect(data->window, &fx, &fy, &fw, &fh, SDL_WINDOWRECT_FLOATING);
windowpos->x = fx;
windowpos->y = fy;
windowpos->cx = fw;
windowpos->cy = fh;
windowpos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE);
}
}
} break;
case WM_WINDOWPOSCHANGED:
{
@ -1082,7 +1109,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
for (win = data->window->first_child; win; win = win->next_sibling) {
/* Don't update hidden child windows, their relative position doesn't change */
if (!(win->flags & SDL_WINDOW_HIDDEN)) {
WIN_SetWindowPositionInternal(win, SWP_NOCOPYBITS | SWP_NOACTIVATE);
WIN_SetWindowPositionInternal(win, SWP_NOCOPYBITS | SWP_NOACTIVATE, SDL_WINDOWRECT_CURRENT);
}
}
} break;
@ -1112,28 +1139,6 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
KillTimer(hwnd, (UINT_PTR)SDL_IterateMainCallbacks);
} break;
case WM_SIZE:
{
switch (wParam) {
case SIZE_MAXIMIZED:
SDL_SendWindowEvent(data->window,
SDL_EVENT_WINDOW_RESTORED, 0, 0);
SDL_SendWindowEvent(data->window,
SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
break;
case SIZE_MINIMIZED:
SDL_SendWindowEvent(data->window,
SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
break;
case SIZE_RESTORED:
SDL_SendWindowEvent(data->window,
SDL_EVENT_WINDOW_RESTORED, 0, 0);
break;
default:
break;
}
} break;
case WM_SETCURSOR:
{
Uint16 hittest;

View File

@ -277,7 +277,8 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
device->free = WIN_DeleteDevice;
device->quirk_flags = VIDEO_DEVICE_QUIRK_HAS_POPUP_WINDOW_SUPPORT;
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
return device;
}

View File

@ -154,7 +154,7 @@ static DWORD GetWindowStyleEx(SDL_Window *window)
* Returns arguments to pass to SetWindowPos - the window rect, including frame, in Windows coordinates.
* Can be called before we have a HWND.
*/
static int WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current)
static int WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_WindowRect rect_type)
{
SDL_VideoData *videodata = SDL_GetVideoDevice() ? SDL_GetVideoDevice()->driverdata : NULL;
RECT rect;
@ -163,12 +163,26 @@ static int WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL m
#endif
/* Client rect, in points */
SDL_RelativeToGlobalForWindow(window,
(use_current ? window->x : window->windowed.x),
(use_current ? window->y : window->windowed.y),
x, y);
*width = (use_current ? window->w : window->windowed.w);
*height = (use_current ? window->h : window->windowed.h);
switch (rect_type) {
case SDL_WINDOWRECT_CURRENT:
SDL_RelativeToGlobalForWindow(window,window->x, window->y, x, y);
*width = window->w;
*height = window->h;
break;
case SDL_WINDOWRECT_WINDOWED:
SDL_RelativeToGlobalForWindow(window,window->windowed.x, window->windowed.y, x, y);
*width = window->windowed.w;
*height = window->windowed.h;
break;
case SDL_WINDOWRECT_FLOATING:
SDL_RelativeToGlobalForWindow(window,window->floating.x, window->floating.y, x, y);
*width = window->floating.w;
*height = window->floating.h;
break;
default:
/* Should never be here */
SDL_assert_release(SDL_FALSE);
}
/* Copy the client size in pixels into this rect structure,
which we'll then adjust with AdjustWindowRectEx */
@ -210,16 +224,16 @@ static int WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL m
#ifdef HIGHDPI_DEBUG
SDL_Log("WIN_AdjustWindowRectWithStyle: in: %d, %d, %dx%d, returning: %d, %d, %dx%d, used dpi %d for frame calculation",
(use_current ? window->x : window->windowed.x),
(use_current ? window->y : window->windowed.y),
(use_current ? window->w : window->windowed.w),
(use_current ? window->h : window->windowed.h),
(use_current ? window->requested.x : window->windowed.x),
(use_current ? window->requested.y : window->windowed.y),
(use_current ? window->requested.w : window->windowed.w),
(use_current ? window->requested.h : window->windowed.h),
*x, *y, *width, *height, frame_dpi);
#endif
return 0;
}
static void WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_bool use_current)
void WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_WindowRect rect_type)
{
SDL_WindowData *data = window->driverdata;
HWND hwnd = data->hwnd;
@ -232,10 +246,10 @@ static void WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width,
#else
menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
#endif
WIN_AdjustWindowRectWithStyle(window, style, menu, x, y, width, height, use_current);
WIN_AdjustWindowRectWithStyle(window, style, menu, x, y, width, height, rect_type);
}
int WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags)
int WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags, SDL_WindowRect rect_type)
{
SDL_Window *child_window;
SDL_WindowData *data = window->driverdata;
@ -252,7 +266,7 @@ int WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags)
top = HWND_NOTOPMOST;
}
WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_TRUE);
WIN_AdjustWindowRect(window, &x, &y, &w, &h, rect_type);
data->expected_resize = SDL_TRUE;
if (SetWindowPos(hwnd, top, x, y, w, h, flags) == 0) {
@ -262,7 +276,7 @@ int WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags)
/* Update any child windows */
for (child_window = window->first_child; child_window; child_window = child_window->next_sibling) {
if (WIN_SetWindowPositionInternal(child_window, flags) < 0) {
if (WIN_SetWindowPositionInternal(child_window, flags, SDL_WINDOWRECT_CURRENT) < 0) {
result = -1;
}
}
@ -353,7 +367,7 @@ static int SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwnd
/* We tried to create a window larger than the desktop and Windows didn't allow it. Override! */
int x, y;
/* Figure out what the window area will be */
WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_FALSE);
WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_WINDOWRECT_FLOATING);
data->expected_resize = SDL_TRUE;
SetWindowPos(hwnd, NULL, x, y, w, h, data->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
data->expected_resize = SDL_FALSE;
@ -518,8 +532,8 @@ static void WIN_ConstrainPopup(SDL_Window *window)
SDL_Window *w;
SDL_DisplayID displayID;
SDL_Rect rect;
int abs_x = window->x;
int abs_y = window->y;
int abs_x = window->floating.x;
int abs_y = window->floating.y;
int offset_x = 0, offset_y = 0;
/* Calculate the total offset from the parents */
@ -536,17 +550,17 @@ static void WIN_ConstrainPopup(SDL_Window *window)
/* Constrain the popup window to the display of the toplevel parent */
displayID = SDL_GetDisplayForWindow(w);
SDL_GetDisplayBounds(displayID, &rect);
if (abs_x + window->w > rect.x + rect.w) {
abs_x -= (abs_x + window->w) - (rect.x + rect.w);
if (abs_x + window->floating.w > rect.x + rect.w) {
abs_x -= (abs_x + window->floating.w) - (rect.x + rect.w);
}
if (abs_y + window->h > rect.y + rect.h) {
abs_y -= (abs_y + window->h) - (rect.y + rect.h);
if (abs_y + window->floating.h > rect.y + rect.h) {
abs_y -= (abs_y + window->floating.h) - (rect.y + rect.h);
}
abs_x = SDL_max(abs_x, rect.x);
abs_y = SDL_max(abs_y, rect.y);
window->x = window->windowed.x = abs_x - offset_x;
window->y = window->windowed.y = abs_y - offset_y;
window->floating.x = abs_x - offset_x;
window->floating.y = abs_y - offset_y;
}
}
@ -590,7 +604,7 @@ int WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesI
/* Figure out what the window area will be */
WIN_ConstrainPopup(window);
WIN_AdjustWindowRectWithStyle(window, style, FALSE, &x, &y, &w, &h, SDL_FALSE);
WIN_AdjustWindowRectWithStyle(window, style, FALSE, &x, &y, &w, &h, SDL_WINDOWRECT_FLOATING);
hwnd = CreateWindowEx(styleEx, SDL_Appname, TEXT(""), style,
x, y, w, h, parent, NULL, SDL_Instance, NULL);
@ -774,13 +788,25 @@ int WIN_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
/* HighDPI support: removed SWP_NOSIZE. If the move results in a DPI change, we need to allow
* the window to resize (e.g. AdjustWindowRectExForDpi frame sizes are different).
*/
WIN_ConstrainPopup(window);
return WIN_SetWindowPositionInternal(window, window->driverdata->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
WIN_ConstrainPopup(window);
return WIN_SetWindowPositionInternal(window,
window->driverdata->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER |
SWP_NOACTIVATE, SDL_WINDOWRECT_FLOATING);
}
} else {
return SDL_UpdateFullscreenMode(window, SDL_TRUE, SDL_TRUE);
}
return 0;
}
void WIN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
{
WIN_SetWindowPositionInternal(window, window->driverdata->copybits_flag | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED))) {
WIN_SetWindowPositionInternal(window, window->driverdata->copybits_flag | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_FLOATING);
}
}
int WIN_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right)
@ -967,14 +993,24 @@ void WIN_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
void WIN_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
/* Other platforms refuse to maximize a non-resizable window, and with win32,
the OS resizes the window weirdly (covering the taskbar) if you don't have
the STYLE_RESIZABLE flag set. So just forbid it for now. */
if (window->flags & SDL_WINDOW_RESIZABLE) {
SDL_WindowData *data = window->driverdata;
HWND hwnd = data->hwnd;
SDL_WindowData *data = window->driverdata;
HWND hwnd = data->hwnd;
data->expected_resize = SDL_TRUE;
ShowWindow(hwnd, SW_MAXIMIZE);
data->expected_resize = SDL_FALSE;
/* Clamp the maximized window size to the max window size.
* This is automatic if maximizing from the window controls.
*/
if (window->max_w || window->max_h) {
int fx, fy, fw, fh;
window->windowed.w = window->max_w ? SDL_min(window->w, window->max_w) : window->windowed.w;
window->windowed.h = window->max_h ? SDL_min(window->h, window->max_h) : window->windowed.h;
WIN_AdjustWindowRect(window, &fx, &fy, &fw, &fh, SDL_WINDOWRECT_WINDOWED);
data->expected_resize = SDL_TRUE;
ShowWindow(hwnd, SW_MAXIMIZE);
SetWindowPos(hwnd, HWND_TOP, fx, fy, fw, fh, data->copybits_flag | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
data->expected_resize = SDL_FALSE;
}
}
@ -997,7 +1033,7 @@ void WIN_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool
data->in_border_change = SDL_TRUE;
SetWindowLong(hwnd, GWL_STYLE, style);
WIN_SetWindowPositionInternal(window, data->copybits_flag | SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
WIN_SetWindowPositionInternal(window, data->copybits_flag | SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_CURRENT);
data->in_border_change = SDL_FALSE;
}
@ -1016,7 +1052,7 @@ void WIN_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool
void WIN_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool on_top)
{
WIN_SetWindowPositionInternal(window, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
WIN_SetWindowPositionInternal(window, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE, SDL_WINDOWRECT_CURRENT);
}
void WIN_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
@ -1031,7 +1067,7 @@ void WIN_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
/**
* Reconfigures the window to fill the given display, if fullscreen is true, otherwise restores the window.
*/
void WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
int WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
{
#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
SDL_DisplayData *displaydata = display->driverdata;
@ -1043,13 +1079,6 @@ void WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_Vid
int x, y;
int w, h;
if (!fullscreen && (window->flags & SDL_WINDOW_FULLSCREEN)) {
/* Resizing the window on hide causes problems restoring it in Wine, and it's unnecessary.
* Also, Windows would preview the minimized window with the wrong size.
*/
return;
}
#ifdef HIGHDPI_DEBUG
SDL_Log("WIN_SetWindowFullscreen: %d", (int)fullscreen);
#endif
@ -1060,19 +1089,20 @@ void WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_Vid
top = HWND_NOTOPMOST;
}
style = GetWindowLong(hwnd, GWL_STYLE);
style &= ~STYLE_MASK;
style |= GetWindowStyle(window);
/* Use GetMonitorInfo instead of WIN_GetDisplayBounds because we want the
monitor bounds in Windows coordinates (pixels) rather than SDL coordinates (points). */
SDL_zero(minfo);
minfo.cbSize = sizeof(MONITORINFO);
if (!GetMonitorInfo(displaydata->MonitorHandle, &minfo)) {
SDL_SetError("GetMonitorInfo failed");
return;
return -1;
}
SDL_SendWindowEvent(window, fullscreen ? SDL_EVENT_WINDOW_ENTER_FULLSCREEN : SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
style = GetWindowLong(hwnd, GWL_STYLE);
style &= ~STYLE_MASK;
style |= GetWindowStyle(window);
if (fullscreen) {
x = minfo.rcMonitor.left;
y = minfo.rcMonitor.top;
@ -1097,11 +1127,14 @@ void WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_Vid
*/
if (data->windowed_mode_was_maximized && !data->in_window_deactivation) {
style |= WS_MAXIMIZE;
data->windowed_mode_was_maximized = SDL_FALSE;
}
menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE);
WIN_AdjustWindowRectWithStyle(window, style, menu,
&x, &y,
&w, &h,
data->windowed_mode_was_maximized ? SDL_WINDOWRECT_WINDOWED : SDL_WINDOWRECT_FLOATING);
data->windowed_mode_was_maximized = SDL_FALSE;
}
SetWindowLong(hwnd, GWL_STYLE, style);
data->expected_resize = SDL_TRUE;
@ -1113,6 +1146,7 @@ void WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_Vid
#endif
#endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
return 0;
}
#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
@ -1307,7 +1341,7 @@ void WIN_OnWindowEnter(SDL_VideoDevice *_this, SDL_Window *window)
}
if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) {
WIN_SetWindowPositionInternal(window, data->copybits_flag | SWP_NOSIZE | SWP_NOACTIVATE);
WIN_SetWindowPositionInternal(window, data->copybits_flag | SWP_NOSIZE | SWP_NOACTIVATE, SDL_WINDOWRECT_CURRENT);
}
}

View File

@ -34,6 +34,13 @@
extern "C" {
#endif
typedef enum SDL_WindowRect
{
SDL_WINDOWRECT_CURRENT,
SDL_WINDOWRECT_WINDOWED,
SDL_WINDOWRECT_FLOATING
} SDL_WindowRect;
struct SDL_WindowData
{
SDL_Window *window;
@ -91,7 +98,7 @@ extern void WIN_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered);
extern void WIN_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool resizable);
extern void WIN_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool on_top);
extern void WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern int WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern void WIN_UpdateWindowICCProfile(SDL_Window *window, SDL_bool send_event);
extern void *WIN_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
extern void WIN_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window);
@ -104,9 +111,10 @@ extern int WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
extern void WIN_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept);
extern int WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
extern void WIN_UpdateDarkModeForHWND(HWND hwnd);
extern int WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags);
extern int WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags, SDL_WindowRect rect_type);
extern void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
extern int WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable);
extern void WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_WindowRect rect_type);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus

View File

@ -76,7 +76,7 @@ static void WINRT_VideoQuit(SDL_VideoDevice *_this);
/* Window functions */
static int WINRT_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
static void WINRT_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
static void WINRT_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
static int WINRT_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
static void WINRT_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
/* Misc functions */
@ -734,12 +734,14 @@ void WINRT_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
{
#if NTDDI_VERSION >= NTDDI_WIN10
SDL_WindowData *data = window->driverdata;
const Windows::Foundation::Size size((float)window->w, (float)window->h);
data->appView->TryResizeView(size); // TODO, WinRT: return failure (to caller?) from TryResizeView()
const Windows::Foundation::Size size((float)window->floating.w, (float)window->floating.h);
if (data->appView->TryResizeView(size)) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h);
}
#endif
}
void WINRT_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
int WINRT_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
{
#if NTDDI_VERSION >= NTDDI_WIN10
SDL_WindowData *data = window->driverdata;
@ -747,7 +749,7 @@ void WINRT_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_V
if (isWindowActive) {
if (fullscreen) {
if (!data->appView->IsFullScreenMode) {
data->appView->TryEnterFullScreenMode(); // TODO, WinRT: return failure (to caller?) from TryEnterFullScreenMode()
return data->appView->TryEnterFullScreenMode() ? 0 : -1;
}
} else {
if (data->appView->IsFullScreenMode) {
@ -756,6 +758,8 @@ void WINRT_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_V
}
}
#endif
return 0;
}
void WINRT_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)

View File

@ -39,6 +39,7 @@
#include "../../events/SDL_touch_c.h"
#include "../../core/linux/SDL_system_theme.h"
#include "../../SDL_utils_c.h"
#include "../SDL_sysvideo.h"
#include <stdio.h>
@ -1330,8 +1331,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
int y = xevent->xconfigure.y;
SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED,
x, y);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
#ifdef SDL_USE_IME
if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
@ -1342,16 +1342,21 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
for (w = data->window->first_child; w; w = w->next_sibling) {
/* Don't update hidden child windows, their relative position doesn't change */
if (!(w->flags & SDL_WINDOW_HIDDEN)) {
X11_UpdateWindowPosition(w);
X11_UpdateWindowPosition(w, SDL_TRUE);
}
}
}
if (xevent->xconfigure.width != data->last_xconfigure.width ||
xevent->xconfigure.height != data->last_xconfigure.height) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
xevent->xconfigure.width,
xevent->xconfigure.height);
if (!data->skip_size_count) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
xevent->xconfigure.width,
xevent->xconfigure.height);
} else {
data->skip_size_count--;
}
}
data->last_xconfigure = xevent->xconfigure;
} break;
@ -1621,17 +1626,60 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
}
}
if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
}
if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
}
if (((changed & SDL_WINDOW_MAXIMIZED) || (changed & SDL_WINDOW_MINIMIZED)) &&
(!(flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
}
if (!SDL_WINDOW_IS_POPUP(data->window)) {
if (changed & SDL_WINDOW_FULLSCREEN) {
data->pending_operation &= ~X11_PENDING_OP_FULLSCREEN;
if (flags & SDL_WINDOW_FULLSCREEN) {
if (!(flags & SDL_WINDOW_MINIMIZED)) {
const SDL_bool commit = data->requested_fullscreen_mode.displayID == 0 ||
SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
if (commit) {
/* This was initiated by the compositor, or the mode was changed between the request and the window
* becoming fullscreen. Switch to the application requested mode if necessary.
*/
SDL_copyp(&data->window->current_fullscreen_mode, &data->window->requested_fullscreen_mode);
SDL_UpdateFullscreenMode(data->window, SDL_TRUE, SDL_TRUE);
} else {
SDL_UpdateFullscreenMode(data->window, SDL_TRUE, SDL_FALSE);
}
}
} else {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
SDL_UpdateFullscreenMode(data->window, SDL_FALSE, SDL_TRUE);
}
if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) {
/* Skip the first resize event if the borders are being turned on/off. */
data->skip_size_count = 1;
}
}
if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
data->pending_operation &= ~X11_PENDING_OP_MAXIMIZE;
if ((changed & SDL_WINDOW_MINIMIZED)) {
data->pending_operation &= ~X11_PENDING_OP_RESTORE;
/* If coming out of minimized, send a restore event before sending maximized. */
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
}
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
}
if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) {
data->pending_operation &= ~X11_PENDING_OP_MINIMIZE;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
}
if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
data->pending_operation &= ~X11_PENDING_OP_RESTORE;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
/* Restore the last known floating state if leaving maximized mode */
if (!(flags & SDL_WINDOW_FULLSCREEN)) {
X11_XMoveWindow(display, data->xwindow, data->window->floating.x, data->window->floating.y);
X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h);
}
}
}
if (changed & SDL_WINDOW_OCCLUDED) {
SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0);
}
@ -1645,6 +1693,14 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
X11_UpdateKeymap(_this, SDL_TRUE);
} else if (xevent->xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
X11_GetBorderValues(data);
if (data->border_top != 0 || data->border_left != 0 || data->border_right != 0 || data->border_bottom != 0) {
/* Adjust if the window size changed to accommodate the borders. */
if (data->window->flags & SDL_WINDOW_MAXIMIZED) {
X11_XResizeWindow(display, data->xwindow, data->window->windowed.w, data->window->windowed.h);
} else {
X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h);
}
}
}
} break;
@ -1824,6 +1880,22 @@ void X11_PumpEvents(SDL_VideoDevice *_this)
XEvent xevent;
int i;
/* Check if a display had the mode changed and is waiting for a window to asynchronously become
* fullscreen. If there is no fullscreen window past the elapsed timeout, revert the mode switch.
*/
for (i = 0; i < _this->num_displays; ++i) {
if (_this->displays[i]->driverdata->mode_switch_deadline_ns &&
SDL_GetTicksNS() >= _this->displays[i]->driverdata->mode_switch_deadline_ns) {
if (_this->displays[i]->fullscreen_window) {
_this->displays[i]->driverdata->mode_switch_deadline_ns = 0;
} else {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO,
"Time out elapsed after mode switch on display %" SDL_PRIu32 " with no window becoming fullscreen; reverting", _this->displays[i]->id);
SDL_SetDisplayModeForDisplay(_this->displays[i], NULL);
}
}
}
if (data->last_mode_change_deadline) {
if (SDL_GetTicks() >= data->last_mode_change_deadline) {
data->last_mode_change_deadline = 0; /* assume we're done. */

View File

@ -27,6 +27,11 @@
/* #define X11MODES_DEBUG */
/* Timeout and revert mode switches if the timespan has elapsed without the window becoming fullscreen.
* 5 seconds seems good from testing.
*/
#define MODE_SWITCH_TIMEOUT_NS SDL_NS_PER_SECOND * 5
/* I'm becoming more and more convinced that the application should never
* use XRandR, and it's the window manager's responsibility to track and
* manage display modes for fullscreen windows. Right now XRandR is completely
@ -903,6 +908,12 @@ int X11_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SD
viddata->last_mode_change_deadline = SDL_GetTicks() + (PENDING_FOCUS_TIME * 2);
if (mode != &sdl_display->desktop_mode) {
data->mode_switch_deadline_ns = SDL_GetTicksNS() + MODE_SWITCH_TIMEOUT_NS;
} else {
data->mode_switch_deadline_ns = 0;
}
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
if (data->use_xrandr) {
Display *display = viddata->display;

View File

@ -32,6 +32,8 @@ struct SDL_DisplayData
int x;
int y;
Uint64 mode_switch_deadline_ns;
SDL_bool use_xrandr;
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR

View File

@ -209,6 +209,7 @@ static SDL_VideoDevice *X11_CreateDevice(void)
device->FlashWindow = X11_FlashWindow;
device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu;
device->SetWindowFocusable = X11_SetWindowFocusable;
device->SyncWindow = X11_SyncWindow;
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
device->SetWindowMouseRect = X11_SetWindowMouseRect;
@ -274,7 +275,8 @@ static SDL_VideoDevice *X11_CreateDevice(void)
device->system_theme = SDL_SystemTheme_Get();
#endif
device->quirk_flags = VIDEO_DEVICE_QUIRK_HAS_POPUP_WINDOW_SUPPORT;
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
return device;
}

View File

@ -169,8 +169,8 @@ static void X11_ConstrainPopup(SDL_Window *window)
SDL_Window *w;
SDL_DisplayID displayID;
SDL_Rect rect;
int abs_x = window->x;
int abs_y = window->y;
int abs_x = window->floating.x;
int abs_y = window->floating.y;
int offset_x = 0, offset_y = 0;
/* Calculate the total offset from the parents */
@ -196,8 +196,8 @@ static void X11_ConstrainPopup(SDL_Window *window)
abs_x = SDL_max(abs_x, rect.x);
abs_y = SDL_max(abs_y, rect.y);
window->x = window->windowed.x = abs_x - offset_x;
window->y = window->windowed.y = abs_y - offset_y;
window->floating.x = window->windowed.x = abs_x - offset_x;
window->floating.y = window->windowed.y = abs_y - offset_y;
}
}
@ -352,11 +352,11 @@ static int SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, Window w)
X11_XGetWindowAttributes(data->videodata->display, w, &attrib);
if (!SDL_WINDOW_IS_POPUP(window)) {
window->x = attrib.x;
window->y = attrib.y;
window->x = data->expected.x = attrib.x;
window->y = data->expected.y = attrib.y - data->border_top;
}
window->w = attrib.width;
window->h = attrib.height;
window->w = data->expected.w = attrib.width;
window->h = data->expected.h = attrib.height;
if (attrib.map_state != IsUnmapped) {
window->flags &= ~SDL_WINDOW_HIDDEN;
} else {
@ -611,7 +611,7 @@ int X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesI
X11_ConstrainPopup(window);
}
SDL_RelativeToGlobalForWindow(window,
window->windowed.x, window->windowed.y,
window->floating.x, window->floating.y,
&win_x, &win_y);
/* Always create this with the window->windowed.* fields; if we're
@ -621,7 +621,7 @@ int X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesI
migration to fullscreen after CreateSDLWindow returns, which will
put all the SDL_Window fields and system state as expected. */
w = X11_XCreateWindow(display, RootWindow(display, screen),
win_x, win_y, window->windowed.w, window->windowed.h,
win_x, win_y, window->floating.w, window->floating.h,
0, depth, InputOutput, visual,
(CWOverrideRedirect | CWBackPixmap | CWBorderPixel |
CWBackingStore | CWColormap),
@ -847,90 +847,44 @@ static int X11_CatchAnyError(Display *d, XErrorEvent *e)
return 0;
}
enum check_method
{
COMPARE_POSITION = 1,
COMPARE_SIZE = 2,
COMPARE_DOUBLE_ATTEMPT = 3,
COMPARE_ORIG = 4,
COMPARE_NO_WAIT = 5
};
/* Wait a brief time, or not, to see if the window manager decided to move/resize the window.
* Send MOVED and RESIZED window events */
static void X11_WaitAndSendWindowEvents(SDL_Window *window, int param_timeout, enum check_method method,
int orig_x, int orig_y, int dest_x, int dest_y,
int orig_w, int orig_h, int dest_w, int dest_h)
static int X11_SyncWindowTimeout(SDL_VideoDevice *_this, SDL_Window *window, int param_timeout)
{
SDL_WindowData *data = window->driverdata;
Display *display = data->videodata->display;
Window xwindow = data->xwindow;
int (*prev_handler)(Display *, XErrorEvent *);
int x, y;
XWindowAttributes attrs;
Uint64 timeout = 0;
Window childReturn, root, parent;
Window *children;
unsigned int childCount;
SDL_bool window_size_changed = SDL_FALSE;
int window_position_changed = 0;
int ret = 0;
X11_XSync(display, False);
prev_handler = X11_XSetErrorHandler(X11_CatchAnyError);
if (method != COMPARE_NO_WAIT) {
if (param_timeout) {
timeout = SDL_GetTicks() + param_timeout;
}
/* Get the parent */
X11_XQueryTree(display, xwindow, &root, &parent, &children, &childCount);
while (SDL_TRUE) {
caught_x11_error = SDL_FALSE;
X11_XSync(display, False);
X11_XGetWindowAttributes(display, xwindow, &attrs);
X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
attrs.x, attrs.y, &x, &y, &childReturn);
X11_PumpEvents(_this);
if (method == COMPARE_NO_WAIT) {
if (window->x == data->expected.x && window->y == data->expected.y &&
window->w == data->expected.w && window->h == data->expected.h &&
data->pending_operation == X11_PENDING_OP_NONE) {
/* The window is where it is wanted and nothing is pending. Done. */
break;
}
if (!caught_x11_error) {
if (method == COMPARE_POSITION) {
if (x != orig_x || y != orig_y) {
break; /* window moved, time to go. */
} else if (x == dest_x && y == dest_y) {
break; /* we're at the place we wanted to be anyhow, drop out. */
}
} else if (method == COMPARE_SIZE) {
if (attrs.width != orig_w || attrs.height != orig_h) {
break; /* window changed, time to go. */
} else if (attrs.width == window->w && attrs.height == window->h) {
break; /* we've size we wanted anyhow, drop out. */
}
} else if (method == COMPARE_ORIG) {
if (x != orig_x || y != orig_y || attrs.width != orig_w || attrs.height != orig_h) {
break; /* window moved or resized, time to go. */
}
} else if (method == COMPARE_DOUBLE_ATTEMPT) {
if (x != orig_x || y != orig_y) {
orig_x = x;
orig_y = y;
window_position_changed += 1;
}
if (attrs.width != orig_w || attrs.height != orig_h) {
orig_w = attrs.width;
orig_h = attrs.height;
window_size_changed = SDL_TRUE;
}
/* Wait for at least 2 moves + 1 size changed to have valid values */
if (window_position_changed >= 2 && window_size_changed) {
break; /* window changed, time to go. */
}
}
}
if (SDL_GetTicks() >= timeout) {
/* Timed out without the expected values. Update the requested data so future sync calls won't block. */
data->expected.x = window->x;
data->expected.y = window->y;
data->expected.w = window->w;
data->expected.h = window->h;
data->pending_operation = X11_PENDING_OP_NONE;
ret = 1;
break;
}
@ -938,16 +892,15 @@ static void X11_WaitAndSendWindowEvents(SDL_Window *window, int param_timeout, e
}
if (!caught_x11_error) {
if (SDL_WINDOW_IS_POPUP(window)) {
SDL_GlobalToRelativeForWindow(window, x, y, &x, &y);
}
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height);
X11_PumpEvents(_this);
} else {
ret = -1;
}
X11_XSetErrorHandler(prev_handler);
caught_x11_error = SDL_FALSE;
return ret;
}
int X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon)
@ -1008,45 +961,41 @@ int X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *i
return rc;
}
void X11_UpdateWindowPosition(SDL_Window *window)
void X11_UpdateWindowPosition(SDL_Window *window, SDL_bool use_current_position)
{
SDL_Window *w;
SDL_WindowData *data = window->driverdata;
Display *display = data->videodata->display;
unsigned int childCount;
Window childReturn, root, parent;
Window *children;
XWindowAttributes attrs;
int dest_x, dest_y;
int orig_x, orig_y;
X11_XSync(display, False);
X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount);
X11_XGetWindowAttributes(display, data->xwindow, &attrs);
X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
attrs.x, attrs.y, &orig_x, &orig_y, &childReturn);
const int rel_x = use_current_position ? window->x : window->floating.x;
const int rel_y = use_current_position ? window->y : window->floating.y;
SDL_RelativeToGlobalForWindow(window,
window->x - data->border_left, window->y - data->border_top,
&dest_x, &dest_y);
rel_x - data->border_left, rel_y - data->border_top,
&data->expected.x, &data->expected.y);
/* Attempt to move the window */
X11_XMoveWindow(display, data->xwindow, dest_x, dest_y);
/* Send MOVED/RESIZED event, if needed. Compare with initial/expected position. Timeout 100 */
X11_WaitAndSendWindowEvents(window, 100, COMPARE_POSITION, orig_x, orig_y, dest_x, dest_y, 0, 0, 0, 0);
for (w = window->first_child; w; w = w->next_sibling) {
X11_UpdateWindowPosition(w);
}
X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y);
}
int X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
{
if (SDL_WINDOW_IS_POPUP(window)) {
X11_ConstrainPopup(window);
/* Sync any pending fullscreen or maximize events. */
if (window->driverdata->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE)) {
X11_SyncWindow(_this, window);
}
/* Position will be set when window is de-maximized */
if (window->flags & SDL_WINDOW_MAXIMIZED) {
return 0;
}
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
if (SDL_WINDOW_IS_POPUP(window)) {
X11_ConstrainPopup(window);
}
X11_UpdateWindowPosition(window, SDL_FALSE);
} else {
SDL_UpdateFullscreenMode(window, SDL_TRUE, SDL_TRUE);
}
X11_UpdateWindowPosition(window);
return 0;
}
@ -1074,10 +1023,10 @@ static void X11_SetWMNormalHints(SDL_VideoDevice *_this, SDL_Window *window, XSi
hide/show, because there are supposedly subtle problems with doing so
and transitioning from windowed to fullscreen in Unity.
*/
X11_XResizeWindow(display, data->xwindow, window->w, window->h);
X11_XResizeWindow(display, data->xwindow, window->floating.w, window->floating.h);
SDL_RelativeToGlobalForWindow(window,
window->x - data->border_left,
window->y - data->border_top,
window->floating.x - data->border_left,
window->floating.y - data->border_top,
&dest_x, &dest_y);
X11_XMoveWindow(display, data->xwindow, dest_x, dest_y);
X11_XRaiseWindow(display, data->xwindow);
@ -1133,45 +1082,42 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->driverdata;
Display *display = data->videodata->display;
XWindowAttributes attrs;
int orig_w, orig_h;
X11_XSync(display, False);
X11_XGetWindowAttributes(display, data->xwindow, &attrs);
orig_w = attrs.width;
orig_h = attrs.height;
if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
/* Apparently, if the X11 Window is set to a 'non-resizable' window, you cannot resize it using the X11_XResizeWindow, thus
we must set the size hints to adjust the window size. */
XSizeHints *sizehints = X11_XAllocSizeHints();
long userhints;
X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
sizehints->min_width = sizehints->max_width = window->w;
sizehints->min_height = sizehints->max_height = window->h;
sizehints->flags |= PMinSize | PMaxSize;
X11_SetWMNormalHints(_this, window, sizehints);
X11_XFree(sizehints);
} else {
X11_XResizeWindow(display, data->xwindow, window->w, window->h);
/* Wait for pending maximize operations to complete, or the window can end up in a weird,
* partially-maximized state.
*/
if (data->pending_operation & (X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_FULLSCREEN)) {
X11_SyncWindow(_this, window);
}
/* Timeout occurred and window size didn't change
* window manager likely denied the resize,
* or the new size is the same as the existing:
* - current width: is 'full width'.
* - try to set new width at 'full width + 1', which get truncated to 'full width'.
* - new width is/remains 'full width'
* So, even if we break here as a timeout, we can send an event, since the requested size isn't the same
* as the final size. (even if final size is same as original size).
*/
/* Don't try to resize a maximized or fullscreen window, it will be done on restore. */
if (window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_FULLSCREEN)) {
return;
}
/* Send MOVED/RESIZED event, if needed. Compare with initial/expected size. Timeout 100 */
X11_WaitAndSendWindowEvents(window, 100, COMPARE_SIZE, 0, 0, 0, 0, orig_w, orig_h, window->w, window->h);
if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
/* Apparently, if the X11 Window is set to a 'non-resizable' window, you cannot resize it using the X11_XResizeWindow, thus
* we must set the size hints to adjust the window size.
*/
XSizeHints *sizehints = X11_XAllocSizeHints();
long userhints;
X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
sizehints->min_width = sizehints->max_width = window->floating.w;
sizehints->min_height = sizehints->max_height = window->floating.h;
sizehints->flags |= PMinSize | PMaxSize;
X11_SetWMNormalHints(_this, window, sizehints);
X11_XFree(sizehints);
}
} else {
data->expected.w = window->floating.w;
data->expected.h = window->floating.h;
X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h);
}
}
int X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right)
@ -1256,7 +1202,7 @@ void X11_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool
X11_XCheckIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
/* Make sure the window manager didn't resize our window for the difference. */
X11_XResizeWindow(display, data->xwindow, window->w, window->h);
X11_XResizeWindow(display, data->xwindow, window->floating.w, window->floating.h);
X11_XSync(display, False);
}
@ -1330,7 +1276,7 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
if (window->parent) {
/* Update our position in case our parent moved while we were hidden */
X11_UpdateWindowPosition(window);
X11_UpdateWindowPosition(window, SDL_TRUE);
}
/* Whether XMapRaised focuses the window is based on the window type and it is
@ -1365,6 +1311,12 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
/* Get some valid border values, if we haven't them yet */
if (data->border_left == 0 && data->border_right == 0 && data->border_top == 0 && data->border_bottom == 0) {
X11_GetBorderValues(data);
if (!data->initial_border_adjustment) {
data->expected.x += data->border_left;
data->expected.y += data->border_top;
data->initial_border_adjustment = SDL_TRUE;
}
}
}
@ -1398,8 +1350,8 @@ void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
}
/* Send MOVED/RESIZED event, if needed. Immediate, no timeout */
X11_WaitAndSendWindowEvents(window, 0, COMPARE_NO_WAIT, 0, 0, 0, 0, 0, 0, 0, 0);
X11_XSync(display, False);
X11_PumpEvents(_this);
}
static void X11_SetWindowActive(SDL_VideoDevice *_this, SDL_Window *window)
@ -1452,36 +1404,17 @@ static void X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, S
Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
if (maximized) {
window->flags |= SDL_WINDOW_MAXIMIZED;
} else {
window->flags &= ~SDL_WINDOW_MAXIMIZED;
if (window->flags & SDL_WINDOW_FULLSCREEN) {
/* Fullscreen windows are maximized on some window managers,
and this is functional behavior, so don't remove that state
now, we'll take care of it when we leave fullscreen mode.
*/
return;
}
if (!maximized && window->flags & SDL_WINDOW_FULLSCREEN) {
/* Fullscreen windows are maximized on some window managers,
and this is functional behavior, so don't remove that state
now, we'll take care of it when we leave fullscreen mode.
*/
return;
}
if (X11_IsWindowMapped(_this, window)) {
XWindowAttributes attrs;
Window childReturn, root, parent;
Window *children;
unsigned int childCount;
int orig_w, orig_h, orig_x, orig_y;
XEvent e;
X11_XSync(display, False);
X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount);
X11_XGetWindowAttributes(display, data->xwindow, &attrs);
X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
attrs.x, attrs.y, &orig_x, &orig_y, &childReturn);
orig_w = attrs.width;
orig_h = attrs.height;
SDL_zero(e);
e.xany.type = ClientMessage;
e.xclient.message_type = _NET_WM_STATE;
@ -1493,12 +1426,26 @@ static void X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, S
e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ;
e.xclient.data.l[3] = 0l;
if (maximized) {
SDL_DisplayID displayID = SDL_GetDisplayForWindow(window);
SDL_Rect bounds;
SDL_zero(bounds);
SDL_GetDisplayUsableBounds(displayID, &bounds);
data->expected.x = bounds.x + data->border_left;
data->expected.y = bounds.y + data->border_top;
data->expected.w = bounds.w - (data->border_left + data->border_right);
data->expected.h = bounds.h - (data->border_top + data->border_bottom);
} else {
data->expected.x = window->floating.x;
data->expected.y = window->floating.y;
data->expected.w = window->floating.w;
data->expected.h = window->floating.h;
}
X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
SubstructureNotifyMask | SubstructureRedirectMask, &e);
/* Send MOVED/RESIZED event, if needed. Compare with initial position and size. Timeout 1000 */
X11_WaitAndSendWindowEvents(window, 1000, COMPARE_ORIG, orig_x, orig_y, 0, 0, orig_w, orig_h, 0, 0);
} else {
X11_SetNetWMState(_this, data->xwindow, window->flags);
}
@ -1507,7 +1454,14 @@ static void X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, S
void X11_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
X11_SetWindowMaximized(_this, window, SDL_TRUE);
if (window->driverdata->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MINIMIZE)) {
SDL_SyncWindow(window);
}
if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MINIMIZED))) {
window->driverdata->pending_operation |= X11_PENDING_OP_MAXIMIZE;
X11_SetWindowMaximized(_this, window, SDL_TRUE);
}
}
void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
@ -1516,19 +1470,33 @@ void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
Display *display = data->videodata->display;
data->pending_operation |= X11_PENDING_OP_MINIMIZE;
data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED);
X11_XIconifyWindow(display, data->xwindow, displaydata->screen);
X11_XFlush(display);
}
void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
X11_SetWindowMaximized(_this, window, SDL_FALSE);
if (window->driverdata->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MINIMIZE)) {
SDL_SyncWindow(window);
}
if (window->flags & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED) ||
(window->driverdata->pending_operation & X11_PENDING_OP_MINIMIZE)) {
window->driverdata->pending_operation |= X11_PENDING_OP_RESTORE;
}
/* If the window was minimized while maximized, restore as maximized. */
const SDL_bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->driverdata->window_was_maximized;
window->driverdata->window_was_maximized = SDL_FALSE;
X11_SetWindowMaximized(_this, window, maximize);
X11_ShowWindow(_this, window);
X11_SetWindowActive(_this, window);
}
/* This asks the Window Manager to handle fullscreen for us. This is the modern way. */
static void X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_bool fullscreen)
static int X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_bool fullscreen)
{
SDL_WindowData *data = window->driverdata;
SDL_DisplayData *displaydata = _display->driverdata;
@ -1538,19 +1506,16 @@ static void X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *win
if (X11_IsWindowMapped(_this, window)) {
XEvent e;
unsigned int childCount;
Window childReturn, root, parent;
Window *children;
XWindowAttributes attrs;
int orig_w, orig_h, orig_x, orig_y;
X11_XSync(display, False);
X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount);
X11_XGetWindowAttributes(display, data->xwindow, &attrs);
X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
attrs.x, attrs.y, &orig_x, &orig_y, &childReturn);
orig_w = attrs.width;
orig_h = attrs.height;
/* Flush any pending fullscreen events. */
if (data->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE)) {
X11_SyncWindow(_this, window);
}
/* Nothing to do */
if (!fullscreen && !(window->flags & SDL_WINDOW_FULLSCREEN)) {
return 0;
}
if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
/* Compiz refuses fullscreen toggle if we're not resizable, so update the hints so we
@ -1585,20 +1550,36 @@ static void X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *win
X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
SubstructureNotifyMask | SubstructureRedirectMask, &e);
/* Set the position so the window will be on the target display */
if (fullscreen) {
X11_XMoveWindow(display, data->xwindow, displaydata->x, displaydata->y);
if (!!(window->flags & SDL_WINDOW_FULLSCREEN) != fullscreen) {
data->pending_operation |= X11_PENDING_OP_FULLSCREEN;
}
/* Fullscreen windows sometimes end up being marked maximized by
window managers. Force it back to how we expect it to be. */
if (!fullscreen) {
/* Set the position so the window will be on the target display */
if (fullscreen) {
SDL_copyp(&data->requested_fullscreen_mode, &window->current_fullscreen_mode);
if (data->requested_fullscreen_mode.displayID == 0) {
data->requested_fullscreen_mode.displayID = _display->id;
}
if (fullscreen != !!(window->flags & SDL_WINDOW_FULLSCREEN)) {
data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED);
}
data->expected.x = displaydata->x;
data->expected.y = displaydata->y;
data->expected.w = _display->current_mode->w;
data->expected.h = _display->current_mode->h;
X11_XMoveWindow(display, data->xwindow, displaydata->x, displaydata->y);
} else {
SDL_zero(data->requested_fullscreen_mode);
/* Fullscreen windows sometimes end up being marked maximized by
* window managers. Force it back to how we expect it to be.
*/
SDL_zero(e);
e.xany.type = ClientMessage;
e.xclient.message_type = _NET_WM_STATE;
e.xclient.format = 32;
e.xclient.window = data->xwindow;
if (window->flags & SDL_WINDOW_MAXIMIZED) {
if (data->window_was_maximized) {
e.xclient.data.l[0] = _NET_WM_STATE_ADD;
} else {
e.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
@ -1608,22 +1589,23 @@ static void X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *win
e.xclient.data.l[3] = 0l;
X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
SubstructureNotifyMask | SubstructureRedirectMask, &e);
if (!data->window_was_maximized) {
/* Attempt to move the window back to where it was. */
SDL_RelativeToGlobalForWindow(window,
window->floating.x - data->border_left, window->floating.y - data->border_top,
&data->expected.x, &data->expected.y);
data->expected.w = window->floating.w;
data->expected.h = window->floating.h;
X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y);
/* If the window is bordered, the size will be set when the borders turn themselves back on. */
if (window->flags & SDL_WINDOW_BORDERLESS) {
X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h);
}
}
}
if (!fullscreen) {
int dest_x = 0, dest_y = 0;
SDL_RelativeToGlobalForWindow(window,
window->windowed.x - data->border_left, window->windowed.y - data->border_top,
&dest_x, &dest_y);
/* Attempt to move the window */
X11_XMoveWindow(display, data->xwindow, dest_x, dest_y);
}
/* Send MOVED/RESIZED event, if needed. Compare with initial position and size. Timeout 100 */
/* Wait for at least 2 moves + 1 size changed to have valid values */
X11_WaitAndSendWindowEvents(window, 100, COMPARE_DOUBLE_ATTEMPT, orig_x, orig_y, 0, 0, orig_w, orig_h, 0, 0);
} else {
Uint32 flags;
@ -1644,12 +1626,12 @@ static void X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *win
}
}
X11_XFlush(display);
return 1;
}
void X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_bool fullscreen)
int X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_bool fullscreen)
{
X11_SetWindowFullscreenViaWM(_this, window, _display, fullscreen);
return X11_SetWindowFullscreenViaWM(_this, window, _display, fullscreen);
}
typedef struct
@ -1980,6 +1962,15 @@ void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
X11_XFlush(display);
}
int X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
/* 100ms is fine for most cases, but, for some reason, maximizing
* a window can take a very long time.
*/
const int timeout = window->driverdata->pending_operation & SDL_WINDOW_MAXIMIZED ? 1000 : 100;
return X11_SyncWindowTimeout(_this, window, timeout);
}
int X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable)
{
SDL_WindowData *data = window->driverdata;

View File

@ -78,6 +78,22 @@ struct SDL_WindowData
SDL_bool pointer_barrier_active;
PointerBarrier barrier[4];
SDL_Rect barrier_rect;
SDL_Rect expected;
SDL_DisplayMode requested_fullscreen_mode;
enum
{
X11_PENDING_OP_NONE = 0x00,
X11_PENDING_OP_RESTORE = 0x01,
X11_PENDING_OP_MINIMIZE = 0x02,
X11_PENDING_OP_MAXIMIZE = 0x04,
X11_PENDING_OP_FULLSCREEN = 0x08
} pending_operation;
SDL_bool initial_border_adjustment;
SDL_bool window_was_maximized;
int skip_size_count;
#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
SDL_HitTestResult hit_test_result;
};
@ -106,7 +122,7 @@ extern void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered);
extern void X11_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool resizable);
extern void X11_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool on_top);
extern void X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern int X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
extern void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
extern void X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed);
extern void X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed);
@ -115,9 +131,10 @@ extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
extern void X11_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept);
extern int X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
extern void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
extern int X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern int X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable);
int SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title);
void X11_UpdateWindowPosition(SDL_Window *window);
void X11_UpdateWindowPosition(SDL_Window *window, SDL_bool use_current_position);
#endif /* SDL_x11window_h_ */

View File

@ -13,9 +13,11 @@
static SDL_Window *createVideoSuiteTestWindow(const char *title)
{
SDL_Window *window;
SDL_Event event;
int w, h;
SDL_WindowFlags flags;
SDL_bool needs_renderer = SDL_FALSE;
SDL_bool needs_events_pumped = SDL_FALSE;
/* Standard window */
w = SDLTest_RandomIntegerInRange(320, 1024);
@ -37,6 +39,9 @@ static SDL_Window *createVideoSuiteTestWindow(const char *title)
if (session_type && SDL_strcasecmp(session_type, "wayland") == 0) {
needs_renderer = SDL_TRUE;
}
/* X11 needs the initial events pumped, or it can erroneously deliver old configuration events at a later time. */
needs_events_pumped = SDL_TRUE;
}
if (needs_renderer) {
@ -55,6 +60,12 @@ static SDL_Window *createVideoSuiteTestWindow(const char *title)
}
}
if (needs_events_pumped) {
/* Pump out the event queue */
while (SDL_PollEvent(&event)) {
}
}
return window;
}
@ -551,14 +562,19 @@ static int video_getSetWindowGrab(void *arg)
* so that it can be "grabbed" */
SDL_RaiseWindow(window);
{
if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS)) {
int count = 0;
SDL_Event evt;
SDL_zero(evt);
while (SDL_PollEvent(&evt)) {
if (evt.type == SDL_EVENT_WINDOW_FOCUS_GAINED) {
hasFocusGained = SDL_TRUE;
while (!hasFocusGained && count++ < 3) {
while (SDL_PollEvent(&evt)) {
if (evt.type == SDL_EVENT_WINDOW_FOCUS_GAINED) {
hasFocusGained = SDL_TRUE;
}
}
}
} else {
hasFocusGained = SDL_TRUE;
}
SDLTest_AssertCheck(hasFocusGained == SDL_TRUE, "Expectded window with focus");
@ -821,10 +837,13 @@ static int video_getSetWindowPosition(void *arg)
{
const char *title = "video_getSetWindowPosition Test Window";
SDL_Window *window;
int result;
int maxxVariation, maxyVariation;
int xVariation, yVariation;
int referenceX, referenceY;
int currentX, currentY;
int desiredX, desiredY;
SDL_Rect display_bounds;
/* Call against new test window */
window = createVideoSuiteTestWindow(title);
@ -832,17 +851,47 @@ static int video_getSetWindowPosition(void *arg)
return TEST_ABORTED;
}
for (xVariation = 0; xVariation < 4; xVariation++) {
for (yVariation = 0; yVariation < 4; yVariation++) {
/* Sanity check */
SDL_GetWindowPosition(window, &currentX, &currentY);
if (SDL_SetWindowPosition(window, currentX, currentY) < 0) {
SDLTest_Log("Skipping window positioning tests: %s reports window positioning as unsupported", SDL_GetCurrentVideoDriver());
goto null_tests;
}
if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) {
/* The X11 server allows arbitrary window placement, but compositing
* window managers such as GNOME and KDE force windows to be within
* desktop bounds.
*/
maxxVariation = 2;
maxyVariation = 2;
SDL_GetDisplayUsableBounds(SDL_GetPrimaryDisplay(), &display_bounds);
} else if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "cocoa") == 0) {
/* Platform doesn't allow windows with negative Y desktop bounds */
maxxVariation = 4;
maxyVariation = 3;
SDL_GetDisplayUsableBounds(SDL_GetPrimaryDisplay(), &display_bounds);
} else {
/* Platform allows windows to be placed out of bounds */
maxxVariation = 4;
maxyVariation = 4;
SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &display_bounds);
}
for (xVariation = 0; xVariation < maxxVariation; xVariation++) {
for (yVariation = 0; yVariation < maxyVariation; yVariation++) {
switch (xVariation) {
default:
case 0:
/* Zero X Position */
desiredX = 0;
desiredX = display_bounds.x > 0 ? display_bounds.x : 0;
break;
case 1:
/* Random X position inside screen */
desiredX = SDLTest_RandomIntegerInRange(1, 100);
desiredX = SDLTest_RandomIntegerInRange(display_bounds.x + 1, display_bounds.x + 100);
break;
case 2:
/* Random X position outside screen (positive) */
@ -857,15 +906,15 @@ static int video_getSetWindowPosition(void *arg)
switch (yVariation) {
default:
case 0:
/* Zero X Position */
desiredY = 0;
/* Zero Y Position */
desiredY = display_bounds.y > 0 ? display_bounds.y : 0;
break;
case 1:
/* Random X position inside screen */
desiredY = SDLTest_RandomIntegerInRange(1, 100);
/* Random Y position inside screen */
desiredY = SDLTest_RandomIntegerInRange(display_bounds.y + 1, display_bounds.y + 100);
break;
case 2:
/* Random X position outside screen (positive) */
/* Random Y position outside screen (positive) */
desiredY = SDLTest_RandomIntegerInRange(10000, 11000);
break;
case 3:
@ -878,6 +927,10 @@ static int video_getSetWindowPosition(void *arg)
SDL_SetWindowPosition(window, desiredX, desiredY);
SDLTest_AssertPass("Call to SDL_SetWindowPosition(...,%d,%d)", desiredX, desiredY);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
/* Get position */
currentX = desiredX + 1;
currentY = desiredY + 1;
@ -891,9 +944,9 @@ static int video_getSetWindowPosition(void *arg)
SDL_bool hasEvent;
/* SDL_SetWindowPosition() and SDL_SetWindowSize() will make requests of the window manager and set the internal position and size,
* and then we get events signaling what actually happened, and they get passed on to the application if they're not what we expect. */
desiredX = currentX + 1;
desiredY = currentY + 1;
hasEvent = getPositionFromEvent(&desiredX, &desiredY);
currentX = desiredX + 1;
currentY = desiredY + 1;
hasEvent = getPositionFromEvent(&currentX, &currentY);
SDLTest_AssertCheck(hasEvent == SDL_TRUE, "Changing position was not honored by WM, checking present of SDL_EVENT_WINDOW_MOVED");
if (hasEvent) {
SDLTest_AssertCheck(desiredX == currentX, "Verify returned X position is the position from SDL event; expected: %d, got: %d", desiredX, currentX);
@ -915,6 +968,8 @@ static int video_getSetWindowPosition(void *arg)
}
}
null_tests:
/* Dummy call with both pointers NULL */
SDL_GetWindowPosition(window, NULL, NULL);
SDLTest_AssertPass("Call to SDL_GetWindowPosition(&x=NULL,&y=NULL)");
@ -989,9 +1044,13 @@ static int video_getSetWindowSize(void *arg)
int referenceW, referenceH;
int currentW, currentH;
int desiredW, desiredH;
const SDL_bool restoreHint = SDL_GetHintBoolean("SDL_BORDERLESS_RESIZABLE_STYLE", SDL_TRUE);
/* Win32 borderless windows are not resizable by default and need this undocumented hint */
SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", "1");
/* Get display bounds for size range */
result = SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &display);
result = SDL_GetDisplayUsableBounds(SDL_GetPrimaryDisplay(), &display);
SDLTest_AssertPass("SDL_GetDisplayBounds()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
if (result != 0) {
@ -1004,15 +1063,22 @@ static int video_getSetWindowSize(void *arg)
return TEST_ABORTED;
}
#ifdef __WIN32__
/* Platform clips window size to screen size */
maxwVariation = 4;
maxhVariation = 4;
#else
/* Platform allows window size >= screen size */
maxwVariation = 5;
maxhVariation = 5;
#endif
SDL_GetWindowSize(window, &currentW, &currentH);
if (SDL_SetWindowSize(window, currentW, currentH)) {
SDLTest_Log("Skipping window resize tests: %s reports window resizing as unsupported", SDL_GetCurrentVideoDriver());
goto null_tests;
}
if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "windows") == 0 ||
SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) {
/* Platform clips window size to screen size */
maxwVariation = 4;
maxhVariation = 4;
} else {
/* Platform allows window size >= screen size */
maxwVariation = 5;
maxhVariation = 5;
}
for (wVariation = 0; wVariation < maxwVariation; wVariation++) {
for (hVariation = 0; hVariation < maxhVariation; hVariation++) {
@ -1068,6 +1134,11 @@ static int video_getSetWindowSize(void *arg)
SDL_SetWindowSize(window, desiredW, desiredH);
SDLTest_AssertPass("Call to SDL_SetWindowSize(...,%d,%d)", desiredW, desiredH);
/* The sync may time out if changing the size changes the window position. */
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result >= 0, "Verify return value; expected: >=0, got: %d", result);
/* Get size */
currentW = desiredW + 1;
currentH = desiredH + 1;
@ -1081,9 +1152,9 @@ static int video_getSetWindowSize(void *arg)
SDL_bool hasEvent;
/* SDL_SetWindowPosition() and SDL_SetWindowSize() will make requests of the window manager and set the internal position and size,
* and then we get events signaling what actually happened, and they get passed on to the application if they're not what we expect. */
desiredW = currentW + 1;
desiredH = currentH + 1;
hasEvent = getSizeFromEvent(&desiredW, &desiredH);
currentW = desiredW + 1;
currentH = desiredH + 1;
hasEvent = getSizeFromEvent(&currentW, &currentH);
SDLTest_AssertCheck(hasEvent == SDL_TRUE, "Changing size was not honored by WM, checking presence of SDL_EVENT_WINDOW_RESIZED");
if (hasEvent) {
SDLTest_AssertCheck(desiredW == currentW, "Verify returned width is the one from SDL event; expected: %d, got: %d", desiredW, currentW);
@ -1106,6 +1177,8 @@ static int video_getSetWindowSize(void *arg)
}
}
null_tests:
/* Dummy call with both pointers NULL */
SDL_GetWindowSize(window, NULL, NULL);
SDLTest_AssertPass("Call to SDL_GetWindowSize(&w=NULL,&h=NULL)");
@ -1154,6 +1227,9 @@ static int video_getSetWindowSize(void *arg)
SDLTest_AssertPass("Call to SDL_SetWindowSize(window=NULL)");
checkInvalidWindowError();
/* Restore the hint to the previous value */
SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", restoreHint ? "1" : "0");
return TEST_COMPLETED;
}
@ -1694,6 +1770,7 @@ static int video_setWindowCenteredOnDisplay(void *arg)
SDL_SetNumberProperty(props, "w", y);
SDL_SetNumberProperty(props, "width", w);
SDL_SetNumberProperty(props, "height", h);
SDL_SetBooleanProperty(props, "borderless", SDL_TRUE);
window = SDL_CreateWindowWithProperties(props);
SDL_DestroyProperties(props);
SDLTest_AssertPass("Call to SDL_CreateWindow('Title',%d,%d,%d,%d,SHOWN)", x, y, w, h);
@ -1741,6 +1818,10 @@ static int video_setWindowCenteredOnDisplay(void *arg)
result = SDL_SetWindowFullscreen(window, SDL_TRUE);
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
/* Check we are filling the full display */
currentDisplay = SDL_GetDisplayForWindow(window);
SDL_GetWindowSize(window, &currentW, &currentH);
@ -1764,6 +1845,10 @@ static int video_setWindowCenteredOnDisplay(void *arg)
result = SDL_SetWindowFullscreen(window, SDL_FALSE);
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
/* Check window was restored correctly */
currentDisplay = SDL_GetDisplayForWindow(window);
SDL_GetWindowSize(window, &currentW, &currentH);
@ -1793,6 +1878,10 @@ static int video_setWindowCenteredOnDisplay(void *arg)
expectedY = (expectedDisplayRect.y + ((expectedDisplayRect.h - h) / 2));
SDL_SetWindowPosition(window, x, y);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
currentDisplay = SDL_GetDisplayForWindow(window);
SDL_GetWindowSize(window, &currentW, &currentH);
SDL_GetWindowPosition(window, &currentX, &currentY);
@ -1822,6 +1911,367 @@ static int video_setWindowCenteredOnDisplay(void *arg)
return TEST_COMPLETED;
}
/**
* Tests calls to SDL_MaximizeWindow(), SDL_RestoreWindow(), and SDL_SetWindowFullscreen(),
* interspersed with calls to set the window size and position, and verifies the flags,
* sizes, and positions of maximized, fullscreen, and restored windows.
*
* NOTE: This test is good on Mac, Win32, GNOME, and KDE (Wayland and X11). Other *nix
* desktops, particularly tiling desktops, may not support the expected behavior,
* so don't be surprised if this fails.
*/
static int video_getSetWindowState(void *arg)
{
const char *title = "video_getSetWindowState Test Window";
SDL_Window *window;
int result;
SDL_Rect display;
Uint32 flags;
int windowedX, windowedY;
int currentX, currentY;
int desiredX = 0, desiredY = 0;
int windowedW, windowedH;
int currentW, currentH;
int desiredW = 0, desiredH = 0;
Uint32 skipFlags = 0;
const SDL_bool restoreHint = SDL_GetHintBoolean("SDL_BORDERLESS_RESIZABLE_STYLE", SDL_TRUE);
const SDL_bool skipPos = SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0;
/* This test is known to be good only on GNOME and KDE. At the time of writing, Weston seems to have maximize related bugs
* that prevent it from running correctly (no configure events are received when unsetting maximize), and tiling window
* managers such as Sway have fundamental behavioral differences that conflict with it.
*
* Other desktops can be enabled in the future as required.
*/
if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0 || SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) {
const char *desktop = SDL_getenv("XDG_CURRENT_DESKTOP");
if (SDL_strcmp(desktop, "GNOME") != 0 && SDL_strcmp(desktop, "KDE") != 0) {
SDLTest_Log("Skipping test video_getSetWindowState: desktop environment %s not supported", desktop);
return TEST_SKIPPED;
}
}
/* Win32 borderless windows are not resizable by default and need this undocumented hint */
SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", "1");
/* Call against new test window */
window = createVideoSuiteTestWindow(title);
if (!window) {
return TEST_ABORTED;
}
SDL_GetWindowSize(window, &windowedW, &windowedH);
SDLTest_AssertPass("SDL_GetWindowSize()");
SDL_GetWindowPosition(window, &windowedX, &windowedY);
SDLTest_AssertPass("SDL_GetWindowPosition()");
if (skipPos) {
SDLTest_Log("Skipping positioning tests: %s reports window positioning as unsupported", SDL_GetCurrentVideoDriver());
}
/* Maximize and check the dimensions */
result = SDL_MaximizeWindow(window);
SDLTest_AssertPass("SDL_MaximizeWindow()");
if (result < 0) {
SDLTest_Log("Skipping state transition tests: %s reports window maximizing as unsupported", SDL_GetCurrentVideoDriver());
skipFlags |= SDL_WINDOW_MAXIMIZED;
goto minimize_test;
}
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
flags = SDL_GetWindowFlags(window);
SDLTest_AssertPass("SDL_GetWindowFlags()");
SDLTest_AssertCheck(flags & SDL_WINDOW_MAXIMIZED, "Verify the `SDL_WINDOW_MAXIMIZED` flag is set: %s", (flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false");
/* Check that the maximized window doesn't extend beyond the usable display bounds.
* FIXME: Maximizing Win32 borderless windows is broken, so this always fails.
* Skip it for now.
*/
if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "windows") != 0) {
result = SDL_GetDisplayUsableBounds(SDL_GetDisplayForWindow(window), &display);
SDLTest_AssertPass("SDL_GetDisplayUsableBounds()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
desiredW = display.w;
desiredH = display.h;
currentW = windowedW + 1;
currentH = windowedH + 1;
SDL_GetWindowSize(window, &currentW, &currentH);
SDLTest_AssertPass("Call to SDL_GetWindowSize()");
SDLTest_AssertCheck(currentW <= desiredW, "Verify returned width; expected: <= %d, got: %d", desiredW,
currentW);
SDLTest_AssertCheck(currentH <= desiredH, "Verify returned height; expected: <= %d, got: %d", desiredH,
currentH);
}
/* Restore and check the dimensions */
result = SDL_RestoreWindow(window);
SDLTest_AssertPass("SDL_RestoreWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
flags = SDL_GetWindowFlags(window);
SDLTest_AssertPass("SDL_GetWindowFlags()");
SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify that the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false");
if (!skipPos) {
currentX = windowedX + 1;
currentY = windowedY + 1;
SDL_GetWindowPosition(window, &currentX, &currentY);
SDLTest_AssertPass("Call to SDL_GetWindowPosition()");
SDLTest_AssertCheck(windowedX == currentX, "Verify returned X coordinate; expected: %d, got: %d", windowedX, currentX);
SDLTest_AssertCheck(windowedY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", windowedY, currentY);
}
currentW = windowedW + 1;
currentH = windowedH + 1;
SDL_GetWindowSize(window, &currentW, &currentH);
SDLTest_AssertPass("Call to SDL_GetWindowSize()");
SDLTest_AssertCheck(windowedW == currentW, "Verify returned width; expected: %d, got: %d", windowedW, currentW);
SDLTest_AssertCheck(windowedH == currentH, "Verify returned height; expected: %d, got: %d", windowedH, currentH);
/* Maximize, then immediately restore */
result = SDL_MaximizeWindow(window);
SDLTest_AssertPass("SDL_MaximizeWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_RestoreWindow(window);
SDLTest_AssertPass("SDL_RestoreWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
flags = SDL_GetWindowFlags(window);
SDLTest_AssertPass("SDL_GetWindowFlags()");
SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify that the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false");
/* Make sure the restored size and position matches the original windowed size and position. */
if (!skipPos) {
currentX = windowedX + 1;
currentY = windowedY + 1;
SDL_GetWindowPosition(window, &currentX, &currentY);
SDLTest_AssertPass("Call to SDL_GetWindowPosition()");
SDLTest_AssertCheck(windowedX == currentX, "Verify returned X coordinate; expected: %d, got: %d", windowedX, currentX);
SDLTest_AssertCheck(windowedY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", windowedY, currentY);
}
currentW = windowedW + 1;
currentH = windowedH + 1;
SDL_GetWindowSize(window, &currentW, &currentH);
SDLTest_AssertPass("Call to SDL_GetWindowSize()");
SDLTest_AssertCheck(windowedW == currentW, "Verify returned width; expected: %d, got: %d", windowedW, currentW);
SDLTest_AssertCheck(windowedH == currentH, "Verify returned height; expected: %d, got: %d", windowedH, currentH);
/* Maximize, then enter fullscreen */
result = SDL_MaximizeWindow(window);
SDLTest_AssertPass("SDL_MaximizeWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SetWindowFullscreen(window, SDL_TRUE);
SDLTest_AssertPass("SDL_SetWindowFullscreen(SDL_TRUE)");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
flags = SDL_GetWindowFlags(window);
SDLTest_AssertPass("SDL_GetWindowFlags()");
SDLTest_AssertCheck(flags & SDL_WINDOW_FULLSCREEN, "Verify the `SDL_WINDOW_FULLSCREEN` flag is set: %s", (flags & SDL_WINDOW_FULLSCREEN) ? "true" : "false");
SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false");
/* Verify the fullscreen size and position */
result = SDL_GetDisplayBounds(SDL_GetDisplayForWindow(window), &display);
SDLTest_AssertPass("SDL_GetDisplayBounds()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
if (!skipPos) {
desiredX = display.x;
desiredY = display.y;
currentX = windowedX + 1;
currentY = windowedY + 1;
SDL_GetWindowPosition(window, &currentX, &currentY);
SDLTest_AssertPass("Call to SDL_GetWindowPosition()");
SDLTest_AssertCheck(desiredX == currentX, "Verify returned X coordinate; expected: %d, got: %d", desiredX, currentX);
SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", desiredY, currentY);
}
desiredW = display.w;
desiredH = display.h;
currentW = windowedW + 1;
currentH = windowedH + 1;
SDL_GetWindowSize(window, &currentW, &currentH);
SDLTest_AssertPass("Call to SDL_GetWindowSize()");
SDLTest_AssertCheck(currentW == desiredW, "Verify returned width; expected: %d, got: %d", desiredW, currentW);
SDLTest_AssertCheck(currentH == desiredH, "Verify returned height; expected: %d, got: %d", desiredH, currentH);
/* Leave fullscreen and restore the window */
result = SDL_SetWindowFullscreen(window, SDL_FALSE);
SDLTest_AssertPass("SDL_SetWindowFullscreen(SDL_FALSE)");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_RestoreWindow(window);
SDLTest_AssertPass("SDL_RestoreWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
flags = SDL_GetWindowFlags(window);
SDLTest_AssertPass("SDL_GetWindowFlags()");
SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify that the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false");
/* Make sure the restored size and position matches the original windowed size and position. */
if (!skipPos) {
currentX = windowedX + 1;
currentY = windowedY + 1;
SDL_GetWindowPosition(window, &currentX, &currentY);
SDLTest_AssertPass("Call to SDL_GetWindowPosition()");
SDLTest_AssertCheck(windowedX == currentX, "Verify returned X coordinate; expected: %d, got: %d", windowedX, currentX);
SDLTest_AssertCheck(windowedY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", windowedY, currentY);
}
currentW = windowedW + 1;
currentH = windowedH + 1;
SDL_GetWindowSize(window, &currentW, &currentH);
SDLTest_AssertPass("Call to SDL_GetWindowSize()");
SDLTest_AssertCheck(windowedW == currentW, "Verify returned width; expected: %d, got: %d", windowedW, currentW);
SDLTest_AssertCheck(windowedH == currentH, "Verify returned height; expected: %d, got: %d", windowedH, currentH);
/* Maximize, change size, and restore */
result = SDL_MaximizeWindow(window);
SDLTest_AssertPass("SDL_MaximizeWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
desiredW = windowedW + 10;
desiredH = windowedH + 10;
result = SDL_SetWindowSize(window, desiredW, desiredH);
SDLTest_AssertPass("SDL_SetWindowSize()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
if (!skipPos) {
desiredX = windowedX + 10;
desiredY = windowedY + 10;
result = SDL_SetWindowPosition(window, desiredX, desiredY);
SDLTest_AssertPass("SDL_SetWindowPosition()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
}
result = SDL_RestoreWindow(window);
SDLTest_AssertPass("SDL_RestoreWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
flags = SDL_GetWindowFlags(window);
SDLTest_AssertPass("SDL_GetWindowFlags()");
SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify that the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false");
if (!skipPos) {
currentX = desiredX + 1;
currentY = desiredY + 1;
SDL_GetWindowPosition(window, &currentX, &currentY);
SDLTest_AssertPass("Call to SDL_GetWindowPosition()");
SDLTest_AssertCheck(desiredX == currentX, "Verify returned X coordinate; expected: %d, got: %d", desiredX, currentX);
SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", desiredY, currentY);
}
currentW = desiredW + 1;
currentH = desiredH + 1;
SDL_GetWindowSize(window, &currentW, &currentH);
SDLTest_AssertPass("Call to SDL_GetWindowSize()");
SDLTest_AssertCheck(desiredW == currentW, "Verify returned width; expected: %d, got: %d", desiredW, currentW);
SDLTest_AssertCheck(desiredH == currentH, "Verify returned height; expected: %d, got: %d", desiredH, currentH);
/* Change size and position, maximize and restore */
desiredW = windowedW - 5;
desiredH = windowedH - 5;
result = SDL_SetWindowSize(window, desiredW, desiredH);
SDLTest_AssertPass("SDL_SetWindowSize()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
if (!skipPos) {
desiredX = windowedX + 5;
desiredY = windowedY + 5;
result = SDL_SetWindowPosition(window, desiredX, desiredY);
SDLTest_AssertPass("SDL_SetWindowPosition()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
}
result = SDL_MaximizeWindow(window);
SDLTest_AssertPass("SDL_MaximizeWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_RestoreWindow(window);
SDLTest_AssertPass("SDL_RestoreWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
flags = SDL_GetWindowFlags(window);
SDLTest_AssertPass("SDL_GetWindowFlags()");
SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify that the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false");
if (!skipPos) {
currentX = desiredX + 1;
currentY = desiredY + 1;
SDL_GetWindowPosition(window, &currentX, &currentY);
SDLTest_AssertPass("Call to SDL_GetWindowPosition()");
SDLTest_AssertCheck(desiredX == currentX, "Verify returned X coordinate; expected: %d, got: %d", desiredX, currentX);
SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", desiredY, currentY);
}
currentW = desiredW + 1;
currentH = desiredH + 1;
SDL_GetWindowSize(window, &currentW, &currentH);
SDLTest_AssertPass("Call to SDL_GetWindowSize()");
SDLTest_AssertCheck(desiredW == currentW, "Verify returned width; expected: %d, got: %d", desiredW, currentW);
SDLTest_AssertCheck(desiredH == currentH, "Verify returned height; expected: %d, got: %d", desiredH, currentH);
minimize_test:
/* Minimize */
result = SDL_MinimizeWindow(window);
if (result == 0) {
SDLTest_AssertPass("SDL_MinimizeWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
result = SDL_SyncWindow(window);
SDLTest_AssertPass("SDL_SyncWindow()");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
flags = SDL_GetWindowFlags(window);
SDLTest_AssertPass("SDL_GetWindowFlags()");
SDLTest_AssertCheck(flags & SDL_WINDOW_MINIMIZED, "Verify that the `SDL_WINDOW_MINIMIZED` flag is set: %s", (flags & SDL_WINDOW_MINIMIZED) ? "true" : "false");
} else {
SDLTest_Log("Skipping minimize test: %s reports window minimizing as unsupported", SDL_GetCurrentVideoDriver());
skipFlags |= SDL_WINDOW_MINIMIZED;
}
/* Clean up */
destroyVideoSuiteTestWindow(window);
/* Restore the hint to the previous value */
SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", restoreHint ? "1" : "0");
return skipFlags != (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED) ? TEST_COMPLETED : TEST_SKIPPED;
}
/* ================= Test References ================== */
/* Video test cases */
@ -1897,12 +2347,16 @@ static const SDLTest_TestCaseReference videoTest18 = {
(SDLTest_TestCaseFp)video_setWindowCenteredOnDisplay, "video_setWindowCenteredOnDisplay", "Checks using SDL_WINDOWPOS_CENTERED_DISPLAY centers the window on a display", TEST_ENABLED
};
static const SDLTest_TestCaseReference videoTest19 = {
(SDLTest_TestCaseFp)video_getSetWindowState, "video_getSetWindowState", "Checks transitioning between windowed, minimized, maximized, and fullscreen states", TEST_ENABLED
};
/* Sequence of Video test cases */
static const SDLTest_TestCaseReference *videoTests[] = {
&videoTest1, &videoTest2, &videoTest3, &videoTest4, &videoTest5, &videoTest6,
&videoTest7, &videoTest8, &videoTest9, &videoTest10, &videoTest11, &videoTest12,
&videoTest13, &videoTest14, &videoTest15, &videoTest16, &videoTest17,
&videoTest18, NULL
&videoTest18, &videoTest19, NULL
};
/* Video test suite (global) */