Creating a pure utility window with properties in Xlib/X11.

This page is now maintained at http://toqoz.fyi/xlib-x11-utility-window.html

When I first started writing yarn (a notification daemon) I knew that I wanted it to be at least relatively lightweight, particularly in comparison to the beloved dunst. Because dunst didn’t use GTK, and because it seemed more simple at the time, I decided to draw the window with Xlib and Cairo instead.

The main thing this meant was that I had to interface with X11 directly. I wanted a pure X11 utility window – no window manager positioning, no window borders, just compositor shadows.

As an example of what I mean, here:
pure utility window

This window can be placed anywhere by us, and resized independently of the window manager. While this approach might not be the most elegant depending on your sentiment, it’s the only real way to have a good level of customization and control over your window. This type of solution is also likely to be necessary for achieving a fullscreen mode in some 3D application, or similar.


To understand what goes into this, we should first have some knowledge on how X11/Xlib actually interacts with the window manager.

In the basic sense, when we first create a window using XCreateWindow(), it creates an object with a number of default parameters. Later, the window manager reads these parameters and decides exactly how to show the window. As long as your window manager supports the parameters / hints that you want to use, we simply need to provide the WM with these hints.

When creating a new window with Xlib, it typically looks a little like the following:

// Return a window.
Drawable *
create_x11_window(int x, int y, int w, int h) {
    Display *display;
    Drawable drawable;
    int screen;   // Screen #.

    // Error if no open..
    if ((display = XOpenDisplay(NULL)) == NULL)   // Set display though.
        exit(1);

    screen = DefaultScreen(display);   // Use primary display.
    XVisualInfo vinfo;
    // Match the display settings.
    XMatchVisualInfo(display, screen, 32, TrueColor, &vinfo);

    XSetWindowAttributes attr;
    // We need all 3 of these attributes, or BadMatch: http://stackoverflow.com/questions/3645632/how-to-create-a-window-with-a-bit-depth-of-32
    attr.colormap = XCreateColormap(display, DefaultRootWindow(display), vinfo.visual, AllocNone);
    attr.border_pixel = 0;
    attr.background_pixel = 0;

    // Returnts a window (a drawable place).
    drawable = XCreateWindow(display, DefaultRootWindow(display),
            x,y,     // Position on screen.
            w,h,     // Width, Height.
            0,       // Border width.
            vinfo.depth, InputOutput, vinfo.visual,   // Depth, Class, Visual type.
            CWColormap | CWBorderPixel | CWBackPixel, // Overwritten attributes.
            &attr);

    // Apply the Atoms to the new window.
    // Request that the X server report these events.
    x_set_wm(drawable, display);
    XSelectInput(display, drawable, ExposureMask | ButtonPressMask | KeyPressMask);

    return drawable;
}

And the property magic is performed in our x_set_wm() function:

// Apply atoms to window.
static void
x_set_wm(Window win, Display *dsp) {
    Atom property[3];  // Change 2 things at once, (parent + 2 children).

    // Set window's WM_NAME property.
    XStoreName(dsp, win, "yarn");
    // No children.
    property[2] = XInternAtom(dsp, "_NET_WM_NAME", false); // Get WM_NAME atom and store it in _net_wm_title.
    XChangeProperty(dsp, win, property[2], XInternAtom(dsp, "UTF8_STRING", false), 8, PropModeReplace, (unsigned char *) "yarn", 4);

    // Set window's class.
    XClassHint classhint = { "yarn", "yarn" };
    XSetClassHint(dsp, win, &classhint);

    // Parent.
    property[2] = XInternAtom(dsp, "_NET_WM_WINDOW_TYPE", false);   // Let WM know type.
    // Children.
    property[0] = XInternAtom(dsp, "_NET_WM_WINDOW_TYPE_NOTIFICATION", false);
    property[1] = XInternAtom(dsp, "_NET_WM_WINDOW_TYPE_UTILITY", false);
    // Reach for 2 longs, (2L).
    XChangeProperty(dsp, win, property[2], XA_ATOM, 32, PropModeReplace, (unsigned char *) property, 2L);

    // Parent.
    property[2] = XInternAtom(dsp, "_NET_WM_STATE", false);   // Let WM know state.
    // Child.
    property[0] = XInternAtom(dsp, "_NET_WM_STATE_ABOVE", false);
    // Reach for 1 long, (1L).
    XChangeProperty(dsp, win, property[2], XA_ATOM, 32, PropModeReplace, (unsigned char *) property, 1L);
}

This function demonstrates the general method of assigning a few properties to your window;

  1. Get the atom of the property/properties that you want to set or add using XInternAtom().
  2. Change / add to the window property using XChangeProperty().

I’ve made an effort to comment the code heavily so that it’s easy to understand, particularly when viewed beside the Xlib function documentation. Hopefully this is enough to make reasonable sense of the situation.


One thing that was initially confusing to me was finding which atoms were available to use, and then figuring out which atoms I should actually use to accomplish what I wanted.

To my understanding, properties starting with “_NET” are part of the newer XDG/Freedesktop window manager specification. There are a lot more of these, and they are generally preferred over legacy variants.

Freedesktop has a list of the available (newer) properties here, whereas the older properties can be found here.

You can also use the xprop utility to see the properties of existing windows you’re using. This can be helpful in making complicated windows “just-so” and really understanding properties. Under most distributions, xprop is available as the xorg-xprop package.

getting window properties with xprop


Hopefully from here you’ll have all you need to explore this field on your own, and correctly propertize your windows.

 
13
Kudos
 
13
Kudos

Now read this

Everything you need to know about writing mods for Duck Game.

This page is now maintained at http://toqoz.fyi/duck-game-mods.html So you want to make some mods for Duck Game? Look no further. This guide should tell you almost everything you need to know about making a good mod that actually... Continue →