TIP 561: Add console Command to Unix

Login
Author:         Eric Taylor <[email protected]>
State:          Draft
Type:           Project
Vote:           Pending
Created:        23-Jan-2020
Post-History:
Tcl-Version:    9.1
Keywords:       Tk
Tk-Branch:      tip-561-revised

Abstract

This TIP proposes that the console command be made an official, supported command for Linux and other Unix platforms (hereafter called Unix). A console can currently be run under Unix, but it requires additional code that is not part of Tk itself. This TIP proposes that official support be given for the console command on Unix, using Tcl's existing autoloading mechanism (tclIndex), with no changes required to Tk's C core.

Testing during development of this TIP found that the same gap - no console command available at all - also exists in some configurations on Windows and macOS: specifically tclsh with Tk loaded via package require Tk on Windows, and wish launched from a terminal on macOS (as opposed to double-clicking an app bundle), with the same expected to hold for tclsh with package require Tk on macOS as well. In all tclsh cases, console remains an entirely undefined command - not merely inert - until package require Tk is actually issued, since tk/library (and its tclIndex) is only added to Tcl's search path as part of Tk's own initialization; this is no different from any other Tk library command's availability before Tk is loaded. Since the mechanism proposed here (Tcl's unknown/autoload hook) is only ever consulted when a command does not already exist, it has no effect at all wherever a platform's native console implementation is already present - it only fills the same gap, using the same mechanism, wherever that gap is found. No changes are made to the existing native implementations on any platform.

Rationale and Discussion

In the Tcl'ers Wiki, there is a submitted code block that adds a console command and text window to a Tcl/Tk program on Unix. This works well and has been available since 2005. However, it is not supported "out of the box" - a developer has to find the code, source it themselves, and rely on it continuing to work.

The wiki code itself notes that it depends on some undocumented Tk internals and could break in a future release. Since this code has worked reliably for 20 years and is in real, ongoing use (see below), it seems worth making it an official, supported feature on Unix rather than leaving it as an informally shared script.

This TIP would eliminate an existing incompatibility between platforms and protect this code from breakage in future releases.

An earlier draft of this TIP proposed a core-level stub command using vwait/tailcall to defer setup until first use. Discussion on the tcl-core list pointed out that Tcl already has a mechanism built for exactly this - tclIndex-based autoloading - which is what every other on-demand command in tk/library already uses (tk_chooseColor, tk::fontchooser, and many others). This revision uses that existing mechanism instead of introducing a new one.

Proposal

Two files are added to tk/library:

One line is added to the existing tk/library/tclIndex:

set auto_index(console) [list source [file join $dir consolecmd.tcl]]

No special setup is required to use the console command. The first time console is called, Tcl's normal unknown/autoload mechanism sources consolecmd.tcl, which in turn loads consolecmd.impl and forwards the original call to it. From that point on, console behaves as a normal, permanently-defined command, exactly like any other autoloaded Tk command.

Until console is called for the first time, stdout and stderr continue to go to the terminal window unchanged from current behavior, regardless of whether Tk was loaded via wish or via tclsh with package require Tk.

The existing console.tcl (the shared script that defines the console window's own behavior - text widget, menus, bindings - used by Windows and Mac as well as this new Unix support) is not modified at all. Wherever a platform already provides console natively, this proposal has no effect whatsoever, since unknown is only ever consulted for a command that does not already exist. See the Abstract for the specific Windows and macOS configurations where no native implementation exists today, and where this proposal fills that gap the same way it does on Unix.

Differences in Behavior Across Systems

Currently on Unix, stdout/stderr always go to the terminal, and there is no console command at all. After this change, the first call to console (even a bad one, such as console foobar) triggers the one-time setup described above - a hidden console interpreter and window are created - but stdout/stderr are not yet redirected, and nothing is visible; output continues to go to the terminal exactly as before. Only an explicit console show redirects output and shows the window.

This matters more on Unix than it might elsewhere: many Unix Tk users have never had a console command available at all, and are used to wish behaving exactly like any other program run from a terminal. Keeping that behavior completely unchanged by default - until a script or user explicitly asks for a console - avoids surprising a large group of users who have no existing expectation of this feature, while still making it available to those who want it.

Once console show has been called, stdout/stderr are redirected to the console window until the window is closed (via the window's close button) or console hide is called, at which point output reverts to the terminal.

The same hidden-by-default behavior applies equally to the Windows and macOS configurations described in the Abstract where this proposal fills a gap rather than being unnecessary - a user in tclsh on Windows, or wish from a Terminal on macOS, sees no change at all unless they explicitly call console. The same is expected for tclsh with package require Tk on macOS, since the underlying native trigger checks isatty at the point Tk is loaded regardless of which binary loaded it, though this specific combination has not been directly tested.

Implementation

The following has been implemented and tested via real builds on Linux and Windows (both wish and tclsh with Tk loaded via package require Tk), and confirmed working on macOS, fixing several issues found during testing beyond the original wiki code:

Reference implementation: consolecmd.tcl and consolecmd.impl.

This has also been committed directly to the tk repository, on branch tip-561-revised.

A Note on console eval as a Semi-Public Interface

Beyond simply opening a console window, console eval gives a script direct access to the console's own widgets and to the main interpreter. This is already used in real, long-standing code - for example, colorized output via ::tk::ConsoleOutput with custom tags (note: stdout and stderr are themselves implemented as text tags), or adding extra menu items and buttons to the console window itself (see the Tcler's Wiki for examples in active use for close to 20 years).

None of this is documented as a stable interface today. The manual page itself lists tk::ConsoleOutput under a section titled "Additional Trap Calls"

As part of making console an officially supported command on Unix, it would be worth the core team also considering tk::ConsoleOutput and the small set of related internals it documents as a semi-public interface worth keeping stable going forward, given how much existing code already quietly depends on it.

The hide and show console subcommands are designed around normal Unix terminal use: hide pops the transforms, and output resumes going to the terminal exactly as it did before the console was first shown.

For cases where this is not the desired behavior — for example, if output should keep accumulating in the console's own buffer even while it is hidden — the console's toplevel can be withdrawn and deiconified directly instead, leaving the transforms in place:

console eval {wm withdraw .}   ;# hide console, but keep capturing output
console eval {wm deiconify .}  ;# show console again

As another small example of what this enables: undo/redo can be added to the console with nothing more than:

console eval {
    .console configure -undo 1
    bind Console <Control-z> {%W edit undo}
    bind Console <Control-y> {%W edit redo}
}

Neither require any core changes at all - a documentation-only addition, offered here simply as evidence of how much is already possible through this interface as it stands today.

Future Work

One case is not addressed by this proposal: a wish process started with no script argument and no real controlling terminal attached - for example, launched by double-clicking wish itself from a desktop file manager. On Windows, this case automatically opens a console. This proposal does not attempt to replicate that behavior on Unix, and leaves it as a follow-up item for future consideration.

Copyright

This document has been placed in the public domain.