How to create semi transparent white window in XLib
Asked Answered
V

2

10

I would like to create a semi transparent white window in XLib, but the window is not semi translucent, it remains fully opaque. I use the compton compositor and there are transparent windows in the system, so the problem is in the code:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    Display* display = XOpenDisplay(NULL);

    XVisualInfo vinfo;

    XMatchVisualInfo(display, DefaultScreen(display), 32, TrueColor, &vinfo);

    XSetWindowAttributes attr;
    attr.colormap = XCreateColormap(display, DefaultRootWindow(display), vinfo.visual, AllocNone);
    attr.border_pixel = 0;
    attr.background_pixel = 0x80ffffff;

    Window win = XCreateWindow(display, DefaultRootWindow(display), 0, 0, 300, 200, 0, vinfo.depth, InputOutput, vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr);
    XSelectInput(display, win, StructureNotifyMask);
    GC gc = XCreateGC(display, win, 0, 0);

    Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", 0);
    XSetWMProtocols(display, win, &wm_delete_window, 1);

    XMapWindow(display, win);

    int keep_running = 1;
    XEvent event;

    while (keep_running) {
        XNextEvent(display, &event);

        switch(event.type) {
            case ClientMessage:
                if (event.xclient.message_type == XInternAtom(display, "WM_PROTOCOLS", 1) && (Atom)event.xclient.data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", 1))
                    keep_running = 0;

                break;

            default:
                break;
        }
    }

    XDestroyWindow(display, win);
    XCloseDisplay(display);
    return 0;
}
Vaudeville answered 6/10, 2016 at 21:48 Comment(1)
Note as the OP mentions that it's necessary to have a compositor program running on your system in order for any of this to work. Lubuntu doesn't, needs xcompmgr or similar.Gainsay
E
15

X11 expects pre-multiplied colours, i.e. real opaque colours need to be multiplied by the alpha value (and scaled accordingly, i.e. divided by 256 when channel widths is 8 bits). This format is easier to work with when you need to combine many levels. See formulas here. There's less computation when everything is pre-multiplied.

So you need to multiply each of your R, G and B channels by the alpha value (0x80) and divide by 256.

Setting the background to 0x80808080 gives the desired result:

Note the result is different from what @patthoyts suggests: here only the window proper is semi-transparent, the WM decoration stays opaque; there both the window proper and the decoration are made transparent by the WM (and the WM does the necessary colour blending).

Electrolyse answered 7/10, 2016 at 23:45 Comment(14)
No, it's not that. Check this image: oscomp.hu/depot/white_transparency_qt5_vs_xlib.png The background color is equivalent in both windows: 0x80ffffff; Qt5 does it like how i need it and XLib is not. It's must be something in the code, i'm doing wrong, but what?Vaudeville
@Vaudeville Have you tried to do this? Look at the image. Do you think I have edited it in gimp or what?Electrolyse
Qt is not XLib. It is a portable toolkit that works with X11, Windows GDI, raw Linux framebuffers and what not. I have no idea what Qt does internally to please the user and keep consistent API. X11 does exactly what you can see on the image I posted.Electrolyse
Qt uses the XRender extension. And this is my actual problem, not the transparent window: Qt blends alpha images correctly to the X11 windows with XRenderComposite, while if i use XRenderComposite, then the same effect happens: the closer is one pixel to white, the less the pixel's transparency works. If a pixel is completely white, then XRenderComposite draws a white pixel there, regardless of what alpha that pixel had. I'm sure, that this is not the supposed behaviour of XRender blending... If you could show me how to use the XRenderComposite correctly, then that would be fine with me.Vaudeville
And of course i tried to change the colour's brightness: The closer the colour is to white, the less the alpha works, the closer the colour is to black, the more the alpha works, but that is erroneous! If i set alpha to zero, then the colour should be fully transparent, regardless of the colour. Check the example pics in my compton topic: github.com/chjj/compton/issues/382Vaudeville
@Vaudeville "that is erroneous" This is what you think from your high-level programmer position, which is not necessary what low-level X11 programmers think. Set transparency channel to x and multiply all other channels by 1.0-x (scale by 256 or whatever the bit depth is). That's the blending you must do manually. Pure X11 doesn't do any blending for you, it more or less interfaces directly with your hardware. XRenderComposite does blending internally. If you want to use XRenderComposite, go ahead, it will do what you expect but it's more code for you to write.Electrolyse
I exactly know, that XLib don't do blending, but i am exactly talking about that XRenderComposite behaves erroneously by the same way as this window! I think i'll post another question with the exact XRenderComposite code and the result images, because i'm getting nowhere...Vaudeville
Hmm it looks like XRender won't do what you want either. It assumes pre-multiplied colours.Electrolyse
Okay, but why does it works for Qt? How can i tell it to assume "raw" alpha? If i can't and i have to manipulate the pixels each-by-each, then it'll be equally slow than an XGetImage, softblend, XPutImage combo.Vaudeville
Qt supports both pre-multiplied and non-pre-multiplied image formats, see QImage::Format_ARGB32_Premultiplied and QImage::Format_ARGB32. How do you think it can cope with both formats when the underlying API expects only one? My uneducated guess is that it goes through the image, pixel by pixel, and converts it to what the underlying API expects, but you might discover something else.Electrolyse
You must be right. I just tried to pre-multiple the background color and now it's exactly behaves as expected. Qt probably already renders the raw image when opens a PNG or SVG as pre-multipled. You have my gratitude. Could you please add your comment about pre-multiplying as a separate answer? I would like to accept that as the correct answer.Vaudeville
Okay, i'll check back and when you corrected it, i'll accept it.Vaudeville
Edited the answer.Electrolyse
Accepted the answer. Thanks again.Vaudeville
C
4

You need to set _NET_WM_WINDOW_OPACITY. Here is a snippet to add in before you map the window:

double alpha = 0.8;
unsigned long opacity = (unsigned long)(0xFFFFFFFFul * alpha);
Atom XA_NET_WM_WINDOW_OPACITY = XInternAtom(display, "_NET_WM_WINDOW_OPACITY", False);
XChangeProperty(display, win, XA_NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
                PropModeReplace, (unsigned char *)&opacity, 1L);

Note you should add #include <X11/Xatom.h> to get the declaration of XA_CARDINAL.

I'm not entirely sure how stable this interface is. This appears to be a proposed extension to the Extended Window Manager Hints specification but has not made it into any final revision from what I can see. I know that this is how Tk implements transparency support on unix though.

The result looks like:

transparent X11 window over editor

Chickpea answered 6/10, 2016 at 23:43 Comment(3)
Unfortunately it did not worked. I tried it in Xfce, TDE and MotifWM with compton and it's still entirely white in all environments. I have a Qt5 code which can set it's background transparency with just by setting the top 8 bit of the background color. The only thing it does before is the setting of the "WA_TranslucentBackground" attribute of the window. I still try to figure out what it does, by looking at at the Qt5 sources, but no luck.Vaudeville
@Vaudeville works with recent KDE. I guess you can change the window manager in Xfce and TDE to something that supports this atom.Electrolyse
I need an universal solution, not a WM dependent one. Besides Qt5 can do the semi-transparent white window: oscomp.hu/depot/white_transparency_qt5_vs_xlib.png The background color is equivalent in the two programs: 0x80ffffff and yet the pure XLib one shows a fully opaque window.Vaudeville

© 2022 - 2024 — McMap. All rights reserved.