TIP 74: wm stackorder command

EuroTcl/OpenACS 11 - 12 JULY 2024, VIENNA
Author:         Mo DeJong <[email protected]>
State:          Final
Type:           Project
Vote:           Done
Created:        12-Nov-2001
Tcl-Version:    8.4
Tk-Ticket:      481148


Tk provides no means to query the stacking order of toplevel windows. This functionality would be useful to applications that wished to save and restore the state and relative order of each toplevel. This functionality would also make it possible to write test cases for window manager related commands like focus, raise, and lower. This document suggests a new wm stackorder command to address this deficiency.


wm stackorder window ?isabove|isbelow? ?window?

The following would return a list of all the toplevels on the display:

% wm stackorder .

The returned list would include the passed in window and its children. Only those toplevel windows that are mapped would be returned. The stacking order is from lowest to highest, so the last element in the list is the window on top of the display.

The wm stackorder command could also be used to compare the relative position in the stackorder. The following command would return true if .a was higher in the stacking order compared to .b.

% wm stackorder .a isabove .b

The isbelow usage is analogous:

% wm stackorder .b isbelow .a

One additional C API would be added. It would accept a Tk window and return an array of Tk windows in stacking order. This function would be implemented in the platform specific window manager code, such as tkUnixWm.c. This function signature is subject to change.

TkWindow ** TkWmStackorderToplevel(TkWindow *parentPtr);


Tk exposes a number of features related to toplevel windows through the wm command. While a user can set the relative position of a toplevel in the stacking order, it is not currently possible to query the stacking order for toplevel windows.

Users are frustrated by the lack of access to this information. This is a posting to news:comp.lang.tcl by Jim Ingham is typical:

This seems pretty basic, but for the life of me I can't figure out how to determine the stacking order of Tk toplevels. I want to save away the currently open windows in my application, and I would like to preserve both positions _and window stacking order. I know how to get the positions of toplevels, but I can't figure out how to get the window manager's stacking order. Should be in the wm commands, but nothing leaps out at me. What am I missing?_

It is simply not logical to provide a means to manipulate the stacking order of toplevel windows without also providing a way to query the stacking order. This functionality is needed, if only to help with the authoring of test cases. For example, one could verify that a call to wm raise actually worked by checking to see if the stacking order was changed.

The second form of the wm stackorder command provides an easy way to compare the relative position of windows in the stacking order. This sort of boolean check is commonly needed in test cases. One could implement the same logic by querying the whole list, searching it twice to find the indices, and then comparing the indices, but the code would not be as easy to understand and it would not be as efficient.

The wm stackorder command also has an extra benefit, it provides an easy way to query the currently mapped toplevel windows. It is not difficult to write a procedure that recursively descends through each window and filters out those windows that are not mapped toplevels. This wm stackorder command would just make it easier to query this list.

Reference Implementation

A reference implementation has been created for X windows and Win32 systems. The X version makes us of the XQueryTree() function while the Windows version depends on the EnumWindows() Win32 API. Both implementations query the stacking order of toplevel windows in the root window. The patch, test cases, and documentation changes can be found in Tk patch 481148 at SourceForge. Porting to MacOS and MacOS X will require assistance from area maintainers.


Instead of adding a new wm stackorder command, one could adjust the behavior of winfo children. The documentation currently reads:

winfo children window: Returns a list containing the path names of all the children of window. The list is in stacking order, with the lowest window first. Top-level windows are returned as children of their logical parents.

A user would no doubt conclude that the stacking order was maintained for both toplevels and contained widgets. Unfortunately, the implementation only tracks the stacking order for contained widgets.

% toplevel .t
% pack [button .t.b1]
% pack [button .t.b2]
% winfo children .t
.t.b1 .t.b2
% raise .t.b1
% winfo children .t
.t.b2 .t.b1

Tk does not track stacking order changes for toplevels.

% toplevel .t2
% winfo children .
.t .t2
% raise .t
% winfo children .
.t .t2

There are two possible ways to "fix" the winfo children command so that it would return toplevels in stacking order. One could call the TkWmStackorderToplevel() function and use the results to sort any toplevels that would be returned by winfo children. The other option would be to resort the TkWindow->childList as toplevels are moved up and down in the stacking order.

Both of these alternatives have some serious implementation issues. The TkWmStackorderToplevel() function is very slow. The X based implementation recurses through each window in the Tk hierarchy to create a mapping of wrapper window ids to TkWindow_s. The function then queries the X server to find each X window that is a child of the root screen and checks to see if the window exists in the mapping. When compared to _winfo children, which just loops over an in-memory list, it is easy to see why wm stackorder is so much slower.

% for {set i 0} {$i < 10} {incr i} {toplevel .t$i}
% time {winfo children .} 100
34 microseconds per iteration
% time {wm stackorder .} 100
394 microseconds per iteration

It would not be wise to make the winfo children command an order of magnitude slower just to add a new capability. One also needs to remember that the winfo children command is often used recursively, so any slowdown would be multiplied by the depth of the window hierarchy.

The second option would be to keep the TkWindow->childList sorted as toplevels are raised and lowered either by the application or the window manager. This would imply binding to the event under X. While the event is defined in Tk, it does not seem to actually be delivered by the window manager. In any event, this seems like an area ripe for incompatibility and error.

Even if one of the above fixes for the winfo children command was doable, it still would not satisfy user's needs. One would be able to compare the relative stacking order of two toplevels that have the same parent.

% toplevel .t1
% toplevel .t2
% winfo children .
{.t1 .t2}

Unfortunately, it would not be possible to compare the stacking order of two toplevel windows that have different parents.

% toplevel .t1
% toplevel .t1.t2
# No help here!
% winfo children .
% winfo children .t1

It would not even be possible to query the position of the . window in the stacking order since it does not have a parent window that can be passed to winfo children.

Since modifying winfo children could cause some serious problems and would ultimately be ineffective, this alternative was rejected. Instead, the documentation for the winfo children command should be updated to indicate that toplevel windows are not returned in stacking order.


It is not entirely clear what risks would be associated with this TIP. The logic of the wm stackorder command is rather insulated from the rest of the Tk core. Changing the implementation of Tk_RestackWindow() and keeping the TkWindow->childList up to date w.r.t. external changes would be more risky since it could affect other parts of the core. Doing an explicit query to find the stacking order seems a lot less error prone when compared to monitoring events from the window manager. We might speed things up by also storing wrapper pointers in the map table so that a call to Tk_IdToWindow() with a wrapper id would work, but it is not clear that would help since the XQueryTree() likely takes up most of the function processing time.

It is also not known how difficult or costly this functionality will be to implement on Mac OS, or Mac OS X.


This document has been placed in the public domain.