浏览代码

Add glfwGetWindowOpacity and glfwSetWindowOpacity

This adds support for setting the opacity of the whole window, including
any decorations.

Fixes #1089.
Camilla Löwy 8 年之前
父节点
当前提交
11e47f08b1
共有 16 个文件被更改,包括 354 次插入12 次删除
  1. 4 2
      README.md
  2. 8 3
      docs/news.dox
  3. 33 0
      docs/window.dox
  4. 56 0
      include/GLFW/glfw3.h
  5. 10 0
      src/cocoa_window.m
  6. 2 0
      src/internal.h
  7. 9 0
      src/mir_window.c
  8. 9 0
      src/null_window.c
  9. 33 0
      src/win32_window.c
  10. 28 0
      src/window.c
  11. 9 0
      src/wl_window.c
  12. 9 0
      src/x11_init.c
  13. 2 0
      src/x11_platform.h
  14. 32 5
      src/x11_window.c
  15. 4 2
      tests/CMakeLists.txt
  16. 106 0
      tests/opacity.c

+ 4 - 2
README.md

@@ -152,8 +152,10 @@ 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_FRAMEBUFFER` window hint for enabling window
-  framebuffer transparency (#197,#663,#715,#723,#1078)
+- Added `GLFW_TRANSPARENT_FRAMEBUFFER` window hint and attribute for controlling
+  per-pixel framebuffer transparency (#197,#663,#715,#723,#1078)
+- Added `glfwGetWindowOpacity` and `glfwSetWindowOpacity` for controlling whole
+  window transparency (#1089)
 - Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering
   (#749,#842)
 - Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889)

+ 8 - 3
docs/news.dox

@@ -94,12 +94,17 @@ be disabled with the @ref GLFW_JOYSTICK_HAT_BUTTONS init hint.
 @see @ref joystick_hat
 
 
-@subsection news_33_transparent Support for transparent window framebuffer
+@subsection news_33_transparent Support for transparent windows and framebuffers
 
 GLFW now supports the creation of windows with transparent framebuffers on
 systems with desktop compositing enabled with the @ref
-GLFW_TRANSPARENT_FRAMEBUFFER window hint and attribute.  Any window decorations
-will still be opaque.
+GLFW_TRANSPARENT_FRAMEBUFFER window hint and attribute.  This hint must be set
+before window creation and leaves any window decorations opaque.
+
+GLFW now also supports whole window transparency with @ref glfwGetWindowOpacity
+and @ref glfwSetWindowOpacity.  This value controls the opacity of the whole
+window including decorations and unlike framebuffer transparency can be changed
+at any time after window creation.
 
 
 @subsection news_33_centercursor Cursor centering window hint

+ 33 - 0
docs/window.dox

@@ -1091,6 +1091,14 @@ the window or framebuffer is resized.
 
 @subsection window_transparency Window transparency
 
+GLFW supports two kinds of transparency for windows; framebuffer transparency
+and whole window transparency.  A single window may not use both methods.  The
+results of doing this are undefined.
+
+Both methods require the platform to support it and not every version of every
+platform GLFW supports does this, so there are mechanisms to check whether the
+window really is transparent.
+
 Window framebuffers can be made transparent on a per-pixel per-frame basis with
 the [GLFW_TRANSPARENT_FRAMEBUFFER](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint)
 window hint.
@@ -1115,6 +1123,31 @@ if (glfwGetWindowAttrib(window, GLFW_TRANSPARENT_FRAMEBUFFER))
 }
 @endcode
 
+GLFW comes with an example that enabled framebuffer transparency called `gears`.
+
+The opacity of the whole window, including any decorations, can be set with @ref
+glfwSetWindowOpacity.
+
+@code
+glfwSetWindowOpacity(window, 0.5f);
+@endcode
+
+The opacity (or alpha) value is a positive finite number between zero and one,
+where 0 (zero) is fully transparent and 1 (one) is fully opaque.  The initial
+opacity value for newly created windows is 1.
+
+The current opacity of a window can be queried with @ref glfwGetWindowOpacity.
+
+@code
+float opacity = glfwGetWindowOpacity(window);
+@endcode
+
+If the system does not support whole window transparency, this function always
+returns one.
+
+GLFW comes with a test program that lets you control whole window transparency
+at run-time called `opacity`.
+
 
 @subsection window_attribs Window attributes
 

+ 56 - 0
include/GLFW/glfw3.h

@@ -2837,6 +2837,62 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int
  */
 GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale);
 
+/*! @brief Returns the opacity of the whole window.
+ *
+ *  This function returns the opacity of the window, including any decorations.
+ *
+ *  The opacity (or alpha) value is a positive finite number between zero and
+ *  one, where zero is fully transparent and one is fully opaque.  If the system
+ *  does not support whole window transparency, this function always returns one.
+ *
+ *  The initial opacity value for newly created windows is one.
+ *
+ *  @param[in] window The window to query.
+ *  @return The opacity value of the specified window.
+ *
+ *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ *  GLFW_PLATFORM_ERROR.
+ *
+ *  @thread_safety This function must only be called from the main thread.
+ *
+ *  @sa @ref window_transparency
+ *  @sa @ref glfwSetWindowOpacity
+ *
+ *  @since Added in version 3.3.
+ *
+ *  @ingroup window
+ */
+GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window);
+
+/*! @brief Sets the opacity of the whole window.
+ *
+ *  This function sets the opacity of the window, including any decorations.
+ *
+ *  The opacity (or alpha) value is a positive finite number between zero and
+ *  one, where zero is fully transparent and one is fully opaque.
+ *
+ *  The initial opacity value for newly created windows is one.
+ *
+ *  A window created with framebuffer transparency may not use whole window
+ *  transparency.  The results of doing this are undefined.
+ *
+ *  @param[in] window The window to set the opacity for.
+ *  @param[in] opacity The desired opacity of the specified window.
+ *
+ *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ *  GLFW_PLATFORM_ERROR.
+ *
+ *  @thread_safety This function must only be called from the main thread.
+ *
+ *  @sa @ref window_transparency
+ *  @sa @ref glfwGetWindowOpacity
+ *
+ *  @since Added in version 3.3.
+ *
+ *  @ingroup window
+ */
+GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity);
+
 /*! @brief Iconifies the specified window.
  *
  *  This function iconifies (minimizes) the specified window if it was

+ 10 - 0
src/cocoa_window.m

@@ -1493,6 +1493,16 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
         [window->ns.object setLevel:NSNormalWindowLevel];
 }
 
+float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
+{
+    return (float) [window->ns.object alphaValue];
+}
+
+void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
+{
+    [window->ns.object setAlphaValue:opacity];
+}
+
 void _glfwPlatformPollEvents(void)
 {
     for (;;)

+ 2 - 0
src/internal.h

@@ -687,9 +687,11 @@ int _glfwPlatformWindowIconified(_GLFWwindow* window);
 int _glfwPlatformWindowVisible(_GLFWwindow* window);
 int _glfwPlatformWindowMaximized(_GLFWwindow* window);
 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window);
+float _glfwPlatformGetWindowOpacity(_GLFWwindow* window);
 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled);
 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled);
 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled);
+void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity);
 
 void _glfwPlatformPollEvents(void);
 void _glfwPlatformWaitEvents(void);

+ 9 - 0
src/mir_window.c

@@ -648,6 +648,15 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
                     "Mir: Unsupported function %s", __PRETTY_FUNCTION__);
 }
 
+float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
+{
+    return 1.f;
+}
+
+void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
+{
+}
+
 void _glfwPlatformPollEvents(void)
 {
     EventNode* node = NULL;

+ 9 - 0
src/null_window.c

@@ -182,6 +182,15 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
 {
 }
 
+float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
+{
+    return 1.f;
+}
+
+void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
+{
+}
+
 void _glfwPlatformShowWindow(_GLFWwindow* window)
 {
 }

+ 33 - 0
src/win32_window.c

@@ -1591,6 +1591,39 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
                  SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
 }
 
+float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
+{
+    BYTE alpha;
+    DWORD flags;
+
+    if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) &&
+        GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags))
+    {
+        if (flags & LWA_ALPHA)
+            return alpha / 255.f;
+    }
+
+    return 1.f;
+}
+
+void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
+{
+    if (opacity < 1.f)
+    {
+        const BYTE alpha = (BYTE) (255 * opacity);
+        DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
+        style |= WS_EX_LAYERED;
+        SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style);
+        SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA);
+    }
+    else
+    {
+        DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
+        style &= ~WS_EX_LAYERED;
+        SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style);
+    }
+}
+
 void _glfwPlatformPollEvents(void)
 {
     MSG msg;

+ 28 - 0
src/window.c

@@ -648,6 +648,34 @@ GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle,
     _glfwPlatformGetWindowContentScale(window, xscale, yscale);
 }
 
+GLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(1.f);
+    return _glfwPlatformGetWindowOpacity(window);
+}
+
+GLFWAPI void glfwSetWindowOpacity(GLFWwindow* handle, float opacity)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+    assert(opacity == opacity);
+    assert(opacity >= 0.f);
+    assert(opacity <= 1.f);
+
+    _GLFW_REQUIRE_INIT();
+
+    if (opacity != opacity || opacity < 0.f || opacity > 1.f)
+    {
+        _glfwInputError(GLFW_INVALID_VALUE, "Invalid window opacity %f", opacity);
+        return;
+    }
+
+    _glfwPlatformSetWindowOpacity(window, opacity);
+}
+
 GLFWAPI void glfwIconifyWindow(GLFWwindow* handle)
 {
     _GLFWwindow* window = (_GLFWwindow*) handle;

+ 9 - 0
src/wl_window.c

@@ -692,6 +692,15 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
                     "Wayland: Window attribute setting not implemented yet");
 }
 
+float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
+{
+    return 1.f;
+}
+
+void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
+{
+}
+
 void _glfwPlatformPollEvents(void)
 {
     handleEvents(0);

+ 9 - 0
src/x11_init.c

@@ -737,9 +737,18 @@ static GLFWbool initExtensions(void)
         XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False);
     _glfw.x11.NET_WM_BYPASS_COMPOSITOR =
         XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False);
+    _glfw.x11.NET_WM_WINDOW_OPACITY =
+        XInternAtom(_glfw.x11.display, "_NET_WM_WINDOW_OPACITY", False);
     _glfw.x11.MOTIF_WM_HINTS =
         XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False);
 
+    // The compositing manager selection name contains the screen number
+    {
+        char name[32];
+        snprintf(name, sizeof(name), "_NET_WM_CM_S%u", _glfw.x11.screen);
+        _glfw.x11.NET_WM_CM_Sx = XInternAtom(_glfw.x11.display, name, False);
+    }
+
     return GLFW_TRUE;
 }
 

+ 2 - 0
src/x11_platform.h

@@ -257,6 +257,8 @@ typedef struct _GLFWlibraryX11
     Atom            NET_WM_STATE_DEMANDS_ATTENTION;
     Atom            NET_WM_BYPASS_COMPOSITOR;
     Atom            NET_WM_FULLSCREEN_MONITORS;
+    Atom            NET_WM_WINDOW_OPACITY;
+    Atom            NET_WM_CM_Sx;
     Atom            NET_ACTIVE_WINDOW;
     Atom            NET_FRAME_EXTENTS;
     Atom            NET_REQUEST_FRAME_EXTENTS;

+ 32 - 5
src/x11_window.c

@@ -2449,11 +2449,7 @@ 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;
+    return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
 }
 
 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
@@ -2559,6 +2555,37 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
     XFlush(_glfw.x11.display);
 }
 
+float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
+{
+    float opacity = 1.f;
+
+    if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
+    {
+        CARD32* value = NULL;
+
+        if (_glfwGetWindowPropertyX11(window->x11.handle,
+                                      _glfw.x11.NET_WM_WINDOW_OPACITY,
+                                      XA_CARDINAL,
+                                      (unsigned char**) &value))
+        {
+            opacity = (float) (*value / (double) 0xffffffffu);
+        }
+
+        if (value)
+            XFree(value);
+    }
+
+    return opacity;
+}
+
+void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
+{
+    const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
+    XChangeProperty(_glfw.x11.display, window->x11.handle,
+                    _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
+                    PropModeReplace, (unsigned char*) &value, 1);
+}
+
 void _glfwPlatformPollEvents(void)
 {
     _GLFWwindow* window;

+ 4 - 2
tests/CMakeLists.txt

@@ -32,6 +32,7 @@ add_executable(gamma WIN32 MACOSX_BUNDLE gamma.c ${GLAD})
 add_executable(icon WIN32 MACOSX_BUNDLE icon.c ${GLAD})
 add_executable(inputlag WIN32 MACOSX_BUNDLE inputlag.c ${GETOPT} ${GLAD})
 add_executable(joysticks WIN32 MACOSX_BUNDLE joysticks.c ${GLAD})
+add_executable(opacity WIN32 MACOSX_BUNDLE opacity.c ${GLAD})
 add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c ${GETOPT} ${GLAD})
 add_executable(threads WIN32 MACOSX_BUNDLE threads.c ${TINYCTHREAD} ${GLAD})
 add_executable(timeout WIN32 MACOSX_BUNDLE timeout.c ${GLAD})
@@ -45,8 +46,8 @@ if (RT_LIBRARY)
     target_link_libraries(threads "${RT_LIBRARY}")
 endif()
 
-set(WINDOWS_BINARIES empty gamma icon inputlag joysticks tearing threads
-                     timeout title windows)
+set(WINDOWS_BINARIES empty gamma icon inputlag joysticks opacity tearing
+                     threads timeout title windows)
 set(CONSOLE_BINARIES clipboard events msaa glfwinfo iconify monitors reopen
                      cursor)
 
@@ -75,6 +76,7 @@ if (APPLE)
     set_target_properties(gamma PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Gamma")
     set_target_properties(inputlag PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Input Lag")
     set_target_properties(joysticks PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Joysticks")
+    set_target_properties(opacity PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Opacity")
     set_target_properties(tearing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Tearing")
     set_target_properties(threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Threads")
     set_target_properties(timeout PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Timeout")

+ 106 - 0
tests/opacity.c

@@ -0,0 +1,106 @@
+//========================================================================
+// Window opacity test program
+// Copyright (c) Camilla Löwy <[email protected]>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//
+//========================================================================
+
+#include <glad/glad.h>
+#include <GLFW/glfw3.h>
+
+#define NK_IMPLEMENTATION
+#define NK_INCLUDE_FIXED_TYPES
+#define NK_INCLUDE_FONT_BAKING
+#define NK_INCLUDE_DEFAULT_FONT
+#define NK_INCLUDE_DEFAULT_ALLOCATOR
+#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+#define NK_INCLUDE_STANDARD_VARARGS
+#include <nuklear.h>
+
+#define NK_GLFW_GL2_IMPLEMENTATION
+#include <nuklear_glfw_gl2.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void error_callback(int error, const char* description)
+{
+    fprintf(stderr, "Error: %s\n", description);
+}
+
+int main(int argc, char** argv)
+{
+    GLFWmonitor* monitor = NULL;
+    GLFWwindow* window;
+    struct nk_context* nk;
+    struct nk_font_atlas* atlas;
+
+    glfwSetErrorCallback(error_callback);
+
+    if (!glfwInit())
+        exit(EXIT_FAILURE);
+
+    window = glfwCreateWindow(400, 400, "Opacity", NULL, NULL);
+    if (!window)
+    {
+        glfwTerminate();
+        exit(EXIT_FAILURE);
+    }
+
+    glfwMakeContextCurrent(window);
+    gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
+    glfwSwapInterval(1);
+
+    nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS);
+    nk_glfw3_font_stash_begin(&atlas);
+    nk_glfw3_font_stash_end();
+
+    while (!glfwWindowShouldClose(window))
+    {
+        int width, height;
+        struct nk_rect area;
+
+        glfwGetWindowSize(window, &width, &height);
+        area = nk_rect(0.f, 0.f, (float) width, (float) height);
+
+        glClear(GL_COLOR_BUFFER_BIT);
+        nk_glfw3_new_frame();
+        if (nk_begin(nk, "", area, 0))
+        {
+            float opacity = glfwGetWindowOpacity(window);
+            nk_layout_row_dynamic(nk, 30, 2);
+            if (nk_slider_float(nk, 0.f, &opacity, 1.f, 0.001f))
+                glfwSetWindowOpacity(window, opacity);
+            nk_labelf(nk, NK_TEXT_LEFT, "%0.3f", opacity);
+        }
+
+        nk_end(nk);
+        nk_glfw3_render(NK_ANTI_ALIASING_ON);
+
+        glfwSwapBuffers(window);
+        glfwWaitEventsTimeout(1.0);
+    }
+
+    nk_glfw3_shutdown();
+    glfwTerminate();
+    exit(EXIT_SUCCESS);
+}
+