Add GLFW_TRANSPARENT attribute and documentation

This completes support for window framebuffer transparency on Windows,
macOS and X11.  Note that the hint/attribute may be renamed before
release to clarify its relationship to GLFW_OPACITY.

Fixes #197.
Closes #1079.
Related to #663.
Related to #715.
Related to #723.
Related to #1078.
This commit is contained in:
Camilla Löwy 2017-09-18 18:10:57 +02:00
parent 93e66661d3
commit 32e78aeb2e
22 changed files with 322 additions and 318 deletions

View File

@ -150,6 +150,8 @@ information on what to include when reporting a bug.
functions for accessing X11 primary selection (#894,#1056) functions for accessing X11 primary selection (#894,#1056)
- Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#850) - Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#850)
- Added definition of `GLAPIENTRY` to public header - 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 - Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering
(#749,#842) (#749,#842)
- Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889) - Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889)
@ -289,6 +291,7 @@ skills.
- Yaron Cohen-Tal - Yaron Cohen-Tal
- Omar Cornut - Omar Cornut
- Andrew Corrigan - Andrew Corrigan
- Bailey Cosier
- Noel Cower - Noel Cower
- Jason Daly - Jason Daly
- Jarrod Davis - Jarrod Davis
@ -297,6 +300,7 @@ skills.
- Michael Dickens - Michael Dickens
- Роман Донченко - Роман Донченко
- Mario Dorn - Mario Dorn
- Wolfgang Draxinger
- Jonathan Dummer - Jonathan Dummer
- Ralph Eastwood - Ralph Eastwood
- Fredrik Ehnbom - Fredrik Ehnbom
@ -322,6 +326,7 @@ skills.
- Erik S. V. Jansson - Erik S. V. Jansson
- Toni Jovanoski - Toni Jovanoski
- Arseny Kapoulkine - Arseny Kapoulkine
- Cem Karan
- Osman Keskin - Osman Keskin
- Josh Kilmer - Josh Kilmer
- Cameron King - Cameron King
@ -363,6 +368,7 @@ skills.
- Andri Pálsson - Andri Pálsson
- Peoro - Peoro
- Braden Pellett - Braden Pellett
- Christopher Pelloux
- Arturo J. Pérez - Arturo J. Pérez
- Anthony Pesch - Anthony Pesch
- Orson Peters - Orson Peters

View File

@ -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 when the cursor is disabled. If the running X server does not support this
extension, regular accelerated mouse motion will be used. 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 @section compat_glx GLX extensions

View File

@ -85,6 +85,13 @@ be disabled with the @ref GLFW_JOYSTICK_HAT_BUTTONS init hint.
@see @ref joystick_hat @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 @subsection news_33_centercursor Cursor centering window hint
GLFW now supports controlling whether the cursor is centered over newly created GLFW now supports controlling whether the cursor is centered over newly created

View File

@ -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 newly created full screen windows. Possible values are `GLFW_TRUE` and
`GLFW_FALSE`. This hint is ignored for windowed mode windows. `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 @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 buffered. You nearly always want to use double buffering. This is a hard
constraint. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. 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 @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_FLOATING | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
GLFW_MAXIMIZED | `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_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_RED_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE`
GLFW_GREEN_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` 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. 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 @subsection window_attribs Window attributes
Windows have a number of attributes that can be returned using @ref 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 [GLFW_FLOATING](@ref GLFW_FLOATING_hint) window hint or after with @ref
glfwSetWindowAttrib. 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 @subsubsection window_attribs_ctx Context related attributes

View File

@ -172,7 +172,7 @@ static GLfloat angle = 0.f;
/* OpenGL draw function & timing */ /* OpenGL draw function & timing */
static void draw(void) 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); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix(); glPushMatrix();
@ -312,7 +312,6 @@ int main(int argc, char *argv[])
} }
glfwWindowHint(GLFW_DEPTH_BITS, 16); glfwWindowHint(GLFW_DEPTH_BITS, 16);
glfwWindowHint(GLFW_ALPHA_BITS, 8);
glfwWindowHint(GLFW_TRANSPARENT, GLFW_TRUE); glfwWindowHint(GLFW_TRANSPARENT, GLFW_TRUE);
window = glfwCreateWindow( 300, 300, "Gears", NULL, NULL ); window = glfwCreateWindow( 300, 300, "Gears", NULL, NULL );

View File

@ -787,6 +787,12 @@ extern "C" {
* Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint).
*/ */
#define GLFW_CENTER_CURSOR 0x00020009 #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. /*! @brief Framebuffer bit depth hint.
* *
@ -868,11 +874,6 @@ extern "C" {
* Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER).
*/ */
#define GLFW_DOUBLEBUFFER 0x00021010 #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. /*! @brief Context client API hint and attribute.
* *

View File

@ -413,11 +413,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (BOOL)isOpaque - (BOOL)isOpaque
{ {
// Set to NO even if alphaMask is not used; return [window->ns.object isOpaque];
// 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;
} }
- (BOOL)canBecomeKeyView - (BOOL)canBecomeKeyView
@ -1016,7 +1012,8 @@ static GLFWbool initializeAppKit(void)
// Create the Cocoa window // Create the Cocoa window
// //
static GLFWbool createNativeWindow(_GLFWwindow* window, static GLFWbool createNativeWindow(_GLFWwindow* window,
const _GLFWwndconfig* wndconfig) const _GLFWwndconfig* wndconfig,
const _GLFWfbconfig* fbconfig)
{ {
window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window]; window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window];
if (window->ns.delegate == nil) if (window->ns.delegate == nil)
@ -1085,7 +1082,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
if (wndconfig->ns.retina) if (wndconfig->ns.retina)
[window->ns.view setWantsBestResolutionOpenGLSurface:YES]; [window->ns.view setWantsBestResolutionOpenGLSurface:YES];
if (_glfw.hints.framebuffer.transparent) if (fbconfig->transparent)
{ {
[window->ns.object setOpaque:NO]; [window->ns.object setOpaque:NO];
[window->ns.object setBackgroundColor:[NSColor clearColor]]; [window->ns.object setBackgroundColor:[NSColor clearColor]];
@ -1114,7 +1111,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
if (!initializeAppKit()) if (!initializeAppKit())
return GLFW_FALSE; return GLFW_FALSE;
if (!createNativeWindow(window, wndconfig)) if (!createNativeWindow(window, wndconfig, fbconfig))
return GLFW_FALSE; return GLFW_FALSE;
if (ctxconfig->client != GLFW_NO_API) if (ctxconfig->client != GLFW_NO_API)
@ -1453,6 +1450,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return [window->ns.object isZoomed]; return [window->ns.object isZoomed];
} }
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return ![window->ns.object isOpaque] && ![window->ns.view isOpaque];
}
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
[window->ns.object setStyleMask:getStyleMask(window)]; [window->ns.object setStyleMask:getStyleMask(window)];

View File

@ -208,6 +208,9 @@ const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
// not important to us here, so we count them as one // not important to us here, so we count them as one
missing++; missing++;
} }
if (desired->transparent != current->transparent)
missing++;
} }
// These polynomials make many small channel size differences matter // These polynomials make many small channel size differences matter

View File

@ -87,21 +87,13 @@ static int getEGLConfigAttrib(EGLConfig config, int attrib)
// //
static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* desired, const _GLFWfbconfig* desired,
EGLConfig* result, EGLConfig* result)
GLFWbool findTransparent)
{ {
EGLConfig* nativeConfigs; EGLConfig* nativeConfigs;
_GLFWfbconfig* usableConfigs; _GLFWfbconfig* usableConfigs;
const _GLFWfbconfig* closest; const _GLFWfbconfig* closest;
int i, nativeCount, usableCount; 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); eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount);
if (!nativeCount) if (!nativeCount)
{ {
@ -115,7 +107,6 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig));
usableCount = 0; usableCount = 0;
selectionloop:
for (i = 0; i < nativeCount; i++) for (i = 0; i < nativeCount; i++)
{ {
const EGLConfig n = nativeConfigs[i]; const EGLConfig n = nativeConfigs[i];
@ -130,31 +121,24 @@ selectionloop:
continue; continue;
#if defined(_GLFW_X11) #if defined(_GLFW_X11)
XVisualInfo vi = {0};
// Only consider EGLConfigs with associated Visuals // Only consider EGLConfigs with associated Visuals
visualTemplate.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID);
if (!visualTemplate.visualid) if (!vi.visualid)
continue; continue;
if( findTransparent ) { if (desired->transparent)
int n_vi; {
XVisualInfo *visualinfo; int count;
XRenderPictFormat *pictFormat; XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display,
VisualIDMask, &vi,
visualinfo = XGetVisualInfo(_glfw.x11.display, VisualIDMask, &visualTemplate, &n_vi); &count);
if (!visualinfo) if (vis)
continue; {
u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
pictFormat = XRenderFindVisualFormat(_glfw.x11.display, visualinfo->visual); XFree(vis);
if( !pictFormat ) { }
XFree( visualinfo );
continue;
}
if( !pictFormat->direct.alphaMask ) {
XFree( visualinfo );
continue;
}
XFree( visualinfo );
} }
#endif // _GLFW_X11 #endif // _GLFW_X11
@ -191,12 +175,6 @@ selectionloop:
u->handle = (uintptr_t) n; u->handle = (uintptr_t) n;
usableCount++; 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); closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
if (closest) if (closest)
@ -493,7 +471,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
if (ctxconfig->share) if (ctxconfig->share)
share = ctxconfig->share->context.egl.handle; share = ctxconfig->share->context.egl.handle;
if (!chooseEGLConfig(ctxconfig, fbconfig, &config, fbconfig->transparent)) if (!chooseEGLConfig(ctxconfig, fbconfig, &config))
{ {
_glfwInputError(GLFW_FORMAT_UNAVAILABLE, _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"EGL: Failed to find a suitable EGLConfig"); "EGL: Failed to find a suitable EGLConfig");
@ -738,7 +716,7 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
EGLint visualID = 0, count = 0; EGLint visualID = 0, count = 0;
const long vimask = VisualScreenMask | VisualIDMask; const long vimask = VisualScreenMask | VisualIDMask;
if (!chooseEGLConfig(ctxconfig, fbconfig, &native, fbconfig->transparent)) if (!chooseEGLConfig(ctxconfig, fbconfig, &native))
{ {
_glfwInputError(GLFW_FORMAT_UNAVAILABLE, _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"EGL: Failed to find a suitable EGLConfig"); "EGL: Failed to find a suitable EGLConfig");

View File

@ -47,10 +47,8 @@ static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib)
// Return the GLXFBConfig most closely matching the specified hints // Return the GLXFBConfig most closely matching the specified hints
// //
static GLFWbool chooseGLXFBConfig( static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired,
const _GLFWfbconfig* desired, GLXFBConfig* result)
GLXFBConfig* result,
GLFWbool findTransparent)
{ {
GLXFBConfig* nativeConfigs; GLXFBConfig* nativeConfigs;
_GLFWfbconfig* usableConfigs; _GLFWfbconfig* usableConfigs;
@ -59,10 +57,6 @@ static GLFWbool chooseGLXFBConfig(
const char* vendor; const char* vendor;
GLFWbool trustWindowBit = GLFW_TRUE; GLFWbool trustWindowBit = GLFW_TRUE;
if ( !(_glfw.xrender.major || _glfw.xrender.minor) ) {
findTransparent = GLFW_FALSE;
}
// HACK: This is a (hopefully temporary) workaround for Chromium // HACK: This is a (hopefully temporary) workaround for Chromium
// (VirtualBox GL) not setting the window bit on any GLXFBConfigs // (VirtualBox GL) not setting the window bit on any GLXFBConfigs
vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR); vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR);
@ -80,7 +74,6 @@ static GLFWbool chooseGLXFBConfig(
usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig));
usableCount = 0; usableCount = 0;
selectionloop:
for (i = 0; i < nativeCount; i++) for (i = 0; i < nativeCount; i++)
{ {
const GLXFBConfig n = nativeConfigs[i]; const GLXFBConfig n = nativeConfigs[i];
@ -97,25 +90,14 @@ selectionloop:
continue; continue;
} }
if( findTransparent ) { if (desired->transparent)
XVisualInfo *visualinfo; {
XRenderPictFormat *pictFormat; XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n);
if (vi)
visualinfo = glXGetVisualFromFBConfig(_glfw.x11.display, n); {
if (!visualinfo) u->transparent = _glfwIsVisualTransparentX11(vi->visual);
continue; XFree(vi);
}
pictFormat = XRenderFindVisualFormat(_glfw.x11.display, visualinfo->visual);
if( !pictFormat ) {
XFree( visualinfo );
continue;
}
if( !pictFormat->direct.alphaMask ) {
XFree( visualinfo );
continue;
}
XFree( visualinfo );
} }
u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE);
@ -147,12 +129,6 @@ selectionloop:
u->handle = (uintptr_t) n; u->handle = (uintptr_t) n;
usableCount++; 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); closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
if (closest) if (closest)
@ -477,7 +453,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
if (ctxconfig->share) if (ctxconfig->share)
share = ctxconfig->share->context.glx.handle; share = ctxconfig->share->context.glx.handle;
if (!chooseGLXFBConfig(fbconfig, &native, fbconfig->transparent)) if (!chooseGLXFBConfig(fbconfig, &native))
{ {
_glfwInputError(GLFW_FORMAT_UNAVAILABLE, _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"GLX: Failed to find a suitable GLXFBConfig"); "GLX: Failed to find a suitable GLXFBConfig");
@ -665,7 +641,7 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
GLXFBConfig native; GLXFBConfig native;
XVisualInfo* result; XVisualInfo* result;
if (!chooseGLXFBConfig(fbconfig, &native, fbconfig->transparent)) if (!chooseGLXFBConfig(fbconfig, &native))
{ {
_glfwInputError(GLFW_FORMAT_UNAVAILABLE, _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"GLX: Failed to find a suitable GLXFBConfig"); "GLX: Failed to find a suitable GLXFBConfig");

View File

@ -682,6 +682,7 @@ int _glfwPlatformWindowFocused(_GLFWwindow* window);
int _glfwPlatformWindowIconified(_GLFWwindow* window); int _glfwPlatformWindowIconified(_GLFWwindow* window);
int _glfwPlatformWindowVisible(_GLFWwindow* window); int _glfwPlatformWindowVisible(_GLFWwindow* window);
int _glfwPlatformWindowMaximized(_GLFWwindow* window); int _glfwPlatformWindowMaximized(_GLFWwindow* window);
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window);
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled);
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled);
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled);

View File

@ -614,6 +614,13 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return mir_window_get_state(window->mir.window) == mir_window_state_maximized; 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) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,

View File

@ -156,6 +156,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return GLFW_FALSE; return GLFW_FALSE;
} }
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return GLFW_FALSE;
}
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
} }

View File

@ -83,20 +83,6 @@ static int choosePixelFormat(_GLFWwindow* window,
{ {
const int n = i + 1; const int n = i + 1;
_GLFWfbconfig* u = usableConfigs + usableCount; _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) if (_glfw.wgl.ARB_pixel_format)
{ {
@ -168,7 +154,9 @@ static int choosePixelFormat(_GLFWwindow* window,
{ {
// Get pixel format attributes through legacy PFDs // Get pixel format attributes through legacy PFDs
if (!fbconfig->transparent && DescribePixelFormat(window->context.wgl.dc, PIXELFORMATDESCRIPTOR pfd;
if (!DescribePixelFormat(window->context.wgl.dc,
n, n,
sizeof(PIXELFORMATDESCRIPTOR), sizeof(PIXELFORMATDESCRIPTOR),
&pfd)) &pfd))
@ -215,14 +203,6 @@ static int choosePixelFormat(_GLFWwindow* window,
u->handle = n; u->handle = n;
usableCount++; 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) if (!usableCount)
{ {
@ -249,21 +229,6 @@ static int choosePixelFormat(_GLFWwindow* window,
return pixelFormat; 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) static void makeContextCurrentWGL(_GLFWwindow* window)
{ {
if (window) if (window)
@ -292,7 +257,7 @@ static void makeContextCurrentWGL(_GLFWwindow* window)
static void swapBuffersWGL(_GLFWwindow* window) static void swapBuffersWGL(_GLFWwindow* window)
{ {
// HACK: Use DwmFlush when desktop composition is enabled // HACK: Use DwmFlush when desktop composition is enabled
if (isCompositionEnabled() && !window->monitor) if (_glfwIsCompositionEnabledWin32() && !window->monitor)
{ {
int count = abs(window->context.wgl.interval); int count = abs(window->context.wgl.interval);
while (count--) while (count--)
@ -310,7 +275,7 @@ static void swapIntervalWGL(int interval)
// HACK: Disable WGL swap interval when desktop composition is enabled to // HACK: Disable WGL swap interval when desktop composition is enabled to
// avoid interfering with DWM vsync // avoid interfering with DWM vsync
if (isCompositionEnabled() && !window->monitor) if (_glfwIsCompositionEnabledWin32() && !window->monitor)
interval = 0; interval = 0;
if (_glfw.wgl.EXT_swap_control) if (_glfw.wgl.EXT_swap_control)
@ -504,75 +469,6 @@ void _glfwTerminateWGL(void)
attribs[index++] = v; \ 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 // Create the OpenGL or OpenGL ES context
// //
GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, 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.makeCurrent = makeContextCurrentWGL;
window->context.swapBuffers = swapBuffersWGL; window->context.swapBuffers = swapBuffersWGL;
window->context.swapInterval = swapIntervalWGL; window->context.swapInterval = swapIntervalWGL;

View File

@ -62,6 +62,17 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
#endif // _GLFW_BUILD_DLL #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) // Load necessary libraries (DLLs)
// //
static GLFWbool loadLibraries(void) static GLFWbool loadLibraries(void)
@ -131,7 +142,7 @@ static GLFWbool loadLibraries(void)
GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled");
_glfw.win32.dwmapi.Flush = (PFN_DwmFlush) _glfw.win32.dwmapi.Flush = (PFN_DwmFlush)
GetProcAddress(_glfw.win32.dwmapi.instance, "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"); GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow");
} }

View File

@ -100,6 +100,9 @@
#ifndef DISPLAY_DEVICE_ACTIVE #ifndef DISPLAY_DEVICE_ACTIVE
#define DISPLAY_DEVICE_ACTIVE 0x00000001 #define DISPLAY_DEVICE_ACTIVE 0x00000001
#endif #endif
#ifndef _WIN32_WINNT_WINBLUE
#define _WIN32_WINNT_WINBLUE 0x0602
#endif
#if WINVER < 0x0601 #if WINVER < 0x0601
typedef struct tagCHANGEFILTERSTRUCT typedef struct tagCHANGEFILTERSTRUCT
@ -113,6 +116,18 @@ typedef struct tagCHANGEFILTERSTRUCT
#endif #endif
#endif /*Windows 7*/ #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 #ifndef DPI_ENUMS_DECLARED
typedef enum PROCESS_DPI_AWARENESS typedef enum PROCESS_DPI_AWARENESS
{ {
@ -122,30 +137,8 @@ typedef enum PROCESS_DPI_AWARENESS
} PROCESS_DPI_AWARENESS; } PROCESS_DPI_AWARENESS;
#endif /*DPI_ENUMS_DECLARED*/ #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 // HACK: Define versionhelpers.h functions manually as MinGW lacks the header
FORCEINLINE BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) 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);
}
#define IsWindowsVistaOrGreater() \ #define IsWindowsVistaOrGreater() \
IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), \ IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), \
LOBYTE(_WIN32_WINNT_VISTA), 0) LOBYTE(_WIN32_WINNT_VISTA), 0)
@ -216,10 +209,10 @@ typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,PCHANGEF
// dwmapi.dll function pointer typedefs // dwmapi.dll function pointer typedefs
typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*);
typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); 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 DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled
#define DwmFlush _glfw.win32.dwmapi.Flush #define DwmFlush _glfw.win32.dwmapi.Flush
#define _glfw_DwmEnableBlurBehindWindow _glfw.win32.dwmapi.DwmEnableBlurBehindWindow #define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow
// shcore.dll function pointer typedefs // shcore.dll function pointer typedefs
typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS);
@ -274,6 +267,8 @@ typedef struct _GLFWwindowWin32
GLFWbool frameAction; GLFWbool frameAction;
GLFWbool iconified; GLFWbool iconified;
GLFWbool maximized; GLFWbool maximized;
// Whether to enable framebuffer transparency on DWM
GLFWbool transparent;
// The last received cursor position, regardless of source // The last received cursor position, regardless of source
int lastCursorPosX, lastCursorPosY; int lastCursorPosX, lastCursorPosY;
@ -325,7 +320,7 @@ typedef struct _GLFWlibraryWin32
HINSTANCE instance; HINSTANCE instance;
PFN_DwmIsCompositionEnabled IsCompositionEnabled; PFN_DwmIsCompositionEnabled IsCompositionEnabled;
PFN_DwmFlush Flush; PFN_DwmFlush Flush;
DWMENABLEBLURBEHINDWINDOW_T DwmEnableBlurBehindWindow; PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow;
} dwmapi; } dwmapi;
struct { struct {
@ -388,6 +383,7 @@ typedef struct _GLFWmutexWin32
GLFWbool _glfwRegisterWindowClassWin32(void); GLFWbool _glfwRegisterWindowClassWin32(void);
void _glfwUnregisterWindowClassWin32(void); void _glfwUnregisterWindowClassWin32(void);
GLFWbool _glfwIsCompositionEnabledWin32(void);
WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source);
char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source);

View File

@ -306,6 +306,55 @@ static void updateWindowStyles(const _GLFWwindow* window)
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); 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 // Translates a GLFW standard cursor to a resource ID
// //
static LPWSTR translateCursorShape(int shape) static LPWSTR translateCursorShape(int shape)
@ -916,6 +965,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
return TRUE; return TRUE;
} }
case WM_DWMCOMPOSITIONCHANGED:
{
if (window->win32.transparent)
updateFramebufferTransparency(window);
return 0;
}
case WM_SETCURSOR: case WM_SETCURSOR:
{ {
if (LOWORD(lParam) == HTCLIENT) if (LOWORD(lParam) == HTCLIENT)
@ -981,7 +1037,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
// Creates the GLFW window // Creates the GLFW window
// //
static int createNativeWindow(_GLFWwindow* window, static int createNativeWindow(_GLFWwindow* window,
const _GLFWwndconfig* wndconfig) const _GLFWwndconfig* wndconfig,
const _GLFWfbconfig* fbconfig)
{ {
int xpos, ypos, fullWidth, fullHeight; int xpos, ypos, fullWidth, fullHeight;
WCHAR* wideTitle; WCHAR* wideTitle;
@ -1051,6 +1108,12 @@ static int createNativeWindow(_GLFWwindow* window,
DragAcceptFiles(window->win32.handle, TRUE); DragAcceptFiles(window->win32.handle, TRUE);
if (fbconfig->transparent)
{
updateFramebufferTransparency(window);
window->win32.transparent = GLFW_TRUE;
}
return GLFW_TRUE; return GLFW_TRUE;
} }
@ -1102,6 +1165,20 @@ void _glfwUnregisterWindowClassWin32(void)
UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); 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 ////// ////// GLFW platform API //////
@ -1112,7 +1189,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig, const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig) const _GLFWfbconfig* fbconfig)
{ {
if (!createNativeWindow(window, wndconfig)) if (!createNativeWindow(window, wndconfig, fbconfig))
return GLFW_FALSE; return GLFW_FALSE;
if (ctxconfig->client != GLFW_NO_API) if (ctxconfig->client != GLFW_NO_API)
@ -1481,6 +1558,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return IsZoomed(window->win32.handle); return IsZoomed(window->win32.handle);
} }
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return window->win32.transparent && _glfwIsCompositionEnabledWin32();
}
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
updateWindowStyles(window); updateWindowStyles(window);

View File

@ -147,7 +147,6 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
fbconfig = _glfw.hints.framebuffer; fbconfig = _glfw.hints.framebuffer;
ctxconfig = _glfw.hints.context; ctxconfig = _glfw.hints.context;
wndconfig = _glfw.hints.window; wndconfig = _glfw.hints.window;
fbconfig.transparent = _glfw.hints.framebuffer.transparent ? GLFW_TRUE : GLFW_FALSE;
wndconfig.width = width; wndconfig.width = width;
wndconfig.height = height; wndconfig.height = height;
@ -728,6 +727,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return _glfwPlatformWindowVisible(window); return _glfwPlatformWindowVisible(window);
case GLFW_MAXIMIZED: case GLFW_MAXIMIZED:
return _glfwPlatformWindowMaximized(window); return _glfwPlatformWindowMaximized(window);
case GLFW_TRANSPARENT:
return _glfwPlatformFramebufferTransparent(window);
case GLFW_RESIZABLE: case GLFW_RESIZABLE:
return window->resizable; return window->resizable;
case GLFW_DECORATED: case GLFW_DECORATED:

View File

@ -654,6 +654,13 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return window->wl.maximized; 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) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
// TODO // TODO

View File

@ -651,6 +651,29 @@ static GLFWbool initExtensions(void)
dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); 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 // Update the key code LUT
// FIXME: We should listen to XkbMapNotify events to track changes to // FIXME: We should listen to XkbMapNotify events to track changes to
// the keyboard mapping. // the keyboard mapping.
@ -717,55 +740,6 @@ static GLFWbool initExtensions(void)
_glfw.x11.MOTIF_WM_HINTS = _glfw.x11.MOTIF_WM_HINTS =
XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); 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; return GLFW_TRUE;
} }

View File

@ -116,6 +116,13 @@ typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
#define XIQueryVersion _glfw.x11.xi.QueryVersion #define XIQueryVersion _glfw.x11.xi.QueryVersion
#define XISelectEvents _glfw.x11.xi.SelectEvents #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 VkXlibSurfaceCreateFlagsKHR;
typedef VkFlags VkXcbSurfaceCreateFlagsKHR; typedef VkFlags VkXcbSurfaceCreateFlagsKHR;
@ -162,20 +169,10 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk
#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display) #define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display)
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11 #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_MONITOR_STATE _GLFWmonitorX11 x11
#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 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 // X11-specific per-window data
// //
@ -189,6 +186,9 @@ typedef struct _GLFWwindowX11
GLFWbool iconified; GLFWbool iconified;
GLFWbool maximized; GLFWbool maximized;
// Whether the visual supports framebuffer transparency
GLFWbool transparent;
// Cached position and size used to filter out duplicate events // Cached position and size used to filter out duplicate events
int width, height; int width, height;
int xpos, ypos; int xpos, ypos;
@ -383,24 +383,20 @@ typedef struct _GLFWlibraryX11
PFN_XISelectEvents SelectEvents; PFN_XISelectEvents SelectEvents;
} xi; } 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; } _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 // X11-specific per-monitor data
// //
typedef struct _GLFWmonitorX11 typedef struct _GLFWmonitorX11
@ -434,6 +430,7 @@ unsigned long _glfwGetWindowPropertyX11(Window window,
Atom property, Atom property,
Atom type, Atom type,
unsigned char** value); unsigned char** value);
GLFWbool _glfwIsVisualTransparentX11(Visual* visual);
void _glfwGrabErrorHandlerX11(void); void _glfwGrabErrorHandlerX11(void);
void _glfwReleaseErrorHandlerX11(void); void _glfwReleaseErrorHandlerX11(void);

View File

@ -364,6 +364,7 @@ static void updateWindowMode(_GLFWwindow* window)
} }
// Enable compositor bypass // Enable compositor bypass
if (!window->x11.transparent)
{ {
const unsigned long value = 1; const unsigned long value = 1;
@ -402,6 +403,7 @@ static void updateWindowMode(_GLFWwindow* window)
} }
// Disable compositor bypass // Disable compositor bypass
if (!window->x11.transparent)
{ {
XDeleteProperty(_glfw.x11.display, window->x11.handle, XDeleteProperty(_glfw.x11.display, window->x11.handle,
_glfw.x11.NET_WM_BYPASS_COMPOSITOR); _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
@ -577,6 +579,8 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
visual, visual,
AllocNone); AllocNone);
window->x11.transparent = _glfwIsVisualTransparentX11(visual);
// Create the actual window // Create the actual window
{ {
XSetWindowAttributes wa; XSetWindowAttributes wa;
@ -1838,6 +1842,15 @@ unsigned long _glfwGetWindowPropertyX11(Window window,
return itemCount; 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 // Push contents of our selection to clipboard manager
// //
void _glfwPushSelectionToManagerX11(void) void _glfwPushSelectionToManagerX11(void)
@ -2422,6 +2435,18 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return maximized; 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) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
int width, height; int width, height;