TIP #47: Modifying Tk to Allow Writing X Window managers
_____________________________________________________________________________
TIP:	47
Title:	Modifying Tk to Allow Writing X Window managers
Version:	$Revision: 1.9 $
Authors:	Neil McKay <mckay@eecs.umich.edu>
	Andreas Kupries <andreas_kupries@users.sourceforge.net>
	Donal K. Fellows <fellowsd@cs.man.ac.uk>
State:	Final
Type:	Project
Tcl-Version:	8.4
Vote:	Done
Created:	Thu Jul 19 00:00:00 UTC 2001
Abstract
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.
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:
draw to, and handle events on, the display's root window (including <Create>, <MapRequest>, <ResizeRequest>, <CirculateRequest>, and <ConfigureRequest> events, which are currently ignored)
embed arbitrary windows inside Tk windows
receive <PropertyNotify> events from embedded windows
perform a variety of other X-specific operations
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 <PropertyNotify> events be received from embedded windows; doing these things requires core modifications.
Root Window Access
The root window is special in many ways:
It does not need to be created
It cannot be destroyed, moved, or resized
Only one process can receive <ButtonPress> and <ButtonRelease> events from it, and only one process can have the SubstructureRedirect and ResizeRedirect masks set
It has no physical parent window
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 Tk_SetClassProcs procedure described in [5]. Likewise, the event handling required by the root window can be enabled in an extension, although some care is required when enabling <ButtonPress> 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 TK_TOP_LEVEL flag. This flag is used to mean different things in different places. In particular, the TK_TOP_LEVEL flag may mean:
This window is a toplevel widget
This widget has a wrapper window
This widget's window is controlled by the window manager
This window is at the top of a physical window hierarchy within the current application
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 TK_TOP_LEVEL flag with at least two distinct flags. A better idea is to replace the 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 Tk_IsTopLevel macro with several macros, or just eliminate it entirely.
One possible set of flag names is:
TK_TOP_LEVEL	this is a toplevel widget
TK_HAS_WRAPPER	this window has a wrapper window
TK_WIN_MANAGED	this window is controlled by the window manager
TK_TOP_HIERARCHY	this window is at the top of a physical window hierarchy
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 <CirculateRequest>, <ConfigureRequest>, <CreateNotify>, <MapRequest>, and <ResizeRequest> 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 [bind] command. Adding this facility is very simple.
Obtaining information about these events is also necessary. This is usually done via %-substitutions in the [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 <CreateNotify> events, and the property name, for handling <PropertyNotify> events. This information could be obtained by adding two new %-substitutions:
%i	substitute the numerical window ID for the event
%P	substitute the atom name for the property being changed
Propagating <PropertyNotify> Events
In order to receive <PropertyNotify> events from embedded windows, the Tk event loop must handle events not just for windows that are represented by Tk_Window structures, but also for their children. One way to accomplish this is to add another flag for the Tk_Window struct, and alter the event loop so that it will also look at a window's parent, if the event is a <PropertyNotify> event. The relevant part of the Tk event loop currently looks like this:

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

If the flag for propagating <PropertyNotify> events is TK_PROP_PROPCHANGE, then the code above must be modified to look approximately like this:

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;

Patches
A patch (against tk8.4a2) that implements the changes described above is available [http://www.eecs.umich.edu/~mckay/computer/wmenablers.84a3.patch.gz].
Notes
Andreas Kupries. There was a ''tkwm' patch once. [http://www.neosoft.com/tcl/ftparchive/sorted/x11/tkwm/] [http://www.ensta.fr/internet/unix/window_managers/tkwm.html]
Copyright
This document is in the public domain.
_____________________________________________________________________________
Colophon
TIP AutoGenerator - written by Donal K. Fellows