TIP:            74
Title:          wm stackorder command
Version:        $Revision: 1.6 $
Author:         Mo DeJong <mdejong@users.sourceforge.net>
State:          Final
Type:           Project
Vote:           Done
Created:        12-Nov-2001
Post-History:   
Tcl-Version:    8.4

~ Abstract

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.

~ Specification

|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);

~ Rationale

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.

~ Alternatives

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 <Circulate> event under X. While the <Circulate> 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 .
|.t1
|% winfo children .t1
|.t1.t2

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.

~ Risks

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.

~ Copyright

This document has been placed in the public domain.

