0
votes

How can I get the title/caption height of a window in X11? I'm using the following to get the border thickness:

XWindowAttributes wndattr;
::XGetWindowAttributes(display, wnd, &wndattr)
... = lWndAttr->border_width;

I can't seem to find anything as straight-forward for the title bar. (This answer seems to imply I need to go through a font, but that can't be right, right?)

4
That answer is of course not right.n. 1.8e9-where's-my-share m.

4 Answers

1
votes

The answers depends really on the window managers used, but most of the WMs reparent target windows to be child of a frame, so the algorithm would be:

  • walk to parent windows until you reach root. The one before root is likely frame
  • compare your target window rectangle with frame rectangle. Frame top minus target top would give you caption height
3
votes

Modern window managers abide by Extended Window Manager Hints specifications, so you just need to check the _NET_FRAME_EXTENTS property.

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

int main ()
{
    Display* d;
    Window w, root;
    Atom a, t;
    int s;
    long fg, bg;
    int f;
    unsigned long n, b;
    unsigned char *data = 0;
    long* extents;
    XEvent e;

    d = XOpenDisplay(0);
    s = DefaultScreen(d);
    root = DefaultRootWindow(d);
    fg = BlackPixel(d, s);
    bg = WhitePixel(d, s);
    w = XCreateSimpleWindow(d, root, 0, 0, 200, 200, 0, fg, bg);
    XSelectInput(d, w, ExposureMask|ButtonPressMask|KeyPressMask|PropertyChangeMask);

    XMapWindow(d,w);

    a = XInternAtom(d, "_NET_FRAME_EXTENTS", True); /* Property to check */

    /* Window manager doesn't set up the extents immediately */
    /* Wait until they are set up and there are 4 of them */
    while (XGetWindowProperty(d, w, a,
                   0, 4, False, AnyPropertyType,
                   &t, &f,
                   &n, &b, &data) != Success || n != 4 || b != 0) 
    {
        printf ("Waiting for extents\n");
        XNextEvent(d, &e);
    }

    /* OK got extents */
    extents = (long*) data;
    printf ("Got frame extents: left %ld right %ld top %ld bottom %ld\n",
            extents[0], extents[1], extents[2], extents[3]);

    return 0;
}

If you are using a less modern WM, you are on your own. Try walking up the window tree as suggested in Andrey's answer. This is likely to work, but it's not guaranteed. Some window managers don't use separate "frame" windows at all (e.g. they can draw all frames on a single window in an overlay visual).

2
votes

XGetWindowAttributes returns a pointer to a XWindowAttributes structurex, which looks like this:

typedef struct {
    int x, y;                   /* location of window */
    int width, height;          /* width and height of window */
    int border_width;           /* border width of window */
    int depth;                  /* depth of window */
    Visual *visual;             /* the associated visual structure */
    Window root;                /* root of screen containing window */
#if defined(__cplusplus) || defined(c_plusplus)
    int c_class;                /* C++ InputOutput, InputOnly*/
#else
    int class;                  /* InputOutput, InputOnly*/
#endif
    int bit_gravity;            /* one of bit gravity values */
    int win_gravity;            /* one of the window gravity values */
    int backing_store;          /* NotUseful, WhenMapped, Always */
    unsigned long backing_planes;/* planes to be preserved if possible */
    unsigned long backing_pixel;/* value to be used when restoring planes */
    Bool save_under;            /* boolean, should bits under be saved? */
    Colormap colormap;          /* color map to be associated with window */
    Bool map_installed;         /* boolean, is color map currently installed*/
    int map_state;              /* IsUnmapped, IsUnviewable, IsViewable */
    long all_event_masks;       /* set of events all people have interest in*/
    long your_event_mask;       /* my event mask */
    long do_not_propagate_mask; /* set of events that should not propagate */
    Bool override_redirect;     /* boolean value for override-redirect */
    Screen *screen;             /* back pointer to correct screen */
} XWindowAttributes;

The only interesting parts are at the beginning, giving the position and size of the widget's window. The title is not part of that window. It is managed by the window manager, and is part of what is called "decoration".

Further reading:

1
votes

So for grins, I found the list of windows to the root:

/* find frame window */
cw = win->xwhan; /* index current window */
do {

    /* find tree parameters */
    XQueryTree(padisplay, cw, &rw, &pw, &cwl, &ncw);
    cw = pw;

    XGetWindowAttributes(padisplay, cw, &xwa);
    dbg_printf(dlinfo, "Window: %d,%d\n", xwa.width, xwa.height);

} while (cw != rw);

/* get actual size of onscreen window, and set that as client space */
XWLOCK();
XGetWindowAttributes(padisplay, win->xwhan, &xwa);
XWUNLOCK();
*x = xwa.width;
*y = xwa.height;
dbg_printf(dlinfo, "Current window: %d,%d\n", xwa.width, xwa.height);

Resulting in:

samiam@samiam-h-pc-2:~/projects/petit_ami$ ./testg
linux/graphics.c:pa_getsizg():8612: Window: 1480,1010
linux/graphics.c:pa_getsizg():8612: Window: 5120,5760
linux/graphics.c:pa_getsizg():8622: Current window: 1440,900

So basically the frame is one window up, and then the root window is the whole screen. This was done on a single (not nested) window.

Note that the windows are nested (contain each other) by definition, so there is nothing that particularly marks the frame except perhaps that it is the closest order frame to the current window.

Lets see if I can spin the logic of this:

  1. Older windows managers don't have the _NET_FRAME_EXTENTS property.

  2. The "walk the frame" algorithm also works on newer windows managers.

Thus looking at the parent will yield the right answer in the majority of cases, but there is no way to be sure.

I am trying this on Ubuntu 20.04, so I presume this qualifies as a "modern window manager".

Why do I use the window size including the frame vs. the client area? Because that is the most universal size. I don't just handle the program window, but also any child windows (widgets, subwindows, etc) and so the parent dimensions are a good way to track child windows as components. I determine the client areas via a function given the parent window parameters and characteristics of the child window (frame enabled, etc).

Scott Franco San Jose, CA