Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait until window is mapped and viewable

Tags:

c

linux

x11

xlib

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.

like image 646
Andreas Avatar asked Jun 10 '14 18:06

Andreas


1 Answers

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:

  1. Check if your window parent, the parent of the parent, ... etc is the mapped window of the event.
  2. Add XSelectInput (d, yourwindow, StructureNotifyMask); to the mix.

Note the first select has SubstructureNotifyMask and the second one StructureNotifyMask, a different mask.

like image 188
n. 1.8e9-where's-my-share m. Avatar answered Sep 20 '22 13:09

n. 1.8e9-where's-my-share m.