| PerlIO-via-EscStatus documentation | view source | Contained in the PerlIO-via-EscStatus distribution. |
PerlIO::via::EscStatus - dumb terminal status display layer
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
An EscStatus layer prints and reprints a status line using carriage returns and backspaces for a dumb terminal. This is meant as 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
using 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, though unfortunately not when the stream is closed, see BUGS below.
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 and will 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 (for
instance Term::ProgressBar does it that way), 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.)
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 for EscStatus is
in the string width calculations for utf8 multibyte sequences. Note that
changing the utf8 flag after pushing doesn't work properly (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 escape is preserved.
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 an 8
character sequence "\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.
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
to STDOUT, 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.
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.
That 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.
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 works when popped).
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
will change in the future, assuming there's sensible uses for turning it on
and off dynamically.
Term::Size version 0.2 uses PerlIO_findFILE and as of Perl 5.10.0 that
turns off the utf8 flag on the stream, preventing wide-char output.
EscStatus has a workaround for its use of Term::Size but 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 already set for wide output.
PerlIO::via, PerlIO::via::EscStatus::ShowAll, PerlIO::via::EscStatus::ShowNone, ProgressMonitor::Stringify::ToEscStatus
Copyright 2008, 2009, 2010, 2011 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/.
| PerlIO-via-EscStatus documentation | view source | Contained in the PerlIO-via-EscStatus distribution. |