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;