Ver Fonte

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

Sylvain há 2 anos atrás
pai
commit
2cafa52598

+ 0 - 11
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.
  *

+ 1 - 0
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;
 

+ 1 - 1
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 */

+ 6 - 1
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;

+ 5 - 2
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;
     }

+ 1 - 1
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);

+ 4 - 3
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)
 {

+ 1 - 1
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);

+ 2 - 1
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;

+ 7 - 2
src/video/cocoa/SDL_cocoametalview.m

@@ -76,7 +76,8 @@ static int SDLCALL SDL_MetalViewEventWatch(void *userdata, SDL_Event *event)
 
 - (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 @@ static int SDLCALL SDL_MetalViewEventWatch(void *userdata, SDL_Event *event)
         /* Allow resize. */
         self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
 
+        self.layer.opaque = opaque;
+
         SDL_AddEventWatch(SDL_MetalViewEventWatch, (__bridge void *)(self));
 
         [self updateDrawableSize];
@@ -136,13 +139,15 @@ Cocoa_Metal_CreateView(_THIS, SDL_Window *window)
         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;

+ 4 - 0
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");

+ 1 - 1
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");

+ 20 - 3
src/video/cocoa/SDL_cocoawindow.m

@@ -1600,11 +1600,14 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL_
     /* 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 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL_
     /* 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];

+ 1 - 1
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");

+ 1 - 1
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");

+ 1 - 1
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");
         }

+ 1 - 1
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");
         }

+ 0 - 2
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) {

+ 0 - 1
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

+ 8 - 41
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 */

+ 0 - 3
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_ */

+ 1 - 1
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");

+ 34 - 14
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,

+ 1 - 1
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_GLContext X11_GL_CreateContext(_THIS, SDL_Window *window);
 extern int X11_GL_MakeCurrent(_THIS, SDL_Window *window,
                               SDL_GLContext context);

+ 18 - 1
src/video/x11/SDL_x11opengles.c

@@ -56,7 +56,7 @@ int X11_GLES_LoadLibrary(_THIS, const char *path)
 }
 
 XVisualInfo *
-X11_GLES_GetVisual(_THIS, Display *display, int screen)
+X11_GLES_GetVisual(_THIS, Display *display, int screen, SDL_bool transparent)
 {
 
     XVisualInfo *egl_visualinfo = NULL;
@@ -79,6 +79,23 @@ X11_GLES_GetVisual(_THIS, Display *display, int screen)
         egl_visualinfo = X11_XGetVisualInfo(display,
                                             VisualScreenMask,
                                             &vi_in, &out_count);
+
+        /* Return the first transparent Visual */
+        if (transparent) {
+            int i;
+            for (i = 0; i < out_count; i++) {
+                XVisualInfo *v = &egl_visualinfo[i];
+                Uint32 format = X11_GetPixelFormatFromVisualInfo(display, v);
+                if (SDL_ISPIXELFORMAT_ALPHA(format)) { /* found! */
+                    /* re-request it to have a copy that can be X11_XFree'ed later */
+                    vi_in.screen = screen;
+                    vi_in.visualid = v->visualid;
+                    X11_XFree(egl_visualinfo);
+                    egl_visualinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &vi_in, &out_count);
+                    return egl_visualinfo;
+                }
+            }
+        }
     } else {
         vi_in.screen = screen;
         vi_in.visualid = visual_id;

+ 1 - 1
src/video/x11/SDL_x11opengles.h

@@ -44,7 +44,7 @@ typedef struct SDL_PrivateGLESData
 #define X11_GLES_DeleteContext   SDL_EGL_DeleteContext
 
 extern int X11_GLES_LoadLibrary(_THIS, const char *path);
-extern XVisualInfo *X11_GLES_GetVisual(_THIS, Display *display, int screen);
+extern XVisualInfo *X11_GLES_GetVisual(_THIS, Display *display, int screen, SDL_bool transparent);
 extern SDL_GLContext X11_GLES_CreateContext(_THIS, SDL_Window *window);
 extern int X11_GLES_SwapWindow(_THIS, SDL_Window *window);
 extern int X11_GLES_MakeCurrent(_THIS, SDL_Window *window, SDL_GLContext context);

+ 4 - 3
src/video/x11/SDL_x11window.c

@@ -431,6 +431,7 @@ int X11_CreateWindow(_THIS, SDL_Window *window)
     SDL_WindowData *windowdata;
     Display *display = data->display;
     int screen = displaydata->screen;
+    const int transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? SDL_TRUE : SDL_FALSE;
     Visual *visual;
     int depth;
     XSetWindowAttributes xattr;
@@ -477,12 +478,12 @@ int X11_CreateWindow(_THIS, SDL_Window *window)
             && (!_this->gl_data || X11_GL_UseEGL(_this))
 #endif
         ) {
-            vinfo = X11_GLES_GetVisual(_this, display, screen);
+            vinfo = X11_GLES_GetVisual(_this, display, screen, transparent);
         } else
 #endif
         {
 #if SDL_VIDEO_OPENGL_GLX
-            vinfo = X11_GL_GetVisual(_this, display, screen);
+            vinfo = X11_GL_GetVisual(_this, display, screen, transparent);
 #endif
         }
 
@@ -720,7 +721,7 @@ int X11_CreateWindow(_THIS, SDL_Window *window)
         }
 
         /* Create the GLES window surface */
-        windowdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)w);
+        windowdata->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)w);
 
         if (windowdata->egl_surface == EGL_NO_SURFACE) {
             return SDL_SetError("Could not create GLES window surface");

+ 1 - 1
test/testgl.c

@@ -92,7 +92,7 @@ static void Render(void)
     };
 
     /* Do our drawing, too. */
-    ctx.glClearColor(0.0, 0.0, 0.0, 1.0);
+    ctx.glClearColor(0.0, 0.0, 0.0, 0.0 /* used with --transparent */);
     ctx.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
     ctx.glBegin(GL_QUADS);

+ 1 - 1
test/testsprite.c

@@ -126,7 +126,7 @@ static void MoveSprites(SDL_Renderer *renderer, SDL_Texture *sprite)
     }
 
     /* Draw a gray background */
-    SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
+    SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0x00 /* used with --transparent */);
     SDL_RenderClear(renderer);
 
     /* Test points */

+ 10 - 2
test/testvulkan.c

@@ -624,6 +624,7 @@ static SDL_bool createSwapchain(void)
     int w, h;
     VkSwapchainCreateInfoKHR createInfo = { 0 };
     VkResult result;
+    Uint32 flags;
 
     // pick an image count
     vulkanContext->swapchainDesiredImageCount = vulkanContext->surfaceCapabilities.minImageCount + 1;
@@ -651,6 +652,9 @@ static SDL_bool createSwapchain(void)
     // get size
     SDL_GetWindowSizeInPixels(vulkanContext->window, &w, &h);
 
+    // get flags
+    flags = SDL_GetWindowFlags(vulkanContext->window);
+
     // Clamp the size to the allowable image extent.
     // SDL_GetWindowSizeInPixels()'s result it not always in this range (bug #3287)
     vulkanContext->swapchainSize.width = SDL_clamp((uint32_t)w,
@@ -677,7 +681,11 @@ static SDL_bool createSwapchain(void)
     createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
     createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
     createInfo.preTransform = vulkanContext->surfaceCapabilities.currentTransform;
-    createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+    if (flags & SDL_WINDOW_TRANSPARENT) {
+        createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
+    } else {
+        createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+    }
     createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
     createInfo.clipped = VK_TRUE;
     createInfo.oldSwapchain = vulkanContext->swapchain;
@@ -1037,7 +1045,7 @@ static SDL_bool render(void)
     clearColor.float32[0] = (float)(0.5 + 0.5 * SDL_sin(currentTime));
     clearColor.float32[1] = (float)(0.5 + 0.5 * SDL_sin(currentTime + SDL_PI_D * 2 / 3));
     clearColor.float32[2] = (float)(0.5 + 0.5 * SDL_sin(currentTime + SDL_PI_D * 4 / 3));
-    clearColor.float32[3] = 1;
+    clearColor.float32[3] = 0.5; // for SDL_WINDOW_TRANSPARENT, ignored with VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
     rerecordCommandBuffer(frameIndex, &clearColor);
     submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
     submitInfo.waitSemaphoreCount = 1;