diff --git a/README.md b/README.md index dbdafe00..ff753270 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,8 @@ information on what to include when reporting a bug. functions for accessing X11 primary selection (#894,#1056) - Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#850) - Added definition of `GLAPIENTRY` to public header +- Added `GLFW_TRANSPARENT` window hint for enabling window framebuffer + transparency (#197,#663,#715,#723,#1078) - Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering (#749,#842) - Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889) @@ -289,6 +291,7 @@ skills. - Yaron Cohen-Tal - Omar Cornut - Andrew Corrigan + - Bailey Cosier - Noel Cower - Jason Daly - Jarrod Davis @@ -297,6 +300,7 @@ skills. - Michael Dickens - Роман Донченко - Mario Dorn + - Wolfgang Draxinger - Jonathan Dummer - Ralph Eastwood - Fredrik Ehnbom @@ -322,6 +326,7 @@ skills. - Erik S. V. Jansson - Toni Jovanoski - Arseny Kapoulkine + - Cem Karan - Osman Keskin - Josh Kilmer - Cameron King @@ -363,6 +368,7 @@ skills. - Andri Pálsson - Peoro - Braden Pellett + - Christopher Pelloux - Arturo J. Pérez - Anthony Pesch - Orson Peters diff --git a/docs/compat.dox b/docs/compat.dox index cabe18c3..a27f58f3 100644 --- a/docs/compat.dox +++ b/docs/compat.dox @@ -80,6 +80,11 @@ GLFW uses the XInput2 extension to provide raw, non-accelerated mouse motion when the cursor is disabled. If the running X server does not support this extension, regular accelerated mouse motion will be used. +GLFW uses both the XRender extension and the compositing manager to support +transparent window framebuffers. If the running X server does not support this +extension or there is no running compositing manager, the `GLFW_TRANSPARENT` +framebuffer hint will have no effect. + @section compat_glx GLX extensions diff --git a/docs/news.dox b/docs/news.dox index 042468f1..86b68c41 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -85,6 +85,13 @@ be disabled with the @ref GLFW_JOYSTICK_HAT_BUTTONS init hint. @see @ref joystick_hat +@subsection news_33_transparent Support for transparent window framebuffer + +GLFW now supports the creation of windows with transparent framebuffers on +systems with desktop compositing enabled with the @ref GLFW_TRANSPARENT window +hint and attribute. Any window decorations will still be opaque. + + @subsection news_33_centercursor Cursor centering window hint GLFW now supports controlling whether the cursor is centered over newly created diff --git a/docs/window.dox b/docs/window.dox index 17fa3002..5dbf15bf 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -225,6 +225,13 @@ __GLFW_CENTER_CURSOR__ specifies whether the cursor should be centered over newly created full screen windows. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. This hint is ignored for windowed mode windows. +@anchor GLFW_TRANSPARENT_hint +__GLFW_TRANSPARENT__ specifies whether the window framebuffer will be +transparent. If enabled and supported by the system, the window framebuffer +alpha channel will be used to combine the framebuffer with the background. This +does not affect window decorations. Possible values are `GLFW_TRUE` and +`GLFW_FALSE`. + @subsubsection window_hints_fb Framebuffer related hints @@ -287,10 +294,6 @@ __GLFW_DOUBLEBUFFER__ specifies whether the framebuffer should be double buffered. You nearly always want to use double buffering. This is a hard constraint. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. -@anchor GLFW_TRANSPARENT_hint -__GLFW_TRANSPARENT__ specifies whether the framebuffer will support transparency -in the background. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. - @subsubsection window_hints_mtr Monitor related hints @@ -474,6 +477,7 @@ GLFW_AUTO_ICONIFY | `GLFW_TRUE` | `GLFW_TRUE` or `GL GLFW_FLOATING | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_MAXIMIZED | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_CENTER_CURSOR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` +GLFW_TRANSPARENT | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_RED_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` GLFW_GREEN_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` GLFW_BLUE_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` @@ -1065,6 +1069,30 @@ window contents are saved off-screen, this callback might only be called when the window or framebuffer is resized. +@subsection window_transparency Window transparency + +Window framebuffers can be made transparent on a per-pixel per-frame basis with +the [GLFW_TRANSPARENT](@ref GLFW_TRANSPARENT_hint) window hint. + +@code +glfwWindowHint(GLFW_TRANSPARENT, GLFW_TRUE); +@endcode + +If supported by the system, the window framebuffer will be composited with the +background using the framebuffer per-pixel alpha channel. This requires desktop +compositing to be enabled on the system. It does not affect window decorations. + +You can check whether the window framebuffer was successfully made transparent +with the [GLFW_TRANSPARENT](@ref GLFW_TRANSPARENT_attrib) window attribute. + +@code +if (glfwGetWindowAttrib(window, GLFW_TRANSPARENT)) +{ + // window framebuffer is currently transparent +} +@endcode + + @subsection window_attribs Window attributes Windows have a number of attributes that can be returned using @ref @@ -1134,6 +1162,11 @@ called topmost or always-on-top. This can be set before creation with the [GLFW_FLOATING](@ref GLFW_FLOATING_hint) window hint or after with @ref glfwSetWindowAttrib. +@anchor GLFW_TRANSPARENT_attrib +__GLFW_TRANSPARENT__ indicates whether the specified window has a transparent +framebuffer, i.e. the window contents is composited with the background using +the window framebuffer alpha channel. See @ref window_transparency for details. + @subsubsection window_attribs_ctx Context related attributes diff --git a/examples/gears.c b/examples/gears.c index f7526e05..f14b300a 100644 --- a/examples/gears.c +++ b/examples/gears.c @@ -172,7 +172,7 @@ static GLfloat angle = 0.f; /* OpenGL draw function & timing */ static void draw(void) { - glClearColor(0., 0., 0., 0.); + glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); @@ -312,7 +312,6 @@ int main(int argc, char *argv[]) } glfwWindowHint(GLFW_DEPTH_BITS, 16); - glfwWindowHint(GLFW_ALPHA_BITS, 8); glfwWindowHint(GLFW_TRANSPARENT, GLFW_TRUE); window = glfwCreateWindow( 300, 300, "Gears", NULL, NULL ); diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 5e9eac6f..819d2cd6 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -787,6 +787,12 @@ extern "C" { * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). */ #define GLFW_CENTER_CURSOR 0x00020009 +/*! @brief Window framebuffer transparency hint and attribute + * + * Window framebuffer transparency [window hint](@ref GLFW_TRANSPARENT_hint) + * and [window attribute](@ref GLFW_TRANSPARENT_attrib). + */ +#define GLFW_TRANSPARENT 0x0002000A /*! @brief Framebuffer bit depth hint. * @@ -868,11 +874,6 @@ extern "C" { * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). */ #define GLFW_DOUBLEBUFFER 0x00021010 -/*! @brief Framebuffer transparency hint. - * - * Framebuffer transparency [hint](@ref GLFW_TRANSPARENT_hint). - */ -#define GLFW_TRANSPARENT 0x00021011 /*! @brief Context client API hint and attribute. * diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 5b2736b4..9d947662 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -413,11 +413,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (BOOL)isOpaque { - // Set to NO even if alphaMask is not used; - // The NSView/GLFWContentView does not need to be opaque anyway, - // and to avoid keeping track of alphaMask inside the NSView we - // just return NO here instead. - return NO; + return [window->ns.object isOpaque]; } - (BOOL)canBecomeKeyView @@ -1016,7 +1012,8 @@ static GLFWbool initializeAppKit(void) // Create the Cocoa window // static GLFWbool createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig) + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) { window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window]; if (window->ns.delegate == nil) @@ -1085,7 +1082,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, if (wndconfig->ns.retina) [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; - if (_glfw.hints.framebuffer.transparent) + if (fbconfig->transparent) { [window->ns.object setOpaque:NO]; [window->ns.object setBackgroundColor:[NSColor clearColor]]; @@ -1114,7 +1111,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!initializeAppKit()) return GLFW_FALSE; - if (!createNativeWindow(window, wndconfig)) + if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) @@ -1453,6 +1450,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return [window->ns.object isZoomed]; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return ![window->ns.object isOpaque] && ![window->ns.view isOpaque]; +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { [window->ns.object setStyleMask:getStyleMask(window)]; diff --git a/src/context.c b/src/context.c index 1a26356e..3842f0a3 100644 --- a/src/context.c +++ b/src/context.c @@ -208,6 +208,9 @@ const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, // not important to us here, so we count them as one missing++; } + + if (desired->transparent != current->transparent) + missing++; } // These polynomials make many small channel size differences matter diff --git a/src/egl_context.c b/src/egl_context.c index bba1efb7..b2d11a47 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -87,21 +87,13 @@ static int getEGLConfigAttrib(EGLConfig config, int attrib) // static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* desired, - EGLConfig* result, - GLFWbool findTransparent) + EGLConfig* result) { EGLConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; int i, nativeCount, usableCount; -#if defined(_GLFW_X11) - XVisualInfo visualTemplate = {0}; - if ( !(_glfw.xrender.major || _glfw.xrender.minor) ) { - findTransparent = GLFW_FALSE; - } -#endif // _GLFW_X11 - eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); if (!nativeCount) { @@ -115,7 +107,6 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; -selectionloop: for (i = 0; i < nativeCount; i++) { const EGLConfig n = nativeConfigs[i]; @@ -130,31 +121,24 @@ selectionloop: continue; #if defined(_GLFW_X11) + XVisualInfo vi = {0}; + // Only consider EGLConfigs with associated Visuals - visualTemplate.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); - if (!visualTemplate.visualid) + vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); + if (!vi.visualid) continue; - if( findTransparent ) { - int n_vi; - XVisualInfo *visualinfo; - XRenderPictFormat *pictFormat; - - visualinfo = XGetVisualInfo(_glfw.x11.display, VisualIDMask, &visualTemplate, &n_vi); - if (!visualinfo) - continue; - - pictFormat = XRenderFindVisualFormat(_glfw.x11.display, visualinfo->visual); - if( !pictFormat ) { - XFree( visualinfo ); - continue; - } - - if( !pictFormat->direct.alphaMask ) { - XFree( visualinfo ); - continue; - } - XFree( visualinfo ); + if (desired->transparent) + { + int count; + XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display, + VisualIDMask, &vi, + &count); + if (vis) + { + u->transparent = _glfwIsVisualTransparentX11(vis[0].visual); + XFree(vis); + } } #endif // _GLFW_X11 @@ -191,12 +175,6 @@ selectionloop: u->handle = (uintptr_t) n; usableCount++; } - // reiterate the selection loop without looking for transparency supporting - // formats if no matchig FB configs for a transparent window were found. - if( findTransparent && !usableCount ) { - findTransparent = GLFW_FALSE; - goto selectionloop; - } closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); if (closest) @@ -493,7 +471,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (ctxconfig->share) share = ctxconfig->share->context.egl.handle; - if (!chooseEGLConfig(ctxconfig, fbconfig, &config, fbconfig->transparent)) + if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Failed to find a suitable EGLConfig"); @@ -738,7 +716,7 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, EGLint visualID = 0, count = 0; const long vimask = VisualScreenMask | VisualIDMask; - if (!chooseEGLConfig(ctxconfig, fbconfig, &native, fbconfig->transparent)) + if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Failed to find a suitable EGLConfig"); diff --git a/src/glx_context.c b/src/glx_context.c index 712e8c78..708663a9 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -47,10 +47,8 @@ static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib) // Return the GLXFBConfig most closely matching the specified hints // -static GLFWbool chooseGLXFBConfig( - const _GLFWfbconfig* desired, - GLXFBConfig* result, - GLFWbool findTransparent) +static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, + GLXFBConfig* result) { GLXFBConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; @@ -59,10 +57,6 @@ static GLFWbool chooseGLXFBConfig( const char* vendor; GLFWbool trustWindowBit = GLFW_TRUE; - if ( !(_glfw.xrender.major || _glfw.xrender.minor) ) { - findTransparent = GLFW_FALSE; - } - // HACK: This is a (hopefully temporary) workaround for Chromium // (VirtualBox GL) not setting the window bit on any GLXFBConfigs vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR); @@ -80,7 +74,6 @@ static GLFWbool chooseGLXFBConfig( usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; -selectionloop: for (i = 0; i < nativeCount; i++) { const GLXFBConfig n = nativeConfigs[i]; @@ -97,25 +90,14 @@ selectionloop: continue; } - if( findTransparent ) { - XVisualInfo *visualinfo; - XRenderPictFormat *pictFormat; - - visualinfo = glXGetVisualFromFBConfig(_glfw.x11.display, n); - if (!visualinfo) - continue; - - pictFormat = XRenderFindVisualFormat(_glfw.x11.display, visualinfo->visual); - if( !pictFormat ) { - XFree( visualinfo ); - continue; - } - - if( !pictFormat->direct.alphaMask ) { - XFree( visualinfo ); - continue; - } - XFree( visualinfo ); + if (desired->transparent) + { + XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n); + if (vi) + { + u->transparent = _glfwIsVisualTransparentX11(vi->visual); + XFree(vi); + } } u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); @@ -147,12 +129,6 @@ selectionloop: u->handle = (uintptr_t) n; usableCount++; } - // reiterate the selection loop without looking for transparency supporting - // formats if no matchig FB configs for a transparent window were found. - if( findTransparent && !usableCount ) { - findTransparent = GLFW_FALSE; - goto selectionloop; - } closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); if (closest) @@ -477,7 +453,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, if (ctxconfig->share) share = ctxconfig->share->context.glx.handle; - if (!chooseGLXFBConfig(fbconfig, &native, fbconfig->transparent)) + if (!chooseGLXFBConfig(fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); @@ -665,7 +641,7 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, GLXFBConfig native; XVisualInfo* result; - if (!chooseGLXFBConfig(fbconfig, &native, fbconfig->transparent)) + if (!chooseGLXFBConfig(fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); diff --git a/src/internal.h b/src/internal.h index 2e4e0150..b9f55da7 100644 --- a/src/internal.h +++ b/src/internal.h @@ -682,6 +682,7 @@ int _glfwPlatformWindowFocused(_GLFWwindow* window); int _glfwPlatformWindowIconified(_GLFWwindow* window); int _glfwPlatformWindowVisible(_GLFWwindow* window); int _glfwPlatformWindowMaximized(_GLFWwindow* window); +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); diff --git a/src/mir_window.c b/src/mir_window.c index 4ed71f97..8b763de1 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -614,6 +614,13 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return mir_window_get_state(window->mir.window) == mir_window_state_maximized; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + return GLFW_FALSE; +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { _glfwInputError(GLFW_PLATFORM_ERROR, diff --git a/src/null_window.c b/src/null_window.c index 2e627672..33ff6c33 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -156,6 +156,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return GLFW_FALSE; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { } diff --git a/src/wgl_context.c b/src/wgl_context.c index 0834e0a8..d864a47c 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -83,20 +83,6 @@ static int choosePixelFormat(_GLFWwindow* window, { const int n = i + 1; _GLFWfbconfig* u = usableConfigs + usableCount; - PIXELFORMATDESCRIPTOR pfd; - - if (fbconfig->transparent) { - if (!DescribePixelFormat(window->context.wgl.dc, - n, - sizeof(PIXELFORMATDESCRIPTOR), - &pfd)) - { - continue; - } - - if (!(pfd.dwFlags & PFD_SUPPORT_COMPOSITION)) - continue; - } if (_glfw.wgl.ARB_pixel_format) { @@ -168,7 +154,9 @@ static int choosePixelFormat(_GLFWwindow* window, { // Get pixel format attributes through legacy PFDs - if (!fbconfig->transparent && DescribePixelFormat(window->context.wgl.dc, + PIXELFORMATDESCRIPTOR pfd; + + if (!DescribePixelFormat(window->context.wgl.dc, n, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) @@ -215,14 +203,6 @@ static int choosePixelFormat(_GLFWwindow* window, u->handle = n; usableCount++; } - // Reiterate the selection loop without looking for transparency supporting - // formats if no matching pixelformat for a transparent window were found. - if (fbconfig->transparent && !usableCount) { - free(usableConfigs); - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: No pixel format found for transparent window. Ignoring transparency."); - return choosePixelFormat(window, ctxconfig, fbconfig); - } if (!usableCount) { @@ -249,21 +229,6 @@ static int choosePixelFormat(_GLFWwindow* window, return pixelFormat; } -// Returns whether desktop compositing is enabled -// -static GLFWbool isCompositionEnabled(void) -{ - if (_glfw.win32.dwmapi.instance) - { - BOOL enabled; - - if (DwmIsCompositionEnabled(&enabled) == S_OK) - return enabled; - } - - return FALSE; -} - static void makeContextCurrentWGL(_GLFWwindow* window) { if (window) @@ -292,7 +257,7 @@ static void makeContextCurrentWGL(_GLFWwindow* window) static void swapBuffersWGL(_GLFWwindow* window) { // HACK: Use DwmFlush when desktop composition is enabled - if (isCompositionEnabled() && !window->monitor) + if (_glfwIsCompositionEnabledWin32() && !window->monitor) { int count = abs(window->context.wgl.interval); while (count--) @@ -310,7 +275,7 @@ static void swapIntervalWGL(int interval) // HACK: Disable WGL swap interval when desktop composition is enabled to // avoid interfering with DWM vsync - if (isCompositionEnabled() && !window->monitor) + if (_glfwIsCompositionEnabledWin32() && !window->monitor) interval = 0; if (_glfw.wgl.EXT_swap_control) @@ -504,75 +469,6 @@ void _glfwTerminateWGL(void) attribs[index++] = v; \ } -static GLFWbool setupTransparentWindow(_GLFWwindow* window) -{ - if (!isCompositionEnabled) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Composition needed for transparent window is disabled"); - } - if (!_glfw_DwmEnableBlurBehindWindow) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Unable to load DwmEnableBlurBehindWindow required for transparent window"); - return GLFW_FALSE; - } - - HRESULT hr = S_OK; - HWND handle = window->win32.handle; - - DWM_BLURBEHIND bb = { 0 }; - bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; - bb.hRgnBlur = CreateRectRgn(0, 0, -1, -1); // makes the window transparent - bb.fEnable = TRUE; - hr = _glfw_DwmEnableBlurBehindWindow(handle, &bb); - - if (!SUCCEEDED(hr)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to enable blur behind window required for transparent window"); - return GLFW_FALSE; - } - - // Decorated windows on Windows 8+ don't repaint the transparent background - // leaving a trail behind animations. - // Hack: making the window layered with a transparency color key seems to fix this. - // Normally, when specifying a transparency color key to be used when composing - // the layered window, all pixels painted by the window in this color will be transparent. - // That doesn't seem to be the case anymore on Windows 8+, at least when used with - // DwmEnableBlurBehindWindow + negative region. - if (window->decorated && IsWindows8OrGreater()) - { - long style = GetWindowLong(handle, GWL_EXSTYLE); - if (!style) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve extended styles. GetLastError: %d", - GetLastError()); - return GLFW_FALSE; - } - style |= WS_EX_LAYERED; - if (!SetWindowLongPtr(handle, GWL_EXSTYLE, style)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to add layered style. GetLastError: %d", - GetLastError()); - return GLFW_FALSE; - } - if (!SetLayeredWindowAttributes(handle, - // Using a color key not equal to black to fix the trailing issue. - // When set to black, something is making the hit test not resize with the - // window frame. - RGB(0, 193, 48), - 255, - LWA_COLORKEY)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to set layered window. GetLastError: %d", - GetLastError()); - return GLFW_FALSE; - } - } - - return GLFW_TRUE; -} - // Create the OpenGL or OpenGL ES context // GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, @@ -802,14 +698,6 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, } } - if (fbconfig->transparent) - { - if (!setupTransparentWindow(window)) - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to setup window as transparent as requested"); - - } - window->context.makeCurrent = makeContextCurrentWGL; window->context.swapBuffers = swapBuffersWGL; window->context.swapInterval = swapIntervalWGL; diff --git a/src/win32_init.c b/src/win32_init.c index 619fcfb1..0531ff13 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -62,6 +62,17 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) #endif // _GLFW_BUILD_DLL +// HACK: Define versionhelpers.h functions manually as MinGW lacks the header +BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; + ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + return VerifyVersionInfoW(&osvi, mask, cond); +} + // Load necessary libraries (DLLs) // static GLFWbool loadLibraries(void) @@ -131,7 +142,7 @@ static GLFWbool loadLibraries(void) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); _glfw.win32.dwmapi.Flush = (PFN_DwmFlush) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); - _glfw.win32.dwmapi.DwmEnableBlurBehindWindow = (DWMENABLEBLURBEHINDWINDOW_T) + _glfw.win32.dwmapi.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); } diff --git a/src/win32_platform.h b/src/win32_platform.h index bfed5051..acbb5ca2 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -100,6 +100,9 @@ #ifndef DISPLAY_DEVICE_ACTIVE #define DISPLAY_DEVICE_ACTIVE 0x00000001 #endif +#ifndef _WIN32_WINNT_WINBLUE + #define _WIN32_WINNT_WINBLUE 0x0602 +#endif #if WINVER < 0x0601 typedef struct tagCHANGEFILTERSTRUCT @@ -113,6 +116,18 @@ typedef struct tagCHANGEFILTERSTRUCT #endif #endif /*Windows 7*/ +#if WINVER < 0x0600 +#define DWM_BB_ENABLE 0x00000001 +#define DWM_BB_BLURREGION 0x00000002 +typedef struct +{ + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; +} DWM_BLURBEHIND; +#endif /*Windows Vista*/ + #ifndef DPI_ENUMS_DECLARED typedef enum PROCESS_DPI_AWARENESS { @@ -122,30 +137,8 @@ typedef enum PROCESS_DPI_AWARENESS } PROCESS_DPI_AWARENESS; #endif /*DPI_ENUMS_DECLARED*/ -#if !defined(_DWMAPI_H_) -#define DWM_BB_ENABLE 0x00000001 -#define DWM_BB_BLURREGION 0x00000002 - -typedef struct -{ - DWORD dwFlags; - BOOL fEnable; - HRGN hRgnBlur; - BOOL fTransitionOnMaximized; -} DWM_BLURBEHIND; -#endif - // HACK: Define versionhelpers.h functions manually as MinGW lacks the header -FORCEINLINE BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) -{ - OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; - DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; - ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); - cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); - cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); - return VerifyVersionInfoW(&osvi, mask, cond); -} - +BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp); #define IsWindowsVistaOrGreater() \ IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), \ LOBYTE(_WIN32_WINNT_VISTA), 0) @@ -216,10 +209,10 @@ typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,PCHANGEF // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); -typedef HRESULT(WINAPI * DWMENABLEBLURBEHINDWINDOW_T)(HWND, const DWM_BLURBEHIND*); +typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled #define DwmFlush _glfw.win32.dwmapi.Flush -#define _glfw_DwmEnableBlurBehindWindow _glfw.win32.dwmapi.DwmEnableBlurBehindWindow +#define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); @@ -274,6 +267,8 @@ typedef struct _GLFWwindowWin32 GLFWbool frameAction; GLFWbool iconified; GLFWbool maximized; + // Whether to enable framebuffer transparency on DWM + GLFWbool transparent; // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; @@ -325,7 +320,7 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_DwmIsCompositionEnabled IsCompositionEnabled; PFN_DwmFlush Flush; - DWMENABLEBLURBEHINDWINDOW_T DwmEnableBlurBehindWindow; + PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; } dwmapi; struct { @@ -388,6 +383,7 @@ typedef struct _GLFWmutexWin32 GLFWbool _glfwRegisterWindowClassWin32(void); void _glfwUnregisterWindowClassWin32(void); +GLFWbool _glfwIsCompositionEnabledWin32(void); WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); diff --git a/src/win32_window.c b/src/win32_window.c index 9ce9e436..6bebb19b 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -306,6 +306,55 @@ static void updateWindowStyles(const _GLFWwindow* window) SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); } +// Update window framebuffer transparency +// +static void updateFramebufferTransparency(const _GLFWwindow* window) +{ + if (!IsWindowsVistaOrGreater()) + return; + + if (_glfwIsCompositionEnabledWin32()) + { + HRGN region = CreateRectRgn(0, 0, -1, -1); + DWM_BLURBEHIND bb = {0}; + bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + bb.hRgnBlur = region; + bb.fEnable = TRUE; + + if (SUCCEEDED(DwmEnableBlurBehindWindow(window->win32.handle, &bb))) + { + // Decorated windows don't repaint the transparent background + // leaving a trail behind animations + // HACK: Making the window layered with a transparency color key + // seems to fix this. Normally, when specifying + // a transparency color key to be used when composing the + // layered window, all pixels painted by the window in this + // color will be transparent. That doesn't seem to be the + // case anymore, at least when used with blur behind window + // plus negative region. + LONG style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + style |= WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); + + // Using a color key not equal to black to fix the trailing + // issue. When set to black, something is making the hit test + // not resize with the window frame. + SetLayeredWindowAttributes(window->win32.handle, + RGB(0, 193, 48), 255, LWA_COLORKEY); + } + + DeleteObject(region); + } + else + { + LONG style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + style &= ~WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); + RedrawWindow(window->win32.handle, NULL, NULL, + RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); + } +} + // Translates a GLFW standard cursor to a resource ID // static LPWSTR translateCursorShape(int shape) @@ -916,6 +965,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return TRUE; } + case WM_DWMCOMPOSITIONCHANGED: + { + if (window->win32.transparent) + updateFramebufferTransparency(window); + return 0; + } + case WM_SETCURSOR: { if (LOWORD(lParam) == HTCLIENT) @@ -981,7 +1037,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, // Creates the GLFW window // static int createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig) + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) { int xpos, ypos, fullWidth, fullHeight; WCHAR* wideTitle; @@ -1051,6 +1108,12 @@ static int createNativeWindow(_GLFWwindow* window, DragAcceptFiles(window->win32.handle, TRUE); + if (fbconfig->transparent) + { + updateFramebufferTransparency(window); + window->win32.transparent = GLFW_TRUE; + } + return GLFW_TRUE; } @@ -1102,6 +1165,20 @@ void _glfwUnregisterWindowClassWin32(void) UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); } +// Returns whether desktop compositing is enabled +// +GLFWbool _glfwIsCompositionEnabledWin32(void) +{ + if (IsWindowsVistaOrGreater()) + { + BOOL enabled; + if (SUCCEEDED(DwmIsCompositionEnabled(&enabled))) + return enabled; + } + + return FALSE; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -1112,7 +1189,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - if (!createNativeWindow(window, wndconfig)) + if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) @@ -1481,6 +1558,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return IsZoomed(window->win32.handle); } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return window->win32.transparent && _glfwIsCompositionEnabledWin32(); +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { updateWindowStyles(window); diff --git a/src/window.c b/src/window.c index 863f0ed2..57845579 100644 --- a/src/window.c +++ b/src/window.c @@ -147,7 +147,6 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, fbconfig = _glfw.hints.framebuffer; ctxconfig = _glfw.hints.context; wndconfig = _glfw.hints.window; - fbconfig.transparent = _glfw.hints.framebuffer.transparent ? GLFW_TRUE : GLFW_FALSE; wndconfig.width = width; wndconfig.height = height; @@ -728,6 +727,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return _glfwPlatformWindowVisible(window); case GLFW_MAXIMIZED: return _glfwPlatformWindowMaximized(window); + case GLFW_TRANSPARENT: + return _glfwPlatformFramebufferTransparent(window); case GLFW_RESIZABLE: return window->resizable; case GLFW_DECORATED: diff --git a/src/wl_window.c b/src/wl_window.c index 6c974dfd..caa51d3a 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -654,6 +654,13 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return window->wl.maximized; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Framebuffer transparency attribute not implemented yet"); + return GLFW_FALSE; +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { // TODO diff --git a/src/x11_init.c b/src/x11_init.c index 195c8ce4..526d9ca2 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -651,6 +651,29 @@ static GLFWbool initExtensions(void) dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); } + _glfw.x11.xrender.handle = dlopen("libXrender.so.1", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.xrender.handle) + { + _glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension) + dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension"); + _glfw.x11.xrender.QueryVersion = (PFN_XRenderQueryVersion) + dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion"); + _glfw.x11.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat) + dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat"); + + if (XRenderQueryExtension(_glfw.x11.display, + &_glfw.x11.xrender.errorBase, + &_glfw.x11.xrender.eventBase)) + { + if (XRenderQueryVersion(_glfw.x11.display, + &_glfw.x11.xrender.major, + &_glfw.x11.xrender.minor)) + { + _glfw.x11.xrender.available = GLFW_TRUE; + } + } + } + // Update the key code LUT // FIXME: We should listen to XkbMapNotify events to track changes to // the keyboard mapping. @@ -717,55 +740,6 @@ static GLFWbool initExtensions(void) _glfw.x11.MOTIF_WM_HINTS = XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); - int i; - const char* sonames_xrender[] = - { -#if defined(__CYGWIN__) - "libXrender-1.so", -#else - "libXrender.so.1", - "libXrender.so", -#endif - NULL - }; - - // Xrender support is optional and not a requirement for GLX/EGL - // to work. Xrender is required for selecting a FB config that - // supports a picture format with an alpha mask, which in turn - // is required for transparent windows. I Xrender is not supported - // the GLFW_TRANSPARENT window hint is ignored. - for (i = 0; sonames_xrender[i]; i++) - { - _glfw.xrender.handle = dlopen(sonames_xrender[i], RTLD_LAZY | RTLD_GLOBAL); - if (_glfw.xrender.handle) - break; - } - _glfw.xrender.errorBase = 0; - _glfw.xrender.eventBase = 0; - _glfw.xrender.major = 0; - _glfw.xrender.minor = 0; - if (_glfw.xrender.handle) do { - int errorBase, eventBase, major, minor; - _glfw.xrender.QueryExtension = - dlsym(_glfw.xrender.handle, "XRenderQueryExtension"); - _glfw.xrender.QueryVersion = - dlsym(_glfw.xrender.handle, "XRenderQueryVersion"); - _glfw.xrender.FindVisualFormat = - dlsym(_glfw.xrender.handle, "XRenderFindVisualFormat"); - - if ( !XRenderQueryExtension(_glfw.x11.display, &errorBase, &eventBase)) { - break; - } - if ( !XRenderQueryVersion(_glfw.x11.display, &major, &minor)) { - break; - } - - _glfw.xrender.errorBase = errorBase; - _glfw.xrender.eventBase = eventBase; - _glfw.xrender.major = major; - _glfw.xrender.minor = minor; - } while(0); - return GLFW_TRUE; } diff --git a/src/x11_platform.h b/src/x11_platform.h index 9890f703..2b1c0c62 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -116,6 +116,13 @@ typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); #define XIQueryVersion _glfw.x11.xi.QueryVersion #define XISelectEvents _glfw.x11.xi.SelectEvents +typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*); +typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*); +typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*); +#define XRenderQueryExtension _glfw.x11.xrender.QueryExtension +#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion +#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat + typedef VkFlags VkXlibSurfaceCreateFlagsKHR; typedef VkFlags VkXcbSurfaceCreateFlagsKHR; @@ -162,20 +169,10 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk #define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display) #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11 -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 ; _GLFWlibraryXrender xrender +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11 #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11 -// libXrender.so function pointer typedefs -typedef Bool (*PFNXRENDERQUERYEXTENSIONPROC)(Display*,int*,int*); -typedef Status (*PFNXRENDERQUERYVERSIONPROC)(Display*dpy,int*,int*); -typedef XRenderPictFormat* (*PFNXRENDERFINDVISUALFORMATPROC)(Display*,Visual const *); - -// libXrender.so function identifier overlays -#define XRenderQueryExtension _glfw.xrender.QueryExtension -#define XRenderQueryVersion _glfw.xrender.QueryVersion -#define XRenderFindVisualFormat _glfw.xrender.FindVisualFormat - // X11-specific per-window data // @@ -189,6 +186,9 @@ typedef struct _GLFWwindowX11 GLFWbool iconified; GLFWbool maximized; + // Whether the visual supports framebuffer transparency + GLFWbool transparent; + // Cached position and size used to filter out duplicate events int width, height; int xpos, ypos; @@ -383,24 +383,20 @@ typedef struct _GLFWlibraryX11 PFN_XISelectEvents SelectEvents; } xi; + struct { + GLFWbool available; + void* handle; + int major; + int minor; + int eventBase; + int errorBase; + PFN_XRenderQueryExtension QueryExtension; + PFN_XRenderQueryVersion QueryVersion; + PFN_XRenderFindVisualFormat FindVisualFormat; + } xrender; + } _GLFWlibraryX11; -// Xrender-specific global data -typedef struct _GLFWlibraryXrender -{ - int major, minor; - int eventBase; - int errorBase; - - // dlopen handle for libGL.so.1 - void* handle; - - // Xrender functions (subset required for transparent window) - PFNXRENDERQUERYEXTENSIONPROC QueryExtension; - PFNXRENDERQUERYVERSIONPROC QueryVersion; - PFNXRENDERFINDVISUALFORMATPROC FindVisualFormat; -} _GLFWlibraryXrender; - // X11-specific per-monitor data // typedef struct _GLFWmonitorX11 @@ -434,6 +430,7 @@ unsigned long _glfwGetWindowPropertyX11(Window window, Atom property, Atom type, unsigned char** value); +GLFWbool _glfwIsVisualTransparentX11(Visual* visual); void _glfwGrabErrorHandlerX11(void); void _glfwReleaseErrorHandlerX11(void); diff --git a/src/x11_window.c b/src/x11_window.c index 3addee6c..cf5483b0 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -364,6 +364,7 @@ static void updateWindowMode(_GLFWwindow* window) } // Enable compositor bypass + if (!window->x11.transparent) { const unsigned long value = 1; @@ -402,6 +403,7 @@ static void updateWindowMode(_GLFWwindow* window) } // Disable compositor bypass + if (!window->x11.transparent) { XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_BYPASS_COMPOSITOR); @@ -577,6 +579,8 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, visual, AllocNone); + window->x11.transparent = _glfwIsVisualTransparentX11(visual); + // Create the actual window { XSetWindowAttributes wa; @@ -1838,6 +1842,15 @@ unsigned long _glfwGetWindowPropertyX11(Window window, return itemCount; } +GLFWbool _glfwIsVisualTransparentX11(Visual* visual) +{ + if (!_glfw.x11.xrender.available) + return GLFW_FALSE; + + XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual); + return pf && pf->direct.alphaMask; +} + // Push contents of our selection to clipboard manager // void _glfwPushSelectionToManagerX11(void) @@ -2422,6 +2435,18 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return maximized; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + if (!window->x11.transparent) + return GLFW_FALSE; + + // Check whether a compositing manager is running + char name[32]; + snprintf(name, sizeof(name), "_NET_WM_CM_S%u", _glfw.x11.screen); + const Atom selection = XInternAtom(_glfw.x11.display, name, False); + return XGetSelectionOwner(_glfw.x11.display, selection) != None; +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { int width, height;