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

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

View File

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

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

View File

@ -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 );

View File

@ -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.
*

View File

@ -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)];

View File

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

View File

@ -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 (desired->transparent)
{
int count;
XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display,
VisualIDMask, &vi,
&count);
if (vis)
{
u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
XFree(vis);
}
if( !pictFormat->direct.alphaMask ) {
XFree( visualinfo );
continue;
}
XFree( visualinfo );
}
#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");

View File

@ -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 (desired->transparent)
{
XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n);
if (vi)
{
u->transparent = _glfwIsVisualTransparentX11(vi->visual);
XFree(vi);
}
if( !pictFormat->direct.alphaMask ) {
XFree( visualinfo );
continue;
}
XFree( visualinfo );
}
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");

View File

@ -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);

View File

@ -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,

View File

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

View File

@ -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;

View File

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

View File

@ -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);

View File

@ -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);

View File

@ -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:

View File

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

View File

@ -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;
}

View File

@ -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,23 +383,19 @@ typedef struct _GLFWlibraryX11
PFN_XISelectEvents SelectEvents;
} xi;
} _GLFWlibraryX11;
// Xrender-specific global data
typedef struct _GLFWlibraryXrender
{
int major, minor;
struct {
GLFWbool available;
void* handle;
int major;
int minor;
int eventBase;
int errorBase;
PFN_XRenderQueryExtension QueryExtension;
PFN_XRenderQueryVersion QueryVersion;
PFN_XRenderFindVisualFormat FindVisualFormat;
} xrender;
// dlopen handle for libGL.so.1
void* handle;
// Xrender functions (subset required for transparent window)
PFNXRENDERQUERYEXTENSIONPROC QueryExtension;
PFNXRENDERQUERYVERSIONPROC QueryVersion;
PFNXRENDERFINDVISUALFORMATPROC FindVisualFormat;
} _GLFWlibraryXrender;
} _GLFWlibraryX11;
// X11-specific per-monitor data
//
@ -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);

View File

@ -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;