\documentclass[]{article}
\usepackage{amsmath,graphicx,supertabular,hyperref,tabularx,ifthen}
\title{TIP \#47: Modifying Tk to Allow Writing X Window managers}
\date{July 19, 2001}
\author{Neil McKay, Andreas Kupries, Donal K. Fellows}
\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 \#47}&\textbf{Modifying Tk to Allow Writing X Window managers}\\\hline
Author&

Neil McKay \tipmail{mckay}{eecs.umich.edu}
\par
Andreas Kupries \tipmail{andreas_kupries}{users.sourceforge.net}
\par
Donal K. Fellows \tipmail{fellowsd}{cs.man.ac.uk}
\\
Created&Thursday, \(\text{19}^{\text{th}}\) July 2001\\
Type&Project\\
Tcl Version&\(8.4\)\\
State&Final\\
Vote&Done\\
Version&\tipversion$Revision: 1.9 $\\
Post History&\\
\hline\end{tabularx}\end{center}
\thispagestyle{empty}\pagestyle{empty}
\begin{tipabstract}

With a few modifications to the Tk core, extensions could be written that would allow X window managers to be implemented as Tcl/Tk scripts.
\end{tipabstract}
\tableofcontents\setcounter{page}{0}\clearpage\pagestyle{plain}

\section{Requirements}

Writing X window managers in Tk requires some facilities that the current Tk core doesn't provide. A window manager must be able to:
\begin{itemize}
\item{}
draw to, and handle events on, the display's root window (including \textit{\tiplangle{}Create\tiprangle{}}, \textit{\tiplangle{}MapRequest\tiprangle{}}, \textit{\tiplangle{}ResizeRequest\tiprangle{}}, \textit{\tiplangle{}CirculateRequest\tiprangle{}}, and \textit{\tiplangle{}ConfigureRequest\tiprangle{}} events, which are currently ignored)
\item{}
embed arbitrary windows inside Tk windows
\item{}
receive \textit{\tiplangle{}PropertyNotify\tiprangle{}} events from embedded windows
\item{}
perform a variety of other X-specific operations
\end{itemize}

Window embedding can be handled by an extension, if it is not incorporated into the Tk frame widget at some later time. Likewise, the X-specific operations can be handled by an extension. However, Tk as it currently stands cannot access the display's root window, nor can \textit{\tiplangle{}PropertyNotify\tiprangle{}} events be received from embedded windows; doing these things requires core modifications.

\section{Root Window Access}

The root window is special in many ways:
\begin{itemize}
\item{}
It does not need to be created
\item{}
It cannot be destroyed, moved, or resized
\item{}
Only one process can receive \textit{\tiplangle{}ButtonPress\tiprangle{}} and \textit{\tiplangle{}ButtonRelease\tiprangle{}} events from it, and only one process can have the \textit{SubstructureRedirect} and \textit{ResizeRedirect} masks set
\item{}
It has no physical parent window
\end{itemize}

Because of these properties, access to the root window via a Tk widget presents some difficulties. First, the widget's window cannot be created in the standard way; however, this problem may be solved by providing a non-standard creation routine via the \textit{Tk\_SetClassProcs} procedure described in \cite{tip5}. Likewise, the event handling required by the root window can be enabled in an extension, although some care is required when enabling \textit{\tiplangle{}ButtonPress\tiprangle{}} and certain other events. What really causes problems is the lack of a physical parent. There are many places in Tk where it is assumed that only toplevel widgets have no physical parent within the application; this is reflected in the Tk source by the use of the \textit{TK\_TOP\_LEVEL} flag. This flag is used to mean different things in different places. In particular, the \textit{TK\_TOP\_LEVEL} flag may mean:
\begin{itemize}
\item{}
This window is a toplevel widget
\item{}
This widget has a wrapper window
\item{}
This widget's window is controlled by the window manager
\item{}
This window is at the top of a physical window hierarchy within the current application
\end{itemize}

In the current version of Tk, toplevel widgets have all of these properties, and no other widgets have any of these properties; hence a single flag suffices. If we create a widget whose window is the display's root, then this is no longer the case; a root window has the last property, but not the first three. For this reason, it is necessary to replace the \textit{TK\_TOP\_LEVEL} flag with at least two distinct flags. A better idea is to replace the \textit{TK\_TOP\_LEVEL} flag with four flags, one for each of the properties listed above. (Even in a standard Tk distribution, this replacement is desirable for documentation reasons, since it will indicate what property of a toplevel widget is important in the current circumstances.) We must also replace the \textit{Tk\_IsTopLevel} macro with several macros, or just eliminate it entirely.

One possible set of flag names is:
\begin{description}
\item[TK\_TOP\_LEVEL]
this is a toplevel widget
\item[TK\_HAS\_WRAPPER]
this window has a wrapper window
\item[TK\_WIN\_MANAGED]
this window is controlled by the window manager
\item[TK\_TOP\_HIERARCHY]
this window is at the top of a physical window hierarchy
\end{description}

\section{New Event Bindings and Substitutions}

A window manager must be able to intercept certain events on the root window that the standard Tk distribution doesn't recognize, and it must be able to obtain information about those events. In particular, it needs to respond to \textit{\tiplangle{}CirculateRequest\tiprangle{}}, \textit{\tiplangle{}ConfigureRequest\tiprangle{}}, \textit{\tiplangle{}CreateNotify\tiprangle{}}, \textit{\tiplangle{}MapRequest\tiprangle{}}, and \textit{\tiplangle{}ResizeRequest\tiprangle{}} events. These events are ignored by standard Tk, and need not be enabled by default; however, they need to be included in the list of events recognized by the Tk \textit{[bind]} command. Adding this facility is very simple.

Obtaining information about these events is also necessary. This is usually done via \%-substitutions in the \textit{[bind]} command; however, there are two pieces of information that are necessary for implementing a window manager that cannot be obtained via the current \%-substitution mechanism: the numerical X window ID, required to handle \textit{\tiplangle{}CreateNotify\tiprangle{}} events, and the property name, for handling \textit{\tiplangle{}PropertyNotify\tiprangle{}} events. This information could be obtained by adding two new \%-substitutions:
\begin{description}
\item[\%i]
substitute the numerical window ID for the event
\item[\%P]
substitute the atom name for the property being changed
\end{description}

\section{Propagating \tiplangle{}PropertyNotify\tiprangle{} Events}

In order to receive \textit{\tiplangle{}PropertyNotify\tiprangle{}} events from embedded windows, the Tk event loop must handle events not just for windows that are represented by \textit{Tk\_Window} structures, but also for their children. One way to accomplish this is to add another flag for the \textit{Tk\_Window} struct, and alter the event loop so that it will also look at a window's parent, if the event is a \textit{\tiplangle{}PropertyNotify\tiprangle{}} event. The relevant part of the Tk event loop currently looks like this:
\begingroup\small\begin{verbatim}

winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, handlerWindow);
if (winPtr == NULL) {
    if (eventPtr->type == PropertyNotify) {
	TkSelPropProc(eventPtr);
    }
    return;
}

\end{verbatim}
\endgroup

If the flag for propagating \textit{\tiplangle{}PropertyNotify\tiprangle{}} events is \textit{TK\_PROP\_PROPCHANGE}, then the code above must be modified to look approximately like this:
\begingroup\small\begin{verbatim}

winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, handlerWindow);
if (winPtr == NULL) {
    if (eventPtr->type != PropertyNotify) {
	return;
    }
    TkSelPropProc(eventPtr);
    parentXId = (parent of handlerWindow);
    winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, parentXId);
    if (winPtr == NULL) {
	return;
    }
    if (!(winPtr->flags & TK_PROP_PROPCHANGE)) {
	return;
    }
    handlerWindow = parentXId;
    return;
}

\end{verbatim}
\endgroup

\section{Patches}

A patch (against tk8.4a2) that implements the changes described above is available [\url{http://www.eecs.umich.edu/~mckay/computer/wmenablers.84a3.patch.gz}].

\section{Notes}

\textit{Andreas Kupries.} There was a \(''\)tkwm' patch once. [\url{http://www.neosoft.com/tcl/ftparchive/sorted/x11/tkwm/}] [\url{http://www.ensta.fr/internet/unix/window_managers/tkwm.html}]

\section{Copyright}

This document is in the public domain.

\section{Colophon}

\textit{TIP AutoGenerator --- written by Donal K. Fellows}
\begin{thebibliography}{TIP \#1}
\addcontentsline{toc}{section}{References}
\bibitem[TIP \#5]{tip5}
Eric Melski,
\emph{Make TkClassProcs and TkSetClassProcs Public and Extensible},
on-line at \url{http://purl.org/tcl/tip/5.html}
\end{thebibliography}
\end{document}
