diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index 81c930237c..8d59923658 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -1666,7 +1666,7 @@ static bool GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Pr SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR); - if (!SDL_RecreateWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)) | SDL_WINDOW_OPENGL)) { + if (!SDL_ReconfigureWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)) | SDL_WINDOW_OPENGL)) { goto error; } } diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 4cec2f21e0..ec4211fd02 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -2176,7 +2176,7 @@ static bool GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR); - if (!SDL_RecreateWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)) | SDL_WINDOW_OPENGL)) { + if (!SDL_ReconfigureWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)) | SDL_WINDOW_OPENGL)) { goto error; } } diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index f1f7985bb2..3de1c53b88 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -314,6 +314,7 @@ struct SDL_VideoDevice bool (*ApplyWindowProgress)(SDL_VideoDevice *_this, SDL_Window *window); bool (*SetWindowFocusable)(SDL_VideoDevice *_this, SDL_Window *window, bool focusable); bool (*SyncWindow)(SDL_VideoDevice *_this, SDL_Window *window); + bool (*ReconfigureWindow)(SDL_VideoDevice *_this, SDL_Window *window, SDL_WindowFlags flags); /* * * */ /* @@ -574,6 +575,7 @@ extern void SDL_SetWindowSafeAreaInsets(SDL_Window *window, int left, int right, extern void SDL_GL_DeduceMaxSupportedESProfile(int *major, int *minor); extern bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags); +extern bool SDL_ReconfigureWindow(SDL_Window *window, SDL_WindowFlags flags); extern bool SDL_HasWindows(void); extern void SDL_RelativeToGlobalForWindow(SDL_Window *window, int rel_x, int rel_y, int *abs_x, int *abs_y); extern void SDL_GlobalToRelativeForWindow(SDL_Window *window, int abs_x, int abs_y, int *rel_x, int *rel_y); diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index c6e4112dd6..4ce3248dda 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -2634,6 +2634,66 @@ SDL_Window *SDL_CreatePopupWindow(SDL_Window *parent, int offset_x, int offset_y return window; } +static bool SDL_ReconfigureWindowInternal(SDL_Window *window, SDL_WindowFlags flags) +{ + bool loaded_opengl = false; + bool loaded_vulkan = false; + + if (!_this->ReconfigureWindow) { + return false; + } + + // Only attempt to reconfigure if the window has no existing graphics flags. + if (window->flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN)) { + return false; + } + + const SDL_WindowFlags graphics_flags = flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN); + if (graphics_flags & (graphics_flags - 1)) { + return SDL_SetError("Conflicting window flags specified"); + } + + if ((flags & SDL_WINDOW_OPENGL) && !_this->GL_CreateContext) { + return SDL_ContextNotSupported("OpenGL"); + } + if ((flags & SDL_WINDOW_VULKAN) && !_this->Vulkan_CreateSurface) { + return SDL_ContextNotSupported("Vulkan"); + } + if ((flags & SDL_WINDOW_METAL) && !_this->Metal_CreateView) { + return SDL_ContextNotSupported("Metal"); + } + + SDL_DestroyWindowSurface(window); + + if (graphics_flags & SDL_WINDOW_OPENGL) { + loaded_opengl = SDL_GL_LoadLibrary(NULL); + if (!loaded_opengl) { + return false; + } + } else if (graphics_flags & SDL_WINDOW_VULKAN) { + loaded_vulkan = SDL_GL_LoadLibrary(NULL); + if (!loaded_vulkan) { + return false; + } + } + + // Try to reconfigure the window for the requested graphics flags. + if (!_this->ReconfigureWindow(_this, window, graphics_flags)) { + if (loaded_opengl) { + SDL_GL_UnloadLibrary(); + } + if (loaded_vulkan) { + SDL_Vulkan_UnloadLibrary(); + } + + return false; + } + + window->flags |= graphics_flags; + + return true; +} + bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags) { bool loaded_opengl = false; @@ -2788,6 +2848,16 @@ bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags) return true; } +bool SDL_ReconfigureWindow(SDL_Window *window, SDL_WindowFlags flags) +{ + // Try to reconfigure the window for the desired flags first, before completely destroying and recreating it. + if (!SDL_ReconfigureWindowInternal(window, flags)) { + return SDL_RecreateWindow(window, flags); + } + + return true; +} + bool SDL_HasWindows(void) { return _this && _this->windows; diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 68fb9961c8..3286f8aec7 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -660,6 +660,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu; device->SyncWindow = Wayland_SyncWindow; device->SetWindowFocusable = Wayland_SetWindowFocusable; + device->ReconfigureWindow = Wayland_ReconfigureWindow; #ifdef SDL_USE_LIBDBUS if (SDL_SystemTheme_Init()) diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index a8dd4251f8..54f3c71201 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -2543,6 +2543,49 @@ bool Wayland_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, b return true; } +bool Wayland_ReconfigureWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_WindowFlags flags) +{ + SDL_WindowData *data = window->internal; + + if (data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_SHOWN) { + // Window is already mapped; abort. + return false; + } + + /* The caller guarantees that only one of the GL or Vulkan flags will be set, + * and the window will have no previous video flags. + */ + if (flags & SDL_WINDOW_OPENGL) { + if (!data->egl_window) { + data->egl_window = WAYLAND_wl_egl_window_create(data->surface, data->current.pixel_width, data->current.pixel_height); + } + +#ifdef SDL_VIDEO_OPENGL_EGL + // Create the GLES window surface + data->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)data->egl_window); + + if (data->egl_surface == EGL_NO_SURFACE) { + return false; // SDL_EGL_CreateSurface should have set error + } +#endif + + if (!data->gles_swap_frame_event_queue) { + data->gles_swap_frame_event_queue = WAYLAND_wl_display_create_queue(data->waylandData->display); + data->gles_swap_frame_surface_wrapper = WAYLAND_wl_proxy_create_wrapper(data->surface); + WAYLAND_wl_proxy_set_queue((struct wl_proxy *)data->gles_swap_frame_surface_wrapper, data->gles_swap_frame_event_queue); + data->gles_swap_frame_callback = wl_surface_frame(data->gles_swap_frame_surface_wrapper); + wl_callback_add_listener(data->gles_swap_frame_callback, &gles_swap_frame_listener, data); + } + + return true; + } else if (flags & SDL_WINDOW_VULKAN) { + // Nothing to configure for Vulkan. + return true; + } + + return false; +} + bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) { SDL_WindowData *data; diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 945b753365..e16f2d9d1a 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -253,6 +253,7 @@ extern void *Wayland_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *win extern bool Wayland_SetWindowHitTest(SDL_Window *window, bool enabled); extern bool Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); extern bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window); +extern bool Wayland_ReconfigureWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_WindowFlags flags); extern void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, SDL_DisplayData *display_data);