sidorares/node-x11

Question: how to create a transparent window

Closed this issue · 17 comments

asvd commented

Hello Andrey,

I am trying to create a 'transparent' window which will not create any graphical content by itself, but will be used for more convenient geometry and z-index management for a set of hosted sub-windows in case creating some graphical content. This window should simply show and properly update the content underneath.

I'm not sure if this question is related to node-x11 or to X protocol itself, but I hope you could help me. This is what I do:

var x11 = require('x11');


x11.createClient(
    function(err, display) {
        if (!err) {
            // root window
            var X = display.client;
            var root = display.screen[0].root;
            var rootWindowId = X.AllocID();

            X.CreateWindow(
                rootWindowId, root,
                0, 0, 400, 400,
                0, 0, 0, 0,
                { backgroundPixel: display.screen[0].white_pixel }
            );
            X.MapWindow(rootWindowId);

            display.client.on( 'error', function(e) {
                console.log(e);
            });



            // container - this is the window I wish to be transparent
            var containerId = X.AllocID();
            X.CreateWindow(
                containerId, rootWindowId,
                100, 100, 200, 200,
                0, 0, 0, 0,
                {
//                    backgroundPixel: display.screen[0].white_pixel
                }
            );
            X.MapWindow(containerId);


        } else {
            console.log(err);
        }
    }
);

The root window is created as white, and the "container" takes the middle part. The content displayed inside the container is simply what was on that part of the screen before the window was created:

If I uncomment the white_pixel setting for the container, it will be rendered properly of course, but I need it to be transparent in case when there will be some graphical content under the container.

Thanks in advnace,
Dmitry

@asvd What you'd need is a Compositing Manager http://en.wikipedia.org/wiki/Compositing_window_manager. You can implement one yourself OR you can use one and change the opacity of the window by setting the _NET_WM_WINDOW_OPACITY property.

This code is working for me with Compiz and I think it should work for you with other compositing managers such as xcompmgr

var wm = require('wm');
var wid = 0x0240017d;
var opacity = 0.4;
var raw = new Buffer(4);
raw.writeUInt32LE(Math.floor(opacity * 0xffffffff), 0);

wm.getX11Client(function(err, X) {
    X.InternAtom(false, '_NET_WM_WINDOW_OPACITY', function(err, atom) {                                                                                                                                                                                                   
        console.log(atom);                                                                                                                                                                                                                                                
        X.ChangeProperty(0, wid, atom, X.atoms.CARDINAL, 32, raw);                                                                                                                                                                                                        
    });                                                                                                                                                                                                                                                                   
});
asvd commented

thanks, will try to check it, but I still think this is not exactly what I need: I don't want the whole window to be transparent, instead I need a window to not produce any content and delegate that function to the subwindows (without making them opaque)

@asvd not very clear to me. Can you make main window transparent and non-main windows non-transparent? Also you can do the same with good old Shape extension: construct a region based on all subwindows and make main window shape to be that region

asvd commented

Can you make main window transparent and non-main windows non-transparent?

Yep, I think this is what I need, but I am not sure how to do this. On my opinion, it should something like creating a 32-bit depth window and setting it's background pixel to be transparent, so the code seems to be something like:

            X.CreateWindow(
                rootWindowId, root,
                0, 0, 400, 400,
                0, 32,  // depth
                0, 0,
                { backgroundPixel: transparent_pixel }
            );

But 32-bit window creation attempt fails into the BadMatch error for me, same as createwindow.js example shipped with the node-x11 dist (x11/examples/simple/createwindow.js).

Maybe my question could be splitted into:

  1. How to create 32-bit depth window, and
  2. How to set a transparent background pixel for that window.

Look at "transparent window" example - https://github.com/sidorares/node-x11/blob/master/examples/smoketest/transpwindow.js

"Bad match" is because default visual is not compatible with 32 bit depth.
To set window (or any drawable) pxels use "PutImage" request. Look at http://stackoverflow.com/questions/10492275/how-to-upload-32-bit-image-to-server-side-pixmap SO question

You don't need _NET_WM_WINDOW_OPACITY flag for 32 bit windows - if you have alpha channel composite manager just uses its pixel values

@sidorares wow lots of good info there. Thanks

asvd commented

@sidorares Great, thanks! Will try to play around with that info this (european) evening.

@asvd it should be possible to use transparent pixel instead of sending whole pixmap to window, I just never tried it that way. Post here if you have working example

asvd commented

yep, sure! thanks!

asvd commented

@sidorares: thanks, the "transparent window" example was helpful - I have finally managed to create a window with 32-bit depth with a transparent subwindow inside. But it looks like X server treats the transparency in a different way - instead of making a subwindow transparent by itself, it makes the main window transparent in the area where the subwindow was drawn:

Here is my code:

var x11 = require('x11');


x11.createClient(
    function(err, display) {
        if (!err) {
            var visual = null;
            var depths = display.screen[0].depths[32];
            for (vid in depths) {
                if (depths[vid]['class'] === 4) {
                    visual = vid;
                    break;
                }
            }

            if (!visual) {
                console.log('No RGBA visual found');
                return;
            }

            var X = display.client;
            var root = display.screen[0].root;
            var rootWindowId = X.AllocID();

            var cmid = X.AllocID();
            X.CreateColormap(cmid, root, visual, 0);

            X.CreateWindow(
                rootWindowId, root,
                0, 0, 400, 400,  // x, y, width, height
                1,   // border
                32,  // depth
                1,   // class
                visual,
                {
                    backgroundPixel: display.screen[0].white_pixel,
                    colormap: cmid,
                    borderPixel: 0                    
                }
            );
            X.MapWindow(rootWindowId);

            display.client.on( 'error', function(e) {
                console.log(e);
            });

            // transparent container
            var containerId = X.AllocID();
            X.CreateWindow(
                containerId, rootWindowId,
                100, 100, 200, 200,
                1, 32, 1, visual,
                {
                    backgroundPixel: 0,
                    colormap: cmid,
                    borderPixel: 0
                }
            );
            X.MapWindow(containerId);
        } else {
            console.log(err);
        }
    }
);

I would like to only make the subwindow transparent which should result to a window filled with white background in this case.

Is there some way to specify this kind of rendering behaviour?

afaik this is very much comosite manager specific. Try xcompmgr or compton for example and see if there is a difference.

asvd commented

Well I actually need to create some kind of "layers" inside a window in order to manage it's content in a handy way, and as far as I understand, in the X the subwindows are supposed to be used for that purpose, so this is something that should happen inside the application window.

On my opinion composite manager deals with the "external visual environment" of an application, while layers is something inside and therefore is managed by the application.

Or did I probably picked the wrong thing to build the layers?

how does a comositing manager work? does it alter frame buffers?
we should have one of these in javascript, really!

I'm working on one :)
Need to get BindTexImage extension to work so I can use pixmaps as gl textures but with xrender backend everything is ready to make comp manager.

Basically you use Composite extension NameWindowPixmap and RedirectWindow request so instead of displaying on screen everything goes to server side pixmaps + Damage extension AddDamage to watch for pixmap updates. All the rest is up to you - yo have bunch of (optionally) RGBA transparent pixmaps with positions and z order, you do something with this (maybe adding blur and other effects) and display result on screen

asvd commented

@sidorares I still think layers should be implemented as windows in X (according to my understanding of http://www.sbin.org/doc/Xlib/chapt_02.html). Right?

If you need to clip everything outside your rectangle then yes, it's natural to use underlying windowing system windows as "layers". If you don't need clipping than manual compositing equally viable option (most html engines for example have their own composition and don't use native windows)