diff --git a/.gitignore b/.gitignore index dd88e65e4..de69337eb 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ Release MinSizeRel RelWithDebInfo *.xcodeproj +.vs +*.VC.opendb # CMake files Makefile diff --git a/docs/window.dox b/docs/window.dox index d9a837f63..30b426206 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -228,6 +228,10 @@ newly created full screen windows. Possible values are `GLFW_TRUE` and @subsubsection window_hints_fb Framebuffer related hints +@anchor GLFW_TRANSPARENT_hint +__GLFW_TRANSPARENT__ specifies whether the framebuffer will support transparency +in the background. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. + @anchor GLFW_RED_BITS @anchor GLFW_GREEN_BITS @anchor GLFW_BLUE_BITS diff --git a/examples/gears.c b/examples/gears.c index a787baedf..669931b65 100644 --- a/examples/gears.c +++ b/examples/gears.c @@ -172,6 +172,7 @@ static GLfloat angle = 0.f; /* OpenGL draw function & timing */ static void draw(void) { + glClearColor(0., 0., 0., 0.); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); @@ -297,7 +298,6 @@ static void init(void) glEnable(GL_NORMALIZE); } - /* program entry */ int main(int argc, char *argv[]) { @@ -311,6 +311,9 @@ int main(int argc, char *argv[]) } glfwWindowHint(GLFW_DEPTH_BITS, 16); + glfwWindowHint(GLFW_ALPHA_BITS, 8); + glfwWindowHint(GLFW_TRANSPARENT, GLFW_TRUE); + glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); window = glfwCreateWindow( 300, 300, "Gears", NULL, NULL ); if (!window) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 05e76a9ae..e1f1e8a43 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -788,6 +788,7 @@ extern "C" { */ #define GLFW_CENTER_CURSOR 0x00020009 + /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). @@ -868,6 +869,11 @@ 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. * * Context client API [hint](@ref GLFW_CLIENT_API_hint) and diff --git a/src/cocoa_window.m b/src/cocoa_window.m index cd4f3f894..e4af6d9fc 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -413,7 +413,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (BOOL)isOpaque { - return YES; + // Set to NO even if transparent 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; } - (BOOL)canBecomeKeyView @@ -1081,6 +1085,12 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, if (wndconfig->ns.retina) [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; + if (_glfw.hints.framebuffer.transparent) + { + [window->ns.object setOpaque:NO]; + [window->ns.object setBackgroundColor:[NSColor clearColor]]; + } + [window->ns.object setContentView:window->ns.view]; [window->ns.object makeFirstResponder:window->ns.view]; [window->ns.object setTitle:[NSString stringWithUTF8String:wndconfig->title]]; diff --git a/src/egl_context.c b/src/egl_context.c index 54257f25d..ff8b8aedc 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -93,6 +93,14 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; int i, nativeCount, usableCount; + GLFWbool findTransparent = desired->transparent; + +#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) @@ -107,6 +115,7 @@ 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]; @@ -124,6 +133,28 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, // Only consider EGLConfigs with associated Visuals if (!getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID)) 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 ); + } #endif // _GLFW_X11 if (ctxconfig->client == GLFW_OPENGL_ES_API) @@ -159,6 +190,12 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, 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) @@ -699,6 +736,7 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, EGLint visualID = 0, count = 0; const long vimask = VisualScreenMask | VisualIDMask; + if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, @@ -766,4 +804,3 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle) return window->context.egl.surface; } - diff --git a/src/glx_context.c b/src/glx_context.c index e545fa0fe..a6ce6e672 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -55,6 +55,11 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res int i, nativeCount, usableCount; const char* vendor; GLFWbool trustWindowBit = GLFW_TRUE; + GLFWbool findTransparent = desired->transparent; + + 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 @@ -64,6 +69,7 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res nativeConfigs = glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount); + if (!nativeConfigs || !nativeCount) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned"); @@ -73,6 +79,7 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; +selectionloop: for (i = 0; i < nativeCount; i++) { const GLXFBConfig n = nativeConfigs[i]; @@ -89,6 +96,29 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res 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 ); + } + u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE); u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE); @@ -118,6 +148,12 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res 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) @@ -683,4 +719,3 @@ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle) return window->context.glx.window; } - diff --git a/src/glx_context.h b/src/glx_context.h index 30743c112..a8cdf7398 100644 --- a/src/glx_context.h +++ b/src/glx_context.h @@ -72,6 +72,7 @@ typedef struct __GLXFBConfig* GLXFBConfig; typedef struct __GLXcontext* GLXContext; typedef void (*__GLXextproc)(void); +// libGL.so function pointer typedefs typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); @@ -92,7 +93,6 @@ typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); -// libGL.so function pointer typedefs #define glXGetFBConfigs _glfw.glx.GetFBConfigs #define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib #define glXGetClientString _glfw.glx.GetClientString @@ -168,7 +168,6 @@ typedef struct _GLFWlibraryGLX } _GLFWlibraryGLX; - GLFWbool _glfwInitGLX(void); void _glfwTerminateGLX(void); GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, diff --git a/src/internal.h b/src/internal.h index a95d1f867..eb80e24ff 100644 --- a/src/internal.h +++ b/src/internal.h @@ -357,7 +357,8 @@ struct _GLFWfbconfig int auxBuffers; GLFWbool stereo; int samples; - GLFWbool sRGB; + GLFWbool sRGB; + GLFWbool transparent; GLFWbool doublebuffer; uintptr_t handle; }; diff --git a/src/nsgl_context.m b/src/nsgl_context.m index 8504f0b97..a7cbf00f3 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -296,6 +296,12 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, return GLFW_FALSE; } + if (fbconfig->transparent) + { + GLint opaque = 0; + [window->context.nsgl.object setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; + } + [window->context.nsgl.object setView:window->ns.view]; window->context.makeCurrent = makeContextCurrentNSGL; diff --git a/src/wgl_context.c b/src/wgl_context.c index 1f2b12a50..e1f817008 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -56,7 +56,8 @@ static int getPixelFormatAttrib(_GLFWwindow* window, int pixelFormat, int attrib // static int choosePixelFormat(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) + const _GLFWfbconfig* fbconfig, + const GLFWbool transparent) { _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; @@ -83,6 +84,20 @@ 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) { @@ -152,11 +167,9 @@ static int choosePixelFormat(_GLFWwindow* window, } else { - PIXELFORMATDESCRIPTOR pfd; - // Get pixel format attributes through legacy PFDs - if (!DescribePixelFormat(window->context.wgl.dc, + if (!fbconfig->transparent && !DescribePixelFormat(window->context.wgl.dc, n, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) @@ -201,8 +214,20 @@ static int choosePixelFormat(_GLFWwindow* window, } u->handle = n; + + // always able to go transparent on win dwmapi + u->transparent = GLFW_TRUE; + 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, GLFW_FALSE); + } if (!usableCount) { @@ -484,6 +509,101 @@ void _glfwTerminateWGL(void) attribs[index++] = v; \ } +// Reliably check windows version as done in VersionHelpers.h +// needed for transparent window +static GLFWbool +isWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0,{ 0 }, 0, 0 }; + DWORDLONG const dwlConditionMask = VerSetConditionMask( + VerSetConditionMask( + VerSetConditionMask( + 0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_GREATER_EQUAL), + VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + + osvi.dwMajorVersion = wMajorVersion; + osvi.dwMinorVersion = wMinorVersion; + osvi.wServicePackMajor = wServicePackMajor; + + return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; +} + +static GLFWbool isWindows8OrGreater() +{ + GLFWbool isWin8OrGreater = isWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0) ? GLFW_TRUE: GLFW_FALSE; + return isWin8OrGreater; +} + +static GLFWbool setupTransparentWindow(_GLFWwindow* window) +{ + if (!DwmIsCompositionEnabled) { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Composition needed for transparent window is disabled"); + } + if (!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 = 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 = GetWindowLongPtrW(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 (!SetWindowLongPtrW(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, @@ -509,7 +629,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, return GLFW_FALSE; } - pixelFormat = choosePixelFormat(window, ctxconfig, fbconfig); + pixelFormat = choosePixelFormat(window, ctxconfig, fbconfig, fbconfig->transparent); if (!pixelFormat) return GLFW_FALSE; @@ -664,8 +784,9 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, ctxconfig->major, ctxconfig->minor); } - } - else if (error == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) + } + else if (error == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) + { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "WGL: Driver does not support the requested OpenGL profile"); @@ -713,6 +834,13 @@ 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 60c974224..3695cb9ee 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -131,6 +131,8 @@ 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.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow) + GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); } _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); diff --git a/src/win32_platform.h b/src/win32_platform.h index 63aaa94dc..1943f879b 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -153,7 +153,22 @@ typedef enum PROCESS_DPI_AWARENESS // HACK: Define macros that some dinput.h variants don't #ifndef DIDFT_OPTIONAL - #define DIDFT_OPTIONAL 0x80000000 +#define DIDFT_OPTIONAL 0x80000000 +#endif + +#if !defined(_DWMAPI_H_) +// Blur behind data structures +#define DWM_BB_ENABLE 0x00000001 // fEnable has been specified +#define DWM_BB_BLURREGION 0x00000002 // hRgnBlur has been specified +#define DWM_BB_TRANSITIONONMAXIMIZED 0x00000004 // fTransitionOnMaximized has been specified + +typedef struct _DWM_BLURBEHIND +{ + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; +} DWM_BLURBEHIND, *PDWM_BLURBEHIND; #endif // winmm.dll function pointer typedefs @@ -179,9 +194,13 @@ 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); + #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled #define DwmFlush _glfw.win32.dwmapi.Flush +typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND, const DWM_BLURBEHIND*); +#define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow + // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); #define _glfw_SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness @@ -286,6 +305,7 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_DwmIsCompositionEnabled IsCompositionEnabled; PFN_DwmFlush Flush; + PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; } dwmapi; struct { diff --git a/src/window.c b/src/window.c index 5c33217de..3590ce830 100644 --- a/src/window.c +++ b/src/window.c @@ -147,6 +147,7 @@ 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; @@ -262,6 +263,7 @@ void glfwDefaultWindowHints(void) _glfw.hints.framebuffer.depthBits = 24; _glfw.hints.framebuffer.stencilBits = 8; _glfw.hints.framebuffer.doublebuffer = GLFW_TRUE; + _glfw.hints.framebuffer.transparent = GLFW_FALSE; // The default is to select the highest available refresh rate _glfw.hints.refreshRate = GLFW_DONT_CARE; @@ -315,6 +317,9 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_DOUBLEBUFFER: _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; return; + case GLFW_TRANSPARENT: + _glfw.hints.framebuffer.transparent = value; + return; case GLFW_SAMPLES: _glfw.hints.framebuffer.samples = value; return; diff --git a/src/x11_init.c b/src/x11_init.c index 3bf07d2d5..44644e291 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -480,6 +480,7 @@ static GLFWbool initExtensions(void) } _glfw.x11.xi.handle = dlopen("libXi.so.6", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.xi.handle) { _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) @@ -505,7 +506,9 @@ static GLFWbool initExtensions(void) } } + // Check for RandR extension _glfw.x11.randr.handle = dlopen("libXrandr.so.2", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.randr.handle) { _glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma) @@ -717,6 +720,76 @@ static GLFWbool initExtensions(void) _glfw.x11.MOTIF_WM_HINTS = XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); + + const char* sonames_xrender[] = + { +#if defined(__CYGWIN__) + "libXrender-1.so", +#else + "libXrender.so.1", + "libXrender.so", +#endif + NULL + }; + + // Find or create window manager atoms + _glfw.x11.WM_PROTOCOLS = XInternAtom(_glfw.x11.display, + "WM_PROTOCOLS", + False); + _glfw.x11.WM_STATE = XInternAtom(_glfw.x11.display, "WM_STATE", False); + _glfw.x11.WM_DELETE_WINDOW = XInternAtom(_glfw.x11.display, + "WM_DELETE_WINDOW", + False); + _glfw.x11.MOTIF_WM_HINTS = XInternAtom(_glfw.x11.display, + "_MOTIF_WM_HINTS", + False); + +#if defined(_GLFW_HAS_XF86VM) + // Check for XF86VidMode extension + _glfw.x11.vidmode.available = + XF86VidModeQueryExtension(_glfw.x11.display, + &_glfw.x11.vidmode.eventBase, + &_glfw.x11.vidmode.errorBase); +#endif /*_GLFW_HAS_XF86VM*/ + + // 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. + int i; + 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; } @@ -979,4 +1052,3 @@ const char* _glfwPlatformGetVersionString(void) #endif ; } - diff --git a/src/x11_platform.h b/src/x11_platform.h index 39eaec056..9890f7034 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -162,10 +162,20 @@ 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 +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 ; _GLFWlibraryXrender xrender #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 // @@ -375,6 +385,22 @@ typedef struct _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 // typedef struct _GLFWmonitorX11