TIP 765: Support for off-screen rendering in macOS

Login
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.

Crossing events

A crossing event is one of type or . For all ports other than X11, these events are generated by generic code in tkPointer.c. That file maintains thread-specific data indicating which Tk window (toplevel or not) is the current "pointer window", as well as the current mouse coordinates. The generic code cannot generate that data, so each platform is responsible for notifying Tk whenever the "pointer window" changes by calling Tk_UpdatePointer().

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:

to:

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.