SDL: - Added SDL_WINDOW_TRANSPARENT to request a window with transparent framebuffer

From 2cafa525981b1ec2fe25ced632221d639ec11f96 Mon Sep 17 00:00:00 2001
From: Sylvain <[EMAIL REDACTED]>
Date: Sat, 11 Mar 2023 13:53:14 +0100
Subject: [PATCH] - Added SDL_WINDOW_TRANSPARENT to request a window with
 transparent framebuffer - Remove SDL_VIDEO_EGL_ALLOW_TRANSPARENCY hint, EGL
 now checks 'window->flags & SDL_WINDOW_TRANSPARENT'

---
 include/SDL3/SDL_hints.h                | 11 ------
 include/SDL3/SDL_video.h                |  1 +
 src/core/android/SDL_android.c          |  2 +-
 src/test/SDL_test_common.c              |  7 +++-
 src/video/SDL_egl.c                     |  7 +++-
 src/video/SDL_egl_c.h                   |  2 +-
 src/video/SDL_video.c                   |  7 ++--
 src/video/android/SDL_androidwindow.c   |  2 +-
 src/video/cocoa/SDL_cocoametalview.h    |  3 +-
 src/video/cocoa/SDL_cocoametalview.m    |  9 ++++-
 src/video/cocoa/SDL_cocoaopengl.m       |  4 ++
 src/video/cocoa/SDL_cocoaopengles.m     |  2 +-
 src/video/cocoa/SDL_cocoawindow.m       | 23 ++++++++++--
 src/video/kmsdrm/SDL_kmsdrmvideo.c      |  2 +-
 src/video/raspberry/SDL_rpivideo.c      |  2 +-
 src/video/vita/SDL_vitavideo.c          |  2 +-
 src/video/vivante/SDL_vivantevideo.c    |  2 +-
 src/video/wayland/SDL_waylandvideo.c    |  2 -
 src/video/wayland/SDL_waylandvideo.h    |  1 -
 src/video/wayland/SDL_waylandwindow.c   | 49 ++++---------------------
 src/video/wayland/SDL_waylandwindow.h   |  3 --
 src/video/windows/SDL_windowsopengles.c |  2 +-
 src/video/x11/SDL_x11opengl.c           | 48 +++++++++++++++++-------
 src/video/x11/SDL_x11opengl.h           |  2 +-
 src/video/x11/SDL_x11opengles.c         | 19 +++++++++-
 src/video/x11/SDL_x11opengles.h         |  2 +-
 src/video/x11/SDL_x11window.c           |  7 ++--
 test/testgl.c                           |  2 +-
 test/testsprite.c                       |  2 +-
 test/testvulkan.c                       | 12 +++++-
 30 files changed, 136 insertions(+), 103 deletions(-)

diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index 552d59234d3d..1d2bf9b53a9d 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -1602,17 +1602,6 @@ extern "C" {
  */
 #define SDL_HINT_VIDEO_DOUBLE_BUFFER      "SDL_VIDEO_DOUBLE_BUFFER"
 
-/**
- * \brief A variable controlling whether the EGL window is allowed to be
- * composited as transparent, rather than opaque.
- *
- * Most window systems will always render windows opaque, even if the surface
- * format has an alpha channel. This is not always true, however, so by default
- * SDL will try to enforce opaque composition. To override this behavior, you
- * can set this hint to "1".
- */
-#define SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY "SDL_VIDEO_EGL_ALLOW_TRANSPARENCY"
-
 /**
  * \brief If eglGetPlatformDisplay fails, fall back to calling eglGetDisplay.
  *
diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h
index b0f6dbd629b3..be728962571a 100644
--- a/include/SDL3/SDL_video.h
+++ b/include/SDL3/SDL_video.h
@@ -152,6 +152,7 @@ typedef enum
     SDL_WINDOW_KEYBOARD_GRABBED     = 0x00100000,   /**< window has grabbed keyboard input */
     SDL_WINDOW_VULKAN               = 0x10000000,   /**< window usable for Vulkan surface */
     SDL_WINDOW_METAL                = 0x20000000,   /**< window usable for Metal view */
+    SDL_WINDOW_TRANSPARENT          = 0x40000000,   /**< window with transparent buffer */
 
 } SDL_WindowFlags;
 
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index 20f8b35c47e0..8a927fc22dbf 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -1057,7 +1057,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, j
 
         /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
         if (data->egl_surface == EGL_NO_SURFACE) {
-            data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)data->native_window);
+            data->egl_surface = SDL_EGL_CreateSurface(_this, Android_Window, (NativeWindowType)data->native_window);
         }
 
         /* GL Context handling is done in the event loop because this function is run from the Java thread */
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index 69fa3300338b..b90e05ea0f48 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -33,7 +33,8 @@ static const char *video_usage[] = {
     "[--logical-presentation disabled|match|stretch|letterbox|overscan|integer_scale]",
     "[--logical-scale-quality nearest|linear|best]",
     "[--scale N]", "[--depth N]", "[--refresh R]", "[--vsync]", "[--noframe]",
-    "[--resizable]", "[--minimize]", "[--maximize]", "[--grab]", "[--keyboard-grab]",
+    "[--resizable]", "[--transparent]",
+    "[--minimize]", "[--maximize]", "[--grab]", "[--keyboard-grab]",
     "[--hidden]", "[--input-focus]", "[--mouse-focus]",
     "[--flash-on-focus-loss]", "[--allow-highdpi]", "[--confine-cursor X,Y,W,H]",
     "[--usable-bounds]"
@@ -490,6 +491,10 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
         state->window_flags |= SDL_WINDOW_RESIZABLE;
         return 1;
     }
+    if (SDL_strcasecmp(argv[index], "--transparent") == 0) {
+        state->window_flags |= SDL_WINDOW_TRANSPARENT;
+        return 1;
+    }
     if (SDL_strcasecmp(argv[index], "--minimize") == 0) {
         state->window_flags |= SDL_WINDOW_MINIMIZED;
         return 1;
diff --git a/src/video/SDL_egl.c b/src/video/SDL_egl.c
index ea9a1d372a2d..857dbce177d0 100644
--- a/src/video/SDL_egl.c
+++ b/src/video/SDL_egl.c
@@ -1218,7 +1218,7 @@ int SDL_EGL_DeleteContext(_THIS, SDL_GLContext context)
 }
 
 EGLSurface *
-SDL_EGL_CreateSurface(_THIS, NativeWindowType nw)
+SDL_EGL_CreateSurface(_THIS, SDL_Window *window, NativeWindowType nw)
 {
 #if SDL_VIDEO_DRIVER_ANDROID
     EGLint format_wanted;
@@ -1260,7 +1260,10 @@ SDL_EGL_CreateSurface(_THIS, NativeWindowType nw)
 
 #ifdef EGL_EXT_present_opaque
     if (SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_EXT_present_opaque")) {
-        const SDL_bool allow_transparent = SDL_GetHintBoolean(SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY, SDL_FALSE);
+        SDL_bool allow_transparent = SDL_FALSE;
+        if (window && (window->flags & SDL_WINDOW_TRANSPARENT)) {
+            allow_transparent = SDL_TRUE;
+        }
         attribs[attr++] = EGL_PRESENT_OPAQUE_EXT;
         attribs[attr++] = allow_transparent ? EGL_FALSE : EGL_TRUE;
     }
diff --git a/src/video/SDL_egl_c.h b/src/video/SDL_egl_c.h
index e177b8fd8967..603b21663768 100644
--- a/src/video/SDL_egl_c.h
+++ b/src/video/SDL_egl_c.h
@@ -129,7 +129,7 @@ extern int SDL_EGL_ChooseConfig(_THIS);
 extern int SDL_EGL_SetSwapInterval(_THIS, int interval);
 extern int SDL_EGL_GetSwapInterval(_THIS, int *interval);
 extern int SDL_EGL_DeleteContext(_THIS, SDL_GLContext context);
-extern EGLSurface *SDL_EGL_CreateSurface(_THIS, NativeWindowType nw);
+extern EGLSurface *SDL_EGL_CreateSurface(_THIS, SDL_Window *window, NativeWindowType nw);
 extern void SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface);
 
 extern EGLSurface SDL_EGL_CreateOffscreenSurface(_THIS, int width, int height);
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 762eb7746685..fa3ffe68aca0 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -206,6 +206,7 @@ static int SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, U
 {
     SDL_RendererInfo info;
     SDL_WindowTextureData *data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA);
+    const int transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? SDL_TRUE : SDL_FALSE;
     int i;
     int w, h;
 
@@ -274,12 +275,12 @@ static int SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, U
     SDL_free(data->pixels);
     data->pixels = NULL;
 
-    /* Find the first format without an alpha channel */
+    /* Find the first format with or without an alpha channel */
     *format = info.texture_formats[0];
 
     for (i = 0; i < (int)info.num_texture_formats; ++i) {
         if (!SDL_ISPIXELFORMAT_FOURCC(info.texture_formats[i]) &&
-            !SDL_ISPIXELFORMAT_ALPHA(info.texture_formats[i])) {
+            transparent == SDL_ISPIXELFORMAT_ALPHA(info.texture_formats[i])) {
             *format = info.texture_formats[i];
             break;
         }
@@ -1646,7 +1647,7 @@ Uint32 SDL_GetWindowPixelFormat(SDL_Window *window)
 }
 
 #define CREATE_FLAGS \
-    (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL)
+    (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL | SDL_WINDOW_TRANSPARENT)
 
 static SDL_INLINE SDL_bool IsAcceptingDragAndDrop(void)
 {
diff --git a/src/video/android/SDL_androidwindow.c b/src/video/android/SDL_androidwindow.c
index c932c34c503b..6abc585a7b11 100644
--- a/src/video/android/SDL_androidwindow.c
+++ b/src/video/android/SDL_androidwindow.c
@@ -79,7 +79,7 @@ int Android_CreateWindow(_THIS, SDL_Window *window)
        incompatible with vkCreateAndroidSurfaceKHR */
 #if SDL_VIDEO_OPENGL_EGL
     if (window->flags & SDL_WINDOW_OPENGL) {
-        data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)data->native_window);
+        data->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)data->native_window);
 
         if (data->egl_surface == EGL_NO_SURFACE) {
             ANativeWindow_release(data->native_window);
diff --git a/src/video/cocoa/SDL_cocoametalview.h b/src/video/cocoa/SDL_cocoametalview.h
index d53fc30f9ec8..2712dadd6498 100644
--- a/src/video/cocoa/SDL_cocoametalview.h
+++ b/src/video/cocoa/SDL_cocoametalview.h
@@ -43,7 +43,8 @@
 
 - (instancetype)initWithFrame:(NSRect)frame
                       highDPI:(BOOL)highDPI
-                     windowID:(Uint32)windowID;
+                     windowID:(Uint32)windowID
+                       opaque:(BOOL)opaque;
 
 - (void)updateDrawableSize;
 - (NSView *)hitTest:(NSPoint)point;
diff --git a/src/video/cocoa/SDL_cocoametalview.m b/src/video/cocoa/SDL_cocoametalview.m
index 71509aec511d..d2e3e2ec6252 100644
--- a/src/video/cocoa/SDL_cocoametalview.m
+++ b/src/video/cocoa/SDL_cocoametalview.m
@@ -76,7 +76,8 @@ - (CALayer *)makeBackingLayer
 
 - (instancetype)initWithFrame:(NSRect)frame
                       highDPI:(BOOL)highDPI
-                     windowID:(Uint32)windowID;
+                     windowID:(Uint32)windowID
+                       opaque:(BOOL)opaque
 {
     self = [super initWithFrame:frame];
     if (self != nil) {
@@ -87,6 +88,8 @@ - (instancetype)initWithFrame:(NSRect)frame
         /* Allow resize. */
         self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
 
+        self.layer.opaque = opaque;
+
         SDL_AddEventWatch(SDL_MetalViewEventWatch, (__bridge void *)(self));
 
         [self updateDrawableSize];
@@ -136,13 +139,15 @@ - (NSView *)hitTest:(NSPoint)point
         SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->driverdata;
         NSView *view = data.nswindow.contentView;
         BOOL highDPI = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
+        BOOL opaque = (window->flags & SDL_WINDOW_TRANSPARENT) == 0;
         Uint32 windowID = SDL_GetWindowID(window);
         SDL_cocoametalview *newview;
         SDL_MetalView metalview;
 
         newview = [[SDL_cocoametalview alloc] initWithFrame:view.frame
                                                     highDPI:highDPI
-                                                   windowID:windowID];
+                                                   windowID:windowID
+                                                     opaque:opaque];
         if (newview == nil) {
             SDL_OutOfMemory();
             return NULL;
diff --git a/src/video/cocoa/SDL_cocoaopengl.m b/src/video/cocoa/SDL_cocoaopengl.m
index 553cfaf62344..175dffb16009 100644
--- a/src/video/cocoa/SDL_cocoaopengl.m
+++ b/src/video/cocoa/SDL_cocoaopengl.m
@@ -270,6 +270,7 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window *window)
         int glversion_minor;
         NSOpenGLPixelFormatAttribute profile;
         int interval;
+        int opaque;
 
         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
 #if SDL_VIDEO_OPENGL_EGL
@@ -381,6 +382,9 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window *window)
         interval = 0;
         [context setValues:&interval forParameter:NSOpenGLCPSwapInterval];
 
+        opaque = (window->flags & SDL_WINDOW_TRANSPARENT) ? 0 : 1;
+        [context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];
+
         if (Cocoa_GL_MakeCurrent(_this, window, sdlcontext) < 0) {
             SDL_GL_DeleteContext(sdlcontext);
             SDL_SetError("Failed making OpenGL context current");
diff --git a/src/video/cocoa/SDL_cocoaopengles.m b/src/video/cocoa/SDL_cocoaopengles.m
index 46c0f66c68ff..8206fe66bedd 100644
--- a/src/video/cocoa/SDL_cocoaopengles.m
+++ b/src/video/cocoa/SDL_cocoaopengles.m
@@ -137,7 +137,7 @@ int Cocoa_GLES_SetupWindow(_THIS, SDL_Window *window)
 
         /* Create the GLES window surface */
         v = windowdata.nswindow.contentView;
-        windowdata.egl_surface = SDL_EGL_CreateSurface(_this, (__bridge NativeWindowType)[v layer]);
+        windowdata.egl_surface = SDL_EGL_CreateSurface(_this, window, (__bridge NativeWindowType)[v layer]);
 
         if (windowdata.egl_surface == EGL_NO_SURFACE) {
             return SDL_SetError("Could not create GLES window surface");
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 02fd42096ccd..c1d4c5946bcd 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -1600,11 +1600,14 @@ - (void)drawRect:(NSRect)dirtyRect
     /* Force the graphics context to clear to black so we don't get a flash of
        white until the app is ready to draw. In practice on modern macOS, this
        only gets called for window creation and other extraordinary events. */
+    BOOL transparent = (_sdlWindow->flags & SDL_WINDOW_TRANSPARENT) != 0;
     if ([NSGraphicsContext currentContext]) {
-        [[NSColor blackColor] setFill];
+        NSColor *fillColor = transparent ? [NSColor clearColor] : [NSColor blackColor];
+        [fillColor setFill];
         NSRectFill(dirtyRect);
     } else if (self.layer) {
-        self.layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
+        CFStringRef color = transparent ? kCGColorClear : kCGColorBlack;
+        self.layer.backgroundColor = CGColorGetConstantColor(color);
     }
 
     SDL_SendWindowEvent(_sdlWindow, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
@@ -1621,7 +1624,9 @@ - (void)updateLayer
     /* Force the graphics context to clear to black so we don't get a flash of
        white until the app is ready to draw. In practice on modern macOS, this
        only gets called for window creation and other extraordinary events. */
-    self.layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
+    BOOL transparent = (_sdlWindow->flags & SDL_WINDOW_TRANSPARENT) != 0;
+    CFStringRef color = transparent ? kCGColorClear : kCGColorBlack;
+    self.layer.backgroundColor = CGColorGetConstantColor(color);
     ScheduleContextUpdates((__bridge SDL_CocoaWindowData *)_sdlWindow->driverdata);
     SDL_SendWindowEvent(_sdlWindow, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
 }
@@ -1744,6 +1749,12 @@ static int SetupWindowData(_THIS, SDL_Window *window, NSWindow *nswindow, NSView
             }
         }
 
+        if (nswindow.isOpaque) {
+            window->flags &= ~SDL_WINDOW_TRANSPARENT;
+        } else {
+            window->flags |= SDL_WINDOW_TRANSPARENT;
+        }
+
         /* SDL_CocoaWindowData will be holding a strong reference to the NSWindow, and
          * it will also call [NSWindow close] in DestroyWindow before releasing the
          * NSWindow, so the extra release provided by releasedWhenClosed isn't
@@ -1841,6 +1852,12 @@ int Cocoa_CreateWindow(_THIS, SDL_Window *window)
             [nswindow setLevel:NSFloatingWindowLevel];
         }
 
+        if (window->flags & SDL_WINDOW_TRANSPARENT) {
+            nswindow.opaque = NO;
+            nswindow.hasShadow = NO;
+            nswindow.backgroundColor = [NSColor clearColor];
+        }
+
         /* Create a default view for this window */
         rect = [nswindow contentRectForFrameRect:[nswindow frame]];
         contentView = [[SDLView alloc] initWithFrame:rect];
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c
index 24b66e92e00c..d8ac3f583207 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.c
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c
@@ -1198,7 +1198,7 @@ int KMSDRM_CreateSurfaces(_THIS, SDL_Window *window)
        but we need an EGL surface NOW, or GL won't be able to render into any surface
        and we won't see the first frame. */
     SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
-    windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
+    windata->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)windata->gs);
 
     if (windata->egl_surface == EGL_NO_SURFACE) {
         ret = SDL_SetError("Could not create EGL window surface");
diff --git a/src/video/raspberry/SDL_rpivideo.c b/src/video/raspberry/SDL_rpivideo.c
index 6c4af7d09282..e9a584799987 100644
--- a/src/video/raspberry/SDL_rpivideo.c
+++ b/src/video/raspberry/SDL_rpivideo.c
@@ -287,7 +287,7 @@ int RPI_CreateWindow(_THIS, SDL_Window *window)
             return -1;
         }
     }
-    wdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)&wdata->dispman_window);
+    wdata->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)&wdata->dispman_window);
 
     if (wdata->egl_surface == EGL_NO_SURFACE) {
         return SDL_SetError("Could not create GLES window surface");
diff --git a/src/video/vita/SDL_vitavideo.c b/src/video/vita/SDL_vitavideo.c
index 6712e2cde0e6..05e647cf3ef6 100644
--- a/src/video/vita/SDL_vitavideo.c
+++ b/src/video/vita/SDL_vitavideo.c
@@ -275,7 +275,7 @@ int VITA_CreateWindow(_THIS, SDL_Window *window)
             _this->gl_config.minor_version = 1;
             _this->gl_config.profile_mask = SDL_GL_CONTEXT_PROFILE_ES;
         }
-        wdata->egl_surface = SDL_EGL_CreateSurface(_this, &win);
+        wdata->egl_surface = SDL_EGL_CreateSurface(_this, window, &win);
         if (wdata->egl_surface == EGL_NO_SURFACE) {
             return SDL_SetError("Could not create GLES window surface");
         }
diff --git a/src/video/vivante/SDL_vivantevideo.c b/src/video/vivante/SDL_vivantevideo.c
index ccf5b4e6f85e..41da76ebfad9 100644
--- a/src/video/vivante/SDL_vivantevideo.c
+++ b/src/video/vivante/SDL_vivantevideo.c
@@ -267,7 +267,7 @@ int VIVANTE_CreateWindow(_THIS, SDL_Window *window)
 
 #if SDL_VIDEO_OPENGL_EGL
     if (window->flags & SDL_WINDOW_OPENGL) {
-        data->egl_surface = SDL_EGL_CreateSurface(_this, data->native_window);
+        data->egl_surface = SDL_EGL_CreateSurface(_this, window, data->native_window);
         if (data->egl_surface == EGL_NO_SURFACE) {
             return SDL_SetError("VIVANTE: Can't create EGL surface");
         }
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index aa4ec89a0678..1f42324e0a56 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -948,7 +948,6 @@ int Wayland_VideoInit(_THIS)
     WAYLAND_wl_display_flush(data->display);
 
     Wayland_InitKeyboard(_this);
-    Wayland_InitWin(data);
 
     data->initializing = SDL_FALSE;
 
@@ -981,7 +980,6 @@ static void Wayland_VideoCleanup(_THIS)
     SDL_VideoData *data = _this->driverdata;
     int i, j;
 
-    Wayland_QuitWin(data);
     Wayland_FiniMouse(data);
 
     for (i = _this->num_displays - 1; i >= 0; --i) {
diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h
index 12530ad491fa..46fd82dc9f17 100644
--- a/src/video/wayland/SDL_waylandvideo.h
+++ b/src/video/wayland/SDL_waylandvideo.h
@@ -97,7 +97,6 @@ struct SDL_VideoData
     char *classname;
 
     int relative_mouse_mode;
-    SDL_bool egl_transparency_enabled;
 };
 
 struct SDL_DisplayData
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index 46f5891a5e90..70ef278b43b9 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -224,7 +224,7 @@ static void ConfigureWindowGeometry(SDL_Window *window)
             xdg_surface_set_window_geometry(data->shell_surface.xdg.surface, 0, 0, data->wl_window_width, data->wl_window_height);
         }
 
-        if (!viddata->egl_transparency_enabled) {
+        if (!(window->flags & SDL_WINDOW_TRANSPARENT)) {
             region = wl_compositor_create_region(viddata->compositor);
             wl_region_add(region, 0, 0,
                           data->wl_window_width, data->wl_window_height);
@@ -2010,12 +2010,18 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window)
     }
 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
 
+    if (window->flags & SDL_WINDOW_TRANSPARENT) {
+        if (_this->gl_config.alpha_size == 0) {
+            _this->gl_config.alpha_size = 8;
+        }
+    }
+
     if (window->flags & SDL_WINDOW_OPENGL) {
         data->egl_window = WAYLAND_wl_egl_window_create(data->surface, data->drawable_width, data->drawable_height);
 
 #if SDL_VIDEO_OPENGL_EGL
         /* Create the GLES window surface */
-        data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)data->egl_window);
+        data->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)data->egl_window);
 
         if (data->egl_surface == EGL_NO_SURFACE) {
             return -1; /* SDL_EGL_CreateSurface should have set error */
@@ -2247,43 +2253,4 @@ void Wayland_DestroyWindow(_THIS, SDL_Window *window)
     window->driverdata = NULL;
 }
 
-static void EGLTransparencyChangedCallback(void *userdata, const char *name, const char *oldValue, const char *newValue)
-{
-    const SDL_bool oldval = SDL_GetStringBoolean(oldValue, SDL_FALSE);
-    const SDL_bool newval = SDL_GetStringBoolean(newValue, SDL_FALSE);
-
-    if (oldval != newval) {
-        SDL_Window *window;
-        SDL_VideoData *viddata = (SDL_VideoData *)userdata;
-        SDL_VideoDevice *dev = SDL_GetVideoDevice();
-
-        viddata->egl_transparency_enabled = newval;
-
-        /* Iterate over all windows and update the surface opaque regions */
-        for (window = dev->windows; window != NULL; window = window->next) {
-            SDL_WindowData *wind = window->driverdata;
-
-            if (!newval) {
-                struct wl_region *region = wl_compositor_create_region(wind->waylandData->compositor);
-                wl_region_add(region, 0, 0, wind->wl_window_width, wind->wl_window_height);
-                wl_surface_set_opaque_region(wind->surface, region);
-                wl_region_destroy(region);
-            } else {
-                wl_surface_set_opaque_region(wind->surface, NULL);
-            }
-        }
-    }
-}
-
-void Wayland_InitWin(SDL_VideoData *data)
-{
-    data->egl_transparency_enabled = SDL_GetHintBoolean(SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY, SDL_FALSE);
-    SDL_AddHintCallback(SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY, EGLTransparencyChangedCallback, data);
-}
-
-void Wayland_QuitWin(SDL_VideoData *data)
-{
-    SDL_DelHintCallback(SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY, EGLTransparencyChangedCallback, data);
-}
-
 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h
index 16271056bfa9..7d17be72a9fb 100644
--- a/src/video/wayland/SDL_waylandwindow.h
+++ b/src/video/wayland/SDL_waylandwindow.h
@@ -155,7 +155,4 @@ extern int Wayland_GetWindowWMInfo(_THIS, SDL_Window *window, SDL_SysWMinfo *inf
 extern int Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
 extern int Wayland_FlashWindow(_THIS, SDL_Window *window, SDL_FlashOperation operation);
 
-extern void Wayland_InitWin(SDL_VideoData *data);
-extern void Wayland_QuitWin(SDL_VideoData *data);
-
 #endif /* SDL_waylandwindow_h_ */
diff --git a/src/video/windows/SDL_windowsopengles.c b/src/video/windows/SDL_windowsopengles.c
index c05b53b15f88..7d181954042c 100644
--- a/src/video/windows/SDL_windowsopengles.c
+++ b/src/video/windows/SDL_windowsopengles.c
@@ -125,7 +125,7 @@ int WIN_GLES_SetupWindow(_THIS, SDL_Window *window)
     }
 
     /* Create the GLES window surface */
-    windowdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windowdata->hwnd);
+    windowdata->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)windowdata->hwnd);
 
     if (windowdata->egl_surface == EGL_NO_SURFACE) {
         return SDL_SetError("Could not create GLES window surface");
diff --git a/src/video/x11/SDL_x11opengl.c b/src/video/x11/SDL_x11opengl.c
index 0c09a8c70107..8f35b0cab317 100644
--- a/src/video/x11/SDL_x11opengl.c
+++ b/src/video/x11/SDL_x11opengl.c
@@ -345,7 +345,7 @@ static void X11_GL_InitExtensions(_THIS)
     const char *(*glXQueryExtensionsStringFunc)(Display *, int);
     const char *extensions;
 
-    vinfo = X11_GL_GetVisual(_this, display, screen);
+    vinfo = X11_GL_GetVisual(_this, display, screen, SDL_FALSE);
     if (vinfo) {
         GLXContext (*glXGetCurrentContextFunc)(void) =
             (GLXContext(*)(void))
@@ -484,7 +484,7 @@ static void X11_GL_InitExtensions(_THIS)
  *  In case of failure, if that pointer is not NULL, set that pointer to None
  *  and try again.
  */
-static int X11_GL_GetAttributes(_THIS, Display *display, int screen, int *attribs, int size, Bool for_FBConfig, int **_pvistypeattr)
+static int X11_GL_GetAttributes(_THIS, Display *display, int screen, int *attribs, int size, Bool for_FBConfig, int **_pvistypeattr, SDL_bool transparent)
 {
     int i = 0;
     const int MAX_ATTRIBUTES = 64;
@@ -583,13 +583,15 @@ static int X11_GL_GetAttributes(_THIS, Display *display, int screen, int *attrib
         attribs[i++] = _this->gl_config.accelerated ? GLX_NONE_EXT : GLX_SLOW_VISUAL_EXT;
     }
 
-    /* If we're supposed to use DirectColor visuals, and we've got the
-       EXT_visual_info extension, then add GLX_X_VISUAL_TYPE_EXT. */
-    if (X11_UseDirectColorVisuals() &&
-        _this->gl_data->HAS_GLX_EXT_visual_info) {
-        pvistypeattr = &attribs[i];
-        attribs[i++] = GLX_X_VISUAL_TYPE_EXT;
-        attribs[i++] = GLX_DIRECT_COLOR_EXT;
+    /* Un-wanted when we request a transparent buffer */
+    if (!transparent) {
+        /* If we're supposed to use DirectColor visuals, and we've got the
+           EXT_visual_info extension, then add GLX_X_VISUAL_TYPE_EXT. */
+        if (X11_UseDirectColorVisuals() && _this->gl_data->HAS_GLX_EXT_visual_info) {
+            pvistypeattr = &attribs[i];
+            attribs[i++] = GLX_X_VISUAL_TYPE_EXT;
+            attribs[i++] = GLX_DIRECT_COLOR_EXT;
+        }
     }
 
     attribs[i++] = None;
@@ -603,7 +605,7 @@ static int X11_GL_GetAttributes(_THIS, Display *display, int screen, int *attrib
     return i;
 }
 
-XVisualInfo *X11_GL_GetVisual(_THIS, Display *display, int screen)
+XVisualInfo *X11_GL_GetVisual(_THIS, Display *display, int screen, SDL_bool transparent)
 {
     /* 64 seems nice. */
     int attribs[64];
@@ -620,13 +622,30 @@ XVisualInfo *X11_GL_GetVisual(_THIS, Display *display, int screen)
         GLXFBConfig *framebuffer_config = NULL;
         int fbcount = 0;
 
-        X11_GL_GetAttributes(_this, display, screen, attribs, 64, SDL_TRUE, &pvistypeattr);
+        X11_GL_GetAttributes(_this, display, screen, attribs, 64, SDL_TRUE, &pvistypeattr, transparent);
         framebuffer_config = _this->gl_data->glXChooseFBConfig(display, screen, attribs, &fbcount);
         if (!framebuffer_config && (pvistypeattr != NULL)) {
             *pvistypeattr = None;
             framebuffer_config = _this->gl_data->glXChooseFBConfig(display, screen, attribs, &fbcount);
         }
 
+        if (transparent) {
+            /* Return the first transparent Visual */
+            int i;
+            for (i = 0; i < fbcount; i++) {
+                Uint32 format;
+                vinfo = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[i]);
+                format = X11_GetPixelFormatFromVisualInfo(display, vinfo);
+                if (SDL_ISPIXELFORMAT_ALPHA(format)) { /* found! */
+                    X11_XFree(framebuffer_config);
+                    framebuffer_config = NULL;
+                    break;
+                }
+                X11_XFree(vinfo);
+                vinfo = NULL;
+            }
+        }
+
         if (framebuffer_config) {
             vinfo = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[0]);
         }
@@ -635,7 +654,7 @@ XVisualInfo *X11_GL_GetVisual(_THIS, Display *display, int screen)
     }
 
     if (!vinfo) {
-        X11_GL_GetAttributes(_this, display, screen, attribs, 64, SDL_FALSE, &pvistypeattr);
+        X11_GL_GetAttributes(_this, display, screen, attribs, 64, SDL_FALSE, &pvistypeattr, transparent);
         vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
 
         if (!vinfo && (pvistypeattr != NULL)) {
@@ -696,6 +715,7 @@ SDL_GLContext X11_GL_CreateContext(_THIS, SDL_Window *window)
     XVisualInfo v, *vinfo;
     int n;
     GLXContext context = NULL, share_context;
+    const int transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? SDL_TRUE : SDL_FALSE;
 
     if (_this->gl_config.share_with_current_context) {
         share_context = (GLXContext)SDL_GL_GetCurrentContext();
@@ -716,7 +736,7 @@ SDL_GLContext X11_GL_CreateContext(_THIS, SDL_Window *window)
     if (vinfo) {
         if (_this->gl_config.major_version < 3 &&
             _this->gl_config.profile_mask == 0 &&
-            _this->gl_config.flags == 0) {
+            _this->gl_config.flags == 0 && !transparent) {
             /* Create legacy context */
             context =
                 _this->gl_data->glXCreateContext(display, vinfo, share_context, True);
@@ -776,7 +796,7 @@ SDL_GLContext X11_GL_CreateContext(_THIS, SDL_Window *window)
                 int fbcount = 0;
                 int *pvistypeattr = NULL;
 
-                X11_GL_GetAttributes(_this, display, screen, glxAttribs, 64, SDL_TRUE, &pvistypeattr);
+                X11_GL_GetAttributes(_this, display, screen, glxAttribs, 64, SDL_TRUE, &pvistypeattr, transparent);
 
                 if (_this->gl_data->glXChooseFBConfig) {
                     framebuffer_config = _this->gl_data->glXChooseFBConfig(display,
diff --git a/src/video/x11/SDL_x11opengl.h b/src/video/x11/SDL_x11opengl.h
index af6c13664826..08569739a77b 100644
--- a/src/video/x11/SDL_x11opengl.h
+++ b/src/video/x11/SDL_x11opengl.h
@@ -72,7 +72,7 @@ extern int X11_GL_LoadLibrary(_THIS, const char *path);
 extern SDL_FunctionPointer X11_GL_GetProcAddress(_THIS, const char *proc);
 extern void X11_GL_UnloadLibrary(_THIS);
 extern SDL_bool X11_GL_UseEGL(_THIS);
-extern XVisualInfo *X11_GL_GetVisual(_THIS, Display *display, int screen);
+extern XVisualInfo *X11_GL_GetVisual(_THIS, Display *display, int screen, SDL_bool transparent);
 extern SDL_GLContex

(Patch may be truncated, please check the link at the top of this post.)