NAME
PerlIO::via::EscStatus - dumb terminal status display layer
SYNOPSIS
use PerlIO::via::EscStatus qw(print_status);
binmode (STDOUT, ':via(EscStatus)') or die;
print_status ("Done 10% ...");
print_status ("Done 20% ...");
print "This is ordinary text output.\n";
print_status ("Done 90% ...");
print_status (""); # erase status
DESCRIPTION
An EscStatus layer prints and reprints a status line using carriage returns and backspaces, for a dumb terminal. This is meant for a progress or status display in a command line program.
Working ... record 20 of 80 (25%)
^--cursor left here
Status lines are communicated to EscStatus "in band" in the output stream with an escape sequence. Currently this is an ANSI "APC" application control, followed by the status line. make_status
and print_status
below produce this.
"\e_EscStatus\e\\Status string\n"
The layer clears and redraws the status when ordinary output text is printed, so it appears as normal. The status is also erased when the layer is popped (but unfortunately not when the stream is closed, see "BUGS" below).
Motivation
The idea of an output layer is that it lets you send ordinary output with plain print
, printf
, etc, and the layer takes care of what status is showing, to clear and redraw as necessary.
The alternative is a special message printing function to do the clearing. If you're in full control of your ordinary output then that's fine (and is for instance how Term::ProgressBar
does it), but if you might have parts of a library or program only setup with plain print
then a layer is a good way to keep them from making a mess of the display.
The "in-band" method of passing status strings to the layer has the advantage that higher layers can buffer or do extra transformations and everything stays in the intended sequence. It's even possible for a status stream to come from a child process through a pipe or socket and stay in the escapes form until being re-sent to a final EscStatus layer on STDOUT
.
The escape format chosen is meant to be easy to produce and tolerably readable if for some reason crunching by EscStatus is missed. The EscStatus::ShowAll
layer lets you explicitly print all status lines for development, or the EscStatus::ShowNone
layer lets you strip them for a quiet mode or batch mode operation. (See PerlIO::via::EscStatus::ShowAll and PerlIO::via::EscStatus::ShowNone.)
CHARACTERS
Each status line is truncated to the width of the terminal as determined by Term::Size::chars()
(see Term::Size). No attempt is made (as yet) to monitor SIGWINCH
for changes to the width, though the size is checked for each new line, so the next new status uses the new size.
EscStatus follows the "utf8" flag of the layer below it when first pushed, allowing extended characters to be printed. Often the layer below will be an ":encoding"
for the user's terminal. The difference in EscStatus is that utf8 multibyte sequences are recognised and the string width calculations made accordingly. However changes to the utf8 layer flag after pushing are not recognised (see "BUGS" below).
For string width calculations tabs (\t
) are 8 spaces. Various East Asian "double-width" characters take two columns. BEL (\a
), ANSI escapes, and various unicode modifier characters take no space. If a status line is truncated then all ANSI escapes are kept, so if say bold is turned on and off then the off is retained.
If a lower layer expands a character because it's unencodable on the final output then that's likely to make a mess of the width calculation. For example the :encoding
layer PERLQQ
mode turns unencodables into 8 characters "\x{1234}"
, which is more than EscStatus will have allowed for. The suggestion is to expand or transform before EscStatus so it sees what's really going to go out. (An encode and re-decode is one way to do that, though a bit wasteful.)
FUNCTIONS
print_status ($str,...)
$str = make_status ($str,...)
-
Form a status line output string by concatenating the given
$str
strings and adding the necessary escape marker sequences.print_status
prints it toSTDOUT
,make_status
returns it as a string.Any newlines in the middle of the strings are changed to spaces, since only a single line of status is possible.
OTHER NOTES
The suggestion is to push PerlIO::via::EscStatus
onto STDOUT
and leave STDERR
alone. Leaving STDERR
alone has the advantage of not putting anything in the way of an unexpected error print. You can trap "normal" errors and turn them into a print on STDOUT
, leaving STDERR
only for the unexpected. The alternative is to >&=
alias stderr onto stdout. The latter makes sense since there's only one actual destination (the terminal), once you trust EscStatus not to lose anything!
When updating the displayed status it's important not to hammer the terminal with too much output. It can easily become the speed of the terminal and not the speed of the program which is the limiting factor. The trick generally is to print a new status only say once per second. This means the display isn't perfectly up-to-date, but the only time that's a problem is if the program goes away number crunching for a long time with an old status showing, in which case the wrong processing stage gets the blame for the delay.
BUGS
When the stream is closed the status shown by EscStatus is not erased. This is because PerlIO::via
closes the sublayers first. Perhaps that can change in the future. The suggestion when closing is to either print an empty status to clear, or to pop the EscStatus (erasing when popped works).
print_status ('');
close STDOUT; # or "exit 0" or whatever
If the utf8 flag on the stream is changed (by binmode
) EscStatus doesn't notice and will keep using the state when it was first pushed. Perhaps this can change in the future (assuming there's sensible uses for turning it on and off dynamically).
As of Perl 5.10.0 an xsub using PerlIO_findFILE
like Term::Size
version 0.2 turns off the utf8
flag on the stream, preventing wide-char output. EscStatus has a workaround for its use of Term::Size
and an application might need to do the same. The symptom is the usual "Wide character in print" warning (on a stream you thought you'd setup for wide output).
SEE ALSO
PerlIO::via, PerlIO::via::EscStatus::ShowAll, PerlIO::via::EscStatus::ShowNone, ProgressMonitor::Stringify::ToEscStatus
HOME PAGE
http://www.geocities.com/user42_kevin/perlio-via-escstatus/index.html
LICENSE
Copyright 2008 Kevin Ryde
PerlIO-via-EscStatus is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.
PerlIO-via-EscStatus is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with PerlIO-via-EscStatus. If not, see http://www.gnu.org/licenses/.