Recipe 15.8. Using POSIX termios
Author: Tom Christiansen & Nathan Torkington
Date: 1999-07-02T01:43:21Z

#

| Chapter 15 User Interfaces |

## 15.8. Using POSIX termios

### Problem

You'd like to manipulate your terminal characteristics directly.

### Solution

Use the POSIX `termios` interface.

### 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;

### See Also

POSIX Programmer's Guide, by Donald Lewine; O'Reilly & Associates (1991); the documentation for the standard POSIX module, also in Chapter 7 of Programming Perl; Recipe 15.6; Recipe 15.9

15.7. Ringing the Terminal Bell |  | 15.9. Checking for Waiting Input