如何在Linux上创建透明窗口

9

我希望在 Linux 上制作一个带有启动画面的应用程序。

我想使用 X11 和 glx(OpenGL 应用程序)。

我已经找到了一种去掉窗口周围边框的方法,但我找不到如何使其透明的方法。我该怎么做呢?


这要看你所说的“透明”意味着什么。使用XShape扩展可以实现“简单”的透明(非矩形窗口)。实现“高级”的透明(将窗口与底层屏幕内容混合)需要使用合成管理器。 - Frédéric Hamidi
感谢评论。 我需要更高级的透明度。 无法打开第二个链接。 - themean
你是否在使用任何窗口库?还是只用原生的X11和glx? - Jonathan Henson
2
尝试查看 gdk 的 gdk_window_set_opacity 源代码,那应该会告诉你所需的一切。 - Jonathan Henson
这在这个答案中得到了有效的证明。 - karlphillip
3个回答

7

这是我问题的真正答案。

        /*------------------------------------------------------------------------
     * A demonstration of OpenGL in a  ARGB window
     *    => support for composited window transparency
     *
     * (c) 2011 by Wolfgang 'datenwolf' Draxinger
     *     See me at comp.graphics.api.opengl and StackOverflow.com

     * License agreement: This source code is provided "as is". You
     * can use this source code however you want for your own personal
     * use. If you give this source code to anybody else then you must
     * leave this message in it.
     *
     * This program is based on the simplest possible
     * Linux OpenGL program by FTB (see info below)

      The simplest possible Linux OpenGL program? Maybe...

      (c) 2002 by FTB. See me in comp.graphics.api.opengl

      --
      <\___/>
      / O O \
      \_____/  FTB.

    ------------------------------------------------------------------------*/

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <math.h>

    #include <GL/gl.h>
    #include <GL/glx.h>
    #include <GL/glxext.h>
    #include <X11/Xatom.h>
    #include <X11/extensions/Xrender.h>
    #include <X11/Xutil.h>

    #define USE_CHOOSE_FBCONFIG

    static void fatalError(const char *why)
    {
        fprintf(stderr, "%s", why);
        exit(0x666);
    }

    static int Xscreen;
    static Atom del_atom;
    static Colormap cmap;
    static Display *Xdisplay;
    static XVisualInfo *visual;
    static XRenderPictFormat *pict_format;
    static GLXFBConfig *fbconfigs, fbconfig;
    static int numfbconfigs;
    static GLXContext render_context;
    static Window Xroot, window_handle;
    static GLXWindow glX_window_handle;
    static int width, height;

    static int VisData[] = {
    GLX_RENDER_TYPE, GLX_RGBA_BIT,
    GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
    GLX_DOUBLEBUFFER, True,
    GLX_RED_SIZE, 8,
    GLX_GREEN_SIZE, 8,
    GLX_BLUE_SIZE, 8,
    GLX_ALPHA_SIZE, 8,
    GLX_DEPTH_SIZE, 16,
    None
    };

    static int isExtensionSupported(const char *extList, const char *extension)
    {

      const char *start;
      const char *where, *terminator;

      /* Extension names should not have spaces. */
      where = strchr(extension, ' ');
      if ( where || *extension == '\0' )
        return 0;

      /* It takes a bit of care to be fool-proof about parsing the
         OpenGL extensions string. Don't be fooled by sub-strings,
         etc. */
      for ( start = extList; ; ) {
        where = strstr( start, extension );

        if ( !where )
          break;

        terminator = where + strlen( extension );

        if ( where == start || *(where - 1) == ' ' )
          if ( *terminator == ' ' || *terminator == '\0' )
        return 1;

        start = terminator;
      }
      return 0;
    }

    static Bool WaitForMapNotify(Display *d, XEvent *e, char *arg)
    {
        return d && e && arg && (e->type == MapNotify) && (e->xmap.window == *(Window*)arg);
    }

    static void describe_fbconfig(GLXFBConfig fbconfig)
    {
        int doublebuffer;
        int red_bits, green_bits, blue_bits, alpha_bits, depth_bits;

        glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_DOUBLEBUFFER, &doublebuffer);
        glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_RED_SIZE, &red_bits);
        glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_GREEN_SIZE, &green_bits);
        glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_BLUE_SIZE, &blue_bits);
        glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_ALPHA_SIZE, &alpha_bits);
        glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_DEPTH_SIZE, &depth_bits);

        fprintf(stderr, "FBConfig selected:\n"
        "Doublebuffer: %s\n"
        "Red Bits: %d, Green Bits: %d, Blue Bits: %d, Alpha Bits: %d, Depth Bits: %d\n",
        doublebuffer == True ? "Yes" : "No",
        red_bits, green_bits, blue_bits, alpha_bits, depth_bits);
    }

    static void createTheWindow()
    {
        XEvent event;
        int x,y, attr_mask;
        XSizeHints hints;
        XWMHints *startup_state;
        XTextProperty textprop;
        XSetWindowAttributes attr = {0,};
        static char *title = "FTB's little OpenGL example - ARGB extension by WXD";

        Xdisplay = XOpenDisplay(NULL);
        if (!Xdisplay) {
        fatalError("Couldn't connect to X server\n");
        }
        Xscreen = DefaultScreen(Xdisplay);
        Xroot = RootWindow(Xdisplay, Xscreen);

        fbconfigs = glXChooseFBConfig(Xdisplay, Xscreen, VisData, &numfbconfigs);
        fbconfig = 0;
        for(int i = 0; i<numfbconfigs; i++) {
        visual = (XVisualInfo*) glXGetVisualFromFBConfig(Xdisplay, fbconfigs[i]);
        if(!visual)
            continue;

        pict_format = XRenderFindVisualFormat(Xdisplay, visual->visual);
        if(!pict_format)
            continue;

        fbconfig = fbconfigs[i];
        if(pict_format->direct.alphaMask > 0) {
            break;
        }
        }

        if(!fbconfig) {
        fatalError("No matching FB config found");
        }

        describe_fbconfig(fbconfig);

        /* Create a colormap - only needed on some X clients, eg. IRIX */
        cmap = XCreateColormap(Xdisplay, Xroot, visual->visual, AllocNone);

        attr.colormap = cmap;
        attr.background_pixmap = None;
        attr.border_pixmap = None;
        attr.border_pixel = 0;
        attr.event_mask =
        StructureNotifyMask |
        EnterWindowMask |
        LeaveWindowMask |
        ExposureMask |
        ButtonPressMask |
        ButtonReleaseMask |
        OwnerGrabButtonMask |
        KeyPressMask |
        KeyReleaseMask;

        attr_mask =
        CWBackPixmap|
        CWColormap|
        CWBorderPixel|
        CWEventMask;

        width = DisplayWidth(Xdisplay, DefaultScreen(Xdisplay))/2;
        height = DisplayHeight(Xdisplay, DefaultScreen(Xdisplay))/2;
        x=width/2, y=height/2;

        window_handle = XCreateWindow(  Xdisplay,
                Xroot,
                x, y, width, height,
                0,
                visual->depth,
                InputOutput,
                visual->visual,
                attr_mask, &attr);

        if( !window_handle ) {
        fatalError("Couldn't create the window\n");
        }

    #if USE_GLX_CREATE_WINDOW
        int glXattr[] = { None };
        glX_window_handle = glXCreateWindow(Xdisplay, fbconfig, window_handle, glXattr);
        if( !glX_window_handle ) {
        fatalError("Couldn't create the GLX window\n");
        }
    #else
        glX_window_handle = window_handle;
    #endif

        textprop.value = (unsigned char*)title;
        textprop.encoding = XA_STRING;
        textprop.format = 8;
        textprop.nitems = strlen(title);

        hints.x = x;
        hints.y = y;
        hints.width = width;
        hints.height = height;
        hints.flags = USPosition|USSize;

        startup_state = XAllocWMHints();
        startup_state->initial_state = NormalState;
        startup_state->flags = StateHint;

        XSetWMProperties(Xdisplay, window_handle,&textprop, &textprop,
            NULL, 0,
            &hints,
            startup_state,
            NULL);


        XFree(startup_state);

        XMapWindow(Xdisplay, window_handle);
        XIfEvent(Xdisplay, &event, WaitForMapNotify, (char*)&window_handle);

        if ((del_atom = XInternAtom(Xdisplay, "WM_DELETE_WINDOW", 0)) != None) {
        XSetWMProtocols(Xdisplay, window_handle, &del_atom, 1);
        }
    }

    static int ctxErrorHandler( Display *dpy, XErrorEvent *ev )
    {
        fputs("Error at context creation", stderr);
        return 0;
    }

    static void createTheRenderContext()
    {
        int dummy;
        if (!glXQueryExtension(Xdisplay, &dummy, &dummy)) {
        fatalError("OpenGL not supported by X server\n");
        }

    #if USE_GLX_CREATE_CONTEXT_ATTRIB
        #define GLX_CONTEXT_MAJOR_VERSION_ARB       0x2091
        #define GLX_CONTEXT_MINOR_VERSION_ARB       0x2092
        render_context = NULL;
        if( isExtensionSupported( glXQueryExtensionsString(Xdisplay, DefaultScreen(Xdisplay)), "GLX_ARB_create_context" ) ) {
        typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
        glXCreateContextAttribsARBProc glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );
        if( glXCreateContextAttribsARB ) {
            int context_attribs[] =
            {
            GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
            GLX_CONTEXT_MINOR_VERSION_ARB, 0,
            //GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
            None
            };

            int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler);

            render_context = glXCreateContextAttribsARB( Xdisplay, fbconfig, 0, True, context_attribs );

            XSync( Xdisplay, False );
            XSetErrorHandler( oldHandler );

            fputs("glXCreateContextAttribsARB failed", stderr);
        } else {
            fputs("glXCreateContextAttribsARB could not be retrieved", stderr);
        }
        } else {
            fputs("glXCreateContextAttribsARB not supported", stderr);
        }

        if(!render_context)
        {
    #else
        {
    #endif
        render_context = glXCreateNewContext(Xdisplay, fbconfig, GLX_RGBA_TYPE, 0, True);
        if (!render_context) {
            fatalError("Failed to create a GL context\n");
        }
        }

        if (!glXMakeContextCurrent(Xdisplay, glX_window_handle, glX_window_handle, render_context)) {
        fatalError("glXMakeCurrent failed for window\n");
        }
    }

    static int updateTheMessageQueue()
    {
        XEvent event;
        XConfigureEvent *xc;

        while (XPending(Xdisplay))
        {
        XNextEvent(Xdisplay, &event);
        switch (event.type)
        {
        case ClientMessage:
            if (event.xclient.data.l[0] == del_atom)
            {
            return 0;
            }
        break;

        case ConfigureNotify:
            xc = &(event.xconfigure);
            width = xc->width;
            height = xc->height;
            break;
        }
        }
        return 1;
    }

    /*  6----7
       /|   /|
      3----2 |
      | 5--|-4
      |/   |/
      0----1

    */

    GLfloat cube_vertices[][8] =  {
        /*  X     Y     Z   Nx   Ny   Nz    S    T */
        {-1.0, -1.0,  1.0, 0.0, 0.0, 1.0, 0.0, 0.0}, // 0
        { 1.0, -1.0,  1.0, 0.0, 0.0, 1.0, 1.0, 0.0}, // 1
        { 1.0,  1.0,  1.0, 0.0, 0.0, 1.0, 1.0, 1.0}, // 2
        {-1.0,  1.0,  1.0, 0.0, 0.0, 1.0, 0.0, 1.0}, // 3

        { 1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0}, // 4
        {-1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0}, // 5
        {-1.0,  1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 1.0}, // 6
        { 1.0,  1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 1.0}, // 7

        {-1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0}, // 5
        {-1.0, -1.0,  1.0, -1.0, 0.0, 0.0, 1.0, 0.0}, // 0
        {-1.0,  1.0,  1.0, -1.0, 0.0, 0.0, 1.0, 1.0}, // 3
        {-1.0,  1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0}, // 6

        { 1.0, -1.0,  1.0,  1.0, 0.0, 0.0, 0.0, 0.0}, // 1
        { 1.0, -1.0, -1.0,  1.0, 0.0, 0.0, 1.0, 0.0}, // 4
        { 1.0,  1.0, -1.0,  1.0, 0.0, 0.0, 1.0, 1.0}, // 7
        { 1.0,  1.0,  1.0,  1.0, 0.0, 0.0, 0.0, 1.0}, // 2

        {-1.0, -1.0, -1.0,  0.0, -1.0, 0.0, 0.0, 0.0}, // 5
        { 1.0, -1.0, -1.0,  0.0, -1.0, 0.0, 1.0, 0.0}, // 4
        { 1.0, -1.0,  1.0,  0.0, -1.0, 0.0, 1.0, 1.0}, // 1
        {-1.0, -1.0,  1.0,  0.0, -1.0, 0.0, 0.0, 1.0}, // 0

        {-1.0, 1.0,  1.0,  0.0,  1.0, 0.0, 0.0, 0.0}, // 3
        { 1.0, 1.0,  1.0,  0.0,  1.0, 0.0, 1.0, 0.0}, // 2
        { 1.0, 1.0, -1.0,  0.0,  1.0, 0.0, 1.0, 1.0}, // 7
        {-1.0, 1.0, -1.0,  0.0,  1.0, 0.0, 0.0, 1.0}, // 6
    };

    static void draw_cube(void)
    {
        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);

        glVertexPointer(3, GL_FLOAT, sizeof(GLfloat) * 8, &cube_vertices[0][0]);
        glNormalPointer(GL_FLOAT, sizeof(GLfloat) * 8, &cube_vertices[0][3]);
        glTexCoordPointer(2, GL_FLOAT, sizeof(GLfloat) * 8, &cube_vertices[0][6]);

        glDrawArrays(GL_QUADS, 0, 24);
    }

    float const light0_dir[]={0,1,0,0};
    float const light0_color[]={78./255., 80./255., 184./255.,1};

    float const light1_dir[]={-1,1,1,0};
    float const light1_color[]={255./255., 220./255., 97./255.,1};

    float const light2_dir[]={0,-1,0,0};
    float const light2_color[]={31./255., 75./255., 16./255.,1};

    static void redrawTheWindow()
    {
        float const aspect = (float)width / (float)height;

        static float a=0;
        static float b=0;
        static float c=0;

        glDrawBuffer(GL_BACK);

        glViewport(0, 0, width, height);

        // Clear with alpha = 0.0, i.e. full transparency
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glFrustum(-aspect, aspect, -1, 1, 2.5, 10);

        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glLightfv(GL_LIGHT0, GL_POSITION, light0_dir);
        glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_color);

        glLightfv(GL_LIGHT1, GL_POSITION, light1_dir);
        glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_color);

        glLightfv(GL_LIGHT2, GL_POSITION, light2_dir);
        glLightfv(GL_LIGHT2, GL_DIFFUSE, light2_color);

        glTranslatef(0., 0., -5.);

        glRotatef(a, 1, 0, 0);
        glRotatef(b, 0, 1, 0);
        glRotatef(c, 0, 0, 1);

        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHT1);
        glEnable(GL_LIGHTING);

        glEnable(GL_COLOR_MATERIAL);
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

        glColor4f(1., 1., 1., 0.5);

        glCullFace(GL_FRONT);
        draw_cube();
        glCullFace(GL_BACK);
        draw_cube();

        a = fmod(a+0.1, 360.);
        b = fmod(b+0.5, 360.);
        c = fmod(c+0.25, 360.);

        glXSwapBuffers(Xdisplay, glX_window_handle);
    }

    int main(int argc, char *argv[])
    {
        createTheWindow();
        createTheRenderContext();

        while (updateTheMessageQueue()) {
        redrawTheWindow();
        }

        return 0;
    }

我从这个链接中获取了这个信息。

我花费了10-15分钟的时间寻找这个示例与我的代码之间的差异,又花费了3-4小时才意识到我必须在KDE上做出一些更改。

感谢您的关心。


好的,这个工作正常,但我还有一个问题。当窗口无边框时,它无法完全透明。我通过这个实现了无边框窗口。 - themean
请注意,编译指令可以在链接的问题中找到。 - Seanny123

1

这绝对是您想要卸载到GPU的东西。出于性能原因,我不建议直接使用X11 lib。让OpenGL来做吧。我找到了以下链接glXChooseVisual

此外,这里还有另一个S.O.问题可能会有所帮助。

此外,这个是针对Windows的,但它仍然适用。


抱歉,我无法打开“glXChooseVisual”链接。 对于第二个链接“here”,我找不到任何关于XFixesCreateRegion函数的参考。 对于第三个链接“this”,它非常有帮助。 我正在寻找类似于SetLayeredWindowAttributes但适用于x11的东西。 - themean
我正在尝试使用以下代码: Atom property = XInternAtom(m_Display,"_NET_WM_WINDOW_OPACITY",False); unsigned int Opacity = 0xffffffff; XChangeProperty(m_Display,m_Window,property,XA_CARDINAL,32,PropModeReplace,(unsigned char *)&Opacity,1);这类似于GTK+ gdk_x11_window_set_opacity,但是对我不起作用。 我在Debian 6上运行。 问题可能是操作系统或窗口管理器引起的。 - themean
好的,在设置了一些KDE选项之后,它可以工作了,但现在我无法控制窗口内图像的不透明度。 - themean
你是否启用了Compiz或者你在KDE上使用的任何组合管理器? - Jonathan Henson
5
这个答案令人困惑,没有解决问题。 - exebook

1
为了在X11下拥有一个透明的窗口,您需要:
  • 带有复合扩展的X11服务器
  • 一个复合管理器
  • 用于您的窗口的ARGB视觉效果

您不需要直接使用OpenGL:复合管理器可以使用它来渲染屏幕。

如果您选择GTK路径,请查看以下内容:

  • gdk_x11_display_composite
  • gdk_screen_is_composited
  • gdk_display_supports_composite
  • gdk_screen_get_rgba_visual
  • gdk_screen_get_rgba_colormap

但是您也可以使用Qt或其他工具包。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接