Using POSIX termios (Perl Cookbook, 2nd Edition)

## 15.8. Using POSIX termios

### 15.8.1. Problem

You'd like to manipulate your terminal characteristics directly.

### 15.8.2. Solution

Use the POSIX `termios` interface.

### 15.8.3. Description

Think of everything you can do with the stty command—you can set everything from special characters to flow control and carriage-return mapping. The standard POSIX module provides direct access to the low-level terminal interface to implement stty-like capabilities in your program.

Example 15-2 finds what your tty's erase and kill characters are (probably backspace and Ctrl-U). Then it sets them back to their original values out of antiquity, `#` and `@`, and has you type something. It restores them when done.

#### Example 15-2. demo POSIX termios

>       #!/usr/bin/perl -w
>       # demo POSIX termios

>       use POSIX qw(:termios_h);

>       $term = POSIX::Termios->new;
>       $term->getattr(fileno(STDIN));

>       $erase = $term->getcc(VERASE);
>       $kill = $term->getcc(VKILL);
>       printf "Erase is character %d, %s\n", $erase, uncontrol(chr($erase));
>       printf "Kill is character %d, %s\n", $kill, uncontrol(chr($kill));

>       $term->setcc(VERASE, ord('#'));
>       $term->setcc(VKILL, ord('@'));
>       $term->setattr(1, TCSANOW);

>       print("erase is #, kill is @; type something: ");
>       $line = <STDIN>;
>       print "You typed: $line";

>       $term->setcc(VERASE, $erase);
>       $term->setcc(VKILL, $kill);
>       $term->setattr(1, TCSANOW);

>       sub uncontrol {

>           local $_ = shift;
>           s/([\200-\377])/sprintf("M-%c",ord($1) & 0177)/eg;
>           s/([\0-\37\177])/sprintf("^%c",ord($1) ^ 0100)/eg;
>           return $_;
>       }

Here's a module called HotKey that implements a `readkey` function in pure Perl. It doesn't provide any benefit over Term::ReadKey, but it shows POSIX termios in action:

>     # HotKey.pm
>     package HotKey;

>     @ISA = qw(Exporter);
>     @EXPORT = qw(cbreak cooked readkey);

>     use strict;
>     use POSIX qw(:termios_h);
>     my ($term, $oterm, $echo, $noecho, $fd_stdin);

>     $fd_stdin = fileno(STDIN);
>     $term     = POSIX::Termios->new( );
>     $term->getattr($fd_stdin);
>     $oterm    = $term->getlflag( );

>     $echo     = ECHO | ECHOK | ICANON;
>     $noecho   = $oterm & ~$echo;

>     sub cbreak {
>         $term->setlflag($noecho);  # ok, so i don't want echo either
>         $term->setcc(VTIME, 1);
>         $term->setattr($fd_stdin, TCSANOW);
>     }

>     sub cooked {
>         $term->setlflag($oterm);
>         $term->setcc(VTIME, 0);
>         $term->setattr($fd_stdin, TCSANOW);
>     }

>     sub readkey {
>         my $key = '';
>         cbreak( );
>         sysread(STDIN, $key, 1);
>         cooked( );
>         return $key;
>     }

>     END { cooked( ) }

>     1;

### 15.8.4. See Also

POSIX Programmer's Guide, by Donald Lewine; the documentation for the standard POSIX module, also in Chapter 32 of Programming Perl; Recipe 15.6; Recipe 15.9

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