[comp.windows.x] XReparentWindow vs. ALL window managers

toml@ninja.Solbourne.COM (Tom LaStrange) (04/11/90)

How's this for fun.  The following code fails to work with reparenting
window managers, it will work fine with uwm or no window manager.

Here's what happens:

  client                                 wm
  --------                               -----
  Create two windows, w1 and w2
  Map w1                                 get MapRequest for w1
					   reparent w1
  Map w2                                 get MapRequest for w2
					   reparent w2
  reparent w1 to w2
    server unmaps w1 then reparents      get UnmapNotify for w1
                                           typically will reparent w1 to root

The net effect is that w1 ends up on the root window someplace rather
than as a child of w2.  I've verified this problem with the following
window managers:

  swm (was broken, is now fixed)
  mwm (1.0)
  olwm
  twm (R4)

I thought all I would have to do is unmap the window before making the
reparent request.  This would allow the window manager to complete the
transition to Withdrawn state before the reparent request is issued.
That was the theory, in practice though, the window manager gets around
to handling the UnmapNotify event whenever it wakes up, and by then the
reparent could have already taken place.

Here's the real solution.  I think it's something that every
reparenting window manager is going to have to do.  When you get the
UnmapNotify event, do an XQueryTree on whatever window (typically your
frame) you think should be the parent.  If you don't find the window in
the list of children, the window has been reparented someplace else and
you shouldn't do an explicit reparent back to the root.

I just have one question about all this:  Is the reparent guaranteed to
have taken place by the time the window manager gets the UnmapNotify?
If not, I'm not sure this problem can be reliably solved.

Below is an example Xlib program that exhibits the problem.  If someone
has better solution, or if I'm just missing something, I would love to
hear about it.

--
Tom LaStrange

Solbourne Computer Inc.    ARPA: toml@Solbourne.COM
1900 Pike Rd.              UUCP: ...!{boulder,sun}!stan!toml
Longmont, CO  80501

---------------------------------------------------------------------
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <X11/Xlib.h>


main(argc, argv)
    int argc;
    char *argv[];
{
    Display *dpy;		/* which display are we talking to */
    int screen;			/* the default screen */
    Window w1, w2;
    Window root;
    long foreground, background;
    XEvent event;

    if ((dpy = XOpenDisplay("")) == NULL)
    {
	fprintf(stderr, "can't open the display\n");
	exit(1);
    }
    screen = DefaultScreen(dpy);
    root = RootWindow(dpy, screen);
    foreground = BlackPixel(dpy, screen);
    background = WhitePixel(dpy, screen);

    w1 = XCreateSimpleWindow(dpy, root, 100, 100, 50, 50, 2, foreground,
	background);
    w2 = XCreateSimpleWindow(dpy, root, 100, 100, 100, 100, 2, foreground,
	background);

    XSelectInput(dpy, w1, ButtonPressMask);
    XSelectInput(dpy, w2, ButtonPressMask);

    XMapWindow(dpy, w1);
    XMapWindow(dpy, w2);

    while (True)
    {
	XNextEvent(dpy, &event);
	if (event.type == ButtonPress)
	{
	    XReparentWindow(dpy, w1, w2, 25, 25);
	}
    }
}

colas@avahi.inria.fr (Colas Nahaboo) (04/12/90)

In article <1990Apr10.203116.25044@Solbourne.COM>,
toml@ninja.Solbourne.COM (Tom LaStrange) writes:
|> I've verified this problem with the following window managers:

Just for showing off, gwm works.

I think the real solution is not to do a costly QueryTree but listen to
ReparentNotify events and act sensibly on them.

Colas Nahaboo, Bull Research France -- Koala Project -- GWM X11 Window Manager
colas@avahi.inria.fr            Phone: (33) 93.65.77.70, Fax: (33) 93 65 77 66
INRIA - Sophia Antipolis, 2004, rte des Lucioles, 06565 Valbonne Cedex, FRANCE

rws@EXPO.LCS.MIT.EDU (Bob Scheifler) (04/13/90)

    Here's the real solution.  I think it's something that every
    reparenting window manager is going to have to do.  When you get the
    UnmapNotify event, do an XQueryTree on whatever window (typically your
    frame) you think should be the parent.  If you don't find the window in
    the list of children, the window has been reparented someplace else and
    you shouldn't do an explicit reparent back to the root.

There's a race condition unless the WM does a GrabServer for this.
An alternative is that the client is required to notice that the
window was reparented by the WM, and when it wants to know the
window has been withdrawn, it must wait for the unmap notify and
a subsequent reparent notify back to the root, before itself
attempting to reparent.