What is the proper way to wait until an X11 window is mapped and viewable? Precisely, I want to wait until I can safely call XSetInputFocus() without running into any risks of the X server backfiring with the following error:
// X Error of failed request:  BadMatch (invalid parameter attributes)
// Major opcode of failed request:  42 (X_SetInputFocus)    
Currently this error happens quite often, especially on slow X servers or when trying to open a new window right after having changed the monitor resolution using libXrandr.
I already have a solution for this problem but it is pretty hacky because it polls the window attribute so I'd like to know whether or not there is a cleaner version.
Here is my current approach:
static Bool predicate(Display *display, XEvent *ev, XPointer arg)
{
    return(ev->type == MapNotify);
}
static void waitmapnotify(struct osdisplayinfo *osd)
{
    XEvent ev;
    XWindowAttributes xwa;
    XPeekIfEvent(osd->display, &ev, predicate, NULL);
    do {
        XGetWindowAttributes(osd->display, osd->window, &xwa);
        usleep(1);
    } while(xwa.map_state != IsViewable);   
}
This code works fine but it is hacky so I'm putting it up for debate here - just in case there is a cleaner way of doing this.
Select SubstructureNotifyMask on the root window. You should get an event each time a top-level window is mapped, unmapped, moved, raised, resized etc. These are the events that potentially change visibility of top-level windows. This program prints a message whenever such an event happens:
#include <X11/Xlib.h>
#include <stdio.h>
int main ()
{
  Display* d = XOpenDisplay(0);
  int cnt = 0;
  XEvent ev;    
  XSelectInput (d, RootWindow(d, DefaultScreen(d)), SubstructureNotifyMask);    
  while (1)
  {
    XNextEvent(d, &ev);
    printf ("Got an event %d!\n", cnt++);
    // <----- do your XGetWindowAttributes(...) check here
  }
}
Note that you may not get events about your own windows getting mapped. This is because the WM is likely to reparent top-level windows to be children not of the root, but of intermediate decoration windows.
There are two ways to cope with the situation:
XSelectInput (d, yourwindow, StructureNotifyMask); to the mix.Note the first select has SubstructureNotifyMask and the second one StructureNotifyMask, a different mask.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With