How do I read just one key without waiting for a return key?

Controlling input buffering is a remarkably system-dependent matter. On many systems, you can just use the stty command as shown in perlfunc/getc, but as you see, that's already getting you into portability snags.

    open(TTY, "+</dev/tty") or die "no tty: $!";
    system "stty  cbreak </dev/tty >/dev/tty 2>&1";
    $key = getc(TTY);		# perhaps this works
    # OR ELSE
    sysread(TTY, $key, 1);	# probably this does
    system "stty -cbreak </dev/tty >/dev/tty 2>&1";
The Term::ReadKey module from CPAN offers an easy-to-use interface that should be more efficient than shelling out to stty for each key. It even includes limited support for Windows.

    use Term::ReadKey;
    ReadMode('cbreak');
    $key = ReadKey(0);
    ReadMode('normal');
However, using the code requires that you have a working C compiler and can use it to build and install a CPAN module. Here's a solution using the standard POSIX module, which is already on your systems (assuming your system supports POSIX).

    use HotKey;
    $key = readkey();
And here's the HotKey module, which hides the somewhat mystifying calls to manipulate the POSIX termios structures.

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

Back to perlfaq8