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;