\documentclass[]{article}
\usepackage{amsmath,graphicx,supertabular,hyperref,tabularx,ifthen}
\title{TIP \#74: wm stackorder command}
\date{November 12, 2001}
\author{Mo DeJong}
\urlstyle{sf}
\setlength{\parskip}{1ex}
\setlength{\parindent}{0pt}
\def\tipversion$#1${\texttt{\$#1\$}}
\def\tiplangle#1{\ensuremath{<}}
\def\tiprangle#1{\ensuremath{>}}
\def\tipbar#1{\ensuremath{|}}
\def\tipmail#1#2{\(\langle\){\small\expandafter\url{#1@#2}}\(\rangle\)}
\ifx\pdfoutput\undefined
\newcommand{\tipimage}[2]{\typeout{Make sure you download #1.eps}\ifthenelse{\lengthtest{0.8\textwidth>#2}\and\lengthtest{0pt<#2}}{\includegraphics{#1.eps}}{\includegraphics[width=0.8\textwidth]{#1.eps}}}
\newcommand{\tipxref}[1]{}
\newcommand{\tipxrefend}{}
\else
\newcommand{\tipimage}[2]{\typeout{Make sure you create #1.pdf}\ifthenelse{\lengthtest{0.8\textwidth>#2}\and\lengthtest{0pt<#2}}{\includegraphics{#1.pdf}}{\includegraphics[width=0.8\textwidth]{#1.pdf}}}
\pdfcatalog{/PageMode /UseOutlines}
\newcommand{\tipxref}[1]{\pdfannotlink attr {/C [0.5 0.5 1.0] /Border [0 0 1]} goto name {#1}}
\newcommand{\tipxrefend}{\pdfendlink}
\fi
\newenvironment{tipabstract}{\begin{abstract}}{\end{abstract}}
\begin{document}\maketitle
\begin{center}\begin{tabularx}{\linewidth}{|r@{: }X|}\hline
\textbf{TIP \#74}&\textbf{wm stackorder command}\\\hline
Author&

Mo DeJong \tipmail{mdejong}{users.sourceforge.net}
\\
Created&Monday, \(\text{12}^{\text{th}}\) November 2001\\
Type&Project\\
Tcl Version&\(8.4\)\\
State&Final\\
Vote&Done\\
Version&\tipversion$Revision: 1.6 $\\
Post History&\\
\hline\end{tabularx}\end{center}
\thispagestyle{empty}\pagestyle{empty}
\begin{tipabstract}

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 \textit{wm stackorder} command to address this deficiency.
\end{tipabstract}
\tableofcontents\setcounter{page}{0}\clearpage\pagestyle{plain}

\section{Specification}
\begingroup\small\begin{verbatim}
wm stackorder window ?isabove|isbelow? ?window?
\end{verbatim}
\endgroup

The following would return a list of all the toplevels on the display:
\begingroup\small\begin{verbatim}
% wm stackorder .
\end{verbatim}
\endgroup

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 \textit{wm stackorder} command could also be used to compare the relative position in the stackorder. The following command would return true if \textit{.a} was higher in the stacking order compared to \textit{.b}.
\begingroup\small\begin{verbatim}
% wm stackorder .a isabove .b
\end{verbatim}
\endgroup

The \textit{isbelow} usage is analogous:
\begingroup\small\begin{verbatim}
% wm stackorder .b isbelow .a
\end{verbatim}
\endgroup

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 \textit{tkUnixWm.c}. This function signature is subject to change.
\begingroup\small\begin{verbatim}
TkWindow ** TkWmStackorderToplevel(TkWindow *parentPtr);
\end{verbatim}
\endgroup

\section{Rationale}

Tk exposes a number of features related to toplevel windows through the \textit{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 \url{news:comp.lang.tcl} by Jim Ingham is typical:

\begin{quote}
\textit{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\textit{ 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\textit{ commands, but nothing leaps out at me. What am I missing?}
\end{quote}

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 \textit{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 \textit{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 \textit{wm stackorder} command would just make it easier to query this list.

\section{Reference Implementation}

A reference implementation has been created for X windows and Win32 systems. The X version makes us of the \textit{XQueryTree()} function while the Windows version depends on the \textit{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.

\section{Alternatives}

Instead of adding a new \textit{wm stackorder} command, one could adjust the behavior of \textit{winfo children}. The documentation currently reads:
\begin{description}
\item[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.
\end{description}

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.
\begingroup\small\begin{verbatim}
% 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
\end{verbatim}
\endgroup

Tk does not track stacking order changes for toplevels.
\begingroup\small\begin{verbatim}
% toplevel .t2
% winfo children .
.t .t2
% raise .t
% winfo children .
.t .t2
\end{verbatim}
\endgroup

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

Both of these alternatives have some serious implementation issues. The \textit{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 \textit{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 \textit{winfo children}, which just loops over an in-memory list, it is easy to see why \textit{wm stackorder} is so much slower.
\begingroup\small\begin{verbatim}
% 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
\end{verbatim}
\endgroup

It would not be wise to make the \textit{winfo children} command an order of magnitude slower just to add a new capability. One also needs to remember that the \textit{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 \textit{TkWindow-\tiprangle{}childList} sorted as toplevels are raised and lowered either by the application or the window manager. This would imply binding to the \tiplangle{}Circulate\tiprangle{} event under X. While the \tiplangle{}Circulate\tiprangle{} 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 \textit{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.
\begingroup\small\begin{verbatim}
% toplevel .t1
% toplevel .t2
% winfo children .
{.t1 .t2}
\end{verbatim}
\endgroup

Unfortunately, it would not be possible to compare the stacking order of two toplevel windows that have different parents.
\begingroup\small\begin{verbatim}
% toplevel .t1
% toplevel .t1.t2
# No help here!
% winfo children .
.t1
% winfo children .t1
.t1.t2
\end{verbatim}
\endgroup

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 \textit{winfo children}.

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

\section{Risks}

It is not entirely clear what risks would be associated with this TIP. The logic of the \textit{wm stackorder} command is rather insulated from the rest of the Tk core. Changing the implementation of \textit{Tk\_RestackWindow()} and keeping the \textit{TkWindow-\tiprangle{}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 \textit{Tk\_IdToWindow()} with a wrapper id would work, but it is not clear that would help since the \textit{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.

\section{Copyright}

This document has been placed in the public domain.

\section{Colophon}

\textit{TIP AutoGenerator --- written by Donal K. Fellows}
\end{document}
