Perl Cookbook

Perl CookbookSearch this book
Previous: 15.11. Editing InputChapter 15
User Interfaces
Next: 15.13. Controlling Another Program with Expect
 

15.12. Managing the Screen

Problem

You want to control the screen layout or highlighting, detect when special keys are pressed, or present full-screen menus, but you don't want to think about what kind of display device the user has.

Solution

Use the Curses module from CPAN, which makes use of your native curses (3) library.

Description

The curses library provides easy access to the full screen display in an efficient and device-independent fashion. (By display, we mean any cursor-addressable monitor.) With Curses, you write high-level code to put data on the logical display, building it up character by character or string by string. When you want output to show up, call the refresh function. The library generates output consisting only of the changes on the virtual display since the last call to refresh. This is particularly appreciated on a slow connection.

The example program in Example 15.5, called rep, demonstrates this. Call it with arguments of the program to run, like any of these:

% rep ps aux
% rep netstat
% rep -2.5 lpq

The rep script will repeatedly call the listed command, printing its output to the screen, updating only what has changed since the previous run. This is most effective when the changes between runs are small. It maintains the current date in reverse video at the bottom-right corner of your screen.

By default, rep waits 10 seconds before rerunning the command. You can change this delay period by calling it an optional number of seconds (which can be a decimal number) as shown above when calling lpq. You may also hit any key during the pause for it to run the command right then.

Example 15.5: rep

#!/usr/bin/perl -w
# rep - screen repeat command
use strict;
use Curses;

my $timeout = 10;
if (@ARGV && $ARGV[0] =~ /^-(\d+\.?\d*)$/) { 
    $timeout = $1; 
    shift; 
} 

die "usage: $0 [ -timeout ] cmd args\n" unless @ARGV;
    
initscr();          # start screen
noecho();           
cbreak(); 
nodelay(1);         # so getch() is non-blocking

$SIG{INT} = sub { done("Ouch!") };
sub done { endwin(); print "@_\n"; exit; }

while (1) {
    while ((my $key = getch()) ne ERR) {    # maybe multiple keys
        done("See ya") if $key eq 'q' 
    }
    my @data = `(@ARGV) 2>&1`;              # gather output+errors
    for (my $i = 0; $i < $LINES; $i++) {
        addstr($i, 0, $data[$i] || ' ' x $COLS);
    } 

    standout();
    addstr($LINES-1, $COLS - 24, scalar localtime);
    standend();

    move(0,0); 
    refresh();                              # flush new output to display

    my ($in, $out) = ('', '');
    vec($in,fileno(STDIN),1) = 1;           # look for key on stdin 
    select($out = $in,undef,undef,$timeout);# wait up to this long
}

Curses lets you tell whether the user typed one of the arrow keys or those other funny keys, like HOME or INSERT. This is normally difficult, because those keys send multiple bytes. With Curses, it's easy:

keypad(1);                  # enable keypad mode
$key = getch();
if ($key eq 'k'     ||      # vi mode
    $key eq "\cP"   ||      # emacs mode
    $key eq KEY_UP)         # arrow mode
{
    # do something
} 

Other Curses functions let you read the text at particular screen coordinates, control highlighting and standout mode, and even manage multiple windows.

The perlmenu module, also from CPAN, is built on top of the lower-level Curses module. It provides high-level access to menus and fill-out forms. Here's a sample form from the perlmenu distribution:

                      Template Entry Demonstration 

   Address Data Example                                     Record # ___

   Name: [________________________________________________]
   Addr: [________________________________________________]
   City: [__________________]          State: [__]       Zip: [\\\\\] 

   Phone: (\\\) \\\-\\\\                            Password: [^^^^^^^^]

   Enter all information available.
   Edit fields with left/right arrow keys or "delete".
   Switch fields with "Tab" or up/down arrow keys.
   Indicate completion by pressing "Return".
   Refresh screen with "Control-L".
   Abort this demo here with "Control-X".

The user types in the areas indicated, with regular text indicated by underline fields, numeric data by backslashed fields, and starred-out data with circumflexed fields. This is reminiscent of Perl's formats, except that forms are used for output, not input.

See Also

Your system's curses (3) manpage (if you have it); the documentation for the Curses and the perlmenu modules from CPAN; the section on "Formats" in Chapter 2 of Programming Perl, or perlform (1); Recipe 3.10


Previous: 15.11. Editing InputPerl CookbookNext: 15.13. Controlling Another Program with Expect
15.11. Editing InputBook Index15.13. Controlling Another Program with Expect