Author: Marc Culler
State: Draft
Type: Project
Created: 2026-10-13
Tcl-Version: 9.1
Tcl-Branch: tip_765
Keywords: hidden off-screen
Vote:
Vote-Summary:
Votes-For:
Votes-Against:
Votes-Present:
Forum: a5b73d46d8
Abstract
This TIP proposes to add support for off-screen rendering
of toplevel windows in the macOS Aqua port of Tk. It adds one
attribute (-hidden) to the list of attributes supported by the wm
attributes command.
Target
This TIP targets version 9.1 of Tk.
Rationale
It is a useful feature in any GUI toolkit to be able to render a complex window off screen and show it only after it has been completely rendered. The alternative is to make the user watch the flashes and incremental changes as widgets are laid out and drawn into the window. Rendering the window off screen and presenting it in a single display cycle is a much better user experience.
A common paradigm in Tk, to accomplish this, is:
wm withdraw .
renderWindow
wm deiconify .
Unfortunately, this does not really work on macOS if it takes more
than 200 milliseconds to render the window. Even when the withdraw
command runs before the root window is displayed, avoiding a flash,
the rendering takes place after the wm deiconify command has shown
the window on the screen.
The primary issue is that when a window is withdrawn, it and
all of its widgets are unmapped, and display procs are not
generated as idle tasks for a widget that is not mapped. So,
even if the geometry managers have computed the size and
positions of all of the widgets, the actual rendering does
not happen until the window and its widgets get mapped during
the wm deiconify command.
At the language level, the proposed replacement for this paradigm is:
wm attributes . -hidden 1
renderWindow
wm attributes . -hidden 0
The key idea which makes this work is that the state of the
-hidden attribute has no effect at all on any of the Tk data
structures describing the window or the operations which
Tk can perform on it. A window whose -hidden attribute is
1 can be resized, reconfigured, withdrawn, iconified, deiconified
or drawn in exactly the same way, with exactly the same code
as a window whose -hidden attribute is 0. But when the window
is hidden all of these operations, the drawing in particular,
are invisible to the user
There are a couple of situations in which the hidden attribute
of a window gets set to 0 without calling wm attributes -hidden.
One is when a hidden window is iconified and the user then clicks
the dock icon. The other is when the Tcl script calls raise on
the window. The rationale in both cases is that the user is
requesting (via the program, in the second case) that the window
be shown.
The technical feature which makes this work is that, starting with
Tk 9.0, each window has a CGImage backing store which holds the
current image of the window. Drawing to the backing store is
identical whether the window is displayed on the screen or not. This
make the implementation very simple. An AppKit NSWindow has methods
orderFront and orderOut which have the effect of showing or hiding
the window. These have no effect on the graphics state of the window,
but only determine whether the compositor displays that graphics state
at the current time. So the main thing that is needed is to replace
orderFront by a different method which calls orderFront if the
-hidden attribute is 0 and does nothing if the attribute is 1. I
don't think this would have been possible with Tk 8.6 because the
drawRect method used in 8.6 did not give Tk control of the window
backing store. The implementation also calls orderOut if the
-hidden attribute is changed from 0 to 1 and may, depending
on the Tk state of the window, call orderFront if the value of the
-hidden attribute is changed from 1 to 0.
Specification
The following command will be made available on macOS.
wm attributeswindow-hidden ?true|false?
Crossing events
A crossing event is one of type
A hidden window and its children are not, and cannot be, included in the stacking order at all because the stacking order of toplevel windows is generated by reading the list of all visible windows that is maintained by the Apple AppKit. Consequently a hidden window can never be the "pointer window". Logically speaking, the addition of hidden windows changes the definition of the "pointer window" from:
- The pointer window is the highest window in the stacking order which is mapped and contains the mouse pointer.
to:
- The pointer window is the highest window in the stacking order which is mapped, not hidden, and contains the mouse pointer.
As a consequence, crossing events may need to be generated when a window is hidden or revealed.
The implementation of this TIP includes a full rewrite of the code responsible for calling Tk_UpdatePointer. The utility functions for that task now reside in a new file tkMacOSXPointer.c. The rewrite was done because the attempt to implement the crossing event handling revealed several bugs in the existing code. These bugs were responsible, among other things, for sporadic failures of tests event-9.11 - event-9.20. It is not clear why those tests were passing at all, but in the tip implementation branch they all pass reliably.
The rewrite of the pointer window management code, which is actually a bug fix, accounts for the bulk of the code changes in this TIP implementation..
Compatibility
This TIP does not propose to implement the -hidden attribute for
Windows or linux. But the identical language feature should be usable
on any platform. The key step for an implementation would be
identifying or creating the analogs of orderFront and orderOut.
This will be straightforward for Wayland and may also be
straightforward for X11 and Windows.
Reference Implementation
The TIP is implemented in the branch tip_765 in the Tk fossil repository.
Copyright
This document has been placed in the public domain.
