Posted to tcl by patthoyts at Tue Jan 05 01:08:03 GMT 2010view pretty

If you create a toplevel and make it transient without giving the
master window a chance to be mapped first, then on X11 the transient
state is never set and the window manager is never told to treat the
toplevel as a transient window.

This patch solves this by marking the window wrapper with a pending
flag that causes the transient property to be set when the toplevel
gets mapped finally. Addresses [Bug 1163496]

Currently the decoration for the transient toplevel is wrong but the
behaviour is correct. It seems the window manager does not refresh
the decoration once the window has been mapped.

Sample script:

package require Tk
variable uid 0
proc mkdlg {} {
    variable uid
    set dlg [toplevel .dlg[incr uid]]
    wm transient $dlg .
    wm title $dlg "Transient $uid"
    return $dlg
}
mkdlg
pack [ttk::button .b1 -text "New dialog" -command mkdlg]
wm geometry . 200x200
tkwait window .
exit


Index: unix/tkUnixWm.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/unix/tkUnixWm.c,v
retrieving revision 1.77
diff -u -p -r1.77 tkUnixWm.c
--- unix/tkUnixWm.c	9 Dec 2009 13:55:14 -0000	1.77
+++ unix/tkUnixWm.c	5 Jan 2010 00:55:28 -0000
@@ -261,6 +261,9 @@ typedef struct TkWmInfo {
  * WM_WITHDRAWN -		non-zero means that this window has explicitly
  *				been withdrawn. If it's a transient, it should
  *				not mirror state changes in the master.
+ * WM_TRANSIENT_PENDING -	marks a window that is transient for a master
+ *				but the window manager hints have not yet
+ *				been set. Handled when the window is mapped.
  */
 
 #define WM_NEVER_MAPPED			1
@@ -277,6 +280,7 @@ typedef struct TkWmInfo {
 #define WM_WIDTH_NOT_RESIZABLE		0x1000
 #define WM_HEIGHT_NOT_RESIZABLE		0x2000
 #define WM_WITHDRAWN			0x4000
+#define WM_TRANSIENT_PENDING		0x8000
 
 /*
  * This module keeps a list of all top-level windows, primarily to simplify
@@ -676,6 +680,7 @@ TkWmMapWindow(
 	    if (!Tk_IsMapped(wmPtr->masterPtr)) {
 		wmPtr->withdrawn = 1;
 		wmPtr->hints.initial_state = WithdrawnState;
+		wmPtr->flags |= WM_TRANSIENT_PENDING;
 	    } else {
 		XSetTransientForHint(winPtr->display,
 			wmPtr->wrapperPtr->window,
@@ -730,6 +735,16 @@ TkWmMapWindow(
 	UpdateGeometryInfo(winPtr);
 	return;
     }
+
+    if (wmPtr->flags & WM_TRANSIENT_PENDING) {
+	wmPtr->flags &= ~WM_TRANSIENT_PENDING;
+	if (wmPtr->masterPtr != NULL) {
+	    XSetTransientForHint(winPtr->display,
+		    wmPtr->wrapperPtr->window,
+		    wmPtr->masterPtr->wmInfoPtr->wrapperPtr->window);
+	}
+    }
+
     wmPtr->flags |= WM_ABOUT_TO_MAP;
     if (wmPtr->flags & WM_UPDATE_PENDING) {
 	Tcl_CancelIdleCall(UpdateGeometryInfo, winPtr);