Managing the Screen (Perl Cookbook, 2nd Edition)

## 15.12. Managing the Screen

### 15.12.1. 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.

### 15.12.2. Solution

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

### 15.12.3. 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 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 in the previous example 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.

### 15.12.4. See Also

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

Copyright © 2003 O'Reilly & Associates. All rights reserved.