[comp.windows.x.motif] Waiting for a dialog to display

dbrooks@osf.org (04/20/91)

It's a common requirement that a programmer wants to do some lengthy
computation before returning to XtAppMainLoop, and wants the display to be up
to date, as much as possible, before doing this.  Often, a dialog (such as a
WorkingDialog) is displayed just before doing the work.

Usually, this is done by a routine that processes events for some defined
period.  It's a critical requirement that such a routine not block (until the
user hits a button, for example).  XmUpdateDisplay() is provided to do this. 
It can (and should) be called at intervals during the computation. 
Unfortunately...it doesn't work initially.

The reason turns out to be the reparenting window manager.  The client maps the
dialog, and XmUpdateDisplay does an XSync.  After the sync, the server has
redirected the map request to mwm, but mwm hasn't mapped it yet, so there are
no expose events in the queue.

The following code handles this situation.  It works whether the primary window
is iconified or not.  It works if the system is running mwm, twm or no window
manager (it doesn't work with olwm when the primary window is iconified, but it
doesn't block).

Enjoy.

---snip---snippety---snip---snip---
/*
 * This procedure will ensure that, if a dialog window is being mapped,
 * its contents become visible before returning.  It is intended to be
 * used just before a bout of computing that doesn't service the display.
 * You should still call XmUpdateDisplay() at intervals during this
 * computing if possible.
 *
 * The monitoring of window states is necessary because attempts to map
 * the dialog are redirected to the window manager (if there is one) and
 * this introduces a significant delay before the window is actually mapped
 * and exposed.  This code works under mwm, twm, uwm, and no-wm.  It
 * doesn't work with olwm if the mainwindow is iconified.
 *
 * The argument to ForceDialog is any widget in the dialog (usually it
 * will be the BulletinBoard child of a DialogShell).
 */

ForceDialog(w)
     Widget w;
{
  Widget diashell, topshell;
  Window diawindow, topwindow;
  Display *dpy;
  XWindowAttributes xwa;
  XEvent event;
  XtAppContext cxt;

/* Locate the shell we are interested in */

  for (diashell = w;
       !XtIsShell(diashell);
       diashell = XtParent(diashell))
    ;

/* Locate its primary window's shell (which may be the same) */

  for (topshell = diashell;
       !XtIsTopLevelShell(topshell);
       topshell = XtParent(topshell))
    ;

  if (XtIsRealized(diashell) && XtIsRealized(topshell)) {
    dpy = XtDisplay(topshell);
    diawindow = XtWindow(diashell);
    topwindow = XtWindow(topshell);
    cxt = XtWidgetToApplicationContext(diashell);

/* Wait for the dialog to be mapped.  It's guaranteed to become so unless... */

    while (XGetWindowAttributes(dpy, diawindow, &xwa),
	   xwa.map_state != IsViewable) {

/* ...if the primary is (or becomes) unviewable or unmapped, it's
   probably iconified, and nothing will happen. */

      if (XGetWindowAttributes(dpy, topwindow, &xwa),
	  xwa.map_state != IsViewable)
	break;

/* At this stage, we are guaranteed there will be an event of some kind.
   Beware; we are presumably in a callback, so this can recurse. */

      XtAppNextEvent(cxt, &event);
      XtDispatchEvent(&event);
    }
  }

/* The next XSync() will get an expose event if the dialog was unmapped.
   This can recurse also. */

  XmUpdateDisplay(topshell);
}