iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🐧

Making windows above the active window translucent in X

に公開
2024/10/07

I recently started using Kubuntu and it's been pretty good, but I couldn't find a way to make only the windows above the active window semi-transparent.

You can make everything except the active window semi-transparent in the settings, though.

Actually, I've never seen that feature outside of Compiz, so maybe there's not much demand for it. It can't be helped, as people who use settings where the active window isn't automatically brought to the front are likely in the minority. It's a handy feature, though.

I asked ChatGPT and it seemed like I could control it from outside the window manager, so I decided to build it.

Potential Technical Elements

Specifying Window Opacity

Running the following command will change your mouse cursor to a ┼. If you click a window, that window's opacity will be set to 50%.

% xprop -f _NET_WM_WINDOW_OPACITY 32c -set _NET_WM_WINDOW_OPACITY 0x80000000

In C, it looks like this.

unsigned long opacity = 0x80000000;
XChangeProperty(display, window, XInternAtom(display, "_NET_WM_WINDOW_OPACITY", False), XA_CARDINAL, 32, PropModeReplace, &opacity, 1);

Getting the Window Stacking Order

% xwininfo -root -tree

This displays windows starting from the shallowest.

In C, using XQueryTree() returns a list of windows in order from deepest to shallowest.

Checking if Windows Overlap

Since xwininfo geometry provides the window position, width, and height, you can determine if they overlap through calculation.
However, the window selected with the ┼ in xwininfo is not sufficient; it seems you can't get the correct position on the display unless it's a window directly under the root.

The idea is to trace up the parents until the Parent window id in xwininfo -tree becomes the root window.

% xwininfo -tree -id 0x63ae7b | grep 'Parent window'
  Parent window id: 0x137b273 (has no name)
% xwininfo -tree -id 0x137b273 | grep 'Parent window'
  Parent window id: 0x137b272 (has no name)
% xwininfo -tree -id 0x137b272 | grep 'Parent window'
  Parent window id: 0x5e3 (the root window) (has no name)

In this case, getting the geometry for 0x137b272 should work.

% xwininfo -id 0x137b272 | grep geometry
  -geometry 582x438+2799+1344

In C, this can be obtained with XGetGeometry().

Window root;
int x, y;
unsigned int width, height, border_width, depth;
XGetGeometry(display, window, &root, &x, &y, &width, &height, &border_width, &depth);

Getting the Active Window

% xprop -root _NET_ACTIVE_WINDOW
_NET_ACTIVE_WINDOW(WINDOW): window id # 0x63ae7b

In C, it looks like this:

unsigned long type;
int format;
unsigned long nitems;
unsigned long bytes;
unsigned long *win;
XGetWindowProperty(display, window, XInternAtom(@display, '_NET_ACTIVE_WINDOW', False), 0, ~0, 0, AnyPropertyType, &type, &format, &nitems, &bytes, &win);
// win is the window ID

Detecting Active Window Changes

It seems you just need to monitor the _NET_ACTIVE_WINDOW event.

Running xprop -spy will notify you every time the active window changes.

% xprop -spy -root _NET_ACTIVE_WINDOW
_NET_ACTIVE_WINDOW(WINDOW): window id # 0x4a000b0
_NET_ACTIVE_WINDOW(WINDOW): window id # 0x4a000b0
_NET_ACTIVE_WINDOW(WINDOW): window id # 0x4c0002c
_NET_ACTIVE_WINDOW(WINDOW): window id # 0x4c0002c
_NET_ACTIVE_WINDOW(WINDOW): window id # 0x6000e0
_NET_ACTIVE_WINDOW(WINDOW): window id # 0x6000e0
_NET_ACTIVE_WINDOW(WINDOW): window id # 0x63ae7b
_NET_ACTIVE_WINDOW(WINDOW): window id # 0x63ae7b
^C

For some reason, it outputs two lines each time, but I don't know why.

In C, you can specify PropertyChangeMask with XSelectInput(), and calling XNextEvnet() will resume when a PropertyNotify event occurs.

Built it with Ruby

While writing it in C is a bit much and I could probably have done it with a shell script since commands exist, I like Ruby, so I built it in Ruby.

I looked for libraries to use X from Ruby but couldn't find a good one, so I started by creating wrappers for the necessary functions using Fiddle and then used those.

https://gitlab.com/tmtms/wmadd/

It's working reasonably well now, so I've released it as a Gem. You can install it with gem install wmadd. You can run it with wmadd.

Discussion